mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
323 lines
14 KiB
Swift
323 lines
14 KiB
Swift
import Foundation
|
|
|
|
enum HistoryIndexEntry {
|
|
case Message(MessageIndex)
|
|
case Hole(MessageHistoryHole)
|
|
}
|
|
|
|
enum HoleFillType {
|
|
case UpperToLower
|
|
case LowerToUpper
|
|
case Complete
|
|
}
|
|
|
|
private func readHistoryIndexEntry(peerId: PeerId, namespace: MessageId.Namespace, key: ValueBoxKey, value: ReadBuffer) -> HistoryIndexEntry {
|
|
var type: Int8 = 0
|
|
value.read(&type, offset: 0, length: 1)
|
|
var timestamp: Int32 = 0
|
|
value.read(×tamp, offset: 0, length: 4)
|
|
let index = MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: key.getInt32(8 + 4)), timestamp: timestamp)
|
|
|
|
if type == 0 {
|
|
return .Message(index)
|
|
} else {
|
|
var min: Int32 = 0
|
|
value.read(&min, offset: 0, length: 4)
|
|
return .Hole(MessageHistoryHole(maxIndex: index, min: min))
|
|
}
|
|
}
|
|
|
|
final class MessageHistoryIndexTable {
|
|
let valueBox: ValueBox
|
|
let tableId: Int32
|
|
|
|
init(valueBox: ValueBox, tableId: Int32) {
|
|
self.valueBox = valueBox
|
|
self.tableId = tableId
|
|
}
|
|
|
|
func key(id: MessageId) -> ValueBoxKey {
|
|
let key = ValueBoxKey(length: 8 + 4 + 4)
|
|
key.setInt64(0, value: id.peerId.toInt64())
|
|
key.setInt32(8, value: id.namespace)
|
|
key.setInt32(8 + 4, value: id.id)
|
|
return key
|
|
}
|
|
|
|
func lowerBound(peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey {
|
|
let key = ValueBoxKey(length: 8 + 4)
|
|
key.setInt64(0, value: peerId.toInt64())
|
|
key.setInt32(8, value: namespace)
|
|
return key
|
|
}
|
|
|
|
func upperBound(peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey {
|
|
let key = ValueBoxKey(length: 8 + 4)
|
|
key.setInt64(0, value: peerId.toInt64())
|
|
key.setInt32(8, value: namespace)
|
|
return key.successor
|
|
}
|
|
|
|
func addHole(id: MessageId) {
|
|
let adjacent = self.adjacentItems(id)
|
|
|
|
/*
|
|
|
|
1. [x] nothing
|
|
2. [x] message - * - nothing
|
|
3. [x] nothing - * - message
|
|
4. [x] nothing - * - hole
|
|
5. [x] message - * - message
|
|
6. [x] hole - * - message
|
|
7. [x] message - * - hole
|
|
|
|
*/
|
|
|
|
if let lowerItem = adjacent.lower, upperItem = adjacent.upper {
|
|
switch lowerItem {
|
|
case .Hole:
|
|
break
|
|
case let .Message(lowerMessage):
|
|
switch upperItem {
|
|
case .Hole:
|
|
break
|
|
case let .Message(upperMessage):
|
|
if lowerMessage.id.id < upperMessage.id.id - 1 {
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: id.peerId, namespace: id.namespace, id: upperMessage.id.id - 1), timestamp: upperMessage.timestamp), min: lowerMessage.id.id + 1))
|
|
}
|
|
break
|
|
}
|
|
}
|
|
} else if let lowerItem = adjacent.lower {
|
|
switch lowerItem {
|
|
case let .Message(lowerMessage):
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: id.peerId, namespace: id.namespace, id: Int32.max), timestamp: Int32.max), min: lowerMessage.id.id + 1))
|
|
case .Hole:
|
|
break
|
|
}
|
|
} else if let upperItem = adjacent.upper {
|
|
switch upperItem {
|
|
case let .Message(upperMessage):
|
|
if upperMessage.id.id > 1 {
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: id.peerId, namespace: id.namespace, id: upperMessage.id.id - 1), timestamp: upperMessage.timestamp), min: 1))
|
|
}
|
|
case .Hole:
|
|
break
|
|
}
|
|
} else {
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: id.peerId, namespace: id.namespace, id: Int32.max), timestamp: Int32.max), min: 1))
|
|
}
|
|
}
|
|
|
|
func addMessage(index: MessageIndex) {
|
|
var upperItem: HistoryIndexEntry?
|
|
self.valueBox.range(self.tableId, start: self.key(index.id).predecessor, end: self.upperBound(index.id.peerId, namespace: index.id.namespace), values: { key, value in
|
|
upperItem = readHistoryIndexEntry(index.id.peerId, namespace: index.id.namespace, key: key, value: value)
|
|
return true
|
|
}, limit: 1)
|
|
|
|
if let upperItem = upperItem {
|
|
switch upperItem {
|
|
case let .Hole(upperHole):
|
|
self.justRemove(upperHole.id)
|
|
if upperHole.maxIndex.id.id > index.id.id + 1 {
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: upperHole.maxIndex, min: index.id.id + 1))
|
|
}
|
|
if upperHole.min <= index.id.id - 1 {
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: index.id.peerId, namespace: index.id.namespace, id: index.id.id - 1), timestamp: index.timestamp), min: upperHole.min))
|
|
}
|
|
break
|
|
case .Message:
|
|
break
|
|
}
|
|
}
|
|
|
|
self.justInsertMessage(index)
|
|
}
|
|
|
|
func removeMessage(id: MessageId) {
|
|
let key = self.key(id)
|
|
if self.valueBox.exists(self.tableId, key: key) {
|
|
self.justRemove(id)
|
|
|
|
let adjacent = self.adjacentItems(id)
|
|
|
|
if let lowerItem = adjacent.lower, upperItem = adjacent.upper {
|
|
switch lowerItem {
|
|
case let .Message(lowerMessage):
|
|
switch upperItem {
|
|
case let .Hole(upperHole):
|
|
self.justRemove(upperHole.id)
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: upperHole.maxIndex, min: lowerMessage.id.id + 1))
|
|
case .Message:
|
|
break
|
|
}
|
|
case let .Hole(lowerHole):
|
|
switch upperItem {
|
|
case let .Hole(upperHole):
|
|
self.justRemove(lowerHole.id)
|
|
self.justRemove(upperHole.id)
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: upperHole.maxIndex, min: lowerHole.min))
|
|
case let .Message(upperMessage):
|
|
self.justRemove(lowerHole.id)
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: id.peerId, namespace: id.namespace, id: upperMessage.id.id - 1), timestamp: upperMessage.timestamp), min: lowerHole.min))
|
|
}
|
|
}
|
|
} else if let lowerItem = adjacent.lower {
|
|
switch lowerItem {
|
|
case let .Hole(lowerHole):
|
|
self.justRemove(lowerHole.id)
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: id.peerId, namespace: id.namespace, id: Int32.max), timestamp: Int32.max), min: lowerHole.min))
|
|
break
|
|
case .Message:
|
|
break
|
|
}
|
|
} else if let upperItem = adjacent.upper {
|
|
switch upperItem {
|
|
case let .Hole(upperHole):
|
|
self.justRemove(upperHole.id)
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: upperHole.maxIndex, min: 1))
|
|
break
|
|
case .Message:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func fillHole(id: MessageId, fillType: HoleFillType, indices: [MessageIndex]) {
|
|
var upperItem: HistoryIndexEntry?
|
|
self.valueBox.range(self.tableId, start: self.key(id).predecessor, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in
|
|
upperItem = readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value)
|
|
return true
|
|
}, limit: 1)
|
|
|
|
let sortedByIdIndices = indices.sort({$0.id < $1.id})
|
|
var remainingIndices = sortedByIdIndices
|
|
|
|
if let upperItem = upperItem {
|
|
switch upperItem {
|
|
case let .Hole(upperHole):
|
|
var i = 0
|
|
var minIndexInRange: MessageIndex?
|
|
var maxIndexInRange: MessageIndex?
|
|
var removedHole = false
|
|
while i < remainingIndices.count {
|
|
let index = remainingIndices[i]
|
|
if index.id.id >= upperHole.min && index.id.id <= upperHole.maxIndex.id.id {
|
|
if (fillType == .UpperToLower || fillType == .Complete) && (minIndexInRange == nil || minIndexInRange!.id > index.id) {
|
|
minIndexInRange = index
|
|
if !removedHole {
|
|
removedHole = true
|
|
self.justRemove(upperHole.id)
|
|
}
|
|
}
|
|
if (fillType == .LowerToUpper || fillType == .Complete) && (maxIndexInRange == nil || maxIndexInRange!.id < index.id) {
|
|
maxIndexInRange = index
|
|
if !removedHole {
|
|
removedHole = true
|
|
self.justRemove(upperHole.id)
|
|
}
|
|
}
|
|
self.justInsertMessage(index)
|
|
remainingIndices.removeAtIndex(i)
|
|
} else {
|
|
i++
|
|
}
|
|
}
|
|
switch fillType {
|
|
case .Complete:
|
|
if !removedHole {
|
|
self.justRemove(upperHole.id)
|
|
}
|
|
case .LowerToUpper:
|
|
if let maxIndexInRange = maxIndexInRange where maxIndexInRange.id.id != Int32.max && maxIndexInRange.id.id + 1 <= upperHole.maxIndex.id.id {
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: upperHole.maxIndex, min: maxIndexInRange.id.id + 1))
|
|
}
|
|
case .UpperToLower:
|
|
if let minIndexInRange = minIndexInRange where minIndexInRange.id.id - 1 >= upperHole.min {
|
|
self.justInsertHole(MessageHistoryHole(maxIndex: MessageIndex(id: MessageId(peerId: id.peerId, namespace: id.namespace, id: minIndexInRange.id.id - 1), timestamp: minIndexInRange.timestamp), min: upperHole.min))
|
|
}
|
|
}
|
|
break
|
|
case .Message:
|
|
break
|
|
}
|
|
}
|
|
|
|
for index in remainingIndices {
|
|
self.addMessage(index)
|
|
}
|
|
}
|
|
|
|
func justInsertHole(hole: MessageHistoryHole) {
|
|
let value = WriteBuffer()
|
|
var type: Int8 = 1
|
|
var timestamp: Int32 = hole.maxIndex.timestamp
|
|
var min: Int32 = hole.min
|
|
value.write(&type, offset: 0, length: 1)
|
|
value.write(×tamp, offset: 0, length: 4)
|
|
value.write(&min, offset: 0, length: 4)
|
|
self.valueBox.set(self.tableId, key: self.key(hole.id), value: value)
|
|
}
|
|
|
|
func justInsertMessage(index: MessageIndex) {
|
|
let value = WriteBuffer()
|
|
var type: Int8 = 0
|
|
var timestamp: Int32 = index.timestamp
|
|
value.write(&type, offset: 0, length: 1)
|
|
value.write(×tamp, offset: 0, length: 4)
|
|
self.valueBox.set(self.tableId, key: self.key(index.id), value: value)
|
|
}
|
|
|
|
func justRemove(id: MessageId) {
|
|
self.valueBox.remove(self.tableId, key: self.key(id))
|
|
}
|
|
|
|
func adjacentItems(id: MessageId) -> (lower: HistoryIndexEntry?, upper: HistoryIndexEntry?) {
|
|
let key = self.key(id)
|
|
|
|
var lowerItem: HistoryIndexEntry?
|
|
self.valueBox.range(self.tableId, start: key, end: self.lowerBound(id.peerId, namespace: id.namespace), values: { key, value in
|
|
lowerItem = readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value)
|
|
return true
|
|
}, limit: 1)
|
|
|
|
var upperItem: HistoryIndexEntry?
|
|
self.valueBox.range(self.tableId, start: key.predecessor, end: self.upperBound(id.peerId, namespace: id.namespace), values: { key, value in
|
|
upperItem = readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value)
|
|
return true
|
|
}, limit: 1)
|
|
|
|
return (lower: lowerItem, upper: upperItem)
|
|
}
|
|
|
|
func get(id: MessageId) -> HistoryIndexEntry? {
|
|
let key = self.key(id)
|
|
if let value = self.valueBox.get(self.tableId, key: key) {
|
|
return readHistoryIndexEntry(id.peerId, namespace: id.namespace, key: key, value: value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func messageExists(id: MessageId) -> Bool {
|
|
if let entry = self.get(id) {
|
|
if case .Message = entry {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func debugList(peerId: PeerId, namespace: MessageId.Namespace) -> [HistoryIndexEntry] {
|
|
var list: [HistoryIndexEntry] = []
|
|
self.valueBox.range(self.tableId, start: self.lowerBound(peerId, namespace: namespace), end: self.upperBound(peerId, namespace: namespace), values: { key, value in
|
|
list.append(readHistoryIndexEntry(peerId, namespace: namespace, key: key, value: value))
|
|
|
|
return true
|
|
}, limit: 0)
|
|
return list
|
|
}
|
|
}
|