Swiftgram/submodules/Postbox/Sources/PeerOperationLogTable.swift
Ali 96279df59b Re-implement external sharing to secret chats
(cherry picked from commit 21af13cfdb347b76bf7b504ae5d36067ba693b18)
2023-11-06 20:20:41 +04:00

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)
}
}
}