import Foundation protocol ReverseIndexReference: Comparable, Hashable { static func decodeArray(_ buffer: MemoryBuffer) -> [Self] static func encodeArray(_ array: [Self]) -> MemoryBuffer } private final class ReverseIndexReferencesEntry { var orderedReferences: [T] = [] init() { } init(buffer: MemoryBuffer) { self.orderedReferences = T.decodeArray(buffer) } private func index(of reference: T) -> Int? { var lowerIndex = 0 var upperIndex = self.orderedReferences.count - 1 if lowerIndex > upperIndex { return nil } while (true) { let currentIndex = (lowerIndex + upperIndex) / 2 if self.orderedReferences[currentIndex] == reference { return currentIndex } else if lowerIndex > upperIndex { return nil } else { if self.orderedReferences[currentIndex] > reference { upperIndex = currentIndex - 1 } else { lowerIndex = currentIndex + 1 } } } } func remove(_ reference: T) -> Bool { if let index = self.index(of: reference) { self.orderedReferences.remove(at: index) return true } else { return false } } func insert(_ reference: T) -> Bool { let insertItem = reference var lo = 0 var hi = self.orderedReferences.count - 1 while lo <= hi { let mid = (lo + hi)/2 if self.orderedReferences[mid] < insertItem { lo = mid + 1 } else if insertItem < self.orderedReferences[mid] { hi = mid - 1 } else { return false } } self.orderedReferences.insert(insertItem, at: lo) return true } func withMemoryBuffer(_ f: (MemoryBuffer) -> Void) { f(T.encodeArray(self.orderedReferences)) } } struct ReverseIndexNamespace: Hashable { let value: Int32? init(_ value: Int32?) { self.value = value } } final class ReverseIndexReferenceTable: Table { private var cachedEntriesByNamespace: [ReverseIndexNamespace: [ValueBoxKey: ReverseIndexReferencesEntry]] = [:] private var updatedCachedEntriesByNamespace: [ReverseIndexNamespace: Set] = [:] static func tableSpec(_ id: Int32) -> ValueBoxTable { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true) } private func key(namespace: ReverseIndexNamespace, token: ValueBoxKey) -> ValueBoxKey { if let value = namespace.value { let key = ValueBoxKey(length: 4 + token.length) key.setInt32(0, value: value) memcpy(key.memory.advanced(by: 4), token.memory, token.length) return key } else { return token } } private func getEntry(namespace: ReverseIndexNamespace, token: ValueBoxKey) -> ReverseIndexReferencesEntry? { if let cachedNamespace = self.cachedEntriesByNamespace[namespace], let cached = cachedNamespace[token] { return cached } else { if let value = self.valueBox.get(self.table, key: self.key(namespace: namespace, token: token)) { let entry = ReverseIndexReferencesEntry(buffer: value) if self.cachedEntriesByNamespace[namespace] == nil { self.cachedEntriesByNamespace[namespace] = [token: entry] } else { self.cachedEntriesByNamespace[namespace]![token] = entry } return entry } else { return nil } } } private func add(namespace: ReverseIndexNamespace, token: ValueBoxKey, reference: T) { let entry: ReverseIndexReferencesEntry var addToCache = false if let current = self.getEntry(namespace: namespace, token: token) { entry = current } else { entry = ReverseIndexReferencesEntry() addToCache = true } if entry.insert(reference) { if self.updatedCachedEntriesByNamespace[namespace] == nil { self.updatedCachedEntriesByNamespace[namespace] = Set() } self.updatedCachedEntriesByNamespace[namespace]!.insert(token) if addToCache { if self.cachedEntriesByNamespace[namespace] == nil { self.cachedEntriesByNamespace[namespace] = [token: entry] } else { self.cachedEntriesByNamespace[namespace]![token] = entry } } } } private func remove(namespace: ReverseIndexNamespace, token: ValueBoxKey, reference: T) { let entry: ReverseIndexReferencesEntry var addToCache = false if let current = self.getEntry(namespace: namespace, token: token) { entry = current } else { entry = ReverseIndexReferencesEntry() addToCache = true } if entry.remove(reference) { if self.updatedCachedEntriesByNamespace[namespace] == nil { self.updatedCachedEntriesByNamespace[namespace] = Set() } self.updatedCachedEntriesByNamespace[namespace]!.insert(token) if addToCache { if self.cachedEntriesByNamespace[namespace] == nil { self.cachedEntriesByNamespace[namespace] = [token: entry] } else { self.cachedEntriesByNamespace[namespace]![token] = entry } } } } func add(namespace: ReverseIndexNamespace, reference: T, tokens: [ValueBoxKey]) { for token in tokens { self.add(namespace: namespace, token: token, reference: reference) } } func remove(namespace: ReverseIndexNamespace, reference: T, tokens: [ValueBoxKey]) { for token in tokens { self.remove(namespace: namespace, token: token, reference: reference) } } func matchingReferences(namespace: ReverseIndexNamespace, tokens: [ValueBoxKey], union: Bool = false) -> Set { var references: Set? for token in tokens { if let references = references, references.isEmpty { return Set() } var currentReferences = Set() self.valueBox.range(self.table, start: self.key(namespace: namespace, token: token).predecessor, end: self.key(namespace: namespace, token: token).successor, values: { key, value in if let cachedNamespace = self.cachedEntriesByNamespace[namespace], let cached = cachedNamespace[token] { for reference in cached.orderedReferences { currentReferences.insert(reference) } } else { for reference in ReverseIndexReferencesEntry(buffer: value).orderedReferences { currentReferences.insert(reference) } } return true }, limit: 0) if let previousReferences = references { if union { references = previousReferences.union(currentReferences) } else { references = previousReferences.intersection(currentReferences) } } else { references = currentReferences } } if let references = references { return references } else { return Set() } } func exactReferences(namespace: ReverseIndexNamespace, token: ValueBoxKey) -> [T] { if let value = self.valueBox.get(self.table, key: self.key(namespace: namespace, token: token)) { var currentReferences: [T] = [] if let cachedNamespace = self.cachedEntriesByNamespace[namespace], let cached = cachedNamespace[token] { for reference in cached.orderedReferences { currentReferences.append(reference) } } else { for reference in ReverseIndexReferencesEntry(buffer: value).orderedReferences { currentReferences.append(reference) } } return currentReferences } else { return [] } } override func clearMemoryCache() { for (_, cachedEntries) in self.cachedEntriesByNamespace { assert(cachedEntries.isEmpty) } for (_, updatedCachedEntries) in self.updatedCachedEntriesByNamespace { assert(updatedCachedEntries.isEmpty) } } override func beforeCommit() { if !self.updatedCachedEntriesByNamespace.isEmpty { for (namespace, updatedCachedEntries) in self.updatedCachedEntriesByNamespace { for token in updatedCachedEntries { if let cachedNamespace = self.cachedEntriesByNamespace[namespace], let cached = cachedNamespace[token] { cached.withMemoryBuffer { buffer in if buffer.length == 0 { self.valueBox.remove(self.table, key: self.key(namespace: namespace, token: token), secure: false) } else { self.valueBox.set(self.table, key: self.key(namespace: namespace, token: token), value: buffer) } } } else { assertionFailure() } } } self.updatedCachedEntriesByNamespace.removeAll() } if !self.cachedEntriesByNamespace.isEmpty { self.cachedEntriesByNamespace.removeAll() } } }