import Foundation private func extractKey(_ key: ValueBoxKey) -> MessageIndex { return MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 8), id: key.getInt32(8 + 8 + 4 + 4)), timestamp: key.getInt32(8 + 8 + 4)) } class MessageHistoryThreadsTable: Table { struct ItemId: Hashable { var peerId: PeerId var threadId: Int64 } static func tableSpec(_ id: Int32) -> ValueBoxTable { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true) } private let sharedKey = ValueBoxKey(length: 8 + 8 + 4 + 4 + 4) private(set) var updatedIds = Set() override init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool) { super.init(valueBox: valueBox, table: table, useCaches: useCaches) } private func key(threadId: Int64, index: MessageIndex, key: ValueBoxKey = ValueBoxKey(length: 8 + 8 + 4 + 4 + 4)) -> ValueBoxKey { key.setInt64(0, value: index.id.peerId.toInt64()) key.setInt64(8, value: threadId) key.setInt32(8 + 8, value: index.id.namespace) key.setInt32(8 + 8 + 4, value: index.timestamp) key.setInt32(8 + 8 + 4 + 4, value: index.id.id) return key } private func lowerBound(threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey { let key = ValueBoxKey(length: 8 + 8 + 4) key.setInt64(0, value: peerId.toInt64()) key.setInt64(8, value: threadId) key.setInt32(8 + 8, value: namespace) return key } private func upperBound(threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey { return self.lowerBound(threadId: threadId, peerId: peerId, namespace: namespace).successor } func add(threadId: Int64, index: MessageIndex) { self.valueBox.set(self.table, key: self.key(threadId: threadId, index: index, key: self.sharedKey), value: MemoryBuffer()) self.updatedIds.insert(ItemId(peerId: index.id.peerId, threadId: threadId)) } func remove(threadId: Int64, index: MessageIndex) { self.valueBox.remove(self.table, key: self.key(threadId: threadId, index: index, key: self.sharedKey), secure: false) self.updatedIds.insert(ItemId(peerId: index.id.peerId, threadId: threadId)) } func earlierIndices(threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace, index: MessageIndex?, includeFrom: Bool, count: Int) -> [MessageIndex] { var indices: [MessageIndex] = [] let key: ValueBoxKey if let index = index { if includeFrom { key = self.key(threadId: threadId, index: index).successor } else { key = self.key(threadId: threadId, index: index) } } else { key = self.upperBound(threadId: threadId, peerId: peerId, namespace: namespace) } self.valueBox.range(self.table, start: key, end: self.lowerBound(threadId: threadId, peerId: peerId, namespace: namespace), keys: { key in indices.append(extractKey(key)) return true }, limit: count) return indices } func laterIndices(threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace, index: MessageIndex?, includeFrom: Bool, count: Int) -> [MessageIndex] { var indices: [MessageIndex] = [] let key: ValueBoxKey if let index = index { if includeFrom { key = self.key(threadId: threadId, index: index).predecessor } else { key = self.key(threadId: threadId, index: index) } } else { key = self.lowerBound(threadId: threadId, peerId: peerId, namespace: namespace) } self.valueBox.range(self.table, start: key, end: self.upperBound(threadId: threadId, peerId: peerId, namespace: namespace), keys: { key in indices.append(extractKey(key)) return true }, limit: count) return indices } func getTop(peerId: PeerId, threadId: Int64, namespaces: Set) -> MessageIndex? { var maxIndex: MessageIndex? for namespace in namespaces { var namespaceIndex: MessageIndex? self.valueBox.range(self.table, start: self.upperBound(threadId: threadId, peerId: peerId, namespace: namespace), end: self.lowerBound(threadId: threadId, peerId: peerId, namespace: namespace), keys: { key in namespaceIndex = extractKey(key) return false }, limit: 1) if let namespaceIndex = namespaceIndex { if let maxIndexValue = maxIndex { if namespaceIndex > maxIndexValue { maxIndex = namespaceIndex } } else { maxIndex = namespaceIndex } } } return maxIndex } func getMessageCountInRange(threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace, lowerBound: MessageIndex?, upperBound: MessageIndex?) -> Int { if let lowerBound = lowerBound { precondition(lowerBound.id.namespace == namespace) } if let upperBound = upperBound { precondition(upperBound.id.namespace == namespace) } var lowerBoundKey: ValueBoxKey if let lowerBound = lowerBound { lowerBoundKey = self.key(threadId: threadId, index: lowerBound) if lowerBound.timestamp > 1 { lowerBoundKey = lowerBoundKey.predecessor } } else { lowerBoundKey = self.lowerBound(threadId: threadId, peerId: peerId, namespace: namespace) } var upperBoundKey: ValueBoxKey if let upperBound = upperBound { upperBoundKey = self.key(threadId: threadId, index: upperBound) if upperBound.timestamp < Int32.max - 1 { upperBoundKey = upperBoundKey.successor } } else { upperBoundKey = self.upperBound(threadId: threadId, peerId: peerId, namespace: namespace) } return Int(self.valueBox.count(self.table, start: lowerBoundKey, end: upperBoundKey)) } override func beforeCommit() { super.beforeCommit() self.updatedIds.removeAll() } }