mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
379 lines
17 KiB
Swift
379 lines
17 KiB
Swift
import Foundation
|
|
|
|
enum PeerMergedOperationLogOperation {
|
|
case append(PeerMergedOperationLogEntry)
|
|
case remove(tag: PeerOperationLogTag, peerId: PeerId, mergedIndices: Set<Int32>)
|
|
case updateContents(PeerMergedOperationLogEntry)
|
|
}
|
|
|
|
public struct PeerMergedOperationLogEntry {
|
|
public let peerId: PeerId
|
|
public let tag: PeerOperationLogTag
|
|
public let tagLocalIndex: Int32
|
|
public let mergedIndex: Int32
|
|
public let contents: PostboxCoding
|
|
}
|
|
|
|
public enum StorePeerOperationLogEntryTagLocalIndex {
|
|
case automatic
|
|
case manual(Int32)
|
|
}
|
|
|
|
public enum StorePeerOperationLogEntryTagMergedIndex {
|
|
case none
|
|
case automatic
|
|
}
|
|
|
|
public struct PeerOperationLogEntry {
|
|
public let peerId: PeerId
|
|
public let tag: PeerOperationLogTag
|
|
public let tagLocalIndex: Int32
|
|
public let mergedIndex: Int32?
|
|
public let contents: PostboxCoding
|
|
|
|
public func withUpdatedContents(_ contents: PostboxCoding) -> PeerOperationLogEntry {
|
|
return PeerOperationLogEntry(peerId: self.peerId, tag: self.tag, tagLocalIndex: self.tagLocalIndex, mergedIndex: self.mergedIndex, contents: contents)
|
|
}
|
|
|
|
public var mergedEntry: PeerMergedOperationLogEntry? {
|
|
if let mergedIndex = self.mergedIndex {
|
|
return PeerMergedOperationLogEntry(peerId: self.peerId, tag: self.tag, tagLocalIndex: self.tagLocalIndex, mergedIndex: mergedIndex, contents: self.contents)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct PeerOperationLogTag: Equatable {
|
|
let rawValue: UInt8
|
|
|
|
public init(value: Int) {
|
|
self.rawValue = UInt8(value)
|
|
}
|
|
|
|
public static func ==(lhs: PeerOperationLogTag, rhs: PeerOperationLogTag) -> Bool {
|
|
return lhs.rawValue == rhs.rawValue
|
|
}
|
|
}
|
|
|
|
public enum PeerOperationLogEntryUpdateContents {
|
|
case none
|
|
case update(PostboxCoding)
|
|
}
|
|
|
|
public enum PeerOperationLogEntryUpdateTagMergedIndex {
|
|
case none
|
|
case remove
|
|
case newAutomatic
|
|
}
|
|
|
|
public struct PeerOperationLogEntryUpdate {
|
|
let mergedIndex: PeerOperationLogEntryUpdateTagMergedIndex
|
|
let contents: PeerOperationLogEntryUpdateContents
|
|
|
|
public init(mergedIndex: PeerOperationLogEntryUpdateTagMergedIndex, contents: PeerOperationLogEntryUpdateContents) {
|
|
self.mergedIndex = mergedIndex
|
|
self.contents = contents
|
|
}
|
|
}
|
|
|
|
private func parseEntry(peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ value: ReadBuffer) -> PeerOperationLogEntry? {
|
|
var hasMergedIndex: Int8 = 0
|
|
value.read(&hasMergedIndex, offset: 0, length: 1)
|
|
var mergedIndex: Int32?
|
|
if hasMergedIndex != 0 {
|
|
var mergedIndexValue: Int32 = 0
|
|
value.read(&mergedIndexValue, offset: 0, length: 4)
|
|
mergedIndex = mergedIndexValue
|
|
}
|
|
var contentLength: Int32 = 0
|
|
value.read(&contentLength, offset: 0, length: 4)
|
|
assert(value.length - value.offset == Int(contentLength))
|
|
if let contents = PostboxDecoder(buffer: MemoryBuffer(memory: value.memory.advanced(by: value.offset), capacity: Int(contentLength), length: Int(contentLength), freeWhenDone: false)).decodeRootObject() {
|
|
return PeerOperationLogEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, mergedIndex: mergedIndex, contents: contents)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private func parseMergedEntry(peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ value: ReadBuffer) -> PeerMergedOperationLogEntry? {
|
|
if let entry = parseEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, value), let mergedIndex = entry.mergedIndex {
|
|
return PeerMergedOperationLogEntry(peerId: entry.peerId, tag: entry.tag, tagLocalIndex: entry.tagLocalIndex, mergedIndex: mergedIndex, contents: entry.contents)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
final class PeerOperationLogTable: Table {
|
|
static func tableSpec(_ id: Int32) -> ValueBoxTable {
|
|
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false)
|
|
}
|
|
|
|
private let metadataTable: PeerOperationLogMetadataTable
|
|
private let mergedIndexTable: PeerMergedOperationLogIndexTable
|
|
|
|
init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, metadataTable: PeerOperationLogMetadataTable, mergedIndexTable: PeerMergedOperationLogIndexTable) {
|
|
self.metadataTable = metadataTable
|
|
self.mergedIndexTable = mergedIndexTable
|
|
|
|
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
|
|
}
|
|
|
|
private func key(peerId: PeerId, tag: PeerOperationLogTag, index: Int32) -> ValueBoxKey {
|
|
let key = ValueBoxKey(length: 8 + 1 + 4)
|
|
key.setInt64(0, value: peerId.toInt64())
|
|
key.setUInt8(8, value: tag.rawValue)
|
|
key.setInt32(9, value: index)
|
|
return key
|
|
}
|
|
|
|
func getNextEntryLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> Int32 {
|
|
return self.metadataTable.getNextLocalIndex(peerId: peerId, tag: tag)
|
|
}
|
|
|
|
func resetIndices(peerId: PeerId, tag: PeerOperationLogTag, nextTagLocalIndex: Int32) {
|
|
self.metadataTable.setNextLocalIndex(peerId: peerId, tag: tag, index: nextTagLocalIndex)
|
|
}
|
|
|
|
func addEntry(peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: StorePeerOperationLogEntryTagLocalIndex, tagMergedIndex: StorePeerOperationLogEntryTagMergedIndex, contents: PostboxCoding, operations: inout [PeerMergedOperationLogOperation]) {
|
|
let index: Int32
|
|
switch tagLocalIndex {
|
|
case .automatic:
|
|
index = self.metadataTable.takeNextLocalIndex(peerId: peerId, tag: tag)
|
|
case let .manual(manualIndex):
|
|
index = manualIndex
|
|
}
|
|
|
|
var mergedIndex: Int32?
|
|
switch tagMergedIndex {
|
|
case .automatic:
|
|
mergedIndex = self.mergedIndexTable.add(peerId: peerId, tag: tag, tagLocalIndex: index)
|
|
case .none:
|
|
break
|
|
}
|
|
|
|
let buffer = WriteBuffer()
|
|
var hasMergedIndex: Int8 = mergedIndex != nil ? 1 : 0
|
|
buffer.write(&hasMergedIndex, offset: 0, length: 1)
|
|
if let mergedIndex = mergedIndex {
|
|
var mergedIndexValue: Int32 = mergedIndex
|
|
buffer.write(&mergedIndexValue, offset: 0, length: 4)
|
|
}
|
|
|
|
let encoder = PostboxEncoder()
|
|
encoder.encodeRootObject(contents)
|
|
withExtendedLifetime(encoder, {
|
|
let contentBuffer = encoder.readBufferNoCopy()
|
|
var contentBufferLength: Int32 = Int32(contentBuffer.length)
|
|
buffer.write(&contentBufferLength, offset: 0, length: 4)
|
|
buffer.write(contentBuffer.memory, offset: 0, length: contentBuffer.length)
|
|
})
|
|
|
|
self.valueBox.set(self.table, key: self.key(peerId: peerId, tag: tag, index: index), value: buffer)
|
|
if let mergedIndex = mergedIndex {
|
|
operations.append(.append(PeerMergedOperationLogEntry(peerId: peerId, tag: tag, tagLocalIndex: index, mergedIndex: mergedIndex, contents: contents)))
|
|
}
|
|
}
|
|
|
|
func removeEntry(peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex index: Int32, operations: inout [PeerMergedOperationLogOperation]) -> Bool {
|
|
var indices: [Int32] = []
|
|
var mergedIndices: [Int32] = []
|
|
var removed = false
|
|
if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, tag: tag, index: index)) {
|
|
indices.append(index)
|
|
var hasMergedIndex: Int8 = 0
|
|
value.read(&hasMergedIndex, offset: 0, length: 1)
|
|
if hasMergedIndex != 0 {
|
|
var mergedIndex: Int32 = 0
|
|
value.read(&mergedIndex, offset: 0, length: 4)
|
|
mergedIndices.append(mergedIndex)
|
|
}
|
|
removed = true
|
|
}
|
|
|
|
for index in indices {
|
|
self.valueBox.remove(self.table, key: self.key(peerId: peerId, tag: tag, index: index), secure: false)
|
|
}
|
|
|
|
if !mergedIndices.isEmpty {
|
|
self.mergedIndexTable.remove(tag: tag, mergedIndices: mergedIndices)
|
|
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set(mergedIndices)))
|
|
}
|
|
return removed
|
|
}
|
|
|
|
func removeAllEntries(peerId: PeerId, tag: PeerOperationLogTag, operations: inout [PeerMergedOperationLogOperation]) {
|
|
var indices: [Int32] = []
|
|
var mergedIndices: [Int32] = []
|
|
self.valueBox.range(self.table, start: self.key(peerId: peerId, tag: tag, index: 0).predecessor, end: self.key(peerId: peerId, tag: tag, index: Int32.max).successor, values: { key, value in
|
|
let index = key.getInt32(9)
|
|
indices.append(index)
|
|
var hasMergedIndex: Int8 = 0
|
|
value.read(&hasMergedIndex, offset: 0, length: 1)
|
|
if hasMergedIndex != 0 {
|
|
var mergedIndex: Int32 = 0
|
|
value.read(&mergedIndex, offset: 0, length: 4)
|
|
mergedIndices.append(mergedIndex)
|
|
}
|
|
return true
|
|
}, limit: 0)
|
|
|
|
for index in indices {
|
|
self.valueBox.remove(self.table, key: self.key(peerId: peerId, tag: tag, index: index), secure: false)
|
|
}
|
|
|
|
if !mergedIndices.isEmpty {
|
|
self.mergedIndexTable.remove(tag: tag, mergedIndices: mergedIndices)
|
|
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set(mergedIndices)))
|
|
}
|
|
}
|
|
|
|
func removeEntries(peerId: PeerId, tag: PeerOperationLogTag, withTagLocalIndicesEqualToOrLowerThan maxTagLocalIndex: Int32, operations: inout [PeerMergedOperationLogOperation]) {
|
|
var indices: [Int32] = []
|
|
var mergedIndices: [Int32] = []
|
|
self.valueBox.range(self.table, start: self.key(peerId: peerId, tag: tag, index: 0).predecessor, end: self.key(peerId: peerId, tag: tag, index: maxTagLocalIndex).successor, values: { key, value in
|
|
let index = key.getInt32(9)
|
|
indices.append(index)
|
|
var hasMergedIndex: Int8 = 0
|
|
value.read(&hasMergedIndex, offset: 0, length: 1)
|
|
if hasMergedIndex != 0 {
|
|
var mergedIndex: Int32 = 0
|
|
value.read(&mergedIndex, offset: 0, length: 4)
|
|
mergedIndices.append(mergedIndex)
|
|
}
|
|
return true
|
|
}, limit: 0)
|
|
|
|
for index in indices {
|
|
self.valueBox.remove(self.table, key: self.key(peerId: peerId, tag: tag, index: index), secure: false)
|
|
}
|
|
|
|
if !mergedIndices.isEmpty {
|
|
self.mergedIndexTable.remove(tag: tag, mergedIndices: mergedIndices)
|
|
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set(mergedIndices)))
|
|
}
|
|
}
|
|
|
|
func getMergedEntries(tag: PeerOperationLogTag, fromIndex: Int32, limit: Int) -> [PeerMergedOperationLogEntry] {
|
|
var entries: [PeerMergedOperationLogEntry] = []
|
|
for (peerId, tagLocalIndex, mergedIndex) in self.mergedIndexTable.getTagLocalIndices(tag: tag, fromMergedIndex: fromIndex, limit: limit) {
|
|
if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, tag: tag, index: tagLocalIndex)) {
|
|
if let entry = parseMergedEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, value) {
|
|
entries.append(entry)
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
} else {
|
|
self.mergedIndexTable.remove(tag: tag, mergedIndices: [mergedIndex])
|
|
assertionFailure()
|
|
}
|
|
}
|
|
return entries
|
|
}
|
|
|
|
func getMergedEntries(tag: PeerOperationLogTag, peerId: PeerId, fromIndex: Int32, limit: Int) -> [PeerMergedOperationLogEntry] {
|
|
var entries: [PeerMergedOperationLogEntry] = []
|
|
for (peerId, tagLocalIndex, mergedIndex) in self.mergedIndexTable.getTagLocalIndices(tag: tag, peerId: peerId, fromMergedIndex: fromIndex, limit: limit) {
|
|
if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, tag: tag, index: tagLocalIndex)) {
|
|
if let entry = parseMergedEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, value) {
|
|
entries.append(entry)
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
} else {
|
|
self.mergedIndexTable.remove(tag: tag, mergedIndices: [mergedIndex])
|
|
assertionFailure()
|
|
}
|
|
}
|
|
return entries
|
|
}
|
|
|
|
func enumerateEntries(peerId: PeerId, tag: PeerOperationLogTag, _ f: (PeerOperationLogEntry) -> Bool) {
|
|
self.valueBox.range(self.table, start: self.key(peerId: peerId, tag: tag, index: 0).predecessor, end: self.key(peerId: peerId, tag: tag, index: Int32.max).successor, values: { key, value in
|
|
if let entry = parseEntry(peerId: peerId, tag: tag, tagLocalIndex: key.getInt32(9), value) {
|
|
if !f(entry) {
|
|
return false
|
|
}
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
return true
|
|
}, limit: 0)
|
|
}
|
|
|
|
func updateEntry(peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, f: (PeerOperationLogEntry?) -> PeerOperationLogEntryUpdate, operations: inout [PeerMergedOperationLogOperation]) {
|
|
let key = self.key(peerId: peerId, tag: tag, index: tagLocalIndex)
|
|
if let value = self.valueBox.get(self.table, key: key) {
|
|
var hasMergedIndex: Int8 = 0
|
|
value.read(&hasMergedIndex, offset: 0, length: 1)
|
|
var mergedIndex: Int32?
|
|
if hasMergedIndex != 0 {
|
|
var mergedIndexValue: Int32 = 0
|
|
value.read(&mergedIndexValue, offset: 0, length: 4)
|
|
mergedIndex = mergedIndexValue
|
|
}
|
|
let previousMergedIndex = mergedIndex
|
|
var contentLength: Int32 = 0
|
|
value.read(&contentLength, offset: 0, length: 4)
|
|
assert(value.length - value.offset == Int(contentLength))
|
|
if let contents = PostboxDecoder(buffer: MemoryBuffer(memory: value.memory.advanced(by: value.offset), capacity: Int(contentLength), length: Int(contentLength), freeWhenDone: false)).decodeRootObject() {
|
|
let entryUpdate = f(PeerOperationLogEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, mergedIndex: mergedIndex, contents: contents))
|
|
var updatedContents: PostboxCoding?
|
|
switch entryUpdate.contents {
|
|
case .none:
|
|
break
|
|
case let .update(contents):
|
|
updatedContents = contents
|
|
}
|
|
switch entryUpdate.mergedIndex {
|
|
case .none:
|
|
if let previousMergedIndex = previousMergedIndex, let updatedContents = updatedContents {
|
|
operations.append(.updateContents(PeerMergedOperationLogEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, mergedIndex: previousMergedIndex, contents: updatedContents)))
|
|
}
|
|
case .remove:
|
|
if let mergedIndexValue = mergedIndex {
|
|
mergedIndex = nil
|
|
self.mergedIndexTable.remove(tag: tag, mergedIndices: [mergedIndexValue])
|
|
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set([mergedIndexValue])))
|
|
}
|
|
case .newAutomatic:
|
|
if let mergedIndexValue = mergedIndex {
|
|
self.mergedIndexTable.remove(tag: tag, mergedIndices: [mergedIndexValue])
|
|
operations.append(.remove(tag: tag, peerId: peerId, mergedIndices: Set([mergedIndexValue])))
|
|
}
|
|
let updatedMergedIndexValue = self.mergedIndexTable.add(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex)
|
|
mergedIndex = updatedMergedIndexValue
|
|
operations.append(.append(PeerMergedOperationLogEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, mergedIndex: updatedMergedIndexValue, contents: updatedContents ?? contents)))
|
|
}
|
|
if previousMergedIndex != mergedIndex || updatedContents != nil {
|
|
let buffer = WriteBuffer()
|
|
var hasMergedIndex: Int8 = mergedIndex != nil ? 1 : 0
|
|
buffer.write(&hasMergedIndex, offset: 0, length: 1)
|
|
if let mergedIndex = mergedIndex {
|
|
var mergedIndexValue: Int32 = mergedIndex
|
|
buffer.write(&mergedIndexValue, offset: 0, length: 4)
|
|
}
|
|
|
|
let encoder = PostboxEncoder()
|
|
if let updatedContents = updatedContents {
|
|
encoder.encodeRootObject(updatedContents)
|
|
} else {
|
|
encoder.encodeRootObject(contents)
|
|
}
|
|
let contentBuffer = encoder.readBufferNoCopy()
|
|
withExtendedLifetime(encoder, {
|
|
var contentBufferLength: Int32 = Int32(contentBuffer.length)
|
|
buffer.write(&contentBufferLength, offset: 0, length: 4)
|
|
buffer.write(contentBuffer.memory, offset: 0, length: contentBuffer.length)
|
|
self.valueBox.set(self.table, key: key, value: buffer)
|
|
})
|
|
}
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
} else {
|
|
let _ = f(nil)
|
|
}
|
|
}
|
|
}
|