import Foundation public protocol PendingMessageActionData: PostboxCoding { func isEqual(to: PendingMessageActionData) -> Bool } public struct PendingMessageActionsEntry { public let id: MessageId public let action: PendingMessageActionData public init(id: MessageId, action: PendingMessageActionData) { self.id = id self.action = action } } public struct PendingMessageActionType: RawRepresentable, Equatable, Hashable { public var rawValue: UInt32 public init(rawValue: UInt32) { self.rawValue = rawValue } } enum PendingMessageActionsOperation { case add(PendingMessageActionType, MessageId, PendingMessageActionData) case remove(PendingMessageActionType, MessageId) } struct PendingMessageActionsSummaryKey: Equatable, Hashable { let type: PendingMessageActionType let peerId: PeerId let namespace: MessageId.Namespace } private func getReverseId(_ key: ValueBoxKey) -> MessageId { return MessageId(peerId: PeerId(key.getInt64(1 + 4)), namespace: key.getInt32(1 + 4 + 8), id: key.getInt32(1 + 4 + 8 + 4)) } private func getActionType(_ key: ValueBoxKey) -> PendingMessageActionType { return PendingMessageActionType(rawValue: key.getUInt32(1 + 8 + 4 + 4)) } private enum PendingMessageActionsTableSection: UInt8 { case actions = 0 case index = 1 } final class PendingMessageActionsTable: Table { static func tableSpec(_ id: Int32) -> ValueBoxTable { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true) } private let metadataTable: PendingMessageActionsMetadataTable init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, metadataTable: PendingMessageActionsMetadataTable) { self.metadataTable = metadataTable super.init(valueBox: valueBox, table: table, useCaches: useCaches) } private func forwardKey(id: MessageId, actionType: PendingMessageActionType) -> ValueBoxKey { let key = ValueBoxKey(length: 1 + 8 + 4 + 4 + 4) key.setUInt8(0, value: PendingMessageActionsTableSection.actions.rawValue) key.setInt64(1, value: id.peerId.toInt64()) key.setInt32(1 + 8, value: id.namespace) key.setInt32(1 + 8 + 4, value: id.id) key.setUInt32(1 + 8 + 4 + 4, value: actionType.rawValue) return key } private func reverseKey(id: MessageId, actionType: PendingMessageActionType) -> ValueBoxKey { let key = ValueBoxKey(length: 1 + 8 + 4 + 4 + 4) key.setUInt8(0, value: PendingMessageActionsTableSection.index.rawValue) key.setUInt32(1, value: actionType.rawValue) key.setInt64(1 + 4, value: id.peerId.toInt64()) key.setInt32(1 + 4 + 8, value: id.namespace) key.setInt32(1 + 4 + 8 + 4, value: id.id) return key } private func lowerBoundForward(id: MessageId) -> ValueBoxKey { let key = ValueBoxKey(length: 1 + 8 + 4 + 4) key.setUInt8(0, value: PendingMessageActionsTableSection.actions.rawValue) key.setInt64(1, value: id.peerId.toInt64()) key.setInt32(1 + 8, value: id.namespace) key.setInt32(1 + 8 + 4, value: id.id) return key } private func upperBoundForward(id: MessageId) -> ValueBoxKey { return self.lowerBoundForward(id: id).successor } private func lowerBoundReverse(type: PendingMessageActionType) -> ValueBoxKey { let key = ValueBoxKey(length: 1 + 4) key.setUInt8(0, value: PendingMessageActionsTableSection.index.rawValue) key.setUInt32(1, value: type.rawValue) return key } private func upperBoundReverse(type: PendingMessageActionType) -> ValueBoxKey { return self.lowerBoundReverse(type: type).successor } func getAction(id: MessageId, type: PendingMessageActionType) -> PendingMessageActionData? { if let value = self.valueBox.get(self.table, key: self.forwardKey(id: id, actionType: type)) { if let action = PostboxDecoder(buffer: value).decodeRootObject() as? PendingMessageActionData { return action } else { assertionFailure() return nil } } else { return nil } } func setAction(id: MessageId, type: PendingMessageActionType, action: PendingMessageActionData?, operations: inout [PendingMessageActionsOperation], updatedSummaries: inout [PendingMessageActionsSummaryKey: Int32]) { let currentAction = self.getAction(id: id, type: type) if let action = action { let encoder = PostboxEncoder() encoder.encodeRootObject(action) self.valueBox.set(self.table, key: self.forwardKey(id: id, actionType: type), value: encoder.readBufferNoCopy()) self.valueBox.set(self.table, key: self.reverseKey(id: id, actionType: type), value: MemoryBuffer()) if currentAction != nil { operations.append(.remove(type, id)) } operations.append(.add(type, id, action)) if currentAction == nil { let updatedCount = self.metadataTable.addCount(.peerNamespaceAction(id.peerId, id.namespace, type), value: 1) updatedSummaries[PendingMessageActionsSummaryKey(type: type, peerId: id.peerId, namespace: id.namespace)] = updatedCount let _ = self.metadataTable.addCount(.peerNamespace(id.peerId, id.namespace), value: 1) } } else if currentAction != nil { operations.append(.remove(type, id)) self.valueBox.remove(self.table, key: self.forwardKey(id: id, actionType: type), secure: false) self.valueBox.remove(self.table, key: self.reverseKey(id: id, actionType: type), secure: false) let updatedCount = self.metadataTable.addCount(.peerNamespaceAction(id.peerId, id.namespace, type), value: -1) updatedSummaries[PendingMessageActionsSummaryKey(type: type, peerId: id.peerId, namespace: id.namespace)] = updatedCount let _ = self.metadataTable.addCount(.peerNamespace(id.peerId, id.namespace), value: -1) } } func removeMessage(id: MessageId, operations: inout [PendingMessageActionsOperation], updatedSummaries: inout [PendingMessageActionsSummaryKey: Int32]) { if self.metadataTable.getCount(.peerNamespace(id.peerId, id.namespace)) != 0 { var removeTypes: [PendingMessageActionType] = [] self.valueBox.range(self.table, start: self.lowerBoundForward(id: id), end: self.upperBoundForward(id: id), keys: { key in removeTypes.append(getActionType(key)) return true }, limit: 0) for type in removeTypes { operations.append(.remove(type, id)) self.valueBox.remove(self.table, key: self.forwardKey(id: id, actionType: type), secure: false) self.valueBox.remove(self.table, key: self.reverseKey(id: id, actionType: type), secure: false) let updatedCount = self.metadataTable.addCount(.peerNamespaceAction(id.peerId, id.namespace, type), value: -1) updatedSummaries[PendingMessageActionsSummaryKey(type: type, peerId: id.peerId, namespace: id.namespace)] = updatedCount } let _ = self.metadataTable.addCount(.peerNamespace(id.peerId, id.namespace), value: Int32(-removeTypes.count)) } } func getActions(type: PendingMessageActionType) -> [PendingMessageActionsEntry] { var ids: [MessageId] = [] self.valueBox.range(self.table, start: self.lowerBoundReverse(type: type), end: self.upperBoundReverse(type: type), keys: { key in ids.append(getReverseId(key)) return true }, limit: 0) var entries: [PendingMessageActionsEntry] = [] for id in ids { if let action = self.getAction(id: id, type: type) { entries.append(PendingMessageActionsEntry(id: id, action: action)) } else { assertionFailure() } } return entries } }