mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
refactor and cleanup [skip ci]
This commit is contained in:
295
submodules/Postbox/Sources/PeerNameIndexTable.swift
Normal file
295
submodules/Postbox/Sources/PeerNameIndexTable.swift
Normal file
@@ -0,0 +1,295 @@
|
||||
import Foundation
|
||||
|
||||
struct PeerNameIndexCategories: OptionSet {
|
||||
var rawValue: Int32
|
||||
|
||||
init() {
|
||||
self.rawValue = 0
|
||||
}
|
||||
|
||||
init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
static let chats = PeerNameIndexCategories(rawValue: 1 << 0)
|
||||
static let contacts = PeerNameIndexCategories(rawValue: 1 << 1)
|
||||
}
|
||||
|
||||
private final class PeerNameIndexCategoriesEntry {
|
||||
let categories: PeerNameIndexCategories
|
||||
let tokens: [ValueBoxKey]
|
||||
|
||||
init(categories: PeerNameIndexCategories, tokens: [ValueBoxKey]) {
|
||||
self.categories = categories
|
||||
self.tokens = tokens
|
||||
}
|
||||
|
||||
init(buffer: MemoryBuffer) {
|
||||
assert(buffer.length >= 8)
|
||||
|
||||
self.categories = PeerNameIndexCategories(rawValue: buffer.memory.load(fromByteOffset: 0, as: Int32.self))
|
||||
let tokenCount = buffer.memory.load(fromByteOffset: 4, as: Int32.self)
|
||||
var offset = 8
|
||||
var tokens: [ValueBoxKey] = []
|
||||
for _ in 0 ..< tokenCount {
|
||||
let length = buffer.memory.load(fromByteOffset: offset, as: Int32.self)
|
||||
offset += 4
|
||||
tokens.append(ValueBoxKey(MemoryBuffer(memory: buffer.memory.advanced(by: offset), capacity: Int(length), length: Int(length), freeWhenDone: false)))
|
||||
offset += Int(length)
|
||||
let paddingLength = length % 4
|
||||
offset += Int(paddingLength)
|
||||
}
|
||||
self.tokens = tokens
|
||||
}
|
||||
|
||||
func write(to buffer: WriteBuffer) {
|
||||
var rawValue: Int32 = self.categories.rawValue
|
||||
buffer.write(&rawValue, offset: 0, length: 4)
|
||||
var count: Int32 = Int32(self.tokens.count)
|
||||
buffer.write(&count, offset: 0, length: 4)
|
||||
for token in self.tokens {
|
||||
var length = Int32(token.length)
|
||||
buffer.write(&length, offset: 0, length: 4)
|
||||
buffer.write(token.memory, offset: 0, length: token.length)
|
||||
var paddingLength = token.length % 4
|
||||
var zero: Int8 = 0
|
||||
while paddingLength > 0 {
|
||||
buffer.write(&zero, offset: 0, length: 1)
|
||||
paddingLength -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerNameIndexCategoriesEntryUpdate {
|
||||
let initialCategories: PeerNameIndexCategories
|
||||
let initialTokens: [ValueBoxKey]
|
||||
|
||||
private(set) var updatedCategories: PeerNameIndexCategories?
|
||||
private(set) var updatedName: PeerIndexNameRepresentation?
|
||||
|
||||
init(initialCategories: PeerNameIndexCategories, initialTokens: [ValueBoxKey]) {
|
||||
self.initialCategories = initialCategories
|
||||
self.initialTokens = initialTokens
|
||||
}
|
||||
|
||||
func updateCategory(_ category: PeerNameIndexCategories, includes: Bool) {
|
||||
var currentCategories: PeerNameIndexCategories
|
||||
if let updatedCategories = self.updatedCategories {
|
||||
currentCategories = updatedCategories
|
||||
} else {
|
||||
currentCategories = self.initialCategories
|
||||
}
|
||||
if includes {
|
||||
currentCategories.insert(category)
|
||||
self.updatedCategories = currentCategories
|
||||
} else {
|
||||
currentCategories.remove(category)
|
||||
self.updatedCategories = currentCategories
|
||||
}
|
||||
}
|
||||
|
||||
func updateName(_ name: PeerIndexNameRepresentation) {
|
||||
self.updatedName = name
|
||||
}
|
||||
}
|
||||
|
||||
struct PeerIdReverseIndexReference: Equatable, Hashable, ReverseIndexReference {
|
||||
let value: Int64
|
||||
|
||||
static func <(lhs: PeerIdReverseIndexReference, rhs: PeerIdReverseIndexReference) -> Bool {
|
||||
return lhs.value < rhs.value
|
||||
}
|
||||
|
||||
static func decodeArray(_ buffer: MemoryBuffer) -> [PeerIdReverseIndexReference] {
|
||||
assert(buffer.length % 8 == 0)
|
||||
var sortedPeerIds: [PeerIdReverseIndexReference] = []
|
||||
sortedPeerIds.reserveCapacity(buffer.length % 8)
|
||||
withExtendedLifetime(buffer, {
|
||||
let memory = buffer.memory.assumingMemoryBound(to: Int64.self)
|
||||
for i in 0 ..< buffer.length / 8 {
|
||||
sortedPeerIds.append(PeerIdReverseIndexReference(value: memory[i]))
|
||||
}
|
||||
})
|
||||
return sortedPeerIds
|
||||
}
|
||||
|
||||
static func encodeArray(_ array: [PeerIdReverseIndexReference]) -> MemoryBuffer {
|
||||
let buffer = MemoryBuffer(memory: malloc(array.count * 8), capacity: array.count * 8, length: array.count * 8, freeWhenDone: true)
|
||||
let memory = buffer.memory.assumingMemoryBound(to: Int64.self)
|
||||
var index = 0
|
||||
for peerId in array {
|
||||
memory[index] = peerId.value
|
||||
index += 1
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
}
|
||||
|
||||
private let reverseIndexNamespace = ReverseIndexNamespace(nil)
|
||||
|
||||
final class PeerNameIndexTable: Table {
|
||||
static func tableSpec(_ id: Int32) -> ValueBoxTable {
|
||||
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
|
||||
}
|
||||
|
||||
private let peerTable: PeerTable
|
||||
private let peerNameTokenIndexTable: ReverseIndexReferenceTable<PeerIdReverseIndexReference>
|
||||
|
||||
private let sharedKey = ValueBoxKey(length: 8)
|
||||
|
||||
private var entryUpdates: [PeerId: PeerNameIndexCategoriesEntryUpdate] = [:]
|
||||
|
||||
init(valueBox: ValueBox, table: ValueBoxTable, peerTable: PeerTable, peerNameTokenIndexTable: ReverseIndexReferenceTable<PeerIdReverseIndexReference>) {
|
||||
self.peerTable = peerTable
|
||||
self.peerNameTokenIndexTable = peerNameTokenIndexTable
|
||||
|
||||
super.init(valueBox: valueBox, table: table)
|
||||
}
|
||||
|
||||
private func key(_ peerId: PeerId) -> ValueBoxKey {
|
||||
self.sharedKey.setInt64(0, value: peerId.toInt64())
|
||||
return self.sharedKey
|
||||
}
|
||||
|
||||
private func updateEntry(_ peerId: PeerId, _ f: (PeerNameIndexCategoriesEntryUpdate) -> Void) {
|
||||
let entryUpdate: PeerNameIndexCategoriesEntryUpdate
|
||||
if let current = self.entryUpdates[peerId] {
|
||||
entryUpdate = current
|
||||
} else {
|
||||
let entry: PeerNameIndexCategoriesEntry
|
||||
if let value = self.valueBox.get(self.table, key: self.key(peerId)) {
|
||||
entry = PeerNameIndexCategoriesEntry(buffer: value)
|
||||
} else {
|
||||
entry = PeerNameIndexCategoriesEntry(categories: [], tokens: [])
|
||||
}
|
||||
entryUpdate = PeerNameIndexCategoriesEntryUpdate(initialCategories: entry.categories, initialTokens: entry.tokens)
|
||||
self.entryUpdates[peerId] = entryUpdate
|
||||
}
|
||||
f(entryUpdate)
|
||||
}
|
||||
|
||||
func setPeerCategoryState(peerId: PeerId, category: PeerNameIndexCategories, includes: Bool) {
|
||||
self.updateEntry(peerId, { entryUpdate in
|
||||
entryUpdate.updateCategory(category, includes: includes)
|
||||
})
|
||||
}
|
||||
|
||||
func markPeerNameUpdated(peerId: PeerId, name: PeerIndexNameRepresentation) {
|
||||
self.updateEntry(peerId, { entryUpdate in
|
||||
entryUpdate.updateName(name)
|
||||
})
|
||||
}
|
||||
|
||||
override func clearMemoryCache() {
|
||||
assert(self.entryUpdates.isEmpty)
|
||||
}
|
||||
|
||||
override func beforeCommit() {
|
||||
if !self.entryUpdates.isEmpty {
|
||||
let sharedBuffer = WriteBuffer()
|
||||
for (peerId, entryUpdate) in self.entryUpdates {
|
||||
if let updatedCategories = entryUpdate.updatedCategories {
|
||||
let wasEmpty = entryUpdate.initialCategories.isEmpty
|
||||
if updatedCategories.isEmpty != wasEmpty {
|
||||
if updatedCategories.isEmpty {
|
||||
if !entryUpdate.initialTokens.isEmpty {
|
||||
self.peerNameTokenIndexTable.remove(namespace: reverseIndexNamespace, reference: PeerIdReverseIndexReference(value: peerId.toInt64()), tokens: entryUpdate.initialTokens)
|
||||
}
|
||||
if !entryUpdate.initialCategories.isEmpty {
|
||||
self.valueBox.remove(self.table, key: self.key(peerId), secure: false)
|
||||
}
|
||||
} else {
|
||||
let updatedTokens: [ValueBoxKey]
|
||||
if let updatedName = entryUpdate.updatedName {
|
||||
updatedTokens = updatedName.indexTokens
|
||||
} else {
|
||||
if let peer = self.peerTable.get(peerId) {
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
if let associatedPeer = self.peerTable.get(associatedPeerId) {
|
||||
updatedTokens = associatedPeer.indexName.indexTokens
|
||||
} else {
|
||||
updatedTokens = []
|
||||
}
|
||||
} else {
|
||||
updatedTokens = peer.indexName.indexTokens
|
||||
}
|
||||
} else {
|
||||
//assertionFailure()
|
||||
updatedTokens = []
|
||||
}
|
||||
}
|
||||
self.peerNameTokenIndexTable.add(namespace: reverseIndexNamespace, reference: PeerIdReverseIndexReference(value: peerId.toInt64()), tokens: updatedTokens)
|
||||
sharedBuffer.reset()
|
||||
PeerNameIndexCategoriesEntry(categories: updatedCategories, tokens: updatedTokens).write(to: sharedBuffer)
|
||||
self.valueBox.set(self.table, key: self.key(peerId), value: sharedBuffer)
|
||||
}
|
||||
} else {
|
||||
if let updatedName = entryUpdate.updatedName {
|
||||
if !entryUpdate.initialTokens.isEmpty {
|
||||
self.peerNameTokenIndexTable.remove(namespace: reverseIndexNamespace, reference: PeerIdReverseIndexReference(value: peerId.toInt64()), tokens: entryUpdate.initialTokens)
|
||||
}
|
||||
let updatedTokens = updatedName.indexTokens
|
||||
self.peerNameTokenIndexTable.add(namespace: reverseIndexNamespace, reference: PeerIdReverseIndexReference(value: peerId.toInt64()), tokens: updatedTokens)
|
||||
sharedBuffer.reset()
|
||||
PeerNameIndexCategoriesEntry(categories: updatedCategories, tokens: updatedTokens).write(to: sharedBuffer)
|
||||
self.valueBox.set(self.table, key: self.key(peerId), value: sharedBuffer)
|
||||
} else {
|
||||
sharedBuffer.reset()
|
||||
PeerNameIndexCategoriesEntry(categories: updatedCategories, tokens: entryUpdate.initialTokens).write(to: sharedBuffer)
|
||||
self.valueBox.set(self.table, key: self.key(peerId), value: sharedBuffer)
|
||||
}
|
||||
}
|
||||
} else if let updatedName = entryUpdate.updatedName {
|
||||
if !entryUpdate.initialCategories.isEmpty {
|
||||
if !entryUpdate.initialTokens.isEmpty {
|
||||
self.peerNameTokenIndexTable.remove(namespace: reverseIndexNamespace, reference: PeerIdReverseIndexReference(value: peerId.toInt64()), tokens: entryUpdate.initialTokens)
|
||||
}
|
||||
let updatedTokens = updatedName.indexTokens
|
||||
self.peerNameTokenIndexTable.add(namespace: reverseIndexNamespace, reference: PeerIdReverseIndexReference(value: peerId.toInt64()), tokens: updatedTokens)
|
||||
sharedBuffer.reset()
|
||||
PeerNameIndexCategoriesEntry(categories: entryUpdate.initialCategories, tokens: updatedTokens).write(to: sharedBuffer)
|
||||
self.valueBox.set(self.table, key: self.key(peerId), value: sharedBuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.entryUpdates.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
func matchingPeerIds(tokens: (regular: [ValueBoxKey], transliterated: [ValueBoxKey]?), categories: PeerNameIndexCategories, chatListIndexTable: ChatListIndexTable, contactTable: ContactTable) -> (chats: [PeerId], contacts: [PeerId]) {
|
||||
if categories.isEmpty {
|
||||
return ([], [])
|
||||
} else {
|
||||
var contacts: [PeerId] = []
|
||||
var chatIndices: [PeerId: ChatListIndex] = [:]
|
||||
var peerIds = self.peerNameTokenIndexTable.matchingReferences(namespace: reverseIndexNamespace, tokens: tokens.regular)
|
||||
if let transliterated = tokens.transliterated, tokens.regular != transliterated {
|
||||
let transliteratedPeerIds = self.peerNameTokenIndexTable.matchingReferences(namespace: reverseIndexNamespace, tokens: transliterated)
|
||||
peerIds.formUnion(transliteratedPeerIds)
|
||||
}
|
||||
for peerIdReference in peerIds {
|
||||
let peerId = PeerId(peerIdReference.value)
|
||||
var foundInChats = false
|
||||
if categories.contains(.chats) {
|
||||
if let (_, index) = chatListIndexTable.get(peerId: peerId).includedIndex(peerId: peerId) {
|
||||
foundInChats = true
|
||||
chatIndices[peerId] = index
|
||||
}
|
||||
}
|
||||
if !foundInChats {
|
||||
if categories.contains(.contacts) {
|
||||
if contactTable.isContact(peerId: peerId) {
|
||||
contacts.append(peerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let chats = chatIndices.keys.sorted(by: { lhs, rhs -> Bool in
|
||||
return chatIndices[lhs]! > chatIndices[rhs]!
|
||||
})
|
||||
return (chats, contacts)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user