diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index 1d86e8a9ee..3e00341027 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -169,6 +169,10 @@ D0E3A7881B28AE9C00A402D9 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A7871B28AE9C00A402D9 /* Coding.swift */; }; D0E3A79E1B28B50400A402D9 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A79D1B28B50400A402D9 /* Message.swift */; }; D0E3A7A21B28B7DC00A402D9 /* Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A7A11B28B7DC00A402D9 /* Media.swift */; }; + D0F3CC721DDE1CDC008148FA /* ItemCacheTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3CC711DDE1CDC008148FA /* ItemCacheTable.swift */; }; + D0F3CC741DDE1EB9008148FA /* ItemCacheMetaTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3CC731DDE1EB9008148FA /* ItemCacheMetaTable.swift */; }; + D0F3CC751DDE2845008148FA /* ItemCacheMetaTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3CC731DDE1EB9008148FA /* ItemCacheMetaTable.swift */; }; + D0F3CC761DDE2845008148FA /* ItemCacheTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3CC711DDE1CDC008148FA /* ItemCacheTable.swift */; }; D0F7AB321DCFAB18009AD9A1 /* PeerChatTopTaggedMessageIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F7AB311DCFAB18009AD9A1 /* PeerChatTopTaggedMessageIds.swift */; }; D0F7AB331DCFAB1C009AD9A1 /* PeerChatTopTaggedMessageIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F7AB311DCFAB18009AD9A1 /* PeerChatTopTaggedMessageIds.swift */; }; D0F9E85B1C565EBB00037222 /* MessageMediaTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F9E85A1C565EBB00037222 /* MessageMediaTable.swift */; }; @@ -284,6 +288,8 @@ D0E3A7871B28AE9C00A402D9 /* Coding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = ""; }; D0E3A79D1B28B50400A402D9 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; D0E3A7A11B28B7DC00A402D9 /* Media.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Media.swift; sourceTree = ""; }; + D0F3CC711DDE1CDC008148FA /* ItemCacheTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCacheTable.swift; sourceTree = ""; }; + D0F3CC731DDE1EB9008148FA /* ItemCacheMetaTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCacheMetaTable.swift; sourceTree = ""; }; D0F7AB311DCFAB18009AD9A1 /* PeerChatTopTaggedMessageIds.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerChatTopTaggedMessageIds.swift; sourceTree = ""; }; D0F9E85A1C565EBB00037222 /* MessageMediaTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageMediaTable.swift; sourceTree = ""; }; D0F9E8601C57766A00037222 /* MessageHistoryTableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryTableTests.swift; sourceTree = ""; }; @@ -415,6 +421,8 @@ D021E0D71DB4FD1300C6B04F /* ItemCollectionItemTable.swift */, D07CFF821DCA909100761F81 /* PeerChatInterfaceStateTable.swift */, D0F7AB311DCFAB18009AD9A1 /* PeerChatTopTaggedMessageIds.swift */, + D0F3CC731DDE1EB9008148FA /* ItemCacheMetaTable.swift */, + D0F3CC711DDE1CDC008148FA /* ItemCacheTable.swift */, ); name = Tables; sourceTree = ""; @@ -748,6 +756,7 @@ D073CE861DCBF3BB007511FD /* MessageHistoryMetadataTable.swift in Sources */, D0B418561D7DFE29004562A4 /* PostboxClientId.swift in Sources */, D073CE781DCBF3B4007511FD /* MessageHistoryHole.swift in Sources */, + D0F3CC751DDE2845008148FA /* ItemCacheMetaTable.swift in Sources */, D0B418501D7DFE20004562A4 /* UnsentMessageIndicesView.swift in Sources */, D073CE891DCBF3BB007511FD /* MessageHistoryOperation.swift in Sources */, D073CE7F1DCBF3B4007511FD /* ItemCollection.swift in Sources */, @@ -796,6 +805,7 @@ D0B4184B1D7DFE20004562A4 /* SynchronizePeerReadStatesView.swift in Sources */, D073CE991DCBF3BB007511FD /* MessageHistoryInvalidatedReadStateTable.swift in Sources */, D073CE981DCBF3BB007511FD /* MessageHistoryReadStateTable.swift in Sources */, + D0F3CC761DDE2845008148FA /* ItemCacheTable.swift in Sources */, D073CE8D1DCBF3BB007511FD /* ChatListIndexTable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -805,6 +815,7 @@ buildActionMask = 2147483647; files = ( D0F9E8631C579F0200037222 /* MediaCleanupTable.swift in Sources */, + D0F3CC741DDE1EB9008148FA /* ItemCacheMetaTable.swift in Sources */, D08D451F1D5D2CA700A7428A /* RatingTable.swift in Sources */, D07CFF831DCA909100761F81 /* PeerChatInterfaceStateTable.swift in Sources */, D0F9E86F1C5A0E7600037222 /* KeychainTable.swift in Sources */, @@ -858,6 +869,7 @@ D033A6F71C73D512006A2EAB /* MessageHistoryUnsentTable.swift in Sources */, D0D511021D64D73D00A97B8A /* IpcNotifier.mm in Sources */, D0B844511DAC04FE005F29E1 /* PeerPresence.swift in Sources */, + D0F3CC721DDE1CDC008148FA /* ItemCacheTable.swift in Sources */, D08C713C1C51283C00779C0F /* MessageHistoryIndexTable.swift in Sources */, D0F9E86D1C5A0E5D00037222 /* MetadataTable.swift in Sources */, D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */, diff --git a/Postbox/CachedPeerDataTable.swift b/Postbox/CachedPeerDataTable.swift index d3db7014f8..2b5027f67d 100644 --- a/Postbox/CachedPeerDataTable.swift +++ b/Postbox/CachedPeerDataTable.swift @@ -1,14 +1,18 @@ import Foundation final class CachedPeerDataTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private let sharedEncoder = Encoder() private let sharedKey = ValueBoxKey(length: 8) private var cachedDatas: [PeerId: CachedPeerData] = [:] private var updatedPeerIds = Set() - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + override init(valueBox: ValueBox, table: ValueBoxTable) { + super.init(valueBox: valueBox, table: table) } private func key(_ id: PeerId) -> ValueBoxKey { @@ -25,7 +29,7 @@ final class CachedPeerDataTable: Table { if let status = self.cachedDatas[id] { return status } - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { if let data = Decoder(buffer: value).decodeRootObject() as? CachedPeerData { self.cachedDatas[id] = data return data @@ -45,7 +49,7 @@ final class CachedPeerDataTable: Table { self.sharedEncoder.reset() self.sharedEncoder.encodeRootObject(data) - self.valueBox.set(self.tableId, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) } } diff --git a/Postbox/ChatListIndexTable.swift b/Postbox/ChatListIndexTable.swift index 15a3539f46..c99cbfbeba 100644 --- a/Postbox/ChatListIndexTable.swift +++ b/Postbox/ChatListIndexTable.swift @@ -1,8 +1,8 @@ import Foundation final class ChatListIndexTable: Table { - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) } private func key(_ peerId: PeerId) -> ValueBoxKey { @@ -19,15 +19,15 @@ final class ChatListIndexTable: Table { writeBuffer.write(&idNamespace, offset: 0, length: 4) writeBuffer.write(&idId, offset: 0, length: 4) writeBuffer.write(&idTimestamp, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(index.id.peerId), value: writeBuffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(index.id.peerId), value: writeBuffer.readBufferNoCopy()) } func remove(_ peerId: PeerId) { - self.valueBox.remove(self.tableId, key: self.key(peerId)) + self.valueBox.remove(self.table, key: self.key(peerId)) } func get(_ peerId: PeerId) -> MessageIndex? { - if let value = self.valueBox.get(self.tableId, key: self.key(peerId)) { + if let value = self.valueBox.get(self.table, key: self.key(peerId)) { var idNamespace: Int32 = 0 var idId: Int32 = 0 var idTimestamp: Int32 = 0 diff --git a/Postbox/ChatListTable.swift b/Postbox/ChatListTable.swift index 01a99a49bf..be9d38bd7e 100644 --- a/Postbox/ChatListTable.swift +++ b/Postbox/ChatListTable.swift @@ -31,17 +31,21 @@ private enum ChatListEntryType: Int8 { } final class ChatListTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + let indexTable: ChatListIndexTable let emptyMemoryBuffer = MemoryBuffer() let metadataTable: MessageHistoryMetadataTable let seedConfiguration: SeedConfiguration - init(valueBox: ValueBox, tableId: Int32, indexTable: ChatListIndexTable, metadataTable: MessageHistoryMetadataTable, seedConfiguration: SeedConfiguration) { + init(valueBox: ValueBox, table: ValueBoxTable, indexTable: ChatListIndexTable, metadataTable: MessageHistoryMetadataTable, seedConfiguration: SeedConfiguration) { self.indexTable = indexTable self.metadataTable = metadataTable self.seedConfiguration = seedConfiguration - super.init(valueBox: valueBox, tableId: tableId) + super.init(valueBox: valueBox, table: table) } private func key(_ index: MessageIndex, type: ChatListEntryType) -> ValueBoxKey { @@ -119,7 +123,7 @@ final class ChatListTable: Table { func addHole(_ hole: ChatListHole, operations: inout [ChatListOperation]) { self.ensureInitialized() - if self.valueBox.get(self.tableId, key: self.key(hole.index, type: .Hole)) == nil { + if self.valueBox.get(self.table, key: self.key(hole.index, type: .Hole)) == nil { self.justInsertHole(hole) operations.append(.InsertHole(hole)) } @@ -128,7 +132,7 @@ final class ChatListTable: Table { func replaceHole(_ index: MessageIndex, hole: ChatListHole?, operations: inout [ChatListOperation]) { self.ensureInitialized() - if self.valueBox.get(self.tableId, key: self.key(index, type: .Hole)) != nil { + if self.valueBox.get(self.table, key: self.key(index, type: .Hole)) != nil { if let hole = hole { if hole.index != index { self.justRemoveHole(index) @@ -144,19 +148,19 @@ final class ChatListTable: Table { } private func justInsertMessage(_ index: MessageIndex) { - self.valueBox.set(self.tableId, key: self.key(index, type: .Message), value: self.emptyMemoryBuffer) + self.valueBox.set(self.table, key: self.key(index, type: .Message), value: self.emptyMemoryBuffer) } private func justRemoveMessage(_ index: MessageIndex) { - self.valueBox.remove(self.tableId, key: self.key(index, type: .Message)) + self.valueBox.remove(self.table, key: self.key(index, type: .Message)) } private func justInsertHole(_ hole: ChatListHole) { - self.valueBox.set(self.tableId, key: self.key(hole.index, type: .Hole), value: self.emptyMemoryBuffer) + self.valueBox.set(self.table, key: self.key(hole.index, type: .Hole), value: self.emptyMemoryBuffer) } private func justRemoveHole(_ index: MessageIndex) { - self.valueBox.remove(self.tableId, key: self.key(index, type: .Hole)) + self.valueBox.remove(self.table, key: self.key(index, type: .Hole)) } func entriesAround(_ index: MessageIndex, messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, count: Int) -> (entries: [ChatListIntermediateEntry], lower: ChatListIntermediateEntry?, upper: ChatListIntermediateEntry?) { @@ -167,7 +171,7 @@ final class ChatListTable: Table { var lower: ChatListIntermediateEntry? var upper: ChatListIntermediateEntry? - self.valueBox.range(self.tableId, start: self.key(index, type: .Message), end: self.lowerBound(), keys: { key in + self.valueBox.range(self.table, start: self.key(index, type: .Message), end: self.lowerBound(), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(4 + 4 + 4)), namespace: key.getInt32(4), id: key.getInt32(4 + 4)), timestamp: key.getInt32(0)) let type: Int8 = key.getInt8(4 + 4 + 4 + 8) if type == ChatListEntryType.Message.rawValue { @@ -186,7 +190,7 @@ final class ChatListTable: Table { lowerEntries.removeLast() } - self.valueBox.range(self.tableId, start: self.key(index, type: .Message).predecessor, end: self.upperBound(), keys: { key in + self.valueBox.range(self.table, start: self.key(index, type: .Message).predecessor, end: self.upperBound(), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(4 + 4 + 4)), namespace: key.getInt32(4), id: key.getInt32(4 + 4)), timestamp: key.getInt32(0)) let type: Int8 = key.getInt8(4 + 4 + 4 + 8) if type == ChatListEntryType.Message.rawValue { @@ -214,7 +218,7 @@ final class ChatListTable: Table { case .Hole: startEntryType = .Hole } - self.valueBox.range(self.tableId, start: self.key(lowerEntries.last!.index, type: startEntryType), end: self.lowerBound(), keys: { key in + self.valueBox.range(self.table, start: self.key(lowerEntries.last!.index, type: startEntryType), end: self.lowerBound(), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(4 + 4 + 4)), namespace: key.getInt32(4), id: key.getInt32(4 + 4)), timestamp: key.getInt32(0)) let type: Int8 = key.getInt8(4 + 4 + 4 + 8) if type == ChatListEntryType.Message.rawValue { @@ -252,7 +256,7 @@ final class ChatListTable: Table { key = self.upperBound() } - self.valueBox.range(self.tableId, start: key, end: self.lowerBound(), keys: { key in + self.valueBox.range(self.table, start: key, end: self.lowerBound(), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(4 + 4 + 4)), namespace: key.getInt32(4), id: key.getInt32(4 + 4)), timestamp: key.getInt32(0)) let type: Int8 = key.getInt8(4 + 4 + 4 + 8) if type == ChatListEntryType.Message.rawValue { @@ -280,7 +284,7 @@ final class ChatListTable: Table { key = self.lowerBound() } - self.valueBox.range(self.tableId, start: key, end: self.upperBound(), keys: { key in + self.valueBox.range(self.table, start: key, end: self.upperBound(), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(4 + 4 + 4)), namespace: key.getInt32(4), id: key.getInt32(4 + 4)), timestamp: key.getInt32(0)) let type: Int8 = key.getInt8(4 + 4 + 4 + 8) if type == ChatListEntryType.Message.rawValue { diff --git a/Postbox/ChatListView.swift b/Postbox/ChatListView.swift index 6cfb85387d..b47cf6b3f4 100644 --- a/Postbox/ChatListView.swift +++ b/Postbox/ChatListView.swift @@ -28,6 +28,9 @@ public func ==(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool { if lhsReadState != rhsReadState { return false } + if lhsMessage.stableVersion != rhsMessage.stableVersion { + return false + } if let lhsSettings = lhsSettings, let rhsSettings = rhsSettings { if !lhsSettings.isEqual(to: rhsSettings) { return false diff --git a/Postbox/ContactTable.swift b/Postbox/ContactTable.swift index 6ac7f1f901..0019e40e78 100644 --- a/Postbox/ContactTable.swift +++ b/Postbox/ContactTable.swift @@ -1,6 +1,10 @@ import Foundation final class ContactTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private var originalPeerIds: Set? private var peerIds: Set? @@ -25,7 +29,7 @@ final class ContactTable: Table { return peerIds.contains(peerId) } else { var peerIds = Set() - self.valueBox.range(self.tableId, start: self.lowerBound(), end: self.upperBound(), keys: { key in + self.valueBox.range(self.table, start: self.lowerBound(), end: self.upperBound(), keys: { key in peerIds.insert(PeerId(key.getInt64(0))) return true }, limit: 0) @@ -40,7 +44,7 @@ final class ContactTable: Table { return peerIds } else { var peerIds = Set() - self.valueBox.range(self.tableId, start: self.lowerBound(), end: self.upperBound(), keys: { key in + self.valueBox.range(self.table, start: self.lowerBound(), end: self.upperBound(), keys: { key in peerIds.insert(PeerId(key.getInt64(0))) return true }, limit: 0) @@ -78,11 +82,11 @@ final class ContactTable: Table { let sharedKey = self.key(PeerId(namespace: 0, id: 0)) for peerId in self.removedPeerIds { - self.valueBox.remove(self.tableId, key: self.key(peerId, sharedKey: sharedKey)) + self.valueBox.remove(self.table, key: self.key(peerId, sharedKey: sharedKey)) } for peerId in self.addedPeerIds { - self.valueBox.set(self.tableId, key: self.key(peerId, sharedKey: sharedKey), value: MemoryBuffer()) + self.valueBox.set(self.table, key: self.key(peerId, sharedKey: sharedKey), value: MemoryBuffer()) } self.originalPeerIds = self.peerIds diff --git a/Postbox/GlobalMessageIdsTable.swift b/Postbox/GlobalMessageIdsTable.swift index 27063a8471..f07248be1c 100644 --- a/Postbox/GlobalMessageIdsTable.swift +++ b/Postbox/GlobalMessageIdsTable.swift @@ -1,19 +1,23 @@ import Foundation final class GlobalMessageIdsTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + let namespace: Int32 - let sharedKey = ValueBoxKey(length: 4) + let sharedKey = ValueBoxKey(length: 8) let sharedBuffer = WriteBuffer() - init(valueBox: ValueBox, tableId: Int32, namespace: Int32) { + init(valueBox: ValueBox, table: ValueBoxTable, namespace: Int32) { self.namespace = namespace - super.init(valueBox: valueBox, tableId: tableId) + super.init(valueBox: valueBox, table: table) } private func key(_ id: Int32) -> ValueBoxKey { - self.sharedKey.setInt32(0, value: id) + self.sharedKey.setInt64(0, value: Int64(id)) return self.sharedKey } @@ -23,11 +27,11 @@ final class GlobalMessageIdsTable: Table { var idNamespace: Int32 = id.namespace self.sharedBuffer.write(&idPeerId, offset: 0, length: 8) self.sharedBuffer.write(&idNamespace, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(globalId), value: self.sharedBuffer) + self.valueBox.set(self.table, key: self.key(globalId), value: self.sharedBuffer) } func get(_ globalId: Int32) -> MessageId? { - if let value = self.valueBox.get(self.tableId, key: self.key(globalId)) { + if let value = self.valueBox.get(self.table, key: self.key(globalId)) { var idPeerId: Int64 = 0 var idNamespace: Int32 = 0 value.read(&idPeerId, offset: 0, length: 8) @@ -38,6 +42,6 @@ final class GlobalMessageIdsTable: Table { } func remove(_ globalId: Int32) { - self.valueBox.remove(self.tableId, key: self.key(globalId)) + self.valueBox.remove(self.table, key: self.key(globalId)) } } diff --git a/Postbox/ItemCacheMetaTable.swift b/Postbox/ItemCacheMetaTable.swift new file mode 100644 index 0000000000..f4d3c6573f --- /dev/null +++ b/Postbox/ItemCacheMetaTable.swift @@ -0,0 +1,80 @@ +import Foundation + +public typealias ItemCacheCollectionId = Int8 + +public struct ItemCacheCollectionSpec { + public let lowWaterItemCount: Int32 + public let highWaterItemCount: Int32 + + public init(lowWaterItemCount: Int32, highWaterItemCount: Int32) { + self.lowWaterItemCount = lowWaterItemCount + self.highWaterItemCount = highWaterItemCount + } +} + +struct ItemCacheCollectionState: Coding { + let nextAccessIndex: Int32 + + init(decoder: Decoder) { + self.nextAccessIndex = decoder.decodeInt32ForKey("i") + } + + func encode(_ encoder: Encoder) { + encoder.encodeInt32(self.nextAccessIndex, forKey: "i") + } +} + +final class ItemCacheMetaTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + + private let sharedKey = ValueBoxKey(length: 8) + + private var cachedCollectionStates: [ItemCacheCollectionId: ItemCacheCollectionState] = [:] + private var updatedCollectionStateIds = Set() + + private func key(_ id: ItemCacheCollectionId) -> ValueBoxKey { + self.sharedKey.setInt64(0, value: Int64(id)) + return self.sharedKey + } + + private func get(_ id: ItemCacheCollectionId) -> ItemCacheCollectionState? { + if let cached = self.cachedCollectionStates[id] { + return cached + } else { + if let value = self.valueBox.get(self.table, key: self.key(id)), let state = Decoder(buffer: value).decodeRootObject() as? ItemCacheCollectionState { + self.cachedCollectionStates[id] = state + return state + } else { + return nil + } + } + } + + private func set(_ id: ItemCacheCollectionId, state: ItemCacheCollectionState) { + self.cachedCollectionStates[id] = state + self.updatedCollectionStateIds.insert(id) + } + + override func clearMemoryCache() { + self.cachedCollectionStates.removeAll() + self.updatedCollectionStateIds.removeAll() + } + + override func beforeCommit() { + if !self.updatedCollectionStateIds.isEmpty { + let sharedEncoder = Encoder() + for id in self.updatedCollectionStateIds { + if let state = self.cachedCollectionStates[id] { + sharedEncoder.reset() + sharedEncoder.encodeRootObject(state) + self.valueBox.set(self.table, key: self.key(id), value: sharedEncoder.readBufferNoCopy()) + } + } + } + + self.updatedCollectionStateIds.removeAll() + } +} + diff --git a/Postbox/ItemCacheTable.swift b/Postbox/ItemCacheTable.swift new file mode 100644 index 0000000000..9b6b1f2aa5 --- /dev/null +++ b/Postbox/ItemCacheTable.swift @@ -0,0 +1,68 @@ +import Foundation + +public final class ItemCacheEntryId { + public let collectionId: ItemCacheCollectionId + public let key: ValueBoxKey + + public init(collectionId: ItemCacheCollectionId, key: ValueBoxKey) { + self.collectionId = collectionId + self.key = key + } +} + +private enum ItemCacheSection: Int8 { + case items = 0 + case accessIndexToItemId = 1 + case itemIdToAccessIndex = 2 +} + +final class ItemCacheTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + + private func itemKey(id: ItemCacheEntryId) -> ValueBoxKey { + let key = ValueBoxKey(length: 1 + 1 + id.key.length) + key.setInt8(0, value: ItemCacheSection.items.rawValue) + key.setInt8(1, value: id.collectionId) + memcpy(key.memory.advanced(by: 2), id.key.memory, id.key.length) + return key + } + + private func itemIdToAccessIndexKey(id: ItemCacheEntryId) -> ValueBoxKey { + let key = ValueBoxKey(length: 1 + 1 + id.key.length) + key.setInt8(0, value: ItemCacheSection.accessIndexToItemId.rawValue) + key.setInt8(1, value: id.collectionId) + memcpy(key.memory.advanced(by: 2), id.key.memory, id.key.length) + return key + } + + private func accessIndexToItemId(collectionId: ItemCacheCollectionId, index: Int32) -> ValueBoxKey { + let key = ValueBoxKey(length: 1 + 1 + 4) + key.setInt8(0, value: ItemCacheSection.accessIndexToItemId.rawValue) + key.setInt8(1, value: collectionId) + key.setInt32(2, value: index) + return key + } + + func put(id: ItemCacheEntryId, entry: Coding, metaTable: ItemCacheMetaTable) { + let encoder = Encoder() + encoder.encodeRootObject(entry) + self.valueBox.set(self.table, key: self.itemKey(id: id), value: encoder.readBufferNoCopy()) + } + + func retrieve(id: ItemCacheEntryId, metaTable: ItemCacheMetaTable) -> Coding? { + if let value = self.valueBox.get(self.table, key: self.itemKey(id: id)), let entry = Decoder(buffer: value).decodeRootObject() { + return entry + } + return nil + } + + override func clearMemoryCache() { + + } + + override func beforeCommit() { + + } +} diff --git a/Postbox/ItemCollectionInfoTable.swift b/Postbox/ItemCollectionInfoTable.swift index 726d36e2bb..d41894589c 100644 --- a/Postbox/ItemCollectionInfoTable.swift +++ b/Postbox/ItemCollectionInfoTable.swift @@ -1,6 +1,10 @@ import Foundation final class ItemCollectionInfoTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + private let sharedKey = ValueBoxKey(length: 4 + 4 + 8) private var cachedInfos: [ItemCollectionId.Namespace: [(Int, ItemCollectionId, ItemCollectionInfo)]] = [:] @@ -29,7 +33,7 @@ final class ItemCollectionInfoTable: Table { return cachedInfo } else { var infos: [(Int, ItemCollectionId, ItemCollectionInfo)] = [] - self.valueBox.range(self.tableId, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), values: { key, value in + self.valueBox.range(self.table, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), values: { key, value in if let info = Decoder(buffer: value).decodeRootObject() as? ItemCollectionInfo { infos.append((Int(key.getInt32(4)), ItemCollectionId(namespace: namespace, id: key.getInt64(4 + 4)), info)) } @@ -45,7 +49,7 @@ final class ItemCollectionInfoTable: Table { var currentKey = self.key(collectionId: collectionId, index: index) while true { var resultCollectionIdAndIndex: (ItemCollectionId, Int32)? - self.valueBox.range(self.tableId, start: currentKey, end: self.lowerBound(namespace: currentNamespace), keys: { key in + self.valueBox.range(self.table, start: currentKey, end: self.lowerBound(namespace: currentNamespace), keys: { key in resultCollectionIdAndIndex = (ItemCollectionId(namespace: currentNamespace, id: key.getInt64(4 + 4)), key.getInt32(4)) return true }, limit: 1) @@ -68,7 +72,7 @@ final class ItemCollectionInfoTable: Table { var currentKey = self.key(collectionId: collectionId, index: index) while true { var resultCollectionIdAndIndex: (ItemCollectionId, Int32)? - self.valueBox.range(self.tableId, start: currentKey, end: self.upperBound(namespace: currentNamespace), keys: { key in + self.valueBox.range(self.table, start: currentKey, end: self.upperBound(namespace: currentNamespace), keys: { key in resultCollectionIdAndIndex = (ItemCollectionId(namespace: currentNamespace, id: key.getInt64(4 + 4)), key.getInt32(4)) return true }, limit: 1) @@ -90,13 +94,13 @@ final class ItemCollectionInfoTable: Table { self.cachedInfos.removeAll() var currentCollectionKeys: [ValueBoxKey] = [] - self.valueBox.range(self.tableId, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), keys: { key in + self.valueBox.range(self.table, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), keys: { key in currentCollectionKeys.append(key) return true }, limit: 0) for key in currentCollectionKeys { - self.valueBox.remove(self.tableId, key: key) + self.valueBox.remove(self.table, key: key) } var index: Int32 = 0 @@ -104,7 +108,7 @@ final class ItemCollectionInfoTable: Table { for (id, info) in infos { sharedEncoder.reset() sharedEncoder.encodeRootObject(info) - self.valueBox.set(self.tableId, key: self.key(collectionId: id, index: index), value: sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(collectionId: id, index: index), value: sharedEncoder.readBufferNoCopy()) index += 1 } } diff --git a/Postbox/ItemCollectionItemTable.swift b/Postbox/ItemCollectionItemTable.swift index ce96f5380c..6245739168 100644 --- a/Postbox/ItemCollectionItemTable.swift +++ b/Postbox/ItemCollectionItemTable.swift @@ -6,6 +6,10 @@ enum ItemCollectionOperation { } final class ItemCollectionItemTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + private let sharedKey = ValueBoxKey(length: 4 + 8 + 4 + 8) private func key(collectionId: ItemCollectionId, index: ItemCollectionItemIndex) -> ValueBoxKey { @@ -44,7 +48,7 @@ final class ItemCollectionItemTable: Table { func lowerItems(collectionId: ItemCollectionId, itemIndex: ItemCollectionItemIndex, count: Int) -> [ItemCollectionItem] { var items: [ItemCollectionItem] = [] - self.valueBox.range(self.tableId, start: self.key(collectionId: collectionId, index: itemIndex), end: self.lowerBound(collectionId: collectionId), values: { _, value in + self.valueBox.range(self.table, start: self.key(collectionId: collectionId, index: itemIndex), end: self.lowerBound(collectionId: collectionId), values: { _, value in if let item = Decoder(buffer: value).decodeRootObject() as? ItemCollectionItem { items.append(item) } @@ -55,7 +59,7 @@ final class ItemCollectionItemTable: Table { func higherItems(collectionId: ItemCollectionId, itemIndex: ItemCollectionItemIndex, count: Int) -> [ItemCollectionItem] { var items: [ItemCollectionItem] = [] - self.valueBox.range(self.tableId, start: self.key(collectionId: collectionId, index: itemIndex), end: self.upperBound(collectionId: collectionId), values: { _, value in + self.valueBox.range(self.table, start: self.key(collectionId: collectionId, index: itemIndex), end: self.upperBound(collectionId: collectionId), values: { _, value in if let item = Decoder(buffer: value).decodeRootObject() as? ItemCollectionItem { items.append(item) } @@ -66,7 +70,7 @@ final class ItemCollectionItemTable: Table { func getSummaryIndices(namespace: ItemCollectionId.Namespace) -> [ItemCollectionId: [ItemCollectionItemIndex]] { var summaryIndices: [ItemCollectionId: [ItemCollectionItemIndex]] = [:] - self.valueBox.range(self.tableId, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), keys: { key in + self.valueBox.range(self.table, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), keys: { key in let collectionId = ItemCollectionId(namespace: namespace, id: key.getInt64(4)) let itemIndex = ItemCollectionItemIndex(index: key.getInt32(4 + 8), id: key.getInt64(4 + 8 + 4)) if summaryIndices[collectionId] != nil { @@ -81,7 +85,7 @@ final class ItemCollectionItemTable: Table { func getItems(namespace: ItemCollectionId.Namespace) -> [ItemCollectionId: [ItemCollectionItem]] { var items: [ItemCollectionId: [ItemCollectionItem]] = [:] - self.valueBox.range(self.tableId, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), values: { key, value in + self.valueBox.range(self.table, start: self.lowerBound(namespace: namespace), end: self.upperBound(namespace: namespace), values: { key, value in let collectionId = ItemCollectionId(namespace: namespace, id: key.getInt64(4)) //let itemIndex = ItemCollectionItemIndex(index: key.getInt32(4 + 8), id: key.getInt64(4 + 8 + 4)) if let item = Decoder(buffer: value).decodeRootObject() as? ItemCollectionItem { @@ -98,7 +102,7 @@ final class ItemCollectionItemTable: Table { func replaceItems(collectionId: ItemCollectionId, items: [ItemCollectionItem]) { var currentIndices = Set() - self.valueBox.range(self.tableId, start: self.lowerBound(collectionId: collectionId), end: self.upperBound(collectionId: collectionId), keys: { key in + self.valueBox.range(self.table, start: self.lowerBound(collectionId: collectionId), end: self.upperBound(collectionId: collectionId), keys: { key in let itemIndex = ItemCollectionItemIndex(index: key.getInt32(4 + 8), id: key.getInt64(4 + 8 + 4)) currentIndices.insert(itemIndex) return true @@ -114,7 +118,7 @@ final class ItemCollectionItemTable: Table { let removedIndices = currentIndices.subtracting(updatedIndices) for index in removedIndices { - self.valueBox.remove(self.tableId, key: self.key(collectionId: collectionId, index: index)) + self.valueBox.remove(self.table, key: self.key(collectionId: collectionId, index: index)) } let sharedEncoder = Encoder() @@ -122,7 +126,7 @@ final class ItemCollectionItemTable: Table { let item = itemByIndex[index]! sharedEncoder.reset() sharedEncoder.encodeRootObject(item) - self.valueBox.set(self.tableId, key: self.key(collectionId: collectionId, index: index), value: sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(collectionId: collectionId, index: index), value: sharedEncoder.readBufferNoCopy()) } } diff --git a/Postbox/KeychainTable.swift b/Postbox/KeychainTable.swift index 451c0e52f3..1c2fd8b68d 100644 --- a/Postbox/KeychainTable.swift +++ b/Postbox/KeychainTable.swift @@ -1,8 +1,8 @@ import Foundation final class KeychainTable: Table { - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) } private func key(_ string: String) -> ValueBoxKey { @@ -10,17 +10,17 @@ final class KeychainTable: Table { } func get(_ key: String) -> Data? { - if let value = self.valueBox.get(self.tableId, key: self.key(key)) { + if let value = self.valueBox.get(self.table, key: self.key(key)) { return Data(bytes: value.memory.assumingMemoryBound(to: UInt8.self), count: value.length) } return nil } func set(_ key: String, value: Data) { - self.valueBox.set(self.tableId, key: self.key(key), value: MemoryBuffer(data: value)) + self.valueBox.set(self.table, key: self.key(key), value: MemoryBuffer(data: value)) } func remove(_ key: String) { - self.valueBox.remove(self.tableId, key: self.key(key)) + self.valueBox.remove(self.table, key: self.key(key)) } } diff --git a/Postbox/MediaCleanupTable.swift b/Postbox/MediaCleanupTable.swift index 042d879c12..4b5d67590d 100644 --- a/Postbox/MediaCleanupTable.swift +++ b/Postbox/MediaCleanupTable.swift @@ -1,17 +1,18 @@ import Foundation final class MediaCleanupTable: Table { - var debugMedia: [Media] = [] + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + override init(valueBox: ValueBox, table: ValueBoxTable) { + super.init(valueBox: valueBox, table: table) } func add(_ media: Media, sharedEncoder: Encoder = Encoder()) { - debugMedia.append(media) } func debugList() -> [Media] { - return self.debugMedia + return [] } } diff --git a/Postbox/MediaResource.swift b/Postbox/MediaResource.swift index 537e300cd9..2b31114fb4 100644 --- a/Postbox/MediaResource.swift +++ b/Postbox/MediaResource.swift @@ -6,18 +6,18 @@ public protocol MediaResourceId { func isEqual(to: MediaResourceId) -> Bool } -struct WrappedMediaResourceId: Hashable { - let id: MediaResourceId +public struct WrappedMediaResourceId: Hashable { + public let id: MediaResourceId - init(_ id: MediaResourceId) { + public init(_ id: MediaResourceId) { self.id = id } - static func ==(lhs: WrappedMediaResourceId, rhs: WrappedMediaResourceId) -> Bool { + public static func ==(lhs: WrappedMediaResourceId, rhs: WrappedMediaResourceId) -> Bool { return lhs.id.isEqual(to: rhs.id) } - var hashValue: Int { + public var hashValue: Int { return self.id.hashValue } } diff --git a/Postbox/MessageHistoryIndexTable.swift b/Postbox/MessageHistoryIndexTable.swift index 4e32585703..ebf2df414c 100644 --- a/Postbox/MessageHistoryIndexTable.swift +++ b/Postbox/MessageHistoryIndexTable.swift @@ -107,6 +107,10 @@ private func modifyHistoryIndexEntryTimestamp(value: ReadBuffer, timestamp: Int3 } final class MessageHistoryIndexTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + let globalMessageIdsNamespace: Int32 let globalMessageIdsTable: GlobalMessageIdsTable let metadataTable: MessageHistoryMetadataTable @@ -114,13 +118,13 @@ final class MessageHistoryIndexTable: Table { var cachedMaxEntryByPeerId: [PeerId: [MessageId.Namespace: ValueBoxKey]] = [:] - init(valueBox: ValueBox, tableId: Int32, globalMessageIdsTable: GlobalMessageIdsTable, metadataTable: MessageHistoryMetadataTable, seedConfiguration: SeedConfiguration) { + init(valueBox: ValueBox, table: ValueBoxTable, globalMessageIdsTable: GlobalMessageIdsTable, metadataTable: MessageHistoryMetadataTable, seedConfiguration: SeedConfiguration) { self.globalMessageIdsTable = globalMessageIdsTable self.globalMessageIdsNamespace = globalMessageIdsTable.namespace self.seedConfiguration = seedConfiguration self.metadataTable = metadataTable - super.init(valueBox: valueBox, tableId: tableId) + super.init(valueBox: valueBox, table: table) } private func key(_ id: MessageId) -> ValueBoxKey { @@ -231,7 +235,7 @@ final class MessageHistoryIndexTable: Table { var modifyHole: (MessageIndex, MessageHistoryHole)? let startKey = self.key(MessageId(peerId: peerId, namespace: namespace, id: lowerIndex.id.id)) - self.valueBox.range(self.tableId, start: startKey, end: self.upperBound(peerId, namespace: namespace), values: { key, value in + self.valueBox.range(self.table, start: startKey, end: self.upperBound(peerId, namespace: namespace), values: { key, value in let entry = readHistoryIndexEntry(peerId, namespace: namespace, key: key, value: value) if case let .Hole(hole) = entry { if lowerIndex.id.id <= hole.min { @@ -261,7 +265,7 @@ final class MessageHistoryIndexTable: Table { let index = MessageIndex(id: message.id, timestamp: message.timestamp) var upperItem: HistoryIndexEntry? - self.valueBox.range(self.tableId, start: self.key(index.id).predecessor, end: self.upperBound(index.id.peerId, namespace: index.id.namespace), values: { key, value in + self.valueBox.range(self.table, start: self.key(index.id).predecessor, end: self.upperBound(index.id.peerId, namespace: index.id.namespace), values: { key, value in upperItem = readHistoryIndexEntry(index.id.peerId, namespace: index.id.namespace, key: key, value: value) return true }, limit: 1) @@ -366,10 +370,10 @@ final class MessageHistoryIndexTable: Table { } func updateTimestamp(_ id: MessageId, timestamp: Int32, operations: inout [MessageHistoryIndexOperation]) { - if let previousData = self.valueBox.get(self.tableId, key: self.key(id)), let previousEntry = self.get(id), case let .Message(previousIndex) = previousEntry, previousIndex.timestamp != timestamp { + if let previousData = self.valueBox.get(self.table, key: self.key(id)), let previousEntry = self.get(id), case let .Message(previousIndex) = previousEntry, previousIndex.timestamp != timestamp { let updatedEntry = modifyHistoryIndexEntryTimestamp(value: previousData, timestamp: timestamp) - self.valueBox.remove(self.tableId, key: self.key(id)) - self.valueBox.set(self.tableId, key: self.key(id), value: updatedEntry) + self.valueBox.remove(self.table, key: self.key(id)) + self.valueBox.set(self.table, key: self.key(id), value: updatedEntry) operations.append(.UpdateTimestamp(MessageIndex(id: id, timestamp: previousIndex.timestamp), timestamp)) } @@ -379,7 +383,7 @@ final class MessageHistoryIndexTable: Table { self.ensureInitialized(id.peerId, operations: &operations) var upperItem: HistoryIndexEntry? - self.valueBox.range(self.tableId, start: self.key(id).predecessor, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in + self.valueBox.range(self.table, start: self.key(id).predecessor, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in upperItem = readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value) return true }, limit: 1) @@ -559,7 +563,7 @@ final class MessageHistoryIndexTable: Table { value.write(&stableId, offset: 0, length: 4) value.write(&min, offset: 0, length: 4) value.write(&tags, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(hole.id), value: value) + self.valueBox.set(self.table, key: self.key(hole.id), value: value) operations.append(.InsertHole(hole)) } @@ -575,7 +579,7 @@ final class MessageHistoryIndexTable: Table { var timestamp: Int32 = index.timestamp value.write(&flags, offset: 0, length: 1) value.write(×tamp, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(index.id), value: value) + self.valueBox.set(self.table, key: self.key(index.id), value: value) operations.append(.InsertMessage(message)) @@ -585,7 +589,7 @@ final class MessageHistoryIndexTable: Table { } private func justRemove(_ index: MessageIndex, operations: inout [MessageHistoryIndexOperation]) { - self.valueBox.remove(self.tableId, key: self.key(index.id)) + self.valueBox.remove(self.table, key: self.key(index.id)) operations.append(.Remove(index)) if index.id.namespace == self.globalMessageIdsNamespace { @@ -597,13 +601,13 @@ final class MessageHistoryIndexTable: Table { let key = self.key(id) var lowerItem: HistoryIndexEntry? - self.valueBox.range(self.tableId, start: bindUpper ? key : key.successor, end: self.lowerBound(id.peerId, namespace: id.namespace), values: { key, value in + self.valueBox.range(self.table, start: bindUpper ? key : key.successor, end: self.lowerBound(id.peerId, namespace: id.namespace), values: { key, value in lowerItem = readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value) return true }, limit: 1) var upperItem: HistoryIndexEntry? - self.valueBox.range(self.tableId, start: bindUpper ? key.predecessor : key, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in + self.valueBox.range(self.table, start: bindUpper ? key.predecessor : key, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in upperItem = readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value) return true }, limit: 1) @@ -616,7 +620,7 @@ final class MessageHistoryIndexTable: Table { self.ensureInitialized(id.peerId, operations: &operations) let key = self.key(id) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { return readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value) } return nil @@ -627,7 +631,7 @@ final class MessageHistoryIndexTable: Table { self.ensureInitialized(peerId, operations: &operations) var entry: HistoryIndexEntry? - self.valueBox.range(self.tableId, start: self.upperBound(peerId, namespace: namespace), end: self.lowerBound(peerId, namespace: namespace), values: { key, value in + self.valueBox.range(self.table, start: self.upperBound(peerId, namespace: namespace), end: self.lowerBound(peerId, namespace: namespace), values: { key, value in entry = readHistoryIndexEntry(peerId, namespace: namespace, key: key, value: value) return false }, limit: 1) @@ -636,12 +640,12 @@ final class MessageHistoryIndexTable: Table { } func exists(_ id: MessageId) -> Bool { - return self.valueBox.exists(self.tableId, key: self.key(id)) + return self.valueBox.exists(self.table, key: self.key(id)) } func holeContainingId(_ id: MessageId) -> MessageHistoryHole? { var result: MessageHistoryHole? - self.valueBox.range(self.tableId, start: self.key(MessageId(peerId: id.peerId, namespace: id.namespace, id: id.id)).predecessor, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in + self.valueBox.range(self.table, start: self.key(MessageId(peerId: id.peerId, namespace: id.namespace, id: id.id)).predecessor, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in if case let .Hole(hole) = readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value) { result = hole } @@ -655,7 +659,7 @@ final class MessageHistoryIndexTable: Table { var count = 0 var holes = false - self.valueBox.range(self.tableId, start: self.key(MessageId(peerId: peerId, namespace: namespace, id: minId)).predecessor, end: self.key(MessageId(peerId: peerId, namespace: namespace, id: maxId)).successor, values: { _, value in + self.valueBox.range(self.table, start: self.key(MessageId(peerId: peerId, namespace: namespace, id: minId)).predecessor, end: self.key(MessageId(peerId: peerId, namespace: namespace, id: maxId)).successor, values: { _, value in var flags: Int8 = 0 value.read(&flags, offset: 0, length: 1) if (flags & HistoryEntryTypeMask) == HistoryEntryTypeMessage { @@ -668,7 +672,7 @@ final class MessageHistoryIndexTable: Table { return true }, limit: 0) - self.valueBox.range(self.tableId, start: self.key(MessageId(peerId: peerId, namespace: namespace, id: maxId)), end: self.upperBound(peerId, namespace: namespace), values: { key, value in + self.valueBox.range(self.table, start: self.key(MessageId(peerId: peerId, namespace: namespace, id: maxId)), end: self.upperBound(peerId, namespace: namespace), values: { key, value in var flags: Int8 = 0 value.read(&flags, offset: 0, length: 1) if (flags & HistoryEntryTypeMask) == HistoryEntryTypeHole { @@ -688,7 +692,7 @@ final class MessageHistoryIndexTable: Table { var holes = false for id in ids { - self.valueBox.range(self.tableId, start: self.key(MessageId(peerId: peerId, namespace: namespace, id: id)).predecessor, end: self.upperBound(peerId, namespace: namespace), values: { key, value in + self.valueBox.range(self.table, start: self.key(MessageId(peerId: peerId, namespace: namespace, id: id)).predecessor, end: self.upperBound(peerId, namespace: namespace), values: { key, value in let entryId = key.getInt32(8 + 4) var flags: Int8 = 0 value.read(&flags, offset: 0, length: 1) @@ -714,7 +718,7 @@ final class MessageHistoryIndexTable: Table { func debugList(_ peerId: PeerId, namespace: MessageId.Namespace) -> [HistoryIndexEntry] { var list: [HistoryIndexEntry] = [] - self.valueBox.range(self.tableId, start: self.lowerBound(peerId, namespace: namespace), end: self.upperBound(peerId, namespace: namespace), values: { key, value in + self.valueBox.range(self.table, start: self.lowerBound(peerId, namespace: namespace), end: self.upperBound(peerId, namespace: namespace), values: { key, value in list.append(readHistoryIndexEntry(peerId, namespace: namespace, key: key, value: value)) return true diff --git a/Postbox/MessageHistoryInvalidatedReadStateTable.swift b/Postbox/MessageHistoryInvalidatedReadStateTable.swift index 0aaf0ebd8b..e33f9d16f3 100644 --- a/Postbox/MessageHistoryInvalidatedReadStateTable.swift +++ b/Postbox/MessageHistoryInvalidatedReadStateTable.swift @@ -25,6 +25,10 @@ public func ==(lhs: PeerReadStateSynchronizationOperation, rhs: PeerReadStateSyn } final class MessageHistorySynchronizeReadStateTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private let sharedKey = ValueBoxKey(length: 8) private func key(peerId: PeerId) -> ValueBoxKey { @@ -35,8 +39,8 @@ final class MessageHistorySynchronizeReadStateTable: Table { private var updatedPeerIds: [PeerId: PeerReadStateSynchronizationOperation?] = [:] private func lowerBound() -> ValueBoxKey { - let key = ValueBoxKey(length: 1) - key.setInt8(0, value: 0) + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: 0) return key } @@ -55,7 +59,7 @@ final class MessageHistorySynchronizeReadStateTable: Table { self.beforeCommit() var operations: [PeerId: PeerReadStateSynchronizationOperation] = [:] - self.valueBox.range(self.tableId, start: self.lowerBound(), end: self.upperBound(), values: { key, value in + self.valueBox.range(self.table, start: self.lowerBound(), end: self.upperBound(), values: { key, value in var operationValue: Int8 = 0 value.read(&operationValue, offset: 0, length: 1) @@ -92,9 +96,9 @@ final class MessageHistorySynchronizeReadStateTable: Table { buffer.write(&operationValue, offset: 0, length: 1) } - self.valueBox.set(self.tableId, key: key, value: buffer) + self.valueBox.set(self.table, key: key, value: buffer) } else { - self.valueBox.remove(self.tableId, key: key) + self.valueBox.remove(self.table, key: key) } } self.updatedPeerIds.removeAll() diff --git a/Postbox/MessageHistoryMetadataTable.swift b/Postbox/MessageHistoryMetadataTable.swift index c5ad09cc25..7f34c663f8 100644 --- a/Postbox/MessageHistoryMetadataTable.swift +++ b/Postbox/MessageHistoryMetadataTable.swift @@ -8,6 +8,10 @@ private enum MetadataPrefix: Int8 { } final class MessageHistoryMetadataTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + let sharedPeerHistoryInitializedKey = ValueBoxKey(length: 8 + 1) let sharedPeerNextMessageIdByNamespaceKey = ValueBoxKey(length: 8 + 1 + 4) let sharedBuffer = WriteBuffer() @@ -21,10 +25,6 @@ final class MessageHistoryMetadataTable: Table { private var nextMessageStableId: UInt32? private var nextMessageStableIdUpdated = false - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) - } - private func peerHistoryInitializedKey(_ id: PeerId) -> ValueBoxKey { self.sharedPeerHistoryInitializedKey.setInt64(0, value: id.toInt64()) self.sharedPeerHistoryInitializedKey.setInt8(8, value: MetadataPrefix.PeerHistoryInitialized.rawValue) @@ -47,7 +47,7 @@ final class MessageHistoryMetadataTable: Table { func setInitializedChatList() { self.initializedChatList = true - self.valueBox.set(self.tableId, key: self.key(MetadataPrefix.ChatListInitialized), value: MemoryBuffer()) + self.valueBox.set(self.table, key: self.key(MetadataPrefix.ChatListInitialized), value: MemoryBuffer()) } func isInitializedChatList() -> Bool { @@ -55,7 +55,7 @@ final class MessageHistoryMetadataTable: Table { return initializedChatList } - if self.valueBox.get(self.tableId, key: self.key(MetadataPrefix.ChatListInitialized)) != nil { + if self.valueBox.get(self.table, key: self.key(MetadataPrefix.ChatListInitialized)) != nil { self.initializedChatList = true return true } @@ -66,14 +66,14 @@ final class MessageHistoryMetadataTable: Table { func setInitialized(_ peerId: PeerId) { self.initializedHistoryPeerIds.insert(peerId) self.sharedBuffer.reset() - self.valueBox.set(self.tableId, key: self.peerHistoryInitializedKey(peerId), value: self.sharedBuffer) + self.valueBox.set(self.table, key: self.peerHistoryInitializedKey(peerId), value: self.sharedBuffer) } func isInitialized(_ peerId: PeerId) -> Bool { if self.initializedHistoryPeerIds.contains(peerId) { return true } else { - if self.valueBox.exists(self.tableId, key: self.peerHistoryInitializedKey(peerId)) { + if self.valueBox.exists(self.table, key: self.peerHistoryInitializedKey(peerId)) { self.initializedHistoryPeerIds.insert(peerId) return true } else { @@ -94,7 +94,7 @@ final class MessageHistoryMetadataTable: Table { return MessageId(peerId: peerId, namespace: namespace, id: nextId) } else { var nextId: Int32 = 1 - if let value = self.valueBox.get(self.tableId, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) { + if let value = self.valueBox.get(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) { value.read(&nextId, offset: 0, length: 4) } self.peerNextMessageIdByNamespace[peerId]![namespace] = nextId + 1 @@ -107,7 +107,7 @@ final class MessageHistoryMetadataTable: Table { } } else { var nextId: Int32 = 1 - if let value = self.valueBox.get(self.tableId, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) { + if let value = self.valueBox.get(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) { value.read(&nextId, offset: 0, length: 4) } @@ -127,7 +127,7 @@ final class MessageHistoryMetadataTable: Table { self.nextMessageStableIdUpdated = true return nextId } else { - if let value = self.valueBox.get(self.tableId, key: self.key(.NextStableMessageId)) { + if let value = self.valueBox.get(self.table, key: self.key(.NextStableMessageId)) { var nextId: UInt32 = 0 value.read(&nextId, offset: 0, length: 4) self.nextMessageStableId = nextId + 1 @@ -159,9 +159,9 @@ final class MessageHistoryMetadataTable: Table { sharedBuffer.reset() var mutableMaxId = maxId sharedBuffer.write(&mutableMaxId, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace), value: sharedBuffer) + self.valueBox.set(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace), value: sharedBuffer) } else { - self.valueBox.remove(self.tableId, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) + self.valueBox.remove(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) } } } @@ -170,7 +170,7 @@ final class MessageHistoryMetadataTable: Table { if self.nextMessageStableIdUpdated { if let nextMessageStableId = self.nextMessageStableId { var nextId: UInt32 = nextMessageStableId - self.valueBox.set(self.tableId, key: self.key(.NextStableMessageId), value: MemoryBuffer(memory: &nextId, capacity: 4, length: 4, freeWhenDone: false)) + self.valueBox.set(self.table, key: self.key(.NextStableMessageId), value: MemoryBuffer(memory: &nextId, capacity: 4, length: 4, freeWhenDone: false)) self.nextMessageStableIdUpdated = false } } diff --git a/Postbox/MessageHistoryReadStateTable.swift b/Postbox/MessageHistoryReadStateTable.swift index a6555d9c6a..0be6dfaa79 100644 --- a/Postbox/MessageHistoryReadStateTable.swift +++ b/Postbox/MessageHistoryReadStateTable.swift @@ -16,6 +16,10 @@ private final class InternalPeerReadStates { } final class MessageHistoryReadStateTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private var cachedPeerReadStates: [PeerId: InternalPeerReadStates?] = [:] private var updatedPeerIds = Set() @@ -26,15 +30,15 @@ final class MessageHistoryReadStateTable: Table { return self.sharedKey } - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + override init(valueBox: ValueBox, table: ValueBoxTable) { + super.init(valueBox: valueBox, table: table) } private func get(_ id: PeerId) -> InternalPeerReadStates? { if let states = self.cachedPeerReadStates[id] { return states } else { - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { var count: Int32 = 0 value.read(&count, offset: 0, length: 4) var stateByNamespace: [MessageId.Namespace: PeerReadState] = [:] @@ -278,9 +282,9 @@ final class MessageHistoryReadStateTable: Table { sharedBuffer.write(&maxKnownId, offset: 0, length: 4) sharedBuffer.write(&count, offset: 0, length: 4) } - self.valueBox.set(self.tableId, key: self.key(id), value: sharedBuffer) + self.valueBox.set(self.table, key: self.key(id), value: sharedBuffer) } else { - self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.remove(self.table, key: self.key(id)) } } self.updatedPeerIds.removeAll() diff --git a/Postbox/MessageHistoryTable.swift b/Postbox/MessageHistoryTable.swift index 7cb2fb5100..ffcfeb46e6 100644 --- a/Postbox/MessageHistoryTable.swift +++ b/Postbox/MessageHistoryTable.swift @@ -34,6 +34,10 @@ enum RenderedMessageHistoryEntry { } final class MessageHistoryTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + let messageHistoryIndexTable: MessageHistoryIndexTable let messageMediaTable: MessageMediaTable let historyMetadataTable: MessageHistoryMetadataTable @@ -42,7 +46,7 @@ final class MessageHistoryTable: Table { let readStateTable: MessageHistoryReadStateTable let synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable - init(valueBox: ValueBox, tableId: Int32, messageHistoryIndexTable: MessageHistoryIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, unsentTable: MessageHistoryUnsentTable, tagsTable: MessageHistoryTagsTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable) { + init(valueBox: ValueBox, table: ValueBoxTable, messageHistoryIndexTable: MessageHistoryIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, unsentTable: MessageHistoryUnsentTable, tagsTable: MessageHistoryTagsTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable) { self.messageHistoryIndexTable = messageHistoryIndexTable self.messageMediaTable = messageMediaTable self.historyMetadataTable = historyMetadataTable @@ -51,7 +55,7 @@ final class MessageHistoryTable: Table { self.readStateTable = readStateTable self.synchronizeReadStateTable = synchronizeReadStateTable - super.init(valueBox: valueBox, tableId: tableId) + super.init(valueBox: valueBox, table: table) } private func key(_ index: MessageIndex, key: ValueBoxKey = ValueBoxKey(length: 8 + 4 + 4 + 4)) -> ValueBoxKey { @@ -393,7 +397,7 @@ final class MessageHistoryTable: Table { var currentKey = self.upperBound(peerId) while true { var entry: IntermediateMessageHistoryEntry? - self.valueBox.range(self.tableId, start: currentKey, end: self.lowerBound(peerId), values: { key, value in + self.valueBox.range(self.table, start: currentKey, end: self.lowerBound(peerId), values: { key, value in entry = self.readIntermediateEntry(key, value: value) return true }, limit: 1) @@ -414,7 +418,7 @@ final class MessageHistoryTable: Table { func getMessage(_ index: MessageIndex) -> IntermediateMessage? { let key = self.key(index) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { let entry = self.readIntermediateEntry(key, value: value) if case let .Message(message) = entry { return message @@ -422,7 +426,7 @@ final class MessageHistoryTable: Table { } else if let tableIndex = self.messageHistoryIndexTable.get(index.id) { if case let .Message(updatedIndex) = tableIndex { let key = self.key(updatedIndex) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { let entry = self.readIntermediateEntry(key, value: value) if case let .Message(message) = entry { return message @@ -563,7 +567,7 @@ final class MessageHistoryTable: Table { sharedBuffer.write(&idId, offset: 0, length: 8) } - self.valueBox.set(self.tableId, key: self.key(MessageIndex(message), key: sharedKey), value: sharedBuffer) + self.valueBox.set(self.table, key: self.key(MessageIndex(message), key: sharedKey), value: sharedBuffer) return IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, timestamp: message.timestamp, flags: flags, tags: message.tags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia) } @@ -578,12 +582,12 @@ final class MessageHistoryTable: Table { sharedBuffer.write(&minId, offset: 0, length: 4) var tags: UInt32 = hole.tags sharedBuffer.write(&tags, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(hole.maxIndex), value: sharedBuffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(hole.maxIndex), value: sharedBuffer.readBufferNoCopy()) } private func justRemove(_ index: MessageIndex, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation]) { let key = self.key(index) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { switch self.readIntermediateEntry(key, value: value) { case let .Message(message): let embeddedMediaData = message.embeddedMediaData @@ -639,7 +643,7 @@ final class MessageHistoryTable: Table { } } - self.valueBox.remove(self.tableId, key: key) + self.valueBox.remove(self.table, key: key) } } @@ -780,7 +784,7 @@ final class MessageHistoryTable: Table { } } - self.valueBox.remove(self.tableId, key: self.key(index)) + self.valueBox.remove(self.table, key: self.key(index)) if !previousMessage.tags.isEmpty { self.tagsTable.remove(previousMessage.tags, index: index) } @@ -923,7 +927,7 @@ final class MessageHistoryTable: Table { sharedBuffer.write(&idId, offset: 0, length: 8) } - self.valueBox.set(self.tableId, key: self.key(MessageIndex(message), key: sharedKey), value: sharedBuffer) + self.valueBox.set(self.table, key: self.key(MessageIndex(message), key: sharedKey), value: sharedBuffer) return IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, timestamp: message.timestamp, flags: flags, tags: tags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia) } else { @@ -933,7 +937,7 @@ final class MessageHistoryTable: Table { private func justUpdateTimestamp(_ index: MessageIndex, timestamp: Int32) { if let previousMessage = self.getMessage(index) { - self.valueBox.remove(self.tableId, key: self.key(index)) + self.valueBox.remove(self.table, key: self.key(index)) var updatedMessage = IntermediateMessage(stableId: previousMessage.stableId, stableVersion: previousMessage.stableVersion + 1, id: previousMessage.id, timestamp: timestamp, flags: previousMessage.flags, tags: previousMessage.tags, forwardInfo: previousMessage.forwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributesData: previousMessage.attributesData, embeddedMediaData: previousMessage.embeddedMediaData, referencedMedia: previousMessage.referencedMedia) self.storeIntermediateMessage(updatedMessage, sharedKey: self.key(index)) @@ -1072,7 +1076,7 @@ final class MessageHistoryTable: Table { sharedBuffer.write(&idId, offset: 0, length: 8) } - self.valueBox.set(self.tableId, key: self.key(MessageIndex(id: message.id, timestamp: message.timestamp), key: sharedKey), value: sharedBuffer) + self.valueBox.set(self.table, key: self.key(MessageIndex(id: message.id, timestamp: message.timestamp), key: sharedKey), value: sharedBuffer) } private func readIntermediateEntry(_ key: ValueBoxKey, value: ReadBuffer) -> IntermediateMessageHistoryEntry { @@ -1295,7 +1299,7 @@ final class MessageHistoryTable: Table { var lower: IntermediateMessageHistoryEntry? var upper: IntermediateMessageHistoryEntry? - self.valueBox.range(self.tableId, start: self.key(index), end: self.lowerBound(index.id.peerId), values: { key, value in + self.valueBox.range(self.table, start: self.key(index), end: self.lowerBound(index.id.peerId), values: { key, value in lowerEntries.append(self.readIntermediateEntry(key, value: value)) return true }, limit: count / 2 + 1) @@ -1305,7 +1309,7 @@ final class MessageHistoryTable: Table { lowerEntries.removeLast() } - self.valueBox.range(self.tableId, start: self.key(index).predecessor, end: self.upperBound(index.id.peerId), values: { key, value in + self.valueBox.range(self.table, start: self.key(index).predecessor, end: self.upperBound(index.id.peerId), values: { key, value in upperEntries.append(self.readIntermediateEntry(key, value: value)) return true }, limit: count - lowerEntries.count + 1) @@ -1316,7 +1320,7 @@ final class MessageHistoryTable: Table { if lowerEntries.count != 0 && lowerEntries.count + upperEntries.count < count { var additionalLowerEntries: [IntermediateMessageHistoryEntry] = [] - self.valueBox.range(self.tableId, start: self.key(lowerEntries.last!.index), end: self.lowerBound(index.id.peerId), values: { key, value in + self.valueBox.range(self.table, start: self.key(lowerEntries.last!.index), end: self.lowerBound(index.id.peerId), values: { key, value in additionalLowerEntries.append(self.readIntermediateEntry(key, value: value)) return true }, limit: count - lowerEntries.count - upperEntries.count + 1) @@ -1345,7 +1349,7 @@ final class MessageHistoryTable: Table { var entries: [IntermediateMessageHistoryEntry] = [] for index in indices { let key = self.key(index) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { entries.append(readIntermediateEntry(key, value: value)) } else { assertionFailure() @@ -1357,7 +1361,7 @@ final class MessageHistoryTable: Table { if let lowerIndex = lower { let key = self.key(lowerIndex) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { lowerEntry = readIntermediateEntry(key, value: value) } else { assertionFailure() @@ -1366,7 +1370,7 @@ final class MessageHistoryTable: Table { if let upperIndex = upper { let key = self.key(upperIndex) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { upperEntry = readIntermediateEntry(key, value: value) } else { assertionFailure() @@ -1390,7 +1394,7 @@ final class MessageHistoryTable: Table { } else { key = self.upperBound(peerId) } - self.valueBox.range(self.tableId, start: key, end: self.lowerBound(peerId), values: { key, value in + self.valueBox.range(self.table, start: key, end: self.lowerBound(peerId), values: { key, value in entries.append(self.readIntermediateEntry(key, value: value)) return true }, limit: count) @@ -1409,7 +1413,7 @@ final class MessageHistoryTable: Table { var entries: [IntermediateMessageHistoryEntry] = [] for index in indices { let key = self.key(index) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { entries.append(readIntermediateEntry(key, value: value)) } else { assertionFailure() @@ -1433,7 +1437,7 @@ final class MessageHistoryTable: Table { } else { key = self.lowerBound(peerId) } - self.valueBox.range(self.tableId, start: key, end: self.upperBound(peerId), values: { key, value in + self.valueBox.range(self.table, start: key, end: self.upperBound(peerId), values: { key, value in entries.append(self.readIntermediateEntry(key, value: value)) return true }, limit: count) @@ -1452,7 +1456,7 @@ final class MessageHistoryTable: Table { var entries: [IntermediateMessageHistoryEntry] = [] for index in indices { let key = self.key(index) - if let value = self.valueBox.get(self.tableId, key: key) { + if let value = self.valueBox.get(self.table, key: key) { entries.append(readIntermediateEntry(key, value: value)) } else { assertionFailure() @@ -1485,6 +1489,19 @@ final class MessageHistoryTable: Table { } return nil } + + func findMessageId(peerId: PeerId, namespace: MessageId.Namespace, timestamp: Int32) -> MessageId? { + var result: MessageId? + self.valueBox.range(self.table, start: self.key(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp)), end: self.key(MessageIndex(id: MessageId(peerId: peerId, namespace: Int32.max, id: Int32.max), timestamp: timestamp)), values: { key, value in + let entry = self.readIntermediateEntry(key, value: value) + if case let .Message(message) = entry { + result = entry.index.id + return false + } + return true + }, limit: 0) + return result + } func debugList(_ peerId: PeerId, peerTable: PeerTable) -> [RenderedMessageHistoryEntry] { var operationsByPeerId: [PeerId : [MessageHistoryOperation]] = [:] diff --git a/Postbox/MessageHistoryTagsTable.swift b/Postbox/MessageHistoryTagsTable.swift index 55c6412b36..3514b13ae2 100644 --- a/Postbox/MessageHistoryTagsTable.swift +++ b/Postbox/MessageHistoryTagsTable.swift @@ -1,12 +1,12 @@ import Foundation class MessageHistoryTagsTable: Table { - private let sharedKey = ValueBoxKey(length: 8 + 4 + 4 + 4 + 4) - - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) } + private let sharedKey = ValueBoxKey(length: 8 + 4 + 4 + 4 + 4) + private func key(_ tagMask: MessageTags, index: MessageIndex, key: ValueBoxKey = ValueBoxKey(length: 8 + 4 + 4 + 4 + 4)) -> ValueBoxKey { key.setInt64(0, value: index.id.peerId.toInt64()) key.setUInt32(8, value: tagMask.rawValue) @@ -31,23 +31,23 @@ class MessageHistoryTagsTable: Table { } func add(_ tagMask: MessageTags, index: MessageIndex) { - self.valueBox.set(self.tableId, key: self.key(tagMask, index: index, key: self.sharedKey), value: MemoryBuffer()) + self.valueBox.set(self.table, key: self.key(tagMask, index: index, key: self.sharedKey), value: MemoryBuffer()) } func remove(_ tagMask: MessageTags, index: MessageIndex) { - self.valueBox.remove(self.tableId, key: self.key(tagMask, index: index, key: self.sharedKey)) + self.valueBox.remove(self.table, key: self.key(tagMask, index: index, key: self.sharedKey)) } func entryLocation(at index: MessageIndex, tagMask: MessageTags) -> MessageHistoryEntryLocation? { - if let _ = self.valueBox.get(self.tableId, key: self.key(tagMask, index: index)) { + if let _ = self.valueBox.get(self.table, key: self.key(tagMask, index: index)) { var greaterCount = 0 - self.valueBox.range(self.tableId, start: self.key(tagMask, index: index), end: self.upperBound(tagMask, peerId: index.id.peerId), keys: { _ in + self.valueBox.range(self.table, start: self.key(tagMask, index: index), end: self.upperBound(tagMask, peerId: index.id.peerId), keys: { _ in greaterCount += 1 return true }, limit: 0) var lowerCount = 0 - self.valueBox.range(self.tableId, start: self.key(tagMask, index: index), end: self.lowerBound(tagMask, peerId: index.id.peerId), keys: { _ in + self.valueBox.range(self.table, start: self.key(tagMask, index: index), end: self.lowerBound(tagMask, peerId: index.id.peerId), keys: { _ in lowerCount += 1 return true }, limit: 0) @@ -63,7 +63,7 @@ class MessageHistoryTagsTable: Table { var lower: MessageIndex? var upper: MessageIndex? - self.valueBox.range(self.tableId, start: self.key(tagMask, index: index), end: self.lowerBound(tagMask, peerId: index.id.peerId), keys: { key in + self.valueBox.range(self.table, start: self.key(tagMask, index: index), end: self.lowerBound(tagMask, peerId: index.id.peerId), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 4 + 4), id: key.getInt32(8 + 4 + 4 + 4)), timestamp: key.getInt32(8 + 4)) lowerEntries.append(index) return true @@ -74,7 +74,7 @@ class MessageHistoryTagsTable: Table { lowerEntries.removeLast() } - self.valueBox.range(self.tableId, start: self.key(tagMask, index: index).predecessor, end: self.upperBound(tagMask, peerId: index.id.peerId), keys: { key in + self.valueBox.range(self.table, start: self.key(tagMask, index: index).predecessor, end: self.upperBound(tagMask, peerId: index.id.peerId), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 4 + 4), id: key.getInt32(8 + 4 + 4 + 4)), timestamp: key.getInt32(8 + 4)) upperEntries.append(index) return true @@ -86,7 +86,7 @@ class MessageHistoryTagsTable: Table { if lowerEntries.count != 0 && lowerEntries.count + upperEntries.count < count { var additionalLowerEntries: [MessageIndex] = [] - self.valueBox.range(self.tableId, start: self.key(tagMask, index: lowerEntries.last!), end: self.lowerBound(tagMask, peerId: index.id.peerId), keys: { key in + self.valueBox.range(self.table, start: self.key(tagMask, index: lowerEntries.last!), end: self.lowerBound(tagMask, peerId: index.id.peerId), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 4 + 4), id: key.getInt32(8 + 4 + 4 + 4)), timestamp: key.getInt32(8 + 4)) additionalLowerEntries.append(index) return true @@ -112,7 +112,7 @@ class MessageHistoryTagsTable: Table { } else { key = self.upperBound(tagMask, peerId: peerId) } - self.valueBox.range(self.tableId, start: key, end: self.lowerBound(tagMask, peerId: peerId), keys: { key in + self.valueBox.range(self.table, start: key, end: self.lowerBound(tagMask, peerId: peerId), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 4 + 4), id: key.getInt32(8 + 4 + 4 + 4)), timestamp: key.getInt32(8 + 4)) indices.append(index) return true @@ -128,7 +128,7 @@ class MessageHistoryTagsTable: Table { } else { key = self.lowerBound(tagMask, peerId: peerId) } - self.valueBox.range(self.tableId, start: key, end: self.upperBound(tagMask, peerId: peerId), keys: { key in + self.valueBox.range(self.table, start: key, end: self.upperBound(tagMask, peerId: peerId), keys: { key in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 4 + 4), id: key.getInt32(8 + 4 + 4 + 4)), timestamp: key.getInt32(8 + 4)) indices.append(index) return true diff --git a/Postbox/MessageHistoryUnsentTable.swift b/Postbox/MessageHistoryUnsentTable.swift index a0324ab3fe..783f91d25c 100644 --- a/Postbox/MessageHistoryUnsentTable.swift +++ b/Postbox/MessageHistoryUnsentTable.swift @@ -6,6 +6,10 @@ enum IntermediateMessageHistoryUnsentOperation { } final class MessageHistoryUnsentTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + private let sharedKey = ValueBoxKey(length: 4 + 4 + 8) private func key(_ id: MessageId) -> ValueBoxKey { @@ -28,23 +32,19 @@ final class MessageHistoryUnsentTable: Table { return key } - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) - } - func add(_ id: MessageId, operations: inout [IntermediateMessageHistoryUnsentOperation]) { - self.valueBox.set(self.tableId, key: self.key(id), value: MemoryBuffer()) + self.valueBox.set(self.table, key: self.key(id), value: MemoryBuffer()) operations.append(.Insert(id)) } func remove(_ id: MessageId, operations: inout [IntermediateMessageHistoryUnsentOperation]) { - self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.remove(self.table, key: self.key(id)) operations.append(.Remove(id)) } func get() -> [MessageId] { var ids: [MessageId] = [] - self.valueBox.range(self.tableId, start: self.lowerBound(), end: self.upperBound(), keys: { key in + self.valueBox.range(self.table, start: self.lowerBound(), end: self.upperBound(), keys: { key in ids.append(MessageId(peerId: PeerId(key.getInt64(4 + 4)), namespace: key.getInt32(0), id: key.getInt32(4))) return true }, limit: 0) diff --git a/Postbox/MessageHistoryView.swift b/Postbox/MessageHistoryView.swift index a1e9d9b744..d82a30d75a 100644 --- a/Postbox/MessageHistoryView.swift +++ b/Postbox/MessageHistoryView.swift @@ -592,7 +592,11 @@ final class MutableMessageHistoryView { } } - return (hole, hole.maxIndex <= self.anchorIndex.index ? .UpperToLower : .LowerToUpper) + if hole.maxIndex.timestamp == Int32.max && self.anchorIndex.index.timestamp == Int32.max { + return (hole, .UpperToLower) + } else { + return (hole, hole.maxIndex <= self.anchorIndex.index ? .UpperToLower : .LowerToUpper) + } } } diff --git a/Postbox/MessageMediaTable.swift b/Postbox/MessageMediaTable.swift index fbef514933..b9bf16adb2 100644 --- a/Postbox/MessageMediaTable.swift +++ b/Postbox/MessageMediaTable.swift @@ -21,12 +21,8 @@ enum DebugMediaEntry { } final class MessageMediaTable: Table { - let mediaCleanupTable: MediaCleanupTable - - init(valueBox: ValueBox, tableId: Int32, mediaCleanupTable: MediaCleanupTable) { - self.mediaCleanupTable = mediaCleanupTable - - super.init(valueBox: valueBox, tableId: tableId) + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) } func key(_ id: MediaId, key: ValueBoxKey = ValueBoxKey(length: 4 + 8)) -> ValueBoxKey { @@ -36,7 +32,7 @@ final class MessageMediaTable: Table { } func get(_ id: MediaId, embedded: @noescape(MessageIndex, MediaId) -> Media?) -> Media? { - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { var type: Int8 = 0 value.read(&type, offset: 0, length: 1) if type == MediaEntryType.Direct.rawValue { @@ -65,7 +61,7 @@ final class MessageMediaTable: Table { func set(_ media: Media, index: MessageIndex, messageHistoryTable: MessageHistoryTable, sharedWriteBuffer: WriteBuffer = WriteBuffer(), sharedEncoder: Encoder = Encoder()) -> InsertMediaResult { if let id = media.id { - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { var type: Int8 = 0 value.read(&type, offset: 0, length: 1) if type == MediaEntryType.Direct.rawValue { @@ -81,7 +77,7 @@ final class MessageMediaTable: Table { messageReferenceCount += 1 sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) return .Reference } else if type == MediaEntryType.MessageReference.rawValue { @@ -114,7 +110,7 @@ final class MessageMediaTable: Table { var messageReferenceCount: Int32 = 2 sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) } return .Reference @@ -134,7 +130,7 @@ final class MessageMediaTable: Table { sharedWriteBuffer.write(&idId, offset: 0, length: 4) sharedWriteBuffer.write(&idTimestamp, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) return .Embed(media) } @@ -144,7 +140,7 @@ final class MessageMediaTable: Table { } func removeReference(_ id: MediaId, sharedWriteBuffer: WriteBuffer = WriteBuffer()) -> RemoveMediaResult { - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { var type: Int8 = 0 value.read(&type, offset: 0, length: 1) if type == MediaEntryType.Direct.rawValue { @@ -162,12 +158,9 @@ final class MessageMediaTable: Table { sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4) if messageReferenceCount <= 0 { - if let media = Decoder(buffer: MemoryBuffer(memory: value.memory + mediaOffset, capacity: Int(dataLength), length: Int(dataLength), freeWhenDone: false)).decodeRootObject() as? Media { - self.mediaCleanupTable.add(media) - } - self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.remove(self.table, key: self.key(id)) } else { - self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy()) } return .Reference @@ -183,7 +176,7 @@ final class MessageMediaTable: Table { let referencedMessageIndex = MessageIndex(id: MessageId(peerId: PeerId(idPeerId), namespace: idNamespace, id: idId), timestamp: idTimestamp) - self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.remove(self.table, key: self.key(id)) return .Embedded(referencedMessageIndex) } else { @@ -195,14 +188,13 @@ final class MessageMediaTable: Table { func removeEmbeddedMedia(_ media: Media) { if let id = media.id { - self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.remove(self.table, key: self.key(id)) } - self.mediaCleanupTable.add(media) } func update(_ id: MediaId, media: Media, messageHistoryTable: MessageHistoryTable, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], sharedWriteBuffer: WriteBuffer = WriteBuffer(), sharedEncoder: Encoder = Encoder()) { if let updatedId = media.id { - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { var type: Int8 = 0 value.read(&type, offset: 0, length: 1) if type == MediaEntryType.Direct.rawValue { @@ -227,9 +219,9 @@ final class MessageMediaTable: Table { sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4) if id != updatedId { - self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.remove(self.table, key: self.key(id)) } - self.valueBox.set(self.tableId, key: self.key(updatedId), value: sharedWriteBuffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(updatedId), value: sharedWriteBuffer.readBufferNoCopy()) } else if type == MediaEntryType.MessageReference.rawValue { var idPeerId: Int64 = 0 var idNamespace: Int32 = 0 @@ -252,7 +244,7 @@ final class MessageMediaTable: Table { let upperBoundKey = ValueBoxKey(length: 8 + 4) memset(upperBoundKey.memory, 0xff, 8 + 4) - self.valueBox.range(self.tableId, start: ValueBoxKey(length: 0), end: upperBoundKey, values: { key, value in + self.valueBox.range(self.table, start: ValueBoxKey(length: 0), end: upperBoundKey, values: { key, value in var type: Int8 = 0 value.read(&type, offset: 0, length: 1) if type == MediaEntryType.Direct.rawValue { diff --git a/Postbox/MetadataTable.swift b/Postbox/MetadataTable.swift index b55904b1b6..03dc16dd6b 100644 --- a/Postbox/MetadataTable.swift +++ b/Postbox/MetadataTable.swift @@ -8,20 +8,24 @@ private enum MetadataKey: Int32 { } final class MetadataTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private let sharedBuffer = WriteBuffer() - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + override init(valueBox: ValueBox, table: ValueBoxTable) { + super.init(valueBox: valueBox, table: table) } private func key(_ key: MetadataKey) -> ValueBoxKey { - let valueBoxKey = ValueBoxKey(length: 4) - valueBoxKey.setInt32(0, value: key.rawValue) + let valueBoxKey = ValueBoxKey(length: 8) + valueBoxKey.setInt64(0, value: Int64(key.rawValue)) return valueBoxKey } func userVersion() -> Int32? { - if let value = self.valueBox.get(self.tableId, key: self.key(.UserVersion)) { + if let value = self.valueBox.get(self.table, key: self.key(.UserVersion)) { var version: Int32 = 0 value.read(&version, offset: 0, length: 4) return version @@ -34,11 +38,11 @@ final class MetadataTable: Table { let buffer = sharedBuffer var varVersion: Int32 = version buffer.write(&varVersion, offset: 0, length: 4) - self.valueBox.set(self.tableId, key: self.key(.UserVersion), value: buffer) + self.valueBox.set(self.table, key: self.key(.UserVersion), value: buffer) } func state() -> Coding? { - if let value = self.valueBox.get(self.tableId, key: self.key(.State)) { + if let value = self.valueBox.get(self.table, key: self.key(.State)) { if let state = Decoder(buffer: value).decodeRootObject() { return state } @@ -49,11 +53,11 @@ final class MetadataTable: Table { func setState(_ state: Coding) { let encoder = Encoder() encoder.encodeRootObject(state) - self.valueBox.set(self.tableId, key: self.key(.State), value: encoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(.State), value: encoder.readBufferNoCopy()) } func transactionStateVersion() -> Int64 { - if let value = self.valueBox.get(self.tableId, key: self.key(.TransactionStateVersion)) { + if let value = self.valueBox.get(self.table, key: self.key(.TransactionStateVersion)) { var version: Int64 = 0 value.read(&version, offset: 0, length: 8) return version @@ -67,12 +71,12 @@ final class MetadataTable: Table { sharedBuffer.reset() let buffer = sharedBuffer buffer.write(&version, offset: 0, length: 8) - self.valueBox.set(self.tableId, key: self.key(.TransactionStateVersion), value: buffer) + self.valueBox.set(self.table, key: self.key(.TransactionStateVersion), value: buffer) return version } func masterClientId() -> Int64 { - if let value = self.valueBox.get(self.tableId, key: self.key(.MasterClientId)) { + if let value = self.valueBox.get(self.table, key: self.key(.MasterClientId)) { var clientId: Int64 = 0 value.read(&clientId, offset: 0, length: 8) return clientId @@ -86,6 +90,6 @@ final class MetadataTable: Table { let buffer = sharedBuffer var clientId = id buffer.write(&clientId, offset: 0, length: 8) - self.valueBox.set(self.tableId, key: self.key(.MasterClientId), value: buffer) + self.valueBox.set(self.table, key: self.key(.MasterClientId), value: buffer) } } diff --git a/Postbox/OrderStatisticTable.swift b/Postbox/OrderStatisticTable.swift index 758abf9f86..8957cc9469 100644 --- a/Postbox/OrderStatisticTable.swift +++ b/Postbox/OrderStatisticTable.swift @@ -1,8 +1,8 @@ import Foundation class MessageOrderStatisticTable: Table { - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) } /*private func update(peerId: PeerId, tagMask: MessageTags, id: Int32, count: Int) { @@ -16,14 +16,14 @@ class MessageOrderStatisticTable: Table { while (idx <= 1000000) { key.setInt32(8 + 4, value: idx) var value: Int32 = 0 - if let data = self.valueBox.get(self.tableId, key: key) { + if let data = self.valueBox.get(self.table, key: key) { data.read(&value, offset: 0, length: 4) } if value == 0 { - self.valueBox.remove(self.tableId, key: key) + self.valueBox.remove(self.table, key: key) } else { writeValue = value - self.valueBox.set(self.tableId, key: key, value: buffer) + self.valueBox.set(self.table, key: key, value: buffer) } idx += idx & -idx } @@ -39,7 +39,7 @@ class MessageOrderStatisticTable: Table { while (idx > 0) { key.setInt32(8, value: idx) var value: Int32 = 0 - if let data = self.valueBox.get(self.tableId, key: key) { + if let data = self.valueBox.get(self.table, key: key) { data.read(&value, offset: 0, length: 4) } sum += value diff --git a/Postbox/PeerChatInterfaceStateTable.swift b/Postbox/PeerChatInterfaceStateTable.swift index e92e1288a7..a102bcb205 100644 --- a/Postbox/PeerChatInterfaceStateTable.swift +++ b/Postbox/PeerChatInterfaceStateTable.swift @@ -1,6 +1,10 @@ import Foundation final class PeerChatInterfaceStateTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private var states: [PeerId: PeerChatInterfaceState?] = [:] private var peerIdsWithUpdatedStates = Set() @@ -14,7 +18,7 @@ final class PeerChatInterfaceStateTable: Table { func get(_ peerId: PeerId) -> PeerChatInterfaceState? { if let cachedValue = self.states[peerId] { return cachedValue - } else if let value = self.valueBox.get(self.tableId, key: self.key(peerId, sharedKey: self.sharedKey)), let state = Decoder(buffer: value).decodeRootObject() as? PeerChatInterfaceState { + } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = Decoder(buffer: value).decodeRootObject() as? PeerChatInterfaceState { self.states[peerId] = state return state } else { @@ -62,9 +66,9 @@ final class PeerChatInterfaceStateTable: Table { if let state = state { sharedEncoder.reset() sharedEncoder.encodeRootObject(state) - self.valueBox.set(self.tableId, key: self.key(peerId, sharedKey: self.sharedKey), value: sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: sharedEncoder.readBufferNoCopy()) } else { - self.valueBox.remove(self.tableId, key: self.key(peerId, sharedKey: self.sharedKey)) + self.valueBox.remove(self.table, key: self.key(peerId, sharedKey: self.sharedKey)) } } } diff --git a/Postbox/PeerChatStateTable.swift b/Postbox/PeerChatStateTable.swift index e60e622ff0..6bf55bbc83 100644 --- a/Postbox/PeerChatStateTable.swift +++ b/Postbox/PeerChatStateTable.swift @@ -1,6 +1,10 @@ import Foundation final class PeerChatStateTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private var cachedPeerChatStates: [PeerId: Coding?] = [:] private var updatedPeerIds = Set() @@ -15,7 +19,7 @@ final class PeerChatStateTable: Table { if let state = self.cachedPeerChatStates[id] { return state } else { - if let value = self.valueBox.get(self.tableId, key: self.key(id)), let state = Decoder(buffer: value).decodeRootObject() { + if let value = self.valueBox.get(self.table, key: self.key(id)), let state = Decoder(buffer: value).decodeRootObject() { self.cachedPeerChatStates[id] = state return state } else { @@ -41,9 +45,9 @@ final class PeerChatStateTable: Table { if let wrappedState = self.cachedPeerChatStates[id], let state = wrappedState { sharedEncoder.reset() sharedEncoder.encodeRootObject(state) - self.valueBox.set(self.tableId, key: self.key(id), value: sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(id), value: sharedEncoder.readBufferNoCopy()) } else { - self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.remove(self.table, key: self.key(id)) } } self.updatedPeerIds.removeAll() diff --git a/Postbox/PeerChatTopTaggedMessageIds.swift b/Postbox/PeerChatTopTaggedMessageIds.swift index f7ac6d0dd5..2b3d3a9c7b 100644 --- a/Postbox/PeerChatTopTaggedMessageIds.swift +++ b/Postbox/PeerChatTopTaggedMessageIds.swift @@ -1,6 +1,10 @@ import Foundation final class PeerChatTopTaggedMessageIdsTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + private var cachedTopIds: [PeerId: [MessageId.Namespace: MessageId?]] = [:] private var updatedPeerIds = Set() diff --git a/Postbox/PeerNotificationSettingsTable.swift b/Postbox/PeerNotificationSettingsTable.swift index 21a7c891d7..00d8547b1f 100644 --- a/Postbox/PeerNotificationSettingsTable.swift +++ b/Postbox/PeerNotificationSettingsTable.swift @@ -1,16 +1,16 @@ import Foundation final class PeerNotificationSettingsTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private let sharedEncoder = Encoder() private let sharedKey = ValueBoxKey(length: 8) private var cachedSettings: [PeerId: PeerNotificationSettings] = [:] private var updatedPeerIds = Set() - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) - } - private func key(_ id: PeerId) -> ValueBoxKey { self.sharedKey.setInt64(0, value: id.toInt64()) return self.sharedKey @@ -25,7 +25,7 @@ final class PeerNotificationSettingsTable: Table { if let settings = self.cachedSettings[id] { return settings } - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { if let settings = Decoder(buffer: value).decodeRootObject() as? PeerNotificationSettings { self.cachedSettings[id] = settings return settings @@ -45,7 +45,7 @@ final class PeerNotificationSettingsTable: Table { self.sharedEncoder.reset() self.sharedEncoder.encodeRootObject(settings) - self.valueBox.set(self.tableId, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) } } diff --git a/Postbox/PeerPresenceTable.swift b/Postbox/PeerPresenceTable.swift index 18678c8436..80dca619eb 100644 --- a/Postbox/PeerPresenceTable.swift +++ b/Postbox/PeerPresenceTable.swift @@ -1,16 +1,16 @@ import Foundation final class PeerPresenceTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private let sharedEncoder = Encoder() private let sharedKey = ValueBoxKey(length: 8) private var cachedPresences: [PeerId: PeerPresence] = [:] private var updatedPeerIds = Set() - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) - } - private func key(_ id: PeerId) -> ValueBoxKey { self.sharedKey.setInt64(0, value: id.toInt64()) return self.sharedKey @@ -25,7 +25,7 @@ final class PeerPresenceTable: Table { if let presence = self.cachedPresences[id] { return presence } - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { if let presence = Decoder(buffer: value).decodeRootObject() as? PeerPresence { self.cachedPresences[id] = presence return presence @@ -45,7 +45,7 @@ final class PeerPresenceTable: Table { self.sharedEncoder.reset() self.sharedEncoder.encodeRootObject(presence) - self.valueBox.set(self.tableId, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) } } diff --git a/Postbox/PeerTable.swift b/Postbox/PeerTable.swift index 4b4b12b91b..5439cd664a 100644 --- a/Postbox/PeerTable.swift +++ b/Postbox/PeerTable.swift @@ -1,16 +1,16 @@ import Foundation final class PeerTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + private let sharedEncoder = Encoder() private let sharedKey = ValueBoxKey(length: 8) private var cachedPeers: [PeerId: Peer] = [:] private var updatedPeerIds = Set() - override init(valueBox: ValueBox, tableId: Int32) { - super.init(valueBox: valueBox, tableId: tableId) - } - private func key(_ id: PeerId) -> ValueBoxKey { self.sharedKey.setInt64(0, value: id.toInt64()) return self.sharedKey @@ -25,7 +25,7 @@ final class PeerTable: Table { if let peer = self.cachedPeers[id] { return peer } - if let value = self.valueBox.get(self.tableId, key: self.key(id)) { + if let value = self.valueBox.get(self.table, key: self.key(id)) { if let peer = Decoder(buffer: value).decodeRootObject() as? Peer { self.cachedPeers[id] = peer return peer @@ -45,7 +45,7 @@ final class PeerTable: Table { self.sharedEncoder.reset() self.sharedEncoder.encodeRootObject(peer) - self.valueBox.set(self.tableId, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) } } diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index 7612ea2b8c..351051a8fa 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -161,6 +161,18 @@ public final class Modifier { } return Set() } + + public func storedMessageId(peerId: PeerId, namespace: MessageId.Namespace, timestamp: Int32) -> MessageId? { + return self.postbox?.storedMessageId(peerId: peerId, namespace: namespace, timestamp: timestamp) + } + + public func putItemCacheEntry(id: ItemCacheEntryId, entry: Coding, collectionSpec: ItemCacheCollectionSpec) { + self.postbox?.putItemCacheEntry(id: id, entry: entry, collectionSpec: collectionSpec) + } + + public func retrieveItemCacheEntry(id: ItemCacheEntryId) -> Coding? { + return self.postbox?.retrieveItemCacheEntry(id: id) + } } fileprivate class PipeNotifier: NSObject { @@ -240,7 +252,6 @@ public final class Postbox { var messageHistoryIndexTable: MessageHistoryIndexTable! var messageHistoryTable: MessageHistoryTable! var mediaTable: MessageMediaTable! - var mediaCleanupTable: MediaCleanupTable! var chatListIndexTable: ChatListIndexTable! var chatListTable: ChatListTable! var messageHistoryMetadataTable: MessageHistoryMetadataTable! @@ -253,6 +264,8 @@ public final class Postbox { var itemCollectionInfoTable: ItemCollectionInfoTable! var itemCollectionItemTable: ItemCollectionItemTable! var peerChatInterfaceStateTable: PeerChatInterfaceStateTable! + var itemCacheMetaTable: ItemCacheMetaTable! + var itemCacheTable: ItemCacheTable! //temporary var peerRatingTable: RatingTable! @@ -332,39 +345,40 @@ public final class Postbox { self.valueBox = SqliteValueBox(basePath: self.basePath + "/db", queue: self.queue) - self.metadataTable = MetadataTable(valueBox: self.valueBox, tableId: 0) + self.metadataTable = MetadataTable(valueBox: self.valueBox, table: MetadataTable.tableSpec(0)) let userVersion: Int32? = self.metadataTable.userVersion() - let currentUserVersion: Int32 = 15 + let currentUserVersion: Int32 = 16 if userVersion != currentUserVersion { self.valueBox.drop() self.metadataTable.setUserVersion(currentUserVersion) } - self.keychainTable = KeychainTable(valueBox: self.valueBox, tableId: 1) - self.peerTable = PeerTable(valueBox: self.valueBox, tableId: 2) - self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox, tableId: 3, namespace: self.globalMessageIdsNamespace) - self.messageHistoryMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox, tableId: 10) - self.messageHistoryUnsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox, tableId: 11) - self.messageHistoryTagsTable = MessageHistoryTagsTable(valueBox: self.valueBox, tableId: 12) - self.messageHistoryIndexTable = MessageHistoryIndexTable(valueBox: self.valueBox, tableId: 4, globalMessageIdsTable: self.globalMessageIdsTable, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration) - self.mediaCleanupTable = MediaCleanupTable(valueBox: self.valueBox, tableId: 5) - self.mediaTable = MessageMediaTable(valueBox: self.valueBox, tableId: 6, mediaCleanupTable: self.mediaCleanupTable) - self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox, tableId: 14) - self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox, tableId: 15) - self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, tableId: 7, messageHistoryIndexTable: self.messageHistoryIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, unsentTable: self.messageHistoryUnsentTable!, tagsTable: self.messageHistoryTagsTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable!) - self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox, tableId: 8) - self.chatListTable = ChatListTable(valueBox: self.valueBox, tableId: 9, indexTable: self.chatListIndexTable, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration) - self.peerChatStateTable = PeerChatStateTable(valueBox: self.valueBox, tableId: 13) - self.contactsTable = ContactTable(valueBox: self.valueBox, tableId: 16) - self.peerRatingTable = RatingTable(valueBox: self.valueBox, tableId: 17) - self.cachedPeerDataTable = CachedPeerDataTable(valueBox: self.valueBox, tableId: 18) - self.peerNotificationSettingsTable = PeerNotificationSettingsTable(valueBox: self.valueBox, tableId: 19) - self.peerPresenceTable = PeerPresenceTable(valueBox: self.valueBox, tableId: 20) - self.itemCollectionInfoTable = ItemCollectionInfoTable(valueBox: self.valueBox, tableId: 21) - self.itemCollectionItemTable = ItemCollectionItemTable(valueBox: self.valueBox, tableId: 22) - self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox, tableId: 23) + self.keychainTable = KeychainTable(valueBox: self.valueBox, table: KeychainTable.tableSpec(1)) + self.peerTable = PeerTable(valueBox: self.valueBox, table: PeerTable.tableSpec(2)) + self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox, table: GlobalMessageIdsTable.tableSpec(3), namespace: self.globalMessageIdsNamespace) + self.messageHistoryMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox, table: MessageHistoryMetadataTable.tableSpec(10)) + self.messageHistoryUnsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox, table: MessageHistoryUnsentTable.tableSpec(11)) + self.messageHistoryTagsTable = MessageHistoryTagsTable(valueBox: self.valueBox, table: MessageHistoryTagsTable.tableSpec(12)) + self.messageHistoryIndexTable = MessageHistoryIndexTable(valueBox: self.valueBox, table: MessageHistoryIndexTable.tableSpec(4), globalMessageIdsTable: self.globalMessageIdsTable, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration) + self.mediaTable = MessageMediaTable(valueBox: self.valueBox, table: MessageMediaTable.tableSpec(6)) + self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox, table: MessageHistoryReadStateTable.tableSpec(14)) + self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox, table: MessageHistorySynchronizeReadStateTable.tableSpec(15)) + self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, table: MessageHistoryTable.tableSpec(7), messageHistoryIndexTable: self.messageHistoryIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, unsentTable: self.messageHistoryUnsentTable!, tagsTable: self.messageHistoryTagsTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable!) + self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox, table: ChatListIndexTable.tableSpec(8)) + self.chatListTable = ChatListTable(valueBox: self.valueBox, table: ChatListTable.tableSpec(9), indexTable: self.chatListIndexTable, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration) + self.peerChatStateTable = PeerChatStateTable(valueBox: self.valueBox, table: PeerChatStateTable.tableSpec(13)) + self.contactsTable = ContactTable(valueBox: self.valueBox, table: ContactTable.tableSpec(16)) + self.peerRatingTable = RatingTable(valueBox: self.valueBox, table: RatingTable.tableSpec(17)) + self.cachedPeerDataTable = CachedPeerDataTable(valueBox: self.valueBox, table: CachedPeerDataTable.tableSpec(18)) + self.peerNotificationSettingsTable = PeerNotificationSettingsTable(valueBox: self.valueBox, table: PeerNotificationSettingsTable.tableSpec(19)) + self.peerPresenceTable = PeerPresenceTable(valueBox: self.valueBox, table: PeerPresenceTable.tableSpec(20)) + self.itemCollectionInfoTable = ItemCollectionInfoTable(valueBox: self.valueBox, table: ItemCollectionInfoTable.tableSpec(21)) + self.itemCollectionItemTable = ItemCollectionItemTable(valueBox: self.valueBox, table: ItemCollectionItemTable.tableSpec(22)) + self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox, table: PeerChatInterfaceStateTable.tableSpec(23)) + self.itemCacheMetaTable = ItemCacheMetaTable(valueBox: self.valueBox, table: ItemCacheMetaTable.tableSpec(24)) + self.itemCacheTable = ItemCacheTable(valueBox: self.valueBox, table: ItemCacheTable.tableSpec(25)) self.tables.append(self.keychainTable) self.tables.append(self.peerTable) @@ -373,7 +387,6 @@ public final class Postbox { self.tables.append(self.messageHistoryUnsentTable) self.tables.append(self.messageHistoryTagsTable) self.tables.append(self.messageHistoryIndexTable) - self.tables.append(self.mediaCleanupTable) self.tables.append(self.mediaTable) self.tables.append(self.readStateTable) self.tables.append(self.synchronizeReadStateTable) @@ -389,6 +402,8 @@ public final class Postbox { self.tables.append(self.itemCollectionInfoTable) self.tables.append(self.itemCollectionItemTable) self.tables.append(self.peerChatInterfaceStateTable) + self.tables.append(self.itemCacheMetaTable) + self.tables.append(self.itemCacheTable) self.transactionStateVersion = self.metadataTable.transactionStateVersion() @@ -908,6 +923,18 @@ public final class Postbox { return filteredIds } + fileprivate func storedMessageId(peerId: PeerId, namespace: MessageId.Namespace, timestamp: Int32) -> MessageId? { + return self.messageHistoryTable.findMessageId(peerId: peerId, namespace: namespace, timestamp: timestamp) + } + + fileprivate func putItemCacheEntry(id: ItemCacheEntryId, entry: Coding, collectionSpec: ItemCacheCollectionSpec) { + self.itemCacheTable.put(id: id, entry: entry, metaTable: self.itemCacheMetaTable) + } + + fileprivate func retrieveItemCacheEntry(id: ItemCacheEntryId) -> Coding? { + return self.itemCacheTable.retrieve(id: id, metaTable: self.itemCacheMetaTable) + } + public func modify(userInteractive: Bool = false, _ f: @escaping(Modifier) -> T) -> Signal { return Signal { subscriber in let f: () -> Void = { diff --git a/Postbox/RatingTable.swift b/Postbox/RatingTable.swift index f91d1b08b7..42a1421197 100644 --- a/Postbox/RatingTable.swift +++ b/Postbox/RatingTable.swift @@ -23,6 +23,10 @@ extension PeerId: RatingTableItem { } final class RatingTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + private var items: [T]? func get() -> [T] { @@ -35,7 +39,7 @@ final class RatingTable: Table { memset(upperBound.memory, 0xff, upperBound.length) var result: [T] = [] - self.valueBox.range(self.tableId, start: lowerBound, end: upperBound, keys: { key in + self.valueBox.range(self.table, start: lowerBound, end: upperBound, keys: { key in result.append(T.fromKey(key: key)) return true }, limit: 0) @@ -51,19 +55,19 @@ final class RatingTable: Table { let upperBound = T.emptyKey() memset(lowerBound.memory, 0, lowerBound.length) memset(upperBound.memory, 0xff, upperBound.length) - self.valueBox.range(self.tableId, start: lowerBound, end: upperBound, keys: { key in + self.valueBox.range(self.table, start: lowerBound, end: upperBound, keys: { key in keys.append(key) return true }, limit: 0) for key in keys { - self.valueBox.remove(self.tableId, key: key) + self.valueBox.remove(self.table, key: key) } let sharedKey = T.emptyKey() var index: Int32 = 0 for item in items { - self.valueBox.set(self.tableId, key: item.ratingKey(rating: index, sharedKey: sharedKey), value: MemoryBuffer()) + self.valueBox.set(self.table, key: item.ratingKey(rating: index, sharedKey: sharedKey), value: MemoryBuffer()) index += 1 } diff --git a/Postbox/SqliteValueBox.swift b/Postbox/SqliteValueBox.swift index 12ffba1c27..f1a99b436d 100644 --- a/Postbox/SqliteValueBox.swift +++ b/Postbox/SqliteValueBox.swift @@ -6,6 +6,15 @@ import sqlcipher import SwiftSignalKit #endif +private func checkTableKey(_ table: ValueBoxTable, _ key: ValueBoxKey) { + switch table.keyType { + case .binary: + break + case .int64: + assert(key.length == 8) + } +} + private struct SqlitePreparedStatement { let statement: OpaquePointer? @@ -13,6 +22,10 @@ private struct SqlitePreparedStatement { sqlite3_bind_blob(statement, Int32(index), data, Int32(length), nil) } + func bind(_ index: Int, number: Int64) { + sqlite3_bind_int64(statement, Int32(index), number) + } + func bindNull(_ index: Int) { sqlite3_bind_null(statement, Int32(index)) } @@ -21,10 +34,6 @@ private struct SqlitePreparedStatement { sqlite3_bind_int(statement, Int32(index), number) } - func bind(_ index: Int, number: Int64) { - sqlite3_bind_int64(statement, Int32(index), number) - } - func reset() { sqlite3_reset(statement) sqlite3_clear_bindings(statement) @@ -65,12 +74,12 @@ private struct SqlitePreparedStatement { } } -public final class SqliteValueBox: ValueBox { +final class SqliteValueBox: ValueBox { private let lock = NSRecursiveLock() private let basePath: String private var database: Database! - private var tables = Set() + private var tables: [Int32: ValueBoxTable] = [:] private var getStatements: [Int32 : SqlitePreparedStatement] = [:] private var rangeKeyAscStatementsLimit: [Int32 : SqlitePreparedStatement] = [:] private var rangeKeyAscStatementsNoLimit: [Int32 : SqlitePreparedStatement] = [:] @@ -83,6 +92,7 @@ public final class SqliteValueBox: ValueBox { private var existsStatements: [Int32 : SqlitePreparedStatement] = [:] private var updateStatements: [Int32 : SqlitePreparedStatement] = [:] private var insertStatements: [Int32 : SqlitePreparedStatement] = [:] + private var insertOrReplaceStatements: [Int32 : SqlitePreparedStatement] = [:] private var deleteStatements: [Int32 : SqlitePreparedStatement] = [:] private var readQueryTime: CFAbsoluteTime = 0.0 @@ -137,13 +147,14 @@ public final class SqliteValueBox: ValueBox { sqlite3_busy_timeout(database.handle, 10000000) let result = self.getUserVersion(database) - if result != 1 { - database.execute("PRAGMA user_version=1") - database.execute("CREATE TABLE __meta_tables (name INTEGER)") + if result != 2 { + database.execute("PRAGMA user_version=2") + database.execute("DROP TABLE IF EXISTS __meta_tables") + database.execute("CREATE TABLE __meta_tables (name INTEGER, keyType INTEGER)") } - for table in self.listTables(database).map({Int32($0)}) { - self.tables.insert(table) + for table in self.listTables(database) { + self.tables[table.id] = table } lock.unlock() @@ -195,279 +206,394 @@ public final class SqliteValueBox: ValueBox { return value } - private func listTables(_ database: Database) -> [Int64] { + private func listTables(_ database: Database) -> [ValueBoxTable] { assert(self.queue.isCurrent()) var statement: OpaquePointer? = nil - sqlite3_prepare_v2(database.handle, "SELECT name FROM __meta_tables", -1, &statement, nil) + sqlite3_prepare_v2(database.handle, "SELECT name, keyType FROM __meta_tables", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - var tables: [Int64] = [] + var tables: [ValueBoxTable] = [] while preparedStatement.step() { let value = preparedStatement.int64At(0) - tables.append(value) + let keyType = preparedStatement.int64At(1) + tables.append(ValueBoxTable(id: Int32(value), keyType: ValueBoxKeyType(rawValue: Int32(keyType))!)) } preparedStatement.destroy() return tables } - private func getStatement(_ table: Int32, key: ValueBoxKey) -> SqlitePreparedStatement { + private func checkTable(_ table: ValueBoxTable) { + if let currentTable = self.tables[table.id] { + precondition(currentTable.keyType == table.keyType) + } else { + switch table.keyType { + case .binary: + self.database.execute("CREATE TABLE t\(table.id) (key BLOB, value BLOB)") + self.database.execute("CREATE INDEX t\(table.id)_key ON t\(table.id) (key)") + case .int64: + self.database.execute("CREATE TABLE t\(table.id) (key INTEGER PRIMARY KEY, value BLOB)") + } + self.tables[table.id] = table + self.database.execute("INSERT INTO __meta_tables(name, keyType) VALUES (\(table.id), \(table.keyType.rawValue))") + } + } + + private func getStatement(_ table: ValueBoxTable, key: ValueBoxKey) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, key) + let resultStatement: SqlitePreparedStatement - if let statement = self.getStatements[table] { + if let statement = self.getStatements[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT value FROM t\(table) WHERE key=?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT value FROM t\(table.id) WHERE key=?", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.getStatements[table] = preparedStatement + self.getStatements[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: key.memory, length: key.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: key.memory, length: key.length) + case .int64: + resultStatement.bind(1, number: key.getInt64(0)) + } return resultStatement } - private func rangeKeyAscStatementLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { + private func rangeKeyAscStatementLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, start) + checkTableKey(table, end) + let resultStatement: SqlitePreparedStatement - if let statement = self.rangeKeyAscStatementsLimit[table] { + if let statement = self.rangeKeyAscStatementsLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeKeyAscStatementsLimit[table] = preparedStatement + self.rangeKeyAscStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } resultStatement.bind(3, number: Int32(limit)) return resultStatement } - private func rangeKeyAscStatementNoLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> + private func rangeKeyAscStatementNoLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement { - assert(self.queue.isCurrent()) + assert(self.queue.isCurrent()) + checkTableKey(table, start) + checkTableKey(table, end) + let resultStatement: SqlitePreparedStatement - if let statement = self.rangeKeyAscStatementsNoLimit[table] { + if let statement = self.rangeKeyAscStatementsNoLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeKeyAscStatementsNoLimit[table] = preparedStatement + self.rangeKeyAscStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } return resultStatement } - private func rangeKeyDescStatementLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { + private func rangeKeyDescStatementLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { let resultStatement: SqlitePreparedStatement + checkTableKey(table, start) + checkTableKey(table, end) - if let statement = self.rangeKeyDescStatementsLimit[table] { + if let statement = self.rangeKeyDescStatementsLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeKeyDescStatementsLimit[table] = preparedStatement + self.rangeKeyDescStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } resultStatement.bind(3, number: Int32(limit)) return resultStatement } - private func rangeKeyDescStatementNoLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement { + private func rangeKeyDescStatementNoLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) let resultStatement: SqlitePreparedStatement + checkTableKey(table, start) + checkTableKey(table, end) - if let statement = self.rangeKeyDescStatementsNoLimit[table] { + if let statement = self.rangeKeyDescStatementsNoLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeKeyDescStatementsNoLimit[table] = preparedStatement + self.rangeKeyDescStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } return resultStatement } - private func rangeValueAscStatementLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { + private func rangeValueAscStatementLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, start) + checkTableKey(table, end) + let resultStatement: SqlitePreparedStatement - if let statement = self.rangeValueAscStatementsLimit[table] { + if let statement = self.rangeValueAscStatementsLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeValueAscStatementsLimit[table] = preparedStatement + self.rangeValueAscStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } resultStatement.bind(3, number: Int32(limit)) return resultStatement } - private func rangeValueAscStatementNoLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement { + private func rangeValueAscStatementNoLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, start) + checkTableKey(table, end) + let resultStatement: SqlitePreparedStatement - if let statement = self.rangeValueAscStatementsNoLimit[table] { + if let statement = self.rangeValueAscStatementsNoLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeValueAscStatementsNoLimit[table] = preparedStatement + self.rangeValueAscStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } return resultStatement } - private func rangeValueDescStatementLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { + private func rangeValueDescStatementLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, start) + checkTableKey(table, end) + let resultStatement: SqlitePreparedStatement - if let statement = self.rangeValueDescStatementsLimit[table] { + if let statement = self.rangeValueDescStatementsLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeValueDescStatementsLimit[table] = preparedStatement + self.rangeValueDescStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } resultStatement.bind(3, number: Int32(limit)) return resultStatement } - private func rangeValueDescStatementNoLimit(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement { + private func rangeValueDescStatementNoLimit(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, start) + checkTableKey(table, end) + let resultStatement: SqlitePreparedStatement - if let statement = self.rangeKeyDescStatementsNoLimit[table] { + if let statement = self.rangeKeyDescStatementsNoLimit[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.rangeValueDescStatementsNoLimit[table] = preparedStatement + self.rangeValueDescStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: start.memory, length: start.length) - resultStatement.bind(2, data: end.memory, length: end.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: start.memory, length: start.length) + resultStatement.bind(2, data: end.memory, length: end.length) + case .int64: + resultStatement.bind(1, number: start.getInt64(0)) + resultStatement.bind(2, number: end.getInt64(0)) + } return resultStatement } - private func existsStatement(_ table: Int32, key: ValueBoxKey) -> SqlitePreparedStatement { + private func existsStatement(_ table: ValueBoxTable, key: ValueBoxKey) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, key) + let resultStatement: SqlitePreparedStatement - if let statement = self.existsStatements[table] { + if let statement = self.existsStatements[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT rowid FROM t\(table) WHERE key=?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "SELECT rowid FROM t\(table.id) WHERE key=?", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.existsStatements[table] = preparedStatement + self.existsStatements[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: key.memory, length: key.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: key.memory, length: key.length) + case .int64: + resultStatement.bind(1, number: key.getInt64(0)) + } return resultStatement } - private func updateStatement(_ table: Int32, key: ValueBoxKey, value: MemoryBuffer) -> SqlitePreparedStatement { + private func updateStatement(_ table: ValueBoxTable, key: ValueBoxKey, value: MemoryBuffer) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, key) + let resultStatement: SqlitePreparedStatement - if let statement = self.updateStatements[table] { + if let statement = self.updateStatements[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "UPDATE t\(table) SET value=? WHERE key=?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "UPDATE t\(table.id) SET value=? WHERE key=?", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.updateStatements[table] = preparedStatement + self.updateStatements[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() resultStatement.bind(1, data: value.memory, length: value.length) - resultStatement.bind(2, data: key.memory, length: key.length) + switch table.keyType { + case .binary: + resultStatement.bind(2, data: key.memory, length: key.length) + case .int64: + resultStatement.bind(2, number: key.getInt64(0)) + } return resultStatement } - private func insertStatement(_ table: Int32, key: ValueBoxKey, value: MemoryBuffer) -> SqlitePreparedStatement { + private func insertStatement(_ table: ValueBoxTable, key: ValueBoxKey, value: MemoryBuffer) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, key) + let resultStatement: SqlitePreparedStatement - if let statement = self.insertStatements[table] { + if let statement = self.insertStatements[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "INSERT INTO t\(table) (key, value) VALUES(?, ?)", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "INSERT INTO t\(table.id) (key, value) VALUES(?, ?)", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.insertStatements[table] = preparedStatement + self.insertStatements[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: key.memory, length: key.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: key.memory, length: key.length) + case .int64: + resultStatement.bind(1, number: key.getInt64(0)) + } if value.length == 0 { resultStatement.bindNull(2) } else { @@ -477,31 +603,71 @@ public final class SqliteValueBox: ValueBox { return resultStatement } - private func deleteStatement(_ table: Int32, key: ValueBoxKey) -> SqlitePreparedStatement { + private func insertOrReplaceStatement(_ table: ValueBoxTable, key: ValueBoxKey, value: MemoryBuffer) -> SqlitePreparedStatement { assert(self.queue.isCurrent()) + checkTableKey(table, key) + let resultStatement: SqlitePreparedStatement - if let statement = self.deleteStatements[table] { + if let statement = self.insertOrReplaceStatements[table.id] { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "DELETE FROM t\(table) WHERE key=?", -1, &statement, nil) + sqlite3_prepare_v2(self.database.handle, "INSERT OR REPLACE INTO t\(table.id) (key, value) VALUES(?, ?)", -1, &statement, nil) let preparedStatement = SqlitePreparedStatement(statement: statement) - self.deleteStatements[table] = preparedStatement + self.insertOrReplaceStatements[table.id] = preparedStatement resultStatement = preparedStatement } resultStatement.reset() - resultStatement.bind(1, data: key.memory, length: key.length) + switch table.keyType { + case .binary: + resultStatement.bind(1, data: key.memory, length: key.length) + case .int64: + resultStatement.bind(1, number: key.getInt64(0)) + } + if value.length == 0 { + resultStatement.bindNull(2) + } else { + resultStatement.bind(2, data: value.memory, length: value.length) + } return resultStatement } - public func get(_ table: Int32, key: ValueBoxKey) -> ReadBuffer? { + private func deleteStatement(_ table: ValueBoxTable, key: ValueBoxKey) -> SqlitePreparedStatement { + assert(self.queue.isCurrent()) + checkTableKey(table, key) + + let resultStatement: SqlitePreparedStatement + + if let statement = self.deleteStatements[table.id] { + resultStatement = statement + } else { + var statement: OpaquePointer? = nil + sqlite3_prepare_v2(self.database.handle, "DELETE FROM t\(table.id) WHERE key=?", -1, &statement, nil) + let preparedStatement = SqlitePreparedStatement(statement: statement) + self.deleteStatements[table.id] = preparedStatement + resultStatement = preparedStatement + } + + resultStatement.reset() + + switch table.keyType { + case .binary: + resultStatement.bind(1, data: key.memory, length: key.length) + case .int64: + resultStatement.bind(1, number: key.getInt64(0)) + } + + return resultStatement + } + + public func get(_ table: ValueBoxTable, key: ValueBoxKey) -> ReadBuffer? { assert(self.queue.isCurrent()) let startTime = CFAbsoluteTimeGetCurrent() - if self.tables.contains(table) { + if let _ = self.tables[table.id] { let statement = self.getStatement(table, key: key) var buffer: ReadBuffer? @@ -521,7 +687,7 @@ public final class SqliteValueBox: ValueBox { return nil } - public func exists(_ table: Int32, key: ValueBoxKey) -> Bool { + public func exists(_ table: ValueBoxTable, key: ValueBoxKey) -> Bool { assert(self.queue.isCurrent()) if let _ = self.get(table, key: key) { return true @@ -529,13 +695,13 @@ public final class SqliteValueBox: ValueBox { return false } - public func range(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, values: @noescape(ValueBoxKey, ReadBuffer) -> Bool, limit: Int) { + public func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, values: (ValueBoxKey, ReadBuffer) -> Bool, limit: Int) { assert(self.queue.isCurrent()) if start == end { return } - if self.tables.contains(table) { + if let _ = self.tables[table.id] { let statement: SqlitePreparedStatement var startTime = CFAbsoluteTimeGetCurrent() @@ -577,9 +743,9 @@ public final class SqliteValueBox: ValueBox { } } - public func range(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, keys: @noescape(ValueBoxKey) -> Bool, limit: Int) { + public func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, keys: (ValueBoxKey) -> Bool, limit: Int) { assert(self.queue.isCurrent()) - if self.tables.contains(table) { + if let _ = self.tables[table.id] { let statement: SqlitePreparedStatement var startTime = CFAbsoluteTimeGetCurrent() @@ -620,43 +786,44 @@ public final class SqliteValueBox: ValueBox { } } - public func set(_ table: Int32, key: ValueBoxKey, value: MemoryBuffer) { + public func set(_ table: ValueBoxTable, key: ValueBoxKey, value: MemoryBuffer) { assert(self.queue.isCurrent()) - if !self.tables.contains(table) { - self.database.execute("CREATE TABLE t\(table) (key BLOB, value BLOB)") - self.database.execute("CREATE INDEX t\(table)_key ON t\(table) (key)") - self.tables.insert(table) - self.database.execute("INSERT INTO __meta_tables(name) VALUES (\(table))") - } + self.checkTable(table) let startTime = CFAbsoluteTimeGetCurrent() - var exists = false - let existsStatement = self.existsStatement(table, key: key) - if existsStatement.step() { - exists = true - } - existsStatement.reset() - - if exists { - let statement = self.updateStatement(table, key: key, value: value) + if case .int64 = table.keyType { + let statement = self.insertOrReplaceStatement(table, key: key, value: value) while statement.step() { } statement.reset() } else { - let statement = self.insertStatement(table, key: key, value: value) - while statement.step() { + var exists = false + let existsStatement = self.existsStatement(table, key: key) + if existsStatement.step() { + exists = true + } + existsStatement.reset() + + if exists { + let statement = self.updateStatement(table, key: key, value: value) + while statement.step() { + } + statement.reset() + } else { + let statement = self.insertStatement(table, key: key, value: value) + while statement.step() { + } + statement.reset() } - statement.reset() } self.writeQueryTime += CFAbsoluteTimeGetCurrent() - startTime } - public func remove(_ table: Int32, key: ValueBoxKey) { + public func remove(_ table: ValueBoxTable, key: ValueBoxKey) { assert(self.queue.isCurrent()) - if self.tables.contains(table) { - + if let _ = self.tables[table.id] { let startTime = CFAbsoluteTimeGetCurrent() let statement = self.deleteStatement(table, key: key) @@ -730,6 +897,11 @@ public final class SqliteValueBox: ValueBox { } self.insertStatements.removeAll() + for (_, statement) in self.insertOrReplaceStatements { + statement.destroy() + } + self.insertOrReplaceStatements.removeAll() + for (_, statement) in self.deleteStatements { statement.destroy() } diff --git a/Postbox/Table.swift b/Postbox/Table.swift index 08edb399ff..9f51743dab 100644 --- a/Postbox/Table.swift +++ b/Postbox/Table.swift @@ -2,11 +2,11 @@ import Foundation class Table { final let valueBox: ValueBox - final let tableId: Int32 + final let table: ValueBoxTable - init(valueBox: ValueBox, tableId: Int32) { + init(valueBox: ValueBox, table: ValueBoxTable) { self.valueBox = valueBox - self.tableId = tableId + self.table = table } func clearMemoryCache() { diff --git a/Postbox/ValueBox.swift b/Postbox/ValueBox.swift index 4c6abb138f..1de04fe06f 100644 --- a/Postbox/ValueBox.swift +++ b/Postbox/ValueBox.swift @@ -1,17 +1,27 @@ import Foundation -public protocol ValueBox { +enum ValueBoxKeyType: Int32 { + case binary + case int64 +} + +struct ValueBoxTable { + let id: Int32 + let keyType: ValueBoxKeyType +} + +protocol ValueBox { func begin() func commit() func beginStats() func endStats() - func range(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, values: @noescape(ValueBoxKey, ReadBuffer) -> Bool, limit: Int) - func range(_ table: Int32, start: ValueBoxKey, end: ValueBoxKey, keys: @noescape(ValueBoxKey) -> Bool, limit: Int) - func get(_ table: Int32, key: ValueBoxKey) -> ReadBuffer? - func exists(_ table: Int32, key: ValueBoxKey) -> Bool - func set(_ table: Int32, key: ValueBoxKey, value: MemoryBuffer) - func remove(_ table: Int32, key: ValueBoxKey) + func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, values: @noescape(ValueBoxKey, ReadBuffer) -> Bool, limit: Int) + func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, keys: @noescape(ValueBoxKey) -> Bool, limit: Int) + func get(_ table: ValueBoxTable, key: ValueBoxKey) -> ReadBuffer? + func exists(_ table: ValueBoxTable, key: ValueBoxKey) -> Bool + func set(_ table: ValueBoxTable, key: ValueBoxKey, value: MemoryBuffer) + func remove(_ table: ValueBoxTable, key: ValueBoxKey) func drop() } diff --git a/PostboxTests/ChatListTableTests.swift b/PostboxTests/ChatListTableTests.swift index f0e6bd6c36..3dfd380568 100644 --- a/PostboxTests/ChatListTableTests.swift +++ b/PostboxTests/ChatListTableTests.swift @@ -68,7 +68,6 @@ class ChatListTableTests: XCTestCase { var globalMessageIdsTable: GlobalMessageIdsTable? var indexTable: MessageHistoryIndexTable? var mediaTable: MessageMediaTable? - var mediaCleanupTable: MediaCleanupTable? var historyTable: MessageHistoryTable? var chatListIndexTable: ChatListIndexTable? var chatListTable: ChatListTable? @@ -93,19 +92,18 @@ class ChatListTableTests: XCTestCase { let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [], initializeMessageNamespacesWithHoles: [], existingMessageTags: []) - self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, tableId: 7, namespace: namespace) - self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, tableId: 8) - self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, tableId: 9) - self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, tableId: 10) - self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, tableId: 1, globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) - self.mediaCleanupTable = MediaCleanupTable(valueBox: self.valueBox!, tableId: 3) - self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, tableId: 2, mediaCleanupTable: self.mediaCleanupTable!) - self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, tableId: 11) - self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, tableId: 12) - self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, tableId: 4, messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) - self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox!, tableId: 5) - self.chatListTable = ChatListTable(valueBox: self.valueBox!, tableId: 6, indexTable: self.chatListIndexTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) - self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox!, tableId: 20) + self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, table: GlobalMessageIdsTable.tableSpec(7), namespace: namespace) + self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, table: MessageHistoryMetadataTable.tableSpec(8)) + self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, table: MessageHistoryUnsentTable.tableSpec(9)) + self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, table: MessageHistoryTagsTable.tableSpec(10)) + self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, table: MessageHistoryIndexTable.tableSpec(1), globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) + self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, table: MessageMediaTable.tableSpec(2)) + self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, table: MessageHistoryReadStateTable.tableSpec(11)) + self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, table: MessageHistorySynchronizeReadStateTable.tableSpec(12)) + self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, table: MessageHistoryTable.tableSpec(4), messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) + self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox!, table: ChatListIndexTable.tableSpec(5)) + self.chatListTable = ChatListTable(valueBox: self.valueBox!, table: ChatListTable.tableSpec(6), indexTable: self.chatListIndexTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) + self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox!, table: PeerChatInterfaceStateTable.tableSpec(20)) } override func tearDown() { @@ -114,7 +112,6 @@ class ChatListTableTests: XCTestCase { self.historyTable = nil self.indexTable = nil self.mediaTable = nil - self.mediaCleanupTable = nil self.chatListIndexTable = nil self.chatListTable = nil diff --git a/PostboxTests/MessageHistoryIndexTableTests.swift b/PostboxTests/MessageHistoryIndexTableTests.swift index c39f0cb771..f55ee0490a 100644 --- a/PostboxTests/MessageHistoryIndexTableTests.swift +++ b/PostboxTests/MessageHistoryIndexTableTests.swift @@ -79,9 +79,9 @@ class MessageHistoryIndexTableTests: XCTestCase { let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [], initializeMessageNamespacesWithHoles: [], existingMessageTags: []) - self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, tableId: 2, namespace: namespace) - self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, tableId: 8) - self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, tableId: 1, globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) + self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, table: GlobalMessageIdsTable.tableSpec(2), namespace: namespace) + self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, table: MessageHistoryMetadataTable.tableSpec(8)) + self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, table: MessageHistoryIndexTable.tableSpec(1), globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) } override func tearDown() { diff --git a/PostboxTests/MessageHistoryTableTests.swift b/PostboxTests/MessageHistoryTableTests.swift index 7c6775adf7..153912cd12 100644 --- a/PostboxTests/MessageHistoryTableTests.swift +++ b/PostboxTests/MessageHistoryTableTests.swift @@ -203,7 +203,6 @@ class MessageHistoryTableTests: XCTestCase { var globalMessageIdsTable: GlobalMessageIdsTable? var indexTable: MessageHistoryIndexTable? var mediaTable: MessageMediaTable? - var mediaCleanupTable: MediaCleanupTable? var historyTable: MessageHistoryTable? var historyMetadataTable: MessageHistoryMetadataTable? var unsentTable: MessageHistoryUnsentTable? @@ -229,17 +228,16 @@ class MessageHistoryTableTests: XCTestCase { let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [], initializeMessageNamespacesWithHoles: [], existingMessageTags: [.First, .Second]) - self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, tableId: 5, namespace: namespace) - self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, tableId: 7) - self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, tableId: 8) - self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, tableId: 9) - self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, tableId: 1, globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) - self.mediaCleanupTable = MediaCleanupTable(valueBox: self.valueBox!, tableId: 3) - self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, tableId: 2, mediaCleanupTable: self.mediaCleanupTable!) - self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, tableId: 10) - self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, tableId: 11) - self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, tableId: 4, messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) - self.peerTable = PeerTable(valueBox: self.valueBox!, tableId: 6) + self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, table: GlobalMessageIdsTable.tableSpec(5), namespace: namespace) + self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, table: MessageHistoryMetadataTable.tableSpec(7)) + self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, table: MessageHistoryUnsentTable.tableSpec(8)) + self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, table: MessageHistoryTagsTable.tableSpec(9)) + self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, table: MessageHistoryIndexTable.tableSpec(1), globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) + self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, table: MessageMediaTable.tableSpec(2)) + self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, table: MessageHistoryReadStateTable.tableSpec(10)) + self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, table: MessageHistorySynchronizeReadStateTable.tableSpec(11)) + self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, table: MessageHistoryTable.tableSpec(4), messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) + self.peerTable = PeerTable(valueBox: self.valueBox!, table: PeerTable.tableSpec(6)) self.peerTable!.set(peer) } @@ -249,7 +247,6 @@ class MessageHistoryTableTests: XCTestCase { self.historyTable = nil self.indexTable = nil self.mediaTable = nil - self.mediaCleanupTable = nil self.peerTable = nil self.historyMetadataTable = nil @@ -361,25 +358,6 @@ class MessageHistoryTableTests: XCTestCase { } } - private func expectCleanupMedia(_ media: [Media]) { - let actualMedia = self.mediaCleanupTable!.debugList() - var equal = true - if media.count != actualMedia.count { - equal = false - } else { - for i in 0 ..< media.count { - if !media[i].isEqual(actualMedia[i]) { - equal = false - break - } - } - } - - if !equal { - XCTFail("Expected\n\(media)\nActual\n\(actualMedia)") - } - } - func testInsertMessageIntoEmpty() { addMessage(100, 100, "t100") addMessage(200, 200, "t200") @@ -444,7 +422,6 @@ class MessageHistoryTableTests: XCTestCase { expectEntries([]) expectMedia([]) - expectCleanupMedia([media]) } func testRemoveOnlyReferenceToExternalMedia() { @@ -454,7 +431,6 @@ class MessageHistoryTableTests: XCTestCase { expectEntries([]) expectMedia([]) - expectCleanupMedia([media]) } func testRemoveReferenceToExternalMedia() { @@ -465,13 +441,11 @@ class MessageHistoryTableTests: XCTestCase { expectEntries([.Message(200, 200, "t200", [media], [])]) expectMedia([.Direct(media, 1)]) - expectCleanupMedia([]) removeMessages([200]) expectEntries([]) expectMedia([]) - expectCleanupMedia([media]) } func testAddHoleToEmpty() { diff --git a/PostboxTests/OrderStatisticTreeTests.swift b/PostboxTests/OrderStatisticTreeTests.swift index 2e17bf0b1e..3e40008b8b 100644 --- a/PostboxTests/OrderStatisticTreeTests.swift +++ b/PostboxTests/OrderStatisticTreeTests.swift @@ -71,7 +71,6 @@ class OrderStatisticTreeTests: XCTestCase { var globalMessageIdsTable: GlobalMessageIdsTable? var indexTable: MessageHistoryIndexTable? var mediaTable: MessageMediaTable? - var mediaCleanupTable: MediaCleanupTable? var historyTable: MessageHistoryTable? var historyMetadataTable: MessageHistoryMetadataTable? var unsentTable: MessageHistoryUnsentTable? @@ -89,16 +88,15 @@ class OrderStatisticTreeTests: XCTestCase { let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [], initializeMessageNamespacesWithHoles: [], existingMessageTags: [.First, .Second]) - self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, tableId: 5, namespace: namespace) - self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, tableId: 7) - self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, tableId: 8) - self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, tableId: 9) - self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, tableId: 1, globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) - self.mediaCleanupTable = MediaCleanupTable(valueBox: self.valueBox!, tableId: 3) - self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, tableId: 2, mediaCleanupTable: self.mediaCleanupTable!) - self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, tableId: 10) - self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, tableId: 11) - self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, tableId: 4, messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) + self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, table: GlobalMessageIdsTable.tableSpec(5), namespace: namespace) + self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, table: MessageHistoryMetadataTable.tableSpec(7)) + self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, table: MessageHistoryUnsentTable.tableSpec(8)) + self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, table: MessageHistoryTagsTable.tableSpec(9)) + self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, table: MessageHistoryIndexTable.tableSpec(1), globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) + self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, table: MessageMediaTable.tableSpec(2)) + self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, table: MessageHistoryReadStateTable.tableSpec(10)) + self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, table: MessageHistorySynchronizeReadStateTable.tableSpec(11)) + self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, table: MessageHistoryTable.tableSpec(4), messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) } override func tearDown() { @@ -107,7 +105,6 @@ class OrderStatisticTreeTests: XCTestCase { self.historyTable = nil self.indexTable = nil self.mediaTable = nil - self.mediaCleanupTable = nil self.peerTable = nil self.historyMetadataTable = nil diff --git a/PostboxTests/ReadStateTableTests.swift b/PostboxTests/ReadStateTableTests.swift index 8005f60b71..ddb5b1642a 100644 --- a/PostboxTests/ReadStateTableTests.swift +++ b/PostboxTests/ReadStateTableTests.swift @@ -71,7 +71,6 @@ class ReadStateTableTests: XCTestCase { var globalMessageIdsTable: GlobalMessageIdsTable? var indexTable: MessageHistoryIndexTable? var mediaTable: MessageMediaTable? - var mediaCleanupTable: MediaCleanupTable? var historyTable: MessageHistoryTable? var historyMetadataTable: MessageHistoryMetadataTable? var unsentTable: MessageHistoryUnsentTable? @@ -89,16 +88,15 @@ class ReadStateTableTests: XCTestCase { let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [], initializeMessageNamespacesWithHoles: [], existingMessageTags: [.First, .Second]) - self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, tableId: 5, namespace: namespace) - self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, tableId: 7) - self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, tableId: 8) - self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, tableId: 9) - self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, tableId: 1, globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) - self.mediaCleanupTable = MediaCleanupTable(valueBox: self.valueBox!, tableId: 3) - self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, tableId: 2, mediaCleanupTable: self.mediaCleanupTable!) - self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, tableId: 10) - self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, tableId: 11) - self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, tableId: 4, messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) + self.globalMessageIdsTable = GlobalMessageIdsTable(valueBox: self.valueBox!, table: GlobalMessageIdsTable.tableSpec(5), namespace: namespace) + self.historyMetadataTable = MessageHistoryMetadataTable(valueBox: self.valueBox!, table: MessageHistoryMetadataTable.tableSpec(7)) + self.unsentTable = MessageHistoryUnsentTable(valueBox: self.valueBox!, table: MessageHistoryUnsentTable.tableSpec(8)) + self.tagsTable = MessageHistoryTagsTable(valueBox: self.valueBox!, table: MessageHistoryTagsTable.tableSpec(9)) + self.indexTable = MessageHistoryIndexTable(valueBox: self.valueBox!, table: MessageHistoryIndexTable.tableSpec(1), globalMessageIdsTable: self.globalMessageIdsTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) + self.mediaTable = MessageMediaTable(valueBox: self.valueBox!, table: MessageMediaTable.tableSpec(2)) + self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, table: MessageHistoryReadStateTable.tableSpec(10)) + self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, table: MessageHistorySynchronizeReadStateTable.tableSpec(11)) + self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, table: MessageHistoryTable.tableSpec(4), messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) } override func tearDown() { @@ -107,7 +105,6 @@ class ReadStateTableTests: XCTestCase { self.historyTable = nil self.indexTable = nil self.mediaTable = nil - self.mediaCleanupTable = nil self.peerTable = nil self.historyMetadataTable = nil