mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
262 lines
12 KiB
Swift
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() {
|
|
|
|
}
|
|
}
|