Swiftgram/submodules/Postbox/Sources/ItemCollectionItemTable.swift
2021-09-13 20:40:09 +04:00

262 lines
12 KiB
Swift

import Foundation
enum ItemCollectionItemsOperation {
case replaceItems
}
struct ItemCollectionItemReverseIndexReference: Equatable, Hashable, ReverseIndexReference {
let collectionId: ItemCollectionId
let itemIndex: ItemCollectionItemIndex
static func <(lhs: ItemCollectionItemReverseIndexReference, rhs: ItemCollectionItemReverseIndexReference) -> Bool {
if lhs.collectionId != rhs.collectionId {
return lhs.collectionId < rhs.collectionId
} else {
return lhs.itemIndex < rhs.itemIndex
}
}
static func decodeArray(_ buffer: MemoryBuffer) -> [ItemCollectionItemReverseIndexReference] {
assert(buffer.length % (4 + 8 + 4 + 8) == 0)
var references: [ItemCollectionItemReverseIndexReference] = []
references.reserveCapacity(buffer.length % (4 + 8 + 4 + 8))
withExtendedLifetime(buffer, {
let readBuffer = ReadBuffer(memoryBufferNoCopy: buffer)
for _ in 0 ..< buffer.length / (4 + 8 + 4 + 8) {
var collectionIdNamespace: Int32 = 0
var collectionIdId: Int64 = 0
var indexIdIndex: Int32 = 0
var indexIdId: Int64 = 0
readBuffer.read(&collectionIdNamespace, offset: 0, length: 4)
readBuffer.read(&collectionIdId, offset: 0, length: 8)
readBuffer.read(&indexIdIndex, offset: 0, length: 4)
readBuffer.read(&indexIdId, offset: 0, length: 8)
references.append(ItemCollectionItemReverseIndexReference(collectionId: ItemCollectionId(namespace: collectionIdNamespace, id: collectionIdId), itemIndex: ItemCollectionItemIndex(index: indexIdIndex, id: indexIdId)))
}
})
return references
}
static func encodeArray(_ array: [ItemCollectionItemReverseIndexReference]) -> MemoryBuffer {
let buffer = WriteBuffer()
for reference in array {
var collectionIdNamespace: Int32 = reference.collectionId.namespace
var collectionIdId: Int64 = reference.collectionId.id
var indexIdIndex: Int32 = reference.itemIndex.index
var indexIdId: Int64 = reference.itemIndex.id
buffer.write(&collectionIdNamespace, offset: 0, length: 4)
buffer.write(&collectionIdId, offset: 0, length: 8)
buffer.write(&indexIdIndex, offset: 0, length: 4)
buffer.write(&indexIdId, offset: 0, length: 8)
}
return buffer
}
}
final class ItemCollectionItemTable: Table {
static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false)
}
private let reverseIndexTable: ReverseIndexReferenceTable<ItemCollectionItemReverseIndexReference>
private let sharedKey = ValueBoxKey(length: 4 + 8 + 4 + 8)
init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, reverseIndexTable: ReverseIndexReferenceTable<ItemCollectionItemReverseIndexReference>) {
self.reverseIndexTable = reverseIndexTable
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
}
private func key(collectionId: ItemCollectionId, index: ItemCollectionItemIndex) -> ValueBoxKey {
self.sharedKey.setInt32(0, value: collectionId.namespace)
self.sharedKey.setInt64(4, value: collectionId.id)
self.sharedKey.setInt32(4 + 8, value: index.index)
self.sharedKey.setInt64(4 + 8 + 4, value: index.id)
return self.sharedKey
}
private func lowerBound(namespace: ItemCollectionId.Namespace) -> ValueBoxKey {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: namespace)
return key
}
private func upperBound(namespace: ItemCollectionId.Namespace) -> ValueBoxKey {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: namespace)
return key.successor
}
private func lowerBound(collectionId: ItemCollectionId) -> ValueBoxKey {
let key = ValueBoxKey(length: 4 + 8)
key.setInt32(0, value: collectionId.namespace)
key.setInt64(4, value: collectionId.id)
return key
}
private func upperBound(collectionId: ItemCollectionId) -> ValueBoxKey {
let key = ValueBoxKey(length: 4 + 8)
key.setInt32(0, value: collectionId.namespace)
key.setInt64(4, value: collectionId.id)
return key.successor
}
func lowerItems(collectionId: ItemCollectionId, itemIndex: ItemCollectionItemIndex, count: Int) -> [ItemCollectionItem] {
var items: [ItemCollectionItem] = []
self.valueBox.range(self.table, start: self.key(collectionId: collectionId, index: itemIndex), end: self.lowerBound(collectionId: collectionId), values: { _, value in
if let item = PostboxDecoder(buffer: value).decodeRootObject() as? ItemCollectionItem {
items.append(item)
} else {
assertionFailure()
}
return true
}, limit: count)
return items
}
func itemCount(collectionId: ItemCollectionId) -> Int32 {
var count: Int32 = 0
self.valueBox.range(self.table, start: self.upperBound(collectionId: collectionId), end: self.lowerBound(collectionId: collectionId), keys: { key in
let itemIndex = ItemCollectionItemIndex(index: key.getInt32(4 + 8), id: key.getInt64(4 + 8 + 4))
count = itemIndex.index + 1
return true
}, limit: 1)
return count
}
func higherItems(collectionId: ItemCollectionId, itemIndex: ItemCollectionItemIndex, count: Int) -> [ItemCollectionItem] {
var items: [ItemCollectionItem] = []
self.valueBox.range(self.table, start: self.key(collectionId: collectionId, index: itemIndex), end: self.upperBound(collectionId: collectionId), values: { _, value in
if let item = PostboxDecoder(buffer: value).decodeRootObject() as? ItemCollectionItem {
items.append(item)
} else {
assertionFailure()
}
return true
}, limit: count)
return items
}
func getItems(namespace: ItemCollectionId.Namespace) -> [ItemCollectionId: [ItemCollectionItem]] {
var items: [ItemCollectionId: [ItemCollectionItem]] = [:]
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 = PostboxDecoder(buffer: value).decodeRootObject() as? ItemCollectionItem {
if items[collectionId] != nil {
items[collectionId]!.append(item)
} else {
items[collectionId] = [item]
}
} else {
assertionFailure()
}
return true
}, limit: 0)
return items
}
func collectionItems(collectionId: ItemCollectionId) -> [ItemCollectionItem] {
var items: [ItemCollectionItem] = []
self.valueBox.range(self.table, start: self.lowerBound(collectionId: collectionId), end: self.upperBound(collectionId: collectionId), values: { key, value in
//let itemIndex = ItemCollectionItemIndex(index: key.getInt32(4 + 8), id: key.getInt64(4 + 8 + 4))
if let item = PostboxDecoder(buffer: value).decodeRootObject() as? ItemCollectionItem {
items.append(item)
} else {
assertionFailure()
}
return true
}, limit: 0)
return items
}
func replaceItems(collectionId: ItemCollectionId, items: [ItemCollectionItem]) {
let updatedIndices = Set(items.map({ $0.index }))
var itemByIndex: [ItemCollectionItemIndex: ItemCollectionItem] = [:]
for item in items {
itemByIndex[item.index] = item
}
var currentIndices = Set<ItemCollectionItemIndex>()
var removedIndexKeys: [ItemCollectionItemIndex: [MemoryBuffer]] = [:]
self.valueBox.range(self.table, start: self.lowerBound(collectionId: collectionId), end: self.upperBound(collectionId: collectionId), values: { key, value in
let itemIndex = ItemCollectionItemIndex(index: key.getInt32(4 + 8), id: key.getInt64(4 + 8 + 4))
currentIndices.insert(itemIndex)
if !updatedIndices.contains(itemIndex) {
if let item = PostboxDecoder(buffer: value).decodeRootObject() as? ItemCollectionItem {
if !item.indexKeys.isEmpty {
removedIndexKeys[itemIndex] = item.indexKeys
}
} else {
assertionFailure()
}
}
return true
}, limit: 0)
let addedIndices = updatedIndices.subtracting(currentIndices)
let removedIndices = currentIndices.subtracting(updatedIndices)
for index in removedIndices {
self.valueBox.remove(self.table, key: self.key(collectionId: collectionId, index: index), secure: false)
if let indexKeys = removedIndexKeys[index] {
self.reverseIndexTable.remove(namespace: ReverseIndexNamespace(collectionId.namespace), reference: ItemCollectionItemReverseIndexReference(collectionId: collectionId, itemIndex: index), tokens: indexKeys.map({ ValueBoxKey($0) }))
}
}
let sharedEncoder = PostboxEncoder()
for index in addedIndices {
let item = itemByIndex[index]!
sharedEncoder.reset()
sharedEncoder.encodeRootObject(item)
withExtendedLifetime(sharedEncoder, {
self.valueBox.set(self.table, key: self.key(collectionId: collectionId, index: index), value: sharedEncoder.readBufferNoCopy())
})
if !item.indexKeys.isEmpty {
self.reverseIndexTable.add(namespace: ReverseIndexNamespace(collectionId.namespace), reference: ItemCollectionItemReverseIndexReference(collectionId: collectionId, itemIndex: index), tokens: item.indexKeys.map({ ValueBoxKey($0) }))
}
}
}
func searchIndexedItems(namespace: ItemCollectionId.Namespace, query: ItemCollectionSearchQuery) -> [ItemCollectionId: [ItemCollectionItem]] {
let references: [ItemCollectionItemReverseIndexReference]
switch query {
case let .exact(token):
references = self.reverseIndexTable.exactReferences(namespace: ReverseIndexNamespace(namespace), token: token)
case let .matching(tokens):
references = Array(self.reverseIndexTable.matchingReferences(namespace: ReverseIndexNamespace(namespace), tokens: tokens))
case let .any(tokens):
references = Array(self.reverseIndexTable.matchingReferences(namespace: ReverseIndexNamespace(namespace), tokens: tokens, union: true))
}
var resultsByCollectionId: [ItemCollectionId: [(ItemCollectionItemIndex, ItemCollectionItem)]] = [:]
for reference in references {
if let value = self.valueBox.get(self.table, key: self.key(collectionId: reference.collectionId, index: reference.itemIndex)), let item = PostboxDecoder(buffer: value).decodeRootObject() as? ItemCollectionItem {
if resultsByCollectionId[reference.collectionId] == nil {
resultsByCollectionId[reference.collectionId] = [(reference.itemIndex, item)]
} else {
resultsByCollectionId[reference.collectionId]!.append((reference.itemIndex, item))
}
} else {
assertionFailure()
}
}
var results: [ItemCollectionId: [ItemCollectionItem]] = [:]
for collectionId in resultsByCollectionId.keys {
results[collectionId] = resultsByCollectionId[collectionId]!.sorted(by: { $0.0 < $1.0 }).map({ $0.1 })
}
return results
}
override func clearMemoryCache() {
}
override func beforeCommit() {
}
}