import Foundation private func readPeerIds(_ buffer: ReadBuffer) -> Set { assert(buffer.length % 8 == 0) let count = buffer.length / 8 var result = Set() for _ in 0 ..< count { var value: Int64 = 0 buffer.read(&value, offset: 0, length: 8) result.insert(PeerId(value)) } return result } private func writePeerIds(_ buffer: WriteBuffer, _ peerIds: Set) { for id in peerIds { var value: Int64 = id.toInt64() buffer.write(&value, offset: 0, length: 8) } } final class ReverseAssociatedPeerTable: Table { static func tableSpec(_ id: Int32) -> ValueBoxTable { return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false) } private let sharedKey = ValueBoxKey(length: 8) private var cachedAssociations: [PeerId: Set] = [:] private var updatedAssociations = Set() private func key(_ peerId: PeerId) -> ValueBoxKey { self.sharedKey.setInt64(0, value: peerId.toInt64()) return self.sharedKey } func get(peerId: PeerId) -> Set { if let cached = self.cachedAssociations[peerId] { return cached } else { if let value = self.valueBox.get(self.table, key: self.key(peerId)) { let peerIds = readPeerIds(value) self.cachedAssociations[peerId] = peerIds return peerIds } else { self.cachedAssociations[peerId] = Set() return Set() } } } func addReverseAssociation(target targetPeerId: PeerId, from sourcePeerId: PeerId) { var value = self.get(peerId: targetPeerId) if value.contains(sourcePeerId) { assertionFailure() } else { value.insert(sourcePeerId) self.cachedAssociations[targetPeerId] = value self.updatedAssociations.insert(targetPeerId) } } func removeReverseAssociation(target targetPeerId: PeerId, from sourcePeerId: PeerId) { var value = self.get(peerId: targetPeerId) if !value.contains(sourcePeerId) { assertionFailure() } else { value.remove(sourcePeerId) self.cachedAssociations[targetPeerId] = value self.updatedAssociations.insert(targetPeerId) } } override func clearMemoryCache() { self.cachedAssociations.removeAll() assert(self.updatedAssociations.isEmpty) } override func beforeCommit() { if !self.updatedAssociations.isEmpty { let buffer = WriteBuffer() for peerId in self.updatedAssociations { if let peerIds = self.cachedAssociations[peerId] { if peerIds.isEmpty { self.valueBox.remove(self.table, key: self.key(peerId), secure: false) } else { buffer.reset() writePeerIds(buffer, peerIds) self.valueBox.set(self.table, key: self.key(peerId), value: buffer) } } else { assertionFailure() } } self.updatedAssociations.removeAll() } } }