mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
371 lines
16 KiB
Swift
371 lines
16 KiB
Swift
import Foundation
|
|
|
|
public struct StoredMessageHistoryThreadInfo: Equatable, PostboxCoding {
|
|
public struct Summary: Equatable, PostboxCoding {
|
|
public var totalUnreadCount: Int32
|
|
public var mutedUntil: Int32?
|
|
|
|
public init(totalUnreadCount: Int32, mutedUntil: Int32?) {
|
|
self.totalUnreadCount = totalUnreadCount
|
|
self.mutedUntil = mutedUntil
|
|
}
|
|
|
|
public init(decoder: PostboxDecoder) {
|
|
self.totalUnreadCount = decoder.decodeInt32ForKey("u", orElse: 0)
|
|
self.mutedUntil = decoder.decodeOptionalInt32ForKey("m")
|
|
}
|
|
|
|
public func encode(_ encoder: PostboxEncoder) {
|
|
encoder.encodeInt32(self.totalUnreadCount, forKey: "u")
|
|
if let mutedUntil = self.mutedUntil {
|
|
encoder.encodeInt32(mutedUntil, forKey: "m")
|
|
} else {
|
|
encoder.encodeNil(forKey: "m")
|
|
}
|
|
}
|
|
}
|
|
|
|
public var data: CodableEntry
|
|
public var summary: Summary
|
|
|
|
public init(data: CodableEntry, summary: Summary) {
|
|
self.data = data
|
|
self.summary = summary
|
|
}
|
|
|
|
public init(decoder: PostboxDecoder) {
|
|
self.data = CodableEntry(data: decoder.decodeDataForKey("d")!)
|
|
self.summary = decoder.decodeObjectForKey("s", decoder: { return Summary(decoder: $0) }) as! Summary
|
|
}
|
|
|
|
public func encode(_ encoder: PostboxEncoder) {
|
|
encoder.encodeData(self.data.data, forKey: "d")
|
|
encoder.encodeObject(self.summary, forKey: "s")
|
|
}
|
|
}
|
|
|
|
private func extractKey(_ key: ValueBoxKey) -> MessageIndex {
|
|
return MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 8), id: key.getInt32(8 + 8 + 4 + 4)), timestamp: key.getInt32(8 + 8 + 4))
|
|
}
|
|
|
|
class MessageHistoryThreadReverseIndexTable: Table {
|
|
static func tableSpec(_ id: Int32) -> ValueBoxTable {
|
|
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
|
|
}
|
|
|
|
private let sharedKey = ValueBoxKey(length: 8 + 8)
|
|
|
|
override init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool) {
|
|
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
|
|
}
|
|
|
|
private func key(peerId: PeerId, threadId: Int64, key: ValueBoxKey) -> ValueBoxKey {
|
|
key.setInt64(0, value: peerId.toInt64())
|
|
key.setInt64(8, value: threadId)
|
|
|
|
return key
|
|
}
|
|
|
|
func get(peerId: PeerId, threadId: Int64) -> MessageIndex? {
|
|
if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, threadId: threadId, key: self.sharedKey)) {
|
|
var result: MessageIndex?
|
|
withExtendedLifetime(value, {
|
|
let readBuffer = ReadBuffer(memoryBufferNoCopy: value)
|
|
var namespace: Int32 = 0
|
|
readBuffer.read(&namespace, offset: 0, length: 4)
|
|
var id: Int32 = 0
|
|
readBuffer.read(&id, offset: 0, length: 4)
|
|
var timestamp: Int32 = 0
|
|
readBuffer.read(×tamp, offset: 0, length: 4)
|
|
result = MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp)
|
|
})
|
|
return result
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func set(peerId: PeerId, threadId: Int64, timestamp: Int32, namespace: MessageId.Namespace, id: MessageId.Id, hasValue: Bool) {
|
|
if hasValue {
|
|
let buffer = WriteBuffer()
|
|
var namespace = namespace
|
|
buffer.write(&namespace, length: 4)
|
|
var id = id
|
|
buffer.write(&id, length: 4)
|
|
var timestamp = timestamp
|
|
buffer.write(×tamp, length: 4)
|
|
withExtendedLifetime(buffer, {
|
|
self.valueBox.set(self.table, key: self.key(peerId: peerId, threadId: threadId, key: self.sharedKey), value: buffer.readBufferNoCopy())
|
|
})
|
|
} else {
|
|
self.valueBox.remove(self.table, key: self.key(peerId: peerId, threadId: threadId, key: self.sharedKey), secure: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
class MessageHistoryThreadIndexTable: Table {
|
|
static func tableSpec(_ id: Int32) -> ValueBoxTable {
|
|
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
|
|
}
|
|
|
|
private struct UpdatedEntry {
|
|
var value: StoredMessageHistoryThreadInfo?
|
|
}
|
|
|
|
enum IndexBoundary {
|
|
case lowerBound
|
|
case upperBound
|
|
case index(StoredPeerThreadCombinedState.Index)
|
|
}
|
|
|
|
private let reverseIndexTable: MessageHistoryThreadReverseIndexTable
|
|
private let seedConfiguration: SeedConfiguration
|
|
|
|
private let sharedKey = ValueBoxKey(length: 8 + 4 + 8 + 4 + 4)
|
|
|
|
private var updatedInfoItems: [MessageHistoryThreadsTable.ItemId: UpdatedEntry] = [:]
|
|
|
|
init(valueBox: ValueBox, table: ValueBoxTable, reverseIndexTable: MessageHistoryThreadReverseIndexTable, seedConfiguration: SeedConfiguration, useCaches: Bool) {
|
|
self.reverseIndexTable = reverseIndexTable
|
|
self.seedConfiguration = seedConfiguration
|
|
|
|
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
|
|
}
|
|
|
|
private func key(peerId: PeerId, timestamp: Int32, threadId: Int64, namespace: MessageId.Namespace, id: MessageId.Id, key: ValueBoxKey) -> ValueBoxKey {
|
|
key.setInt64(0, value: peerId.toInt64())
|
|
key.setInt32(8, value: timestamp)
|
|
key.setInt64(8 + 4, value: threadId)
|
|
key.setInt32(8 + 4 + 8, value: namespace)
|
|
key.setInt32(8 + 4 + 8 + 4, value: id)
|
|
|
|
return key
|
|
}
|
|
|
|
private static func extract(key: ValueBoxKey) -> (threadId: Int64, index: MessageIndex) {
|
|
return (
|
|
threadId: key.getInt64(8 + 4),
|
|
index: MessageIndex(
|
|
id: MessageId(
|
|
peerId: PeerId(key.getInt64(0)),
|
|
namespace: key.getInt32(8 + 4 + 8),
|
|
id: key.getInt32(8 + 4 + 8 + 4)
|
|
),
|
|
timestamp: key.getInt32(8)
|
|
)
|
|
)
|
|
}
|
|
|
|
private func lowerBound(peerId: PeerId) -> ValueBoxKey {
|
|
let key = ValueBoxKey(length: 8)
|
|
key.setInt64(0, value: peerId.toInt64())
|
|
return key
|
|
}
|
|
|
|
private func upperBound(peerId: PeerId) -> ValueBoxKey {
|
|
return self.lowerBound(peerId: peerId).successor
|
|
}
|
|
|
|
func get(peerId: PeerId, threadId: Int64) -> StoredMessageHistoryThreadInfo? {
|
|
if let updated = self.updatedInfoItems[MessageHistoryThreadsTable.ItemId(peerId: peerId, threadId: threadId)] {
|
|
return updated.value
|
|
} else {
|
|
if let itemIndex = self.reverseIndexTable.get(peerId: peerId, threadId: threadId) {
|
|
if let value = self.valueBox.get(self.table, key: self.key(peerId: itemIndex.id.peerId, timestamp: itemIndex.timestamp, threadId: threadId, namespace: itemIndex.id.namespace, id: itemIndex.id.id, key: self.sharedKey)) {
|
|
if value.length != 0 {
|
|
let decoder = PostboxDecoder(buffer: value)
|
|
let state = StoredMessageHistoryThreadInfo(decoder: decoder)
|
|
return state
|
|
} else {
|
|
return nil
|
|
}
|
|
} else {
|
|
return nil
|
|
}
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func set(peerId: PeerId, threadId: Int64, info: StoredMessageHistoryThreadInfo?) {
|
|
self.updatedInfoItems[MessageHistoryThreadsTable.ItemId(peerId: peerId, threadId: threadId)] = UpdatedEntry(value: info)
|
|
}
|
|
|
|
func replay(threadsTable: MessageHistoryThreadsTable, namespaces: Set<MessageId.Namespace>, updatedIds: Set<MessageHistoryThreadsTable.ItemId>) -> Set<PeerId> {
|
|
var peerIds = Set<PeerId>()
|
|
for itemId in updatedIds.union(Set(self.updatedInfoItems.keys)) {
|
|
let topIndex = threadsTable.getTop(peerId: itemId.peerId, threadId: itemId.threadId, namespaces: namespaces)
|
|
let previousIndex = self.reverseIndexTable.get(peerId: itemId.peerId, threadId: itemId.threadId)
|
|
if topIndex != previousIndex || self.updatedInfoItems[itemId] != nil {
|
|
peerIds.insert(itemId.peerId)
|
|
|
|
var info: ReadBuffer?
|
|
if let previousIndex = previousIndex {
|
|
let previousKey = self.key(peerId: itemId.peerId, timestamp: previousIndex.timestamp, threadId: itemId.threadId, namespace: previousIndex.id.namespace, id: previousIndex.id.id, key: self.sharedKey)
|
|
if let previousValue = self.valueBox.get(self.table, key: previousKey) {
|
|
if previousValue.length != 0 {
|
|
info = previousValue
|
|
}
|
|
} else {
|
|
assert(false)
|
|
}
|
|
self.valueBox.remove(self.table, key: previousKey, secure: true)
|
|
}
|
|
if let updatedInfo = self.updatedInfoItems[itemId] {
|
|
if let value = updatedInfo.value {
|
|
let encoder = PostboxEncoder()
|
|
value.encode(encoder)
|
|
info = encoder.makeReadBufferAndReset()
|
|
} else {
|
|
info = nil
|
|
}
|
|
}
|
|
if info == nil {
|
|
if let value = self.seedConfiguration.automaticThreadIndexInfo(itemId.peerId, itemId.threadId) {
|
|
let encoder = PostboxEncoder()
|
|
value.encode(encoder)
|
|
info = encoder.makeReadBufferAndReset()
|
|
}
|
|
}
|
|
|
|
if let topIndex = topIndex, let info = info {
|
|
if let previousIndex = previousIndex {
|
|
self.reverseIndexTable.set(peerId: itemId.peerId, threadId: itemId.threadId, timestamp: previousIndex.timestamp, namespace: previousIndex.id.namespace, id: previousIndex.id.id, hasValue: false)
|
|
}
|
|
|
|
self.reverseIndexTable.set(peerId: itemId.peerId, threadId: itemId.threadId, timestamp: topIndex.timestamp, namespace: topIndex.id.namespace, id: topIndex.id.id, hasValue: true)
|
|
self.valueBox.set(self.table, key: self.key(peerId: itemId.peerId, timestamp: topIndex.timestamp, threadId: itemId.threadId, namespace: topIndex.id.namespace, id: topIndex.id.id, key: self.sharedKey), value: info)
|
|
} else {
|
|
if let previousIndex = previousIndex {
|
|
self.reverseIndexTable.set(peerId: itemId.peerId, threadId: itemId.threadId, timestamp: previousIndex.timestamp, namespace: previousIndex.id.namespace, id: previousIndex.id.id, hasValue: false)
|
|
self.valueBox.remove(self.table, key: self.key(peerId: itemId.peerId, timestamp: previousIndex.timestamp, threadId: itemId.threadId, namespace: previousIndex.id.namespace, id: previousIndex.id.id, key: self.sharedKey), secure: true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return peerIds
|
|
}
|
|
|
|
func fetch(peerId: PeerId, namespace: MessageId.Namespace, start: IndexBoundary, end: IndexBoundary, limit: Int) -> [(threadId: Int64, index: MessageIndex, info: StoredMessageHistoryThreadInfo)] {
|
|
let startKey: ValueBoxKey
|
|
switch start {
|
|
case let .index(index):
|
|
startKey = self.key(peerId: peerId, timestamp: index.timestamp, threadId: index.threadId, namespace: namespace, id: index.messageId, key: ValueBoxKey(length: self.sharedKey.length))
|
|
case .lowerBound:
|
|
startKey = self.lowerBound(peerId: peerId)
|
|
case .upperBound:
|
|
startKey = self.upperBound(peerId: peerId)
|
|
}
|
|
|
|
let endKey: ValueBoxKey
|
|
switch end {
|
|
case let .index(index):
|
|
endKey = self.key(peerId: peerId, timestamp: index.timestamp, threadId: index.threadId, namespace: namespace, id: index.messageId, key: ValueBoxKey(length: self.sharedKey.length))
|
|
case .lowerBound:
|
|
endKey = self.lowerBound(peerId: peerId)
|
|
case .upperBound:
|
|
endKey = self.upperBound(peerId: peerId)
|
|
}
|
|
|
|
var result: [(threadId: Int64, index: MessageIndex, info: StoredMessageHistoryThreadInfo)] = []
|
|
self.valueBox.range(self.table, start: startKey, end: endKey, values: { key, value in
|
|
let keyData = MessageHistoryThreadIndexTable.extract(key: key)
|
|
if value.length == 0 {
|
|
return true
|
|
}
|
|
let decoder = PostboxDecoder(buffer: value)
|
|
let state = StoredMessageHistoryThreadInfo(decoder: decoder)
|
|
result.append((keyData.threadId, keyData.index, state))
|
|
return true
|
|
}, limit: limit)
|
|
|
|
return result
|
|
}
|
|
|
|
func getAll(peerId: PeerId) -> [(threadId: Int64, index: MessageIndex, info: StoredMessageHistoryThreadInfo)] {
|
|
var result: [(threadId: Int64, index: MessageIndex, info: StoredMessageHistoryThreadInfo)] = []
|
|
self.valueBox.range(self.table, start: self.upperBound(peerId: peerId), end: self.lowerBound(peerId: peerId), values: { key, value in
|
|
let keyData = MessageHistoryThreadIndexTable.extract(key: key)
|
|
if value.length == 0 {
|
|
return true
|
|
}
|
|
let decoder = PostboxDecoder(buffer: value)
|
|
let state = StoredMessageHistoryThreadInfo(decoder: decoder)
|
|
result.append((keyData.threadId, keyData.index, state))
|
|
return true
|
|
}, limit: 100000)
|
|
|
|
return result
|
|
}
|
|
|
|
func getCount(peerId: PeerId) -> Int {
|
|
return self.valueBox.count(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId))
|
|
}
|
|
|
|
override func beforeCommit() {
|
|
super.beforeCommit()
|
|
|
|
self.updatedInfoItems.removeAll()
|
|
}
|
|
}
|
|
|
|
class MessageHistoryThreadPinnedTable: Table {
|
|
static func tableSpec(_ id: Int32) -> ValueBoxTable {
|
|
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
|
|
}
|
|
|
|
private let sharedKey = ValueBoxKey(length: 8 + 4 + 8)
|
|
|
|
override init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool) {
|
|
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
|
|
}
|
|
|
|
private func key(peerId: PeerId, index: Int32, threadId: Int64) -> ValueBoxKey {
|
|
self.sharedKey.setInt64(0, value: peerId.toInt64())
|
|
self.sharedKey.setInt32(8, value: index)
|
|
self.sharedKey.setInt64(8 + 4, value: threadId)
|
|
|
|
return self.sharedKey
|
|
}
|
|
|
|
private static func extract(key: ValueBoxKey) -> (peerId: PeerId, threadId: Int64) {
|
|
return (
|
|
peerId: PeerId(key.getInt64(0)),
|
|
threadId: key.getInt64(8 + 4)
|
|
)
|
|
}
|
|
|
|
private func lowerBound(peerId: PeerId) -> ValueBoxKey {
|
|
let key = ValueBoxKey(length: 8)
|
|
key.setInt64(0, value: peerId.toInt64())
|
|
return key
|
|
}
|
|
|
|
private func upperBound(peerId: PeerId) -> ValueBoxKey {
|
|
return self.lowerBound(peerId: peerId).successor
|
|
}
|
|
|
|
func get(peerId: PeerId) -> [Int64] {
|
|
var result: [Int64] = []
|
|
self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), keys: { key in
|
|
result.append(MessageHistoryThreadPinnedTable.extract(key: key).threadId)
|
|
return true
|
|
}, limit: 0)
|
|
|
|
return result
|
|
}
|
|
|
|
func set(peerId: PeerId, threadIds: [Int64]) {
|
|
self.valueBox.removeRange(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId))
|
|
for i in 0 ..< threadIds.count {
|
|
self.valueBox.set(self.table, key: self.key(peerId: peerId, index: Int32(i), threadId: threadIds[i]), value: MemoryBuffer())
|
|
}
|
|
}
|
|
|
|
override func beforeCommit() {
|
|
super.beforeCommit()
|
|
}
|
|
}
|