mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
455 lines
21 KiB
Swift
455 lines
21 KiB
Swift
import Foundation
|
|
|
|
private struct PrivatePeerId: Hashable {
|
|
typealias Namespace = Int32
|
|
typealias Id = Int32
|
|
|
|
let namespace: Namespace
|
|
let id: Id
|
|
|
|
init(namespace: Namespace, id: Id) {
|
|
self.namespace = namespace
|
|
self.id = id
|
|
}
|
|
|
|
init(_ n: Int64) {
|
|
self.namespace = Int32((n >> 32) & 0x7fffffff)
|
|
self.id = Int32(bitPattern: UInt32(n & 0xffffffff))
|
|
}
|
|
|
|
func toInt64() -> Int64 {
|
|
return (Int64(self.namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: self.id)))
|
|
}
|
|
}
|
|
|
|
private func localChatListPinningIndexFromKeyValue(_ value: UInt16) -> UInt16? {
|
|
if value == 0 {
|
|
return nil
|
|
} else {
|
|
return UInt16.max - 1 - value
|
|
}
|
|
}
|
|
|
|
private func extractChatListKey(_ key: ValueBoxKey) -> (groupId: Int32?, pinningIndex: UInt16?, index: MessageIndex, type: Int8) {
|
|
let groupIdValue = key.getInt32(0)
|
|
return (
|
|
groupId: groupIdValue == 0 ? nil : groupIdValue,
|
|
pinningIndex: localChatListPinningIndexFromKeyValue(key.getUInt16(4)),
|
|
index: MessageIndex(
|
|
id: MessageId(
|
|
peerId: PeerId(key.getInt64(4 + 2 + 4 + 1 + 4)),
|
|
namespace: Int32(key.getInt8(4 + 2 + 4)),
|
|
id: key.getInt32(4 + 2 + 4 + 1)
|
|
),
|
|
timestamp: key.getInt32(4 + 2)
|
|
),
|
|
type: key.getInt8(4 + 2 + 4 + 1 + 4 + 8)
|
|
)
|
|
}
|
|
|
|
private struct MessageDataFlags: OptionSet {
|
|
var rawValue: Int8
|
|
|
|
init() {
|
|
self.rawValue = 0
|
|
}
|
|
|
|
init(rawValue: Int8) {
|
|
self.rawValue = rawValue
|
|
}
|
|
|
|
static let hasGloballyUniqueId = MessageDataFlags(rawValue: 1 << 0)
|
|
static let hasGlobalTags = MessageDataFlags(rawValue: 1 << 1)
|
|
static let hasGroupingKey = MessageDataFlags(rawValue: 1 << 2)
|
|
static let hasGroupInfo = MessageDataFlags(rawValue: 1 << 3)
|
|
static let hasLocalTags = MessageDataFlags(rawValue: 1 << 4)
|
|
}
|
|
|
|
private enum MediaEntryType: Int8 {
|
|
case Direct
|
|
case MessageReference
|
|
}
|
|
|
|
private func mediaTableKey(_ id: MediaId) -> ValueBoxKey {
|
|
let key = ValueBoxKey(length: 4 + 8)
|
|
key.setInt32(0, value: id.namespace)
|
|
key.setInt64(4, value: id.id)
|
|
return key
|
|
}
|
|
|
|
private func removeMediaReference(valueBox: ValueBox, table: ValueBoxTable, id: MediaId, sharedWriteBuffer: WriteBuffer = WriteBuffer()) {
|
|
guard let value = valueBox.get(table, key: mediaTableKey(id)) else {
|
|
return
|
|
}
|
|
|
|
var type: Int8 = 0
|
|
value.read(&type, offset: 0, length: 1)
|
|
if type == MediaEntryType.Direct.rawValue {
|
|
var dataLength: Int32 = 0
|
|
value.read(&dataLength, offset: 0, length: 4)
|
|
value.skip(Int(dataLength))
|
|
|
|
sharedWriteBuffer.reset()
|
|
sharedWriteBuffer.write(value.memory, offset: 0, length: value.offset)
|
|
|
|
var messageReferenceCount: Int32 = 0
|
|
value.read(&messageReferenceCount, offset: 0, length: 4)
|
|
messageReferenceCount -= 1
|
|
sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4)
|
|
|
|
if messageReferenceCount <= 0 {
|
|
valueBox.remove(table, key: mediaTableKey(id), secure: false)
|
|
} else {
|
|
withExtendedLifetime(sharedWriteBuffer, {
|
|
valueBox.set(table, key: mediaTableKey(id), value: sharedWriteBuffer.readBufferNoCopy())
|
|
})
|
|
}
|
|
} else if type == MediaEntryType.MessageReference.rawValue {
|
|
var idPeerId: Int64 = 0
|
|
var idNamespace: Int32 = 0
|
|
var idId: Int32 = 0
|
|
var idTimestamp: Int32 = 0
|
|
value.read(&idPeerId, offset: 0, length: 8)
|
|
value.read(&idNamespace, offset: 0, length: 4)
|
|
value.read(&idId, offset: 0, length: 4)
|
|
value.read(&idTimestamp, offset: 0, length: 4)
|
|
|
|
valueBox.remove(table, key: mediaTableKey(id), secure: false)
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
}
|
|
|
|
func postboxUpgrade_19to20(metadataTable: MetadataTable, valueBox: ValueBox, progress: (Float) -> Void) {
|
|
postboxLog("Upgrade 19->20 started")
|
|
let startTime = CFAbsoluteTimeGetCurrent()
|
|
|
|
let messageHistoryIndexTable = ValueBoxTable(id: 4, keyType: .binary, compactValuesOnCreation: true)
|
|
let messageHistoryTable = ValueBoxTable(id: 7, keyType: .binary, compactValuesOnCreation: false)
|
|
let messageHistoryMetadataTable = ValueBoxTable(id: 10, keyType: .binary, compactValuesOnCreation: true)
|
|
let chatListIndexTable = ValueBoxTable(id: 8, keyType: .int64, compactValuesOnCreation: false)
|
|
let chatListTable = ValueBoxTable(id: 9, keyType: .binary, compactValuesOnCreation: true)
|
|
let globalMessageIdsTable = ValueBoxTable(id: 3, keyType: .int64, compactValuesOnCreation: false)
|
|
let messageHistoryTagsTable = ValueBoxTable(id: 12, keyType: .binary, compactValuesOnCreation: true)
|
|
let globalMessageHistoryTagsTable = ValueBoxTable(id: 39, keyType: .binary, compactValuesOnCreation: true)
|
|
let localMessageHistoryTagsTable = ValueBoxTable(id: 52, keyType: .binary, compactValuesOnCreation: true)
|
|
let mediaTable = ValueBoxTable(id: 6, keyType: .binary, compactValuesOnCreation: false)
|
|
|
|
var totalMessageCount = 0
|
|
|
|
let absoluteLowerBound = ValueBoxKey(length: 8)
|
|
absoluteLowerBound.setInt64(0, value: 0)
|
|
|
|
let absoluteUpperBound = ValueBoxKey(length: 8)
|
|
absoluteUpperBound.setInt64(0, value: Int64.max - 1)
|
|
|
|
let sharedMessageHistoryKey = ValueBoxKey(length: 8 + 4 + 4 + 4)
|
|
let sharedPeerHistoryInitializedKey = ValueBoxKey(length: 8 + 1)
|
|
let sharedChatListIndexKey = ValueBoxKey(length: 8)
|
|
let sharedGlobalIdsKey = ValueBoxKey(length: 8)
|
|
let sharedMessageHistoryTagsKey = ValueBoxKey(length: 8 + 4 + 4 + 4 + 4)
|
|
let sharedGlobalTagsKey = ValueBoxKey(length: 4 + 4 + 4 + 4 + 8)
|
|
let sharedLocalTagsKey = ValueBoxKey(length: 4 + 4 + 4 + 8)
|
|
let sharedMediaWriteBuffer = WriteBuffer()
|
|
|
|
var matchingPeerIds: [PrivatePeerId] = []
|
|
|
|
var expectedTotalCount = 0
|
|
|
|
let currentLowerBound: ValueBoxKey = absoluteLowerBound
|
|
while true {
|
|
var currentPeerId: PrivatePeerId?
|
|
valueBox.range(messageHistoryIndexTable, start: currentLowerBound, end: absoluteUpperBound, keys: {
|
|
key in
|
|
currentPeerId = PrivatePeerId(key.getInt64(0))
|
|
return true
|
|
}, limit: 1)
|
|
if let currentPeerId = currentPeerId {
|
|
if currentPeerId.namespace == 0 || currentPeerId.namespace == 1 { // CloudUser || CloudGroup
|
|
matchingPeerIds.append(currentPeerId)
|
|
|
|
let nextLowerBound = ValueBoxKey(length: 8)
|
|
nextLowerBound.setInt64(0, value: currentPeerId.toInt64() + 1)
|
|
|
|
expectedTotalCount += valueBox.count(messageHistoryIndexTable, start: currentLowerBound, end: nextLowerBound)
|
|
}
|
|
currentLowerBound.setInt64(0, value: currentPeerId.toInt64() + 1)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
var messageIndex = -1
|
|
let reportBase = max(1, expectedTotalCount / 100)
|
|
|
|
for peerId in matchingPeerIds {
|
|
let peerCloudLowerBound = ValueBoxKey(length: 8 + 4)
|
|
peerCloudLowerBound.setInt64(0, value: peerId.toInt64())
|
|
peerCloudLowerBound.setInt32(8, value: 0) // Cloud
|
|
|
|
valueBox.range(messageHistoryIndexTable, start: peerCloudLowerBound, end: peerCloudLowerBound.successor, values: { key, indexValue in
|
|
totalMessageCount += 1
|
|
|
|
if messageIndex % reportBase == 0 {
|
|
progress(min(1.0, Float(messageIndex) / Float(expectedTotalCount)))
|
|
}
|
|
messageIndex += 1
|
|
|
|
var flags: Int8 = 0
|
|
indexValue.read(&flags, offset: 0, length: 1)
|
|
|
|
var timestamp: Int32 = 0
|
|
indexValue.read(×tamp, offset: 0, length: 4)
|
|
|
|
let id = key.getInt32(8 + 4)
|
|
|
|
let HistoryEntryTypeMask: Int8 = 1
|
|
let HistoryEntryTypeMessage: Int8 = 0
|
|
|
|
if (flags & HistoryEntryTypeMask) == HistoryEntryTypeMessage {
|
|
sharedGlobalIdsKey.setInt64(0, value: Int64(id))
|
|
valueBox.remove(globalMessageIdsTable, key: sharedGlobalIdsKey, secure: false)
|
|
}
|
|
|
|
sharedMessageHistoryKey.setInt64(0, value: peerId.toInt64())
|
|
sharedMessageHistoryKey.setInt32(8, value: timestamp)
|
|
sharedMessageHistoryKey.setInt32(8 + 4, value: 0)
|
|
sharedMessageHistoryKey.setInt32(8 + 4 + 4, value: id)
|
|
|
|
if let value = valueBox.get(messageHistoryTable, key: sharedMessageHistoryKey) {
|
|
var type: Int8 = 0
|
|
value.read(&type, offset: 0, length: 1)
|
|
if type == 0 {
|
|
var stableId: UInt32 = 0
|
|
value.read(&stableId, offset: 0, length: 4)
|
|
|
|
var stableVersion: UInt32 = 0
|
|
value.read(&stableVersion, offset: 0, length: 4)
|
|
|
|
var dataFlagsValue: Int8 = 0
|
|
value.read(&dataFlagsValue, offset: 0, length: 1)
|
|
let dataFlags = MessageDataFlags(rawValue: dataFlagsValue)
|
|
|
|
if dataFlags.contains(.hasGloballyUniqueId) {
|
|
var globallyUniqueIdValue: Int64 = 0
|
|
value.read(&globallyUniqueIdValue, offset: 0, length: 8)
|
|
}
|
|
|
|
var globalTags: GlobalMessageTags = []
|
|
if dataFlags.contains(.hasGlobalTags) {
|
|
var globalTagsValue: UInt32 = 0
|
|
value.read(&globalTagsValue, offset: 0, length: 4)
|
|
globalTags = GlobalMessageTags(rawValue: globalTagsValue)
|
|
}
|
|
|
|
if dataFlags.contains(.hasGroupingKey) {
|
|
var groupingKeyValue: Int64 = 0
|
|
value.read(&groupingKeyValue, offset: 0, length: 8)
|
|
}
|
|
|
|
if dataFlags.contains(.hasGroupInfo) {
|
|
var stableIdValue: UInt32 = 0
|
|
value.read(&stableIdValue, offset: 0, length: 4)
|
|
}
|
|
|
|
var localTags: LocalMessageTags = []
|
|
if dataFlags.contains(.hasLocalTags) {
|
|
var localTagsValue: UInt32 = 0
|
|
value.read(&localTagsValue, offset: 0, length: 4)
|
|
localTags = LocalMessageTags(rawValue: localTagsValue)
|
|
}
|
|
|
|
var flagsValue: UInt32 = 0
|
|
value.read(&flagsValue, offset: 0, length: 4)
|
|
|
|
var tagsValue: UInt32 = 0
|
|
value.read(&tagsValue, offset: 0, length: 4)
|
|
let tags = MessageTags(rawValue: tagsValue)
|
|
|
|
var forwardInfoFlags: Int8 = 0
|
|
value.read(&forwardInfoFlags, offset: 0, length: 1)
|
|
if forwardInfoFlags != 0 {
|
|
var forwardAuthorId: Int64 = 0
|
|
var forwardDate: Int32 = 0
|
|
|
|
value.read(&forwardAuthorId, offset: 0, length: 8)
|
|
value.read(&forwardDate, offset: 0, length: 4)
|
|
|
|
if (forwardInfoFlags & (1 << 1)) != 0 {
|
|
var forwardSourceIdValue: Int64 = 0
|
|
value.read(&forwardSourceIdValue, offset: 0, length: 8)
|
|
}
|
|
|
|
if (forwardInfoFlags & (1 << 2)) != 0 {
|
|
var forwardSourceMessagePeerId: Int64 = 0
|
|
var forwardSourceMessageNamespace: Int32 = 0
|
|
var forwardSourceMessageIdId: Int32 = 0
|
|
value.read(&forwardSourceMessagePeerId, offset: 0, length: 8)
|
|
value.read(&forwardSourceMessageNamespace, offset: 0, length: 4)
|
|
value.read(&forwardSourceMessageIdId, offset: 0, length: 4)
|
|
}
|
|
|
|
if (forwardInfoFlags & (1 << 3)) != 0 {
|
|
var signatureLength: Int32 = 0
|
|
value.read(&signatureLength, offset: 0, length: 4)
|
|
value.skip(Int(signatureLength))
|
|
}
|
|
}
|
|
|
|
var hasAuthor: Int8 = 0
|
|
value.read(&hasAuthor, offset: 0, length: 1)
|
|
if hasAuthor == 1 {
|
|
var varAuthorId: Int64 = 0
|
|
value.read(&varAuthorId, offset: 0, length: 8)
|
|
}
|
|
|
|
var textLength: Int32 = 0
|
|
value.read(&textLength, offset: 0, length: 4)
|
|
value.skip(Int(textLength))
|
|
|
|
var attributeCount: Int32 = 0
|
|
value.read(&attributeCount, offset: 0, length: 4)
|
|
for _ in 0 ..< attributeCount {
|
|
var attributeLength: Int32 = 0
|
|
value.read(&attributeLength, offset: 0, length: 4)
|
|
value.skip(Int(attributeLength))
|
|
}
|
|
|
|
let embeddedMediaOffset = value.offset
|
|
var embeddedMediaCount: Int32 = 0
|
|
value.read(&embeddedMediaCount, offset: 0, length: 4)
|
|
for _ in 0 ..< embeddedMediaCount {
|
|
var mediaLength: Int32 = 0
|
|
value.read(&mediaLength, offset: 0, length: 4)
|
|
value.skip(Int(mediaLength))
|
|
}
|
|
let embeddedMediaLength = value.offset - embeddedMediaOffset
|
|
let embeddedMediaBytes = malloc(embeddedMediaLength)!
|
|
memcpy(embeddedMediaBytes, value.memory + embeddedMediaOffset, embeddedMediaLength)
|
|
let embeddedMediaData = ReadBuffer(memory: embeddedMediaBytes, length: embeddedMediaLength, freeWhenDone: true)
|
|
|
|
var referencedMediaIds: [MediaId] = []
|
|
var referencedMediaIdsCount: Int32 = 0
|
|
value.read(&referencedMediaIdsCount, offset: 0, length: 4)
|
|
for _ in 0 ..< referencedMediaIdsCount {
|
|
var idNamespace: Int32 = 0
|
|
var idId: Int64 = 0
|
|
value.read(&idNamespace, offset: 0, length: 4)
|
|
value.read(&idId, offset: 0, length: 8)
|
|
referencedMediaIds.append(MediaId(namespace: idNamespace, id: idId))
|
|
}
|
|
|
|
for tag in tags {
|
|
sharedMessageHistoryTagsKey.setInt64(0, value: peerId.toInt64())
|
|
sharedMessageHistoryTagsKey.setUInt32(8, value: tag.rawValue)
|
|
sharedMessageHistoryTagsKey.setInt32(8 + 4, value: timestamp)
|
|
sharedMessageHistoryTagsKey.setInt32(8 + 4 + 4, value: 0)
|
|
sharedMessageHistoryTagsKey.setInt32(8 + 4 + 4 + 4, value: id)
|
|
|
|
valueBox.remove(messageHistoryTagsTable, key: sharedMessageHistoryTagsKey, secure: false)
|
|
}
|
|
|
|
for tag in globalTags {
|
|
sharedGlobalTagsKey.setUInt32(0, value: tag.rawValue)
|
|
sharedGlobalTagsKey.setInt32(4, value: timestamp)
|
|
sharedGlobalTagsKey.setInt32(4 + 4, value: 0)
|
|
sharedGlobalTagsKey.setInt32(4 + 4 + 4, value: id)
|
|
sharedGlobalTagsKey.setInt64(4 + 4 + 4 + 4, value: peerId.toInt64())
|
|
|
|
valueBox.remove(globalMessageHistoryTagsTable, key: sharedGlobalTagsKey, secure: false)
|
|
}
|
|
|
|
for tag in localTags {
|
|
sharedLocalTagsKey.setUInt32(0, value: tag.rawValue)
|
|
sharedLocalTagsKey.setInt32(4, value: 0)
|
|
sharedLocalTagsKey.setInt32(4 + 4, value: id)
|
|
sharedLocalTagsKey.setInt64(4 + 4 + 4, value: peerId.toInt64())
|
|
|
|
valueBox.remove(localMessageHistoryTagsTable, key: sharedLocalTagsKey, secure: false)
|
|
}
|
|
|
|
for mediaId in referencedMediaIds {
|
|
removeMediaReference(valueBox: valueBox, table: mediaTable, id: mediaId, sharedWriteBuffer: sharedMediaWriteBuffer)
|
|
}
|
|
|
|
if embeddedMediaData.length > 4 {
|
|
var embeddedMediaCount: Int32 = 0
|
|
embeddedMediaData.read(&embeddedMediaCount, offset: 0, length: 4)
|
|
for _ in 0 ..< embeddedMediaCount {
|
|
var mediaLength: Int32 = 0
|
|
embeddedMediaData.read(&mediaLength, offset: 0, length: 4)
|
|
if let media = PostboxDecoder(buffer: MemoryBuffer(memory: embeddedMediaData.memory + embeddedMediaData.offset, capacity: Int(mediaLength), length: Int(mediaLength), freeWhenDone: false)).decodeRootObject() as? Media {
|
|
if let mediaId = media.id {
|
|
removeMediaReference(valueBox: valueBox, table: mediaTable, id: mediaId, sharedWriteBuffer: sharedMediaWriteBuffer)
|
|
}
|
|
}
|
|
embeddedMediaData.skip(Int(mediaLength))
|
|
}
|
|
}
|
|
} else {
|
|
var stableId: UInt32 = 0
|
|
value.read(&stableId, offset: 0, length: 4)
|
|
|
|
var minId: Int32 = 0
|
|
value.read(&minId, offset: 0, length: 4)
|
|
var tags: UInt32 = 0
|
|
value.read(&tags, offset: 0, length: 4)
|
|
|
|
for tag in MessageTags(rawValue: tags) {
|
|
sharedMessageHistoryTagsKey.setInt64(0, value: peerId.toInt64())
|
|
sharedMessageHistoryTagsKey.setUInt32(8, value: tag.rawValue)
|
|
sharedMessageHistoryTagsKey.setInt32(8 + 4, value: timestamp)
|
|
sharedMessageHistoryTagsKey.setInt32(8 + 4 + 4, value: 0)
|
|
sharedMessageHistoryTagsKey.setInt32(8 + 4 + 4 + 4, value: id)
|
|
|
|
valueBox.remove(messageHistoryTagsTable, key: sharedMessageHistoryTagsKey, secure: false)
|
|
}
|
|
}
|
|
valueBox.remove(messageHistoryTable, key: sharedMessageHistoryKey, secure: false)
|
|
}
|
|
|
|
return true
|
|
}, limit: 0)
|
|
|
|
valueBox.removeRange(messageHistoryIndexTable, start: peerCloudLowerBound, end: peerCloudLowerBound.successor)
|
|
|
|
sharedPeerHistoryInitializedKey.setInt64(0, value: peerId.toInt64())
|
|
sharedPeerHistoryInitializedKey.setInt8(8, value: 1)
|
|
valueBox.remove(messageHistoryMetadataTable, key: sharedPeerHistoryInitializedKey, secure: false)
|
|
|
|
sharedChatListIndexKey.setInt64(0, value: peerId.toInt64())
|
|
valueBox.remove(chatListIndexTable, key: sharedChatListIndexKey, secure: false)
|
|
}
|
|
|
|
var removeChatListKeys: [ValueBoxKey] = []
|
|
valueBox.scan(chatListTable, keys: { key in
|
|
let (_, _, index, type) = extractChatListKey(key)
|
|
if index.id.peerId.namespace != 3 { // Secret Chat
|
|
sharedChatListIndexKey.setInt64(0, value: index.id.peerId.toInt64())
|
|
valueBox.remove(chatListIndexTable, key: sharedChatListIndexKey, secure: false)
|
|
|
|
removeChatListKeys.append(key)
|
|
} else if type == 2 { // Hole
|
|
removeChatListKeys.append(key)
|
|
}
|
|
return true
|
|
})
|
|
|
|
for key in removeChatListKeys {
|
|
valueBox.remove(chatListTable, key: key, secure: false)
|
|
}
|
|
|
|
let chatListInitializedKey = ValueBoxKey(length: 1)
|
|
chatListInitializedKey.setInt8(0, value: 0)
|
|
valueBox.remove(messageHistoryMetadataTable, key: chatListInitializedKey, secure: false)
|
|
|
|
let shouldReindexUnreadCountsKey = ValueBoxKey(length: 1)
|
|
shouldReindexUnreadCountsKey.setInt8(0, value: 8)
|
|
valueBox.set(messageHistoryMetadataTable, key: shouldReindexUnreadCountsKey, value: MemoryBuffer())
|
|
|
|
let endTime = CFAbsoluteTimeGetCurrent()
|
|
postboxLog("Upgrade 19->20 (\(totalMessageCount) messages) took \(endTime - startTime) s")
|
|
|
|
metadataTable.setUserVersion(20)
|
|
}
|