Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2021-02-18 20:09:28 +04:00
commit 0c06906352
52 changed files with 289 additions and 6527 deletions

View File

@ -11,13 +11,6 @@ swift_library(
"//submodules/MtProtoKit:MtProtoKit",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/EncryptionProvider:EncryptionProvider",
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
"//submodules/Database/MessageHistoryReadStateTable:MessageHistoryReadStateTable",
"//submodules/Database/MessageHistoryMetadataTable:MessageHistoryMetadataTable",
"//submodules/Database/PreferencesTable:PreferencesTable",
"//submodules/Database/PeerTable:PeerTable",
"//submodules/sqlcipher:sqlcipher",
"//submodules/AppLockState:AppLockState",
"//submodules/NotificationsPresentationData:NotificationsPresentationData",
"//Telegram/NotificationService/NotificationServiceObjC:NotificationServiceObjC",

View File

@ -1,5 +1,5 @@
import Foundation
import ValueBox
/*import ValueBox
private func applicationSpecificSharedDataKey(_ value: Int32) -> ValueBoxKey {
let key = ValueBoxKey(length: 4)
@ -14,3 +14,4 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 {
public struct ApplicationSpecificSharedDataKeys {
public static let inAppNotificationSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.inAppNotificationSettings.rawValue)
}
*/

View File

@ -1,5 +1,5 @@
import Foundation
import PostboxCoding
/*import PostboxCoding
import PreferencesTable
import MessageHistoryMetadataTable
import PostboxDataTypes
@ -103,3 +103,4 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable {
}
}
}
*/

View File

@ -1,4 +1,4 @@
import PostboxDataTypes
/*import PostboxDataTypes
struct LegacyPeerSummaryCounterTags: OptionSet, Sequence, Hashable {
var rawValue: Int32
@ -52,3 +52,4 @@ struct Namespaces {
static let SecretChat: Int32 = 3
}
}
*/

View File

@ -18,9 +18,10 @@ final class NotificationService: UNNotificationServiceExtension {
f()
}
}, countIncomingMessage: { rootPath, accountId, encryptionParameters, peerId, messageId in
SyncProviderImpl.addIncomingMessage(queue: queue, withRootPath: rootPath, accountId: accountId, encryptionParameters: encryptionParameters, peerId: peerId, messageId: messageId, completion: { count in
/*SyncProviderImpl.addIncomingMessage(queue: queue, withRootPath: rootPath, accountId: accountId, encryptionParameters: encryptionParameters, peerId: peerId, messageId: messageId, completion: { count in
completion?(count)
})
})*/
completion?(0)
}, isLocked: { rootPath in
return SyncProviderImpl.isLocked(withRootPath: rootPath)
}, lockedMessageText: { rootPath in

View File

@ -1,30 +1,30 @@
import Foundation
import SwiftSignalKit
import ValueBox
import PostboxDataTypes
import MessageHistoryReadStateTable
import MessageHistoryMetadataTable
import PreferencesTable
import PeerTable
import PostboxCoding
//import ValueBox
//import PostboxDataTypes
//import MessageHistoryReadStateTable
//import MessageHistoryMetadataTable
//import PreferencesTable
//import PeerTable
//import PostboxCoding
import AppLockState
import NotificationsPresentationData
import BuildConfig
private let registeredTypes: Void = {
/*private let registeredTypes: Void = {
declareEncodable(InAppNotificationSettings.self, f: InAppNotificationSettings.init(decoder:))
declareEncodable(TelegramChannel.self, f: TelegramChannel.init(decoder:))
}()
}()*/
private func accountRecordIdPathName(_ id: Int64) -> String {
return "account-\(UInt64(bitPattern: id))"
}
private final class ValueBoxLoggerImpl: ValueBoxLogger {
/*private final class ValueBoxLoggerImpl: ValueBoxLogger {
func log(_ what: String) {
print("ValueBox: \(what)")
}
}
}*/
enum SyncProviderImpl {
static func isLocked(withRootPath rootPath: String) -> Bool {
@ -43,7 +43,7 @@ enum SyncProviderImpl {
}
}
static func addIncomingMessage(queue: Queue, withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) {
/*static func addIncomingMessage(queue: Queue, withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) {
queue.async {
let _ = registeredTypes
@ -144,5 +144,5 @@ enum SyncProviderImpl {
completion(-1)
}
}
}
}*/
}

View File

@ -1,4 +1,4 @@
import PostboxDataTypes
/*import PostboxDataTypes
import PostboxCoding
public enum TelegramChannelInfo: Int32 {
@ -36,3 +36,4 @@ public final class TelegramChannel: Peer {
return true
}
}
*/

View File

@ -793,7 +793,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
var accountResults: [Signal<INObjectSection<Friend>, Error>] = []
for (accountId, accountPeerId, _) in accounts {
accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> INObjectSection<Friend> in
accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> INObjectSection<Friend> in
var accountTitle: String = ""
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser {
if let username = peer.username, !username.isEmpty {
@ -962,7 +962,7 @@ private final class WidgetIntentHandler {
var accountResults: [Signal<INObjectSection<Friend>, Error>] = []
for (accountId, accountPeerId, _) in accounts {
accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> INObjectSection<Friend> in
accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> INObjectSection<Friend> in
var accountTitle: String = ""
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser {
if let username = peer.username, !username.isEmpty {
@ -1045,7 +1045,7 @@ private final class WidgetIntentHandler {
if !isActive {
continue
}
accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> [Friend] in
accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> [Friend] in
var peers: [Peer] = []
for id in getRecentPeers(transaction: transaction) {

View File

@ -1,4 +0,0 @@
#ifndef Telegram_iOS_Telegram_Bridging_Header_h
#define Telegram_iOS_Telegram_Bridging_Header_h
#endif

View File

@ -65,6 +65,152 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String {
return appGroupPath + "/telegram-data"
}
@available(iOS 14.0, *)
private func getCommonTimeline(friends: [Friend]?, in context: TimelineProviderContext, completion: @escaping (Timeline<SimpleEntry>) -> ()) {
if context.isPreview {
completion(Timeline(entries: [SimpleEntry(date: Date(), contents: .preview)], policy: .atEnd))
return
}
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .hour, value: 0, to: currentDate)!
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
return
}
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
return
}
let rootPath = rootPathForBasePath(appGroupUrl.path)
TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: arc4random64())
let logsPath = rootPath + "/widget-logs"
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath)
initializeAccountManagement()
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
var itemsByAccount: [Int64: [(Int64, Friend)]] = [:]
var itemOrder: [(Int64, Int64)] = []
if let friends = friends {
for item in friends {
guard let identifier = item.identifier else {
continue
}
guard let index = identifier.firstIndex(of: ":") else {
continue
}
guard let accountId = Int64(identifier[identifier.startIndex ..< index]) else {
continue
}
guard let peerId = Int64(identifier[identifier.index(after: index)...]) else {
continue
}
if itemsByAccount[accountId] == nil {
itemsByAccount[accountId] = []
}
itemsByAccount[accountId]?.append((peerId, item))
itemOrder.append((accountId, peerId))
}
}
var friendsByAccount: [Signal<[ParsedPeer], NoError>] = []
for (accountId, items) in itemsByAccount {
friendsByAccount.append(accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, isReadOnly: true, useCopy: true, transaction: { postbox, transaction -> [ParsedPeer] in
guard let state = transaction.getState() as? AuthorizedAccountState else {
return []
}
var result: [ParsedPeer] = []
for (peerId, _) in items {
guard let peer = transaction.getPeer(PeerId(peerId)) else {
continue
}
var name: String = ""
var lastName: String?
if let user = peer as? TelegramUser {
if let firstName = user.firstName {
name = firstName
lastName = user.lastName
} else if let lastName = user.lastName {
name = lastName
} else if let phone = user.phone, !phone.isEmpty {
name = phone
}
} else {
name = peer.debugDisplayTitle
}
var badge: WidgetDataPeer.Badge?
if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 {
var isMuted = false
if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings {
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false)
}
badge = WidgetDataPeer.Badge(
count: Int(readState.count),
isMuted: isMuted
)
}
var mappedMessage: WidgetDataPeer.Message?
if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) {
if let message = transaction.getMessage(index.id) {
mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message)
}
}
let widgetPeer = WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
return postbox.mediaBox.resourcePath(representation.resource)
}, badge: badge, message: mappedMessage)
result.append(ParsedPeer(accountId: accountId, accountPeerId: state.peerId.toInt64(), peer: widgetPeer))
}
return result
})
|> `catch` { _ -> Signal<[ParsedPeer], NoError> in
return .single([])
})
}
let _ = combineLatest(friendsByAccount).start(next: { allPeers in
var orderedPeers: [ParsedPeer] = []
outer: for (accountId, peerId) in itemOrder {
for peerSet in allPeers {
for peer in peerSet {
if peer.accountId == accountId && peer.peer.id == peerId {
orderedPeers.append(peer)
continue outer
}
}
}
}
let result = ParsedPeers(peers: orderedPeers, updateTimestamp: Int32(Date().timeIntervalSince1970))
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(result))], policy: .atEnd))
})
}
@available(iOSApplicationExtension 14.0, iOS 14.0, *)
struct Provider: IntentTimelineProvider {
public typealias Entry = SimpleEntry
@ -79,148 +225,7 @@ struct Provider: IntentTimelineProvider {
}
func getTimeline(for configuration: SelectFriendsIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
if context.isPreview {
completion(Timeline(entries: [SimpleEntry(date: Date(), contents: .preview)], policy: .atEnd))
return
}
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .hour, value: 0, to: currentDate)!
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
return
}
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
return
}
let rootPath = rootPathForBasePath(appGroupUrl.path)
TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: arc4random64())
let logsPath = rootPath + "/widget-logs"
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath)
initializeAccountManagement()
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
var itemsByAccount: [Int64: [(Int64, Friend)]] = [:]
var itemOrder: [(Int64, Int64)] = []
if let friends = configuration.friends {
for item in friends {
guard let identifier = item.identifier else {
continue
}
guard let index = identifier.firstIndex(of: ":") else {
continue
}
guard let accountId = Int64(identifier[identifier.startIndex ..< index]) else {
continue
}
guard let peerId = Int64(identifier[identifier.index(after: index)...]) else {
continue
}
if itemsByAccount[accountId] == nil {
itemsByAccount[accountId] = []
}
itemsByAccount[accountId]?.append((peerId, item))
itemOrder.append((accountId, peerId))
}
}
var friendsByAccount: [Signal<[ParsedPeer], NoError>] = []
for (accountId, items) in itemsByAccount {
friendsByAccount.append(accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> [ParsedPeer] in
guard let state = transaction.getState() as? AuthorizedAccountState else {
return []
}
var result: [ParsedPeer] = []
for (peerId, _) in items {
guard let peer = transaction.getPeer(PeerId(peerId)) else {
continue
}
var name: String = ""
var lastName: String?
if let user = peer as? TelegramUser {
if let firstName = user.firstName {
name = firstName
lastName = user.lastName
} else if let lastName = user.lastName {
name = lastName
} else if let phone = user.phone, !phone.isEmpty {
name = phone
}
} else {
name = peer.debugDisplayTitle
}
var badge: WidgetDataPeer.Badge?
if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 {
var isMuted = false
if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings {
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false)
}
badge = WidgetDataPeer.Badge(
count: Int(readState.count),
isMuted: isMuted
)
}
var mappedMessage: WidgetDataPeer.Message?
if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) {
if let message = transaction.getMessage(index.id) {
mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message)
}
}
let widgetPeer = WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
return postbox.mediaBox.resourcePath(representation.resource)
}, badge: badge, message: mappedMessage)
result.append(ParsedPeer(accountId: accountId, accountPeerId: state.peerId.toInt64(), peer: widgetPeer))
}
return result
})
|> `catch` { _ -> Signal<[ParsedPeer], NoError> in
return .single([])
})
}
let _ = combineLatest(friendsByAccount).start(next: { allPeers in
var orderedPeers: [ParsedPeer] = []
outer: for (accountId, peerId) in itemOrder {
for peerSet in allPeers {
for peer in peerSet {
if peer.accountId == accountId && peer.peer.id == peerId {
orderedPeers.append(peer)
continue outer
}
}
}
}
let result = ParsedPeers(peers: orderedPeers, updateTimestamp: Int32(Date().timeIntervalSince1970))
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(result))], policy: .atEnd))
})
getCommonTimeline(friends: configuration.friends, in: context, completion: completion)
}
}
@ -238,148 +243,7 @@ struct AvatarsProvider: IntentTimelineProvider {
}
func getTimeline(for configuration: SelectAvatarFriendsIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
if context.isPreview {
completion(Timeline(entries: [SimpleEntry(date: Date(), contents: .preview)], policy: .atEnd))
return
}
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .hour, value: 0, to: currentDate)!
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
return
}
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
let appGroupName = "group.\(baseAppBundleId)"
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
guard let appGroupUrl = maybeAppGroupUrl else {
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
return
}
let rootPath = rootPathForBasePath(appGroupUrl.path)
TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: arc4random64())
let logsPath = rootPath + "/widget-logs"
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath)
initializeAccountManagement()
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
var itemsByAccount: [Int64: [(Int64, Any)]] = [:]
var itemOrder: [(Int64, Int64)] = []
if let friends = configuration.friends {
for item in friends {
guard let identifier = item.identifier else {
continue
}
guard let index = identifier.firstIndex(of: ":") else {
continue
}
guard let accountId = Int64(identifier[identifier.startIndex ..< index]) else {
continue
}
guard let peerId = Int64(identifier[identifier.index(after: index)...]) else {
continue
}
if itemsByAccount[accountId] == nil {
itemsByAccount[accountId] = []
}
itemsByAccount[accountId]?.append((peerId, item))
itemOrder.append((accountId, peerId))
}
}
var friendsByAccount: [Signal<[ParsedPeer], NoError>] = []
for (accountId, items) in itemsByAccount {
friendsByAccount.append(accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, isReadOnly: true, transaction: { postbox, transaction -> [ParsedPeer] in
guard let state = transaction.getState() as? AuthorizedAccountState else {
return []
}
var result: [ParsedPeer] = []
for (peerId, _) in items {
guard let peer = transaction.getPeer(PeerId(peerId)) else {
continue
}
var name: String = ""
var lastName: String?
if let user = peer as? TelegramUser {
if let firstName = user.firstName {
name = firstName
lastName = user.lastName
} else if let lastName = user.lastName {
name = lastName
} else if let phone = user.phone, !phone.isEmpty {
name = phone
}
} else {
name = peer.debugDisplayTitle
}
var badge: WidgetDataPeer.Badge?
if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 {
var isMuted = false
if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings {
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false)
}
badge = WidgetDataPeer.Badge(
count: Int(readState.count),
isMuted: isMuted
)
}
var mappedMessage: WidgetDataPeer.Message?
if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) {
if let message = transaction.getMessage(index.id) {
mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message)
}
}
let widgetPeer = WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
return postbox.mediaBox.resourcePath(representation.resource)
}, badge: badge, message: mappedMessage)
result.append(ParsedPeer(accountId: accountId, accountPeerId: state.peerId.toInt64(), peer: widgetPeer))
}
return result
})
|> `catch` { _ -> Signal<[ParsedPeer], NoError> in
return .single([])
})
}
let _ = combineLatest(friendsByAccount).start(next: { allPeers in
var orderedPeers: [ParsedPeer] = []
outer: for (accountId, peerId) in itemOrder {
for peerSet in allPeers {
for peer in peerSet {
if peer.accountId == accountId && peer.peer.id == peerId {
orderedPeers.append(peer)
continue outer
}
}
}
}
let result = ParsedPeers(peers: orderedPeers, updateTimestamp: Int32(Date().timeIntervalSince1970))
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(result))], policy: .atEnd))
})
getCommonTimeline(friends: configuration.friends, in: context, completion: completion)
}
}

View File

@ -1,14 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "Buffers",
module_name = "Buffers",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,173 +0,0 @@
import Foundation
private let emptyMemory = malloc(1)!
public class MemoryBuffer: Equatable, CustomStringConvertible {
public internal(set) var memory: UnsafeMutableRawPointer
var capacity: Int
public internal(set) var length: Int
var freeWhenDone: Bool
public init(copyOf buffer: MemoryBuffer) {
self.memory = malloc(buffer.length)
memcpy(self.memory, buffer.memory, buffer.length)
self.capacity = buffer.length
self.length = buffer.length
self.freeWhenDone = true
}
public init(memory: UnsafeMutableRawPointer, capacity: Int, length: Int, freeWhenDone: Bool) {
self.memory = memory
self.capacity = capacity
self.length = length
self.freeWhenDone = freeWhenDone
}
public init(data: Data) {
if data.count == 0 {
self.memory = emptyMemory
self.capacity = 0
self.length = 0
self.freeWhenDone = false
} else {
self.memory = malloc(data.count)!
data.copyBytes(to: self.memory.assumingMemoryBound(to: UInt8.self), count: data.count)
self.capacity = data.count
self.length = data.count
self.freeWhenDone = false
}
}
public init() {
self.memory = emptyMemory
self.capacity = 0
self.length = 0
self.freeWhenDone = false
}
deinit {
if self.freeWhenDone {
free(self.memory)
}
}
public var description: String {
let hexString = NSMutableString()
let bytes = self.memory.assumingMemoryBound(to: UInt8.self)
for i in 0 ..< self.length {
hexString.appendFormat("%02x", UInt(bytes[i]))
}
return hexString as String
}
public func makeData() -> Data {
if self.length == 0 {
return Data()
} else {
return Data(bytes: self.memory, count: self.length)
}
}
public func withDataNoCopy(_ f: (Data) -> Void) {
f(Data(bytesNoCopy: self.memory, count: self.length, deallocator: .none))
}
public static func ==(lhs: MemoryBuffer, rhs: MemoryBuffer) -> Bool {
return lhs.length == rhs.length && memcmp(lhs.memory, rhs.memory, lhs.length) == 0
}
}
public final class WriteBuffer: MemoryBuffer {
public var offset = 0
public override init() {
super.init(memory: malloc(32), capacity: 32, length: 0, freeWhenDone: true)
}
public func makeReadBufferAndReset() -> ReadBuffer {
let buffer = ReadBuffer(memory: self.memory, length: self.offset, freeWhenDone: true)
self.memory = malloc(32)
self.capacity = 32
self.offset = 0
return buffer
}
public func readBufferNoCopy() -> ReadBuffer {
return ReadBuffer(memory: self.memory, length: self.offset, freeWhenDone: false)
}
override public func makeData() -> Data {
return Data(bytes: self.memory.assumingMemoryBound(to: UInt8.self), count: self.offset)
}
public func reset() {
self.offset = 0
}
public func write(_ data: UnsafeRawPointer, offset: Int, length: Int) {
if self.offset + length > self.capacity {
self.capacity = self.offset + length + 256
if self.length == 0 {
self.memory = malloc(self.capacity)!
} else {
self.memory = realloc(self.memory, self.capacity)
}
}
memcpy(self.memory + self.offset, data + offset, length)
self.offset += length
self.length = self.offset
}
public func write(_ data: Data) {
let length = data.count
if self.offset + length > self.capacity {
self.capacity = self.offset + length + 256
if self.length == 0 {
self.memory = malloc(self.capacity)!
} else {
self.memory = realloc(self.memory, self.capacity)
}
}
data.copyBytes(to: self.memory.advanced(by: offset).assumingMemoryBound(to: UInt8.self), count: length)
self.offset += length
self.length = self.offset
}
}
public final class ReadBuffer: MemoryBuffer {
public var offset = 0
override public init(data: Data) {
super.init(data: data)
}
public init(memory: UnsafeMutableRawPointer, length: Int, freeWhenDone: Bool) {
super.init(memory: memory, capacity: length, length: length, freeWhenDone: freeWhenDone)
}
public init(memoryBufferNoCopy: MemoryBuffer) {
super.init(memory: memoryBufferNoCopy.memory, capacity: memoryBufferNoCopy.length, length: memoryBufferNoCopy.length, freeWhenDone: false)
}
public func dataNoCopy() -> Data {
return Data(bytesNoCopy: self.memory.assumingMemoryBound(to: UInt8.self), count: self.length, deallocator: .none)
}
public func read(_ data: UnsafeMutableRawPointer, offset: Int, length: Int) {
memcpy(data + offset, self.memory.advanced(by: self.offset), length)
self.offset += length
}
public func skip(_ length: Int) {
self.offset += length
}
public func reset() {
self.offset = 0
}
public func sharedBufferNoCopy() -> ReadBuffer {
return ReadBuffer(memory: memory, length: length, freeWhenDone: false)
}
}

View File

@ -1,18 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "MessageHistoryMetadataTable",
module_name = "MessageHistoryMetadataTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
"//submodules/Database/PostboxCoding:PostboxCoding",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,332 +0,0 @@
import Foundation
import ValueBox
import Table
import PostboxCoding
import PostboxDataTypes
import Buffers
private enum MetadataPrefix: Int8 {
case ChatListInitialized = 0
case PeerNextMessageIdByNamespace = 2
case NextStableMessageId = 3
case ChatListTotalUnreadState = 4
case NextPeerOperationLogIndex = 5
case ChatListGroupInitialized = 6
case GroupFeedIndexInitialized = 7
case ShouldReindexUnreadCounts = 8
case PeerHistoryInitialized = 9
}
private struct InitializedChatListKey: Hashable {
let groupId: PeerGroupId
}
public final class MessageHistoryMetadataTable: Table {
public static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
}
private let sharedPeerHistoryInitializedKey = ValueBoxKey(length: 8 + 1)
private let sharedGroupFeedIndexInitializedKey = ValueBoxKey(length: 4 + 1)
private let sharedChatListGroupHistoryInitializedKey = ValueBoxKey(length: 4 + 1)
private let sharedPeerNextMessageIdByNamespaceKey = ValueBoxKey(length: 8 + 1 + 4)
private let sharedBuffer = WriteBuffer()
private var initializedChatList = Set<InitializedChatListKey>()
private var initializedHistoryPeerIds = Set<PeerId>()
private var initializedGroupFeedIndexIds = Set<PeerGroupId>()
private var peerNextMessageIdByNamespace: [PeerId: [MessageId.Namespace: MessageId.Id]] = [:]
private var updatedPeerNextMessageIdByNamespace: [PeerId: Set<MessageId.Namespace>] = [:]
private var nextMessageStableId: UInt32?
private var nextMessageStableIdUpdated = false
private var chatListTotalUnreadState: ChatListTotalUnreadState?
private var chatListTotalUnreadStateUpdated = false
private var nextPeerOperationLogIndex: UInt32?
private var nextPeerOperationLogIndexUpdated = false
private var currentPinnedChatPeerIds: Set<PeerId>?
private var currentPinnedChatPeerIdsUpdated = false
private func peerHistoryInitializedKey(_ id: PeerId) -> ValueBoxKey {
self.sharedPeerHistoryInitializedKey.setInt64(0, value: id.toInt64())
self.sharedPeerHistoryInitializedKey.setInt8(8, value: MetadataPrefix.PeerHistoryInitialized.rawValue)
return self.sharedPeerHistoryInitializedKey
}
private func groupFeedIndexInitializedKey(_ id: PeerGroupId) -> ValueBoxKey {
self.sharedGroupFeedIndexInitializedKey.setInt32(0, value: id.rawValue)
self.sharedGroupFeedIndexInitializedKey.setInt8(4, value: MetadataPrefix.GroupFeedIndexInitialized.rawValue)
return self.sharedGroupFeedIndexInitializedKey
}
private func chatListGroupInitializedKey(_ key: InitializedChatListKey) -> ValueBoxKey {
self.sharedChatListGroupHistoryInitializedKey.setInt32(0, value: key.groupId.rawValue)
self.sharedChatListGroupHistoryInitializedKey.setInt8(4, value: MetadataPrefix.ChatListGroupInitialized.rawValue)
return self.sharedChatListGroupHistoryInitializedKey
}
private func peerNextMessageIdByNamespaceKey(_ id: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey {
self.sharedPeerNextMessageIdByNamespaceKey.setInt64(0, value: id.toInt64())
self.sharedPeerNextMessageIdByNamespaceKey.setInt8(8, value: MetadataPrefix.PeerNextMessageIdByNamespace.rawValue)
self.sharedPeerNextMessageIdByNamespaceKey.setInt32(8 + 1, value: namespace)
return self.sharedPeerNextMessageIdByNamespaceKey
}
private func key(_ prefix: MetadataPrefix) -> ValueBoxKey {
let key = ValueBoxKey(length: 1)
key.setInt8(0, value: prefix.rawValue)
return key
}
public func setInitializedChatList(groupId: PeerGroupId) {
switch groupId {
case .root:
self.valueBox.set(self.table, key: self.key(MetadataPrefix.ChatListInitialized), value: MemoryBuffer())
case .group:
self.valueBox.set(self.table, key: self.chatListGroupInitializedKey(InitializedChatListKey(groupId: groupId)), value: MemoryBuffer())
}
self.initializedChatList.insert(InitializedChatListKey(groupId: groupId))
}
public func isInitializedChatList(groupId: PeerGroupId) -> Bool {
let key = InitializedChatListKey(groupId: groupId)
if self.initializedChatList.contains(key) {
return true
} else {
switch groupId {
case .root:
if self.valueBox.exists(self.table, key: self.key(MetadataPrefix.ChatListInitialized)) {
self.initializedChatList.insert(key)
return true
} else {
return false
}
case .group:
if self.valueBox.exists(self.table, key: self.chatListGroupInitializedKey(key)) {
self.initializedChatList.insert(key)
return true
} else {
return false
}
}
}
}
public func setShouldReindexUnreadCounts(value: Bool) {
if value {
self.valueBox.set(self.table, key: self.key(MetadataPrefix.ShouldReindexUnreadCounts), value: MemoryBuffer())
} else {
self.valueBox.remove(self.table, key: self.key(MetadataPrefix.ShouldReindexUnreadCounts), secure: false)
}
}
public func shouldReindexUnreadCounts() -> Bool {
if self.valueBox.exists(self.table, key: self.key(MetadataPrefix.ShouldReindexUnreadCounts)) {
return true
} else {
return false
}
}
public func setInitialized(_ peerId: PeerId) {
self.initializedHistoryPeerIds.insert(peerId)
self.sharedBuffer.reset()
self.valueBox.set(self.table, key: self.peerHistoryInitializedKey(peerId), value: self.sharedBuffer)
}
public func isInitialized(_ peerId: PeerId) -> Bool {
if self.initializedHistoryPeerIds.contains(peerId) {
return true
} else {
if self.valueBox.exists(self.table, key: self.peerHistoryInitializedKey(peerId)) {
self.initializedHistoryPeerIds.insert(peerId)
return true
} else {
return false
}
}
}
public func setGroupFeedIndexInitialized(_ groupId: PeerGroupId) {
self.initializedGroupFeedIndexIds.insert(groupId)
self.sharedBuffer.reset()
self.valueBox.set(self.table, key: self.groupFeedIndexInitializedKey(groupId), value: self.sharedBuffer)
}
public func isGroupFeedIndexInitialized(_ groupId: PeerGroupId) -> Bool {
if self.initializedGroupFeedIndexIds.contains(groupId) {
return true
} else {
if self.valueBox.exists(self.table, key: self.groupFeedIndexInitializedKey(groupId)) {
self.initializedGroupFeedIndexIds.insert(groupId)
return true
} else {
return false
}
}
}
public func getNextMessageIdAndIncrement(_ peerId: PeerId, namespace: MessageId.Namespace) -> MessageId {
if let messageIdByNamespace = self.peerNextMessageIdByNamespace[peerId] {
if let nextId = messageIdByNamespace[namespace] {
self.peerNextMessageIdByNamespace[peerId]![namespace] = nextId + 1
if updatedPeerNextMessageIdByNamespace[peerId] != nil {
updatedPeerNextMessageIdByNamespace[peerId]!.insert(namespace)
} else {
updatedPeerNextMessageIdByNamespace[peerId] = Set<MessageId.Namespace>([namespace])
}
return MessageId(peerId: peerId, namespace: namespace, id: nextId)
} else {
var nextId: Int32 = 1
if let value = self.valueBox.get(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) {
value.read(&nextId, offset: 0, length: 4)
}
self.peerNextMessageIdByNamespace[peerId]![namespace] = nextId + 1
if updatedPeerNextMessageIdByNamespace[peerId] != nil {
updatedPeerNextMessageIdByNamespace[peerId]!.insert(namespace)
} else {
updatedPeerNextMessageIdByNamespace[peerId] = Set<MessageId.Namespace>([namespace])
}
return MessageId(peerId: peerId, namespace: namespace, id: nextId)
}
} else {
var nextId: Int32 = 1
if let value = self.valueBox.get(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace)) {
value.read(&nextId, offset: 0, length: 4)
}
self.peerNextMessageIdByNamespace[peerId] = [namespace: nextId + 1]
if updatedPeerNextMessageIdByNamespace[peerId] != nil {
updatedPeerNextMessageIdByNamespace[peerId]!.insert(namespace)
} else {
updatedPeerNextMessageIdByNamespace[peerId] = Set<MessageId.Namespace>([namespace])
}
return MessageId(peerId: peerId, namespace: namespace, id: nextId)
}
}
public func getNextStableMessageIndexId() -> UInt32 {
if let nextId = self.nextMessageStableId {
self.nextMessageStableId = nextId + 1
self.nextMessageStableIdUpdated = true
return nextId
} else {
if let value = self.valueBox.get(self.table, key: self.key(.NextStableMessageId)) {
var nextId: UInt32 = 0
value.read(&nextId, offset: 0, length: 4)
self.nextMessageStableId = nextId + 1
self.nextMessageStableIdUpdated = true
return nextId
} else {
let nextId: UInt32 = 1
self.nextMessageStableId = nextId + 1
self.nextMessageStableIdUpdated = true
return nextId
}
}
}
public func getNextPeerOperationLogIndex() -> UInt32 {
if let nextId = self.nextPeerOperationLogIndex {
self.nextPeerOperationLogIndex = nextId + 1
self.nextPeerOperationLogIndexUpdated = true
return nextId
} else {
if let value = self.valueBox.get(self.table, key: self.key(.NextPeerOperationLogIndex)) {
var nextId: UInt32 = 0
value.read(&nextId, offset: 0, length: 4)
self.nextPeerOperationLogIndex = nextId + 1
self.nextPeerOperationLogIndexUpdated = true
return nextId
} else {
let nextId: UInt32 = 1
self.nextPeerOperationLogIndex = nextId + 1
self.nextPeerOperationLogIndexUpdated = true
return nextId
}
}
}
public func getChatListTotalUnreadState() -> ChatListTotalUnreadState {
if let cached = self.chatListTotalUnreadState {
return cached
} else {
if let value = self.valueBox.get(self.table, key: self.key(.ChatListTotalUnreadState)), let state = PostboxDecoder(buffer: value).decodeObjectForKey("_", decoder: {
ChatListTotalUnreadState(decoder: $0)
}) as? ChatListTotalUnreadState {
self.chatListTotalUnreadState = state
return state
} else {
let state = ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:])
self.chatListTotalUnreadState = state
return state
}
}
}
public func setChatListTotalUnreadState(_ state: ChatListTotalUnreadState) {
let current = self.getChatListTotalUnreadState()
if current != state {
self.chatListTotalUnreadState = state
self.chatListTotalUnreadStateUpdated = true
}
}
override public func clearMemoryCache() {
self.initializedChatList.removeAll()
self.initializedHistoryPeerIds.removeAll()
self.peerNextMessageIdByNamespace.removeAll()
self.updatedPeerNextMessageIdByNamespace.removeAll()
self.nextMessageStableId = nil
self.nextMessageStableIdUpdated = false
self.chatListTotalUnreadState = nil
self.chatListTotalUnreadStateUpdated = false
}
override public func beforeCommit() {
let sharedBuffer = WriteBuffer()
for (peerId, namespaces) in self.updatedPeerNextMessageIdByNamespace {
for namespace in namespaces {
if let messageIdByNamespace = self.peerNextMessageIdByNamespace[peerId], let maxId = messageIdByNamespace[namespace] {
sharedBuffer.reset()
var mutableMaxId = maxId
sharedBuffer.write(&mutableMaxId, offset: 0, length: 4)
self.valueBox.set(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace), value: sharedBuffer)
} else {
self.valueBox.remove(self.table, key: self.peerNextMessageIdByNamespaceKey(peerId, namespace: namespace), secure: false)
}
}
}
self.updatedPeerNextMessageIdByNamespace.removeAll()
if self.nextMessageStableIdUpdated {
if let nextMessageStableId = self.nextMessageStableId {
var nextId: UInt32 = nextMessageStableId
self.valueBox.set(self.table, key: self.key(.NextStableMessageId), value: MemoryBuffer(memory: &nextId, capacity: 4, length: 4, freeWhenDone: false))
self.nextMessageStableIdUpdated = false
}
}
if self.nextPeerOperationLogIndexUpdated {
if let nextPeerOperationLogIndex = self.nextPeerOperationLogIndex {
var nextId: UInt32 = nextPeerOperationLogIndex
self.valueBox.set(self.table, key: self.key(.NextPeerOperationLogIndex), value: MemoryBuffer(memory: &nextId, capacity: 4, length: 4, freeWhenDone: false))
self.nextPeerOperationLogIndexUpdated = false
}
}
if self.chatListTotalUnreadStateUpdated {
if let state = self.chatListTotalUnreadState {
let buffer = PostboxEncoder()
buffer.encodeObject(state, forKey: "_")
self.valueBox.set(self.table, key: self.key(.ChatListTotalUnreadState), value: buffer.readBufferNoCopy())
}
self.chatListTotalUnreadStateUpdated = false
}
}
}

View File

@ -1,17 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "MessageHistoryReadStateTable",
module_name = "MessageHistoryReadStateTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,576 +0,0 @@
import Foundation
import PostboxDataTypes
import Table
import ValueBox
import Buffers
private let traceReadStates = false
public enum ApplyInteractiveMaxReadIdResult {
case None
case Push(thenSync: Bool)
}
private final class InternalPeerReadStates {
var namespaces: [MessageId.Namespace: PeerReadState]
init(namespaces: [MessageId.Namespace: PeerReadState]) {
self.namespaces = namespaces
}
}
public final class MessageHistoryReadStateTable: Table {
public static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
}
private let defaultMessageNamespaceReadStates: [MessageId.Namespace: PeerReadState]
private var cachedPeerReadStates: [PeerId: InternalPeerReadStates?] = [:]
private var updatedInitialPeerReadStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
private let sharedKey = ValueBoxKey(length: 8)
private func key(_ id: PeerId) -> ValueBoxKey {
self.sharedKey.setInt64(0, value: id.toInt64())
return self.sharedKey
}
public init(valueBox: ValueBox, table: ValueBoxTable, defaultMessageNamespaceReadStates: [MessageId.Namespace: PeerReadState]) {
self.defaultMessageNamespaceReadStates = defaultMessageNamespaceReadStates
super.init(valueBox: valueBox, table: table)
}
private func get(_ id: PeerId) -> InternalPeerReadStates? {
if let states = self.cachedPeerReadStates[id] {
return states
} else {
if let value = self.valueBox.get(self.table, key: self.key(id)) {
var count: Int32 = 0
value.read(&count, offset: 0, length: 4)
var stateByNamespace: [MessageId.Namespace: PeerReadState] = [:]
for _ in 0 ..< count {
var namespaceId: Int32 = 0
value.read(&namespaceId, offset: 0, length: 4)
let state: PeerReadState
var kind: Int8 = 0
value.read(&kind, offset: 0, length: 1)
if kind == 0 {
var maxIncomingReadId: Int32 = 0
var maxOutgoingReadId: Int32 = 0
var maxKnownId: Int32 = 0
var count: Int32 = 0
value.read(&maxIncomingReadId, offset: 0, length: 4)
value.read(&maxOutgoingReadId, offset: 0, length: 4)
value.read(&maxKnownId, offset: 0, length: 4)
value.read(&count, offset: 0, length: 4)
var flags: Int32 = 0
value.read(&flags, offset: 0, length: 4)
let markedUnread = (flags & (1 << 0)) != 0
state = .idBased(maxIncomingReadId: maxIncomingReadId, maxOutgoingReadId: maxOutgoingReadId, maxKnownId: maxKnownId, count: count, markedUnread: markedUnread)
} else {
var maxIncomingReadTimestamp: Int32 = 0
var maxIncomingReadIdPeerId: Int64 = 0
var maxIncomingReadIdNamespace: Int32 = 0
var maxIncomingReadIdId: Int32 = 0
var maxOutgoingReadTimestamp: Int32 = 0
var maxOutgoingReadIdPeerId: Int64 = 0
var maxOutgoingReadIdNamespace: Int32 = 0
var maxOutgoingReadIdId: Int32 = 0
var count: Int32 = 0
value.read(&maxIncomingReadTimestamp, offset: 0, length: 4)
value.read(&maxIncomingReadIdPeerId, offset: 0, length: 8)
value.read(&maxIncomingReadIdNamespace, offset: 0, length: 4)
value.read(&maxIncomingReadIdId, offset: 0, length: 4)
value.read(&maxOutgoingReadTimestamp, offset: 0, length: 4)
value.read(&maxOutgoingReadIdPeerId, offset: 0, length: 8)
value.read(&maxOutgoingReadIdNamespace, offset: 0, length: 4)
value.read(&maxOutgoingReadIdId, offset: 0, length: 4)
value.read(&count, offset: 0, length: 4)
var flags: Int32 = 0
value.read(&flags, offset: 0, length: 4)
let markedUnread = (flags & (1 << 0)) != 0
state = .indexBased(maxIncomingReadIndex: MessageIndex(id: MessageId(peerId: PeerId(maxIncomingReadIdPeerId), namespace: maxIncomingReadIdNamespace, id: maxIncomingReadIdId), timestamp: maxIncomingReadTimestamp), maxOutgoingReadIndex: MessageIndex(id: MessageId(peerId: PeerId(maxOutgoingReadIdPeerId), namespace: maxOutgoingReadIdNamespace, id: maxOutgoingReadIdId), timestamp: maxOutgoingReadTimestamp), count: count, markedUnread: markedUnread)
}
stateByNamespace[namespaceId] = state
}
let states = InternalPeerReadStates(namespaces: stateByNamespace)
self.cachedPeerReadStates[id] = states
return states
} else {
self.cachedPeerReadStates[id] = nil
return nil
}
}
}
public func getCombinedState(_ peerId: PeerId) -> CombinedPeerReadState? {
if let states = self.get(peerId) {
return CombinedPeerReadState(states: states.namespaces.map({$0}))
}
return nil
}
private func markReadStatesAsUpdated(_ peerId: PeerId, namespaces: [MessageId.Namespace: PeerReadState]) {
if self.updatedInitialPeerReadStates[peerId] == nil {
self.updatedInitialPeerReadStates[peerId] = namespaces
}
}
public func resetStates(_ peerId: PeerId, namespaces: [MessageId.Namespace: PeerReadState]) -> CombinedPeerReadState? {
if traceReadStates {
print("[ReadStateTable] resetStates peerId: \(peerId), namespaces: \(namespaces)")
}
if let states = self.get(peerId) {
var updated = false
for (namespace, state) in namespaces {
if states.namespaces[namespace] == nil || states.namespaces[namespace]! != state {
self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces)
updated = true
}
states.namespaces[namespace] = state
}
if updated {
return CombinedPeerReadState(states: states.namespaces.map({$0}))
} else {
return nil
}
} else {
self.markReadStatesAsUpdated(peerId, namespaces: [:])
let states = InternalPeerReadStates(namespaces: namespaces)
self.cachedPeerReadStates[peerId] = states
return CombinedPeerReadState(states: states.namespaces.map({$0}))
}
}
public func addIncomingMessages(_ peerId: PeerId, indices: Set<MessageIndex>) -> (CombinedPeerReadState?, Bool) {
var indicesByNamespace: [MessageId.Namespace: [MessageIndex]] = [:]
for index in indices {
if indicesByNamespace[index.id.namespace] != nil {
indicesByNamespace[index.id.namespace]!.append(index)
} else {
indicesByNamespace[index.id.namespace] = [index]
}
}
if let states = self.get(peerId) {
if traceReadStates {
print("[ReadStateTable] addIncomingMessages peerId: \(peerId), indices: \(indices) (before: \(states.namespaces))")
}
var updated = false
let invalidated = false
for (namespace, namespaceIndices) in indicesByNamespace {
let currentState = states.namespaces[namespace] ?? self.defaultMessageNamespaceReadStates[namespace]
if let currentState = currentState {
var addedUnreadCount: Int32 = 0
for index in namespaceIndices {
switch currentState {
case let .idBased(maxIncomingReadId, _, maxKnownId, _, _):
if index.id.id > maxKnownId && index.id.id > maxIncomingReadId {
addedUnreadCount += 1
}
case let .indexBased(maxIncomingReadIndex, _, _, _):
if index > maxIncomingReadIndex {
addedUnreadCount += 1
}
}
}
if addedUnreadCount != 0 {
self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces)
states.namespaces[namespace] = currentState.withAddedCount(addedUnreadCount)
updated = true
if traceReadStates {
print("[ReadStateTable] added \(addedUnreadCount)")
}
}
}
}
return (updated ? CombinedPeerReadState(states: states.namespaces.map({$0})) : nil, invalidated)
} else {
if traceReadStates {
print("[ReadStateTable] addIncomingMessages peerId: \(peerId), just invalidated)")
}
return (nil, true)
}
}
public func deleteMessages(_ peerId: PeerId, indices: [MessageIndex], incomingStatsInIndices: (PeerId, MessageId.Namespace, [MessageIndex]) -> (Int, Bool)) -> (CombinedPeerReadState?, Bool) {
var indicesByNamespace: [MessageId.Namespace: [MessageIndex]] = [:]
for index in indices {
if indicesByNamespace[index.id.namespace] != nil {
indicesByNamespace[index.id.namespace]!.append(index)
} else {
indicesByNamespace[index.id.namespace] = [index]
}
}
if let states = self.get(peerId) {
if traceReadStates {
print("[ReadStateTable] deleteMessages peerId: \(peerId), ids: \(indices) (before: \(states.namespaces))")
}
var updated = false
var invalidate = false
for (namespace, namespaceIndices) in indicesByNamespace {
if let currentState = states.namespaces[namespace] {
var unreadIndices: [MessageIndex] = []
for index in namespaceIndices {
if !currentState.isIncomingMessageIndexRead(index) {
unreadIndices.append(index)
}
}
let (knownCount, holes) = incomingStatsInIndices(peerId, namespace, unreadIndices)
if holes {
invalidate = true
}
self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces)
var updatedState = currentState.withAddedCount(Int32(-knownCount))
if updatedState.count < 0 {
invalidate = true
updatedState = currentState.withAddedCount(-updatedState.count)
}
states.namespaces[namespace] = updatedState
updated = true
} else {
invalidate = true
}
}
return (updated ? CombinedPeerReadState(states: states.namespaces.map({$0})) : nil, invalidate)
} else {
return (nil, true)
}
}
public func applyIncomingMaxReadId(_ messageId: MessageId, incomingStatsInRange: (MessageId.Namespace, MessageId.Id, MessageId.Id) -> (count: Int, holes: Bool), topMessageId: (MessageId.Id, Bool)?) -> (CombinedPeerReadState?, Bool) {
if let states = self.get(messageId.peerId), let state = states.namespaces[messageId.namespace] {
if traceReadStates {
print("[ReadStateTable] applyMaxReadId peerId: \(messageId.peerId), maxReadId: \(messageId) (before: \(states.namespaces))")
}
switch state {
case let .idBased(maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread):
if maxIncomingReadId < messageId.id || (topMessageId != nil && (messageId.id == topMessageId!.0 || topMessageId!.1) && state.count != 0) || markedUnread {
var (deltaCount, holes) = incomingStatsInRange(messageId.namespace, maxIncomingReadId + 1, messageId.id)
if traceReadStates {
print("[ReadStateTable] applyMaxReadId after deltaCount: \(deltaCount), holes: \(holes)")
}
if let topMessageId = topMessageId, (messageId.id == topMessageId.0 || topMessageId.1) {
if deltaCount != Int(state.count) {
deltaCount = Int(state.count)
holes = true
}
}
self.markReadStatesAsUpdated(messageId.peerId, namespaces: states.namespaces)
states.namespaces[messageId.namespace] = .idBased(maxIncomingReadId: messageId.id, maxOutgoingReadId: maxOutgoingReadId, maxKnownId: maxKnownId, count: max(0, count - Int32(deltaCount)), markedUnread: false)
return (CombinedPeerReadState(states: states.namespaces.map({$0})), holes)
}
case .indexBased:
assertionFailure()
break
}
} else {
return (nil, true)
}
return (nil, false)
}
public func applyIncomingMaxReadIndex(_ messageIndex: MessageIndex, topMessageIndex: MessageIndex?, incomingStatsInRange: (MessageIndex, MessageIndex) -> (count: Int, holes: Bool, readMesageIds: [MessageId])) -> (CombinedPeerReadState?, Bool, [MessageId]) {
if let states = self.get(messageIndex.id.peerId), let state = states.namespaces[messageIndex.id.namespace] {
if traceReadStates {
print("[ReadStateTable] applyIncomingMaxReadIndex peerId: \(messageIndex.id.peerId), maxReadIndex: \(messageIndex) (before: \(states.namespaces))")
}
switch state {
case .idBased:
assertionFailure()
case let .indexBased(maxIncomingReadIndex, maxOutgoingReadIndex, count, markedUnread):
var readPastTopIndex = false
if let topMessageIndex = topMessageIndex, messageIndex >= topMessageIndex && count != 0 {
readPastTopIndex = true
}
if maxIncomingReadIndex < messageIndex || markedUnread || readPastTopIndex {
let (realDeltaCount, holes, messageIds) = incomingStatsInRange(maxIncomingReadIndex.successor(), messageIndex)
var deltaCount = realDeltaCount
if readPastTopIndex {
deltaCount = max(Int(count), deltaCount)
}
if traceReadStates {
print("[ReadStateTable] applyIncomingMaxReadIndex after deltaCount: \(deltaCount), holes: \(holes)")
}
self.markReadStatesAsUpdated(messageIndex.id.peerId, namespaces: states.namespaces)
states.namespaces[messageIndex.id.namespace] = .indexBased(maxIncomingReadIndex: messageIndex, maxOutgoingReadIndex: maxOutgoingReadIndex, count: max(0, count - Int32(deltaCount)), markedUnread: false)
return (CombinedPeerReadState(states: states.namespaces.map({$0})), holes, messageIds)
}
}
} else {
return (nil, true, [])
}
return (nil, false, [])
}
public func applyOutgoingMaxReadId(_ messageId: MessageId) -> (CombinedPeerReadState?, Bool) {
if let states = self.get(messageId.peerId), let state = states.namespaces[messageId.namespace] {
switch state {
case let .idBased(maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread):
if maxOutgoingReadId < messageId.id {
self.markReadStatesAsUpdated(messageId.peerId, namespaces: states.namespaces)
states.namespaces[messageId.namespace] = .idBased(maxIncomingReadId: maxIncomingReadId, maxOutgoingReadId: messageId.id, maxKnownId: maxKnownId, count: count, markedUnread: markedUnread)
return (CombinedPeerReadState(states: states.namespaces.map({$0})), false)
}
case .indexBased:
assertionFailure()
break
}
} else {
return (nil, true)
}
return (nil, false)
}
public func applyOutgoingMaxReadIndex(_ messageIndex: MessageIndex, outgoingIndexStatsInRange: (MessageIndex, MessageIndex) -> [MessageId]) -> (CombinedPeerReadState?, Bool, [MessageId]) {
if let states = self.get(messageIndex.id.peerId), let state = states.namespaces[messageIndex.id.namespace] {
switch state {
case .idBased:
assertionFailure()
break
case let .indexBased(maxIncomingReadIndex, maxOutgoingReadIndex, count, markedUnread):
if maxOutgoingReadIndex < messageIndex {
let messageIds: [MessageId] = outgoingIndexStatsInRange(maxOutgoingReadIndex.successor(), messageIndex)
self.markReadStatesAsUpdated(messageIndex.id.peerId, namespaces: states.namespaces)
states.namespaces[messageIndex.id.namespace] = .indexBased(maxIncomingReadIndex: maxIncomingReadIndex, maxOutgoingReadIndex: messageIndex, count: count, markedUnread: markedUnread)
return (CombinedPeerReadState(states: states.namespaces.map({$0})), false, messageIds)
}
}
} else {
return (nil, true, [])
}
return (nil, false, [])
}
public func applyInteractiveMaxReadIndex(messageIndex: MessageIndex, incomingStatsInRange: (MessageId.Namespace, MessageId.Id, MessageId.Id) -> (count: Int, holes: Bool), incomingIndexStatsInRange: (MessageIndex, MessageIndex) -> (count: Int, holes: Bool, readMesageIds: [MessageId]), topMessageId: (MessageId.Id, Bool)?, topMessageIndexByNamespace: (MessageId.Namespace) -> MessageIndex?) -> (combinedState: CombinedPeerReadState?, ApplyInteractiveMaxReadIdResult, readMesageIds: [MessageId]) {
if let states = self.get(messageIndex.id.peerId) {
if let state = states.namespaces[messageIndex.id.namespace] {
switch state {
case .idBased:
let (combinedState, holes) = self.applyIncomingMaxReadId(messageIndex.id, incomingStatsInRange: incomingStatsInRange, topMessageId: topMessageId)
if let combinedState = combinedState {
return (combinedState, .Push(thenSync: holes), [])
}
return (combinedState, holes ? .Push(thenSync: true) : .None, [])
case .indexBased:
let topMessageIndex: MessageIndex? = topMessageIndexByNamespace(messageIndex.id.namespace)
let (combinedState, holes, messageIds) = self.applyIncomingMaxReadIndex(messageIndex, topMessageIndex: topMessageIndex, incomingStatsInRange: incomingIndexStatsInRange)
if let combinedState = combinedState {
return (combinedState, .Push(thenSync: holes), messageIds)
}
return (combinedState, holes ? .Push(thenSync: true) : .None, messageIds)
}
} else {
for (namespace, state) in states.namespaces {
if let topIndex = topMessageIndexByNamespace(namespace), topIndex <= messageIndex {
switch state {
case .idBased:
let (combinedState, holes) = self.applyIncomingMaxReadId(topIndex.id, incomingStatsInRange: incomingStatsInRange, topMessageId: nil)
if let combinedState = combinedState {
return (combinedState, .Push(thenSync: holes), [])
}
return (combinedState, holes ? .Push(thenSync: true) : .None, [])
case .indexBased:
let (combinedState, holes, messageIds) = self.applyIncomingMaxReadIndex(topIndex, topMessageIndex: topMessageIndexByNamespace(namespace), incomingStatsInRange: incomingIndexStatsInRange)
if let combinedState = combinedState {
return (combinedState, .Push(thenSync: holes), messageIds)
}
return (combinedState, holes ? .Push(thenSync: true) : .None, messageIds)
}
}
}
return (nil, .Push(thenSync: true), [])
}
} else {
return (nil, .Push(thenSync: true), [])
}
}
public func applyInteractiveMarkUnread(peerId: PeerId, namespace: MessageId.Namespace, value: Bool) -> CombinedPeerReadState? {
if let states = self.get(peerId), let state = states.namespaces[namespace] {
switch state {
case let .idBased(maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread):
if markedUnread != value {
self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces)
states.namespaces[namespace] = .idBased(maxIncomingReadId: maxIncomingReadId, maxOutgoingReadId: maxOutgoingReadId, maxKnownId: maxKnownId, count: count, markedUnread: value)
return CombinedPeerReadState(states: states.namespaces.map({$0}))
} else {
return nil
}
case let .indexBased(maxIncomingReadIndex, maxOutgoingReadIndex, count, markedUnread):
if markedUnread != value {
self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces)
states.namespaces[namespace] = .indexBased(maxIncomingReadIndex: maxIncomingReadIndex, maxOutgoingReadIndex: maxOutgoingReadIndex, count: count, markedUnread: value)
return CombinedPeerReadState(states: states.namespaces.map({$0}))
} else {
return nil
}
}
} else {
return nil
}
}
public func transactionUnreadCountDeltas() -> [PeerId: Int32] {
var deltas: [PeerId: Int32] = [:]
for (id, initialNamespaces) in self.updatedInitialPeerReadStates {
var initialCount: Int32 = 0
for (_, state) in initialNamespaces {
initialCount += state.count
}
var updatedCount: Int32 = 0
if let maybeStates = self.cachedPeerReadStates[id] {
if let states = maybeStates {
for (_, state) in states.namespaces {
updatedCount += state.count
}
}
} else {
assertionFailure()
}
if initialCount != updatedCount {
deltas[id] = updatedCount - initialCount
}
}
return deltas
}
public func transactionAlteredInitialPeerCombinedReadStates() -> [PeerId: CombinedPeerReadState] {
var result: [PeerId: CombinedPeerReadState] = [:]
for (peerId, namespacesAndStates) in self.updatedInitialPeerReadStates {
var states: [(MessageId.Namespace, PeerReadState)] = []
for (namespace, state) in namespacesAndStates {
states.append((namespace, state))
}
result[peerId] = CombinedPeerReadState(states: states)
}
return result
}
override public func clearMemoryCache() {
self.cachedPeerReadStates.removeAll()
assert(self.updatedInitialPeerReadStates.isEmpty)
}
override public func beforeCommit() {
if !self.updatedInitialPeerReadStates.isEmpty {
let sharedBuffer = WriteBuffer()
for (id, initialNamespaces) in self.updatedInitialPeerReadStates {
if let wrappedStates = self.cachedPeerReadStates[id], let states = wrappedStates {
sharedBuffer.reset()
var count: Int32 = Int32(states.namespaces.count)
sharedBuffer.write(&count, offset: 0, length: 4)
for (namespace, state) in states.namespaces {
var namespaceId: Int32 = namespace
sharedBuffer.write(&namespaceId, offset: 0, length: 4)
switch state {
case .idBased(var maxIncomingReadId, var maxOutgoingReadId, var maxKnownId, var count, let markedUnread):
var kind: Int8 = 0
sharedBuffer.write(&kind, offset: 0, length: 1)
sharedBuffer.write(&maxIncomingReadId, offset: 0, length: 4)
sharedBuffer.write(&maxOutgoingReadId, offset: 0, length: 4)
sharedBuffer.write(&maxKnownId, offset: 0, length: 4)
sharedBuffer.write(&count, offset: 0, length: 4)
var flags: Int32 = 0
if markedUnread {
flags |= (1 << 0)
}
sharedBuffer.write(&flags, offset: 0, length: 4)
case .indexBased(let maxIncomingReadIndex, let maxOutgoingReadIndex, var count, let markedUnread):
var kind: Int8 = 1
sharedBuffer.write(&kind, offset: 0, length: 1)
var maxIncomingReadTimestamp: Int32 = maxIncomingReadIndex.timestamp
var maxIncomingReadIdPeerId: Int64 = maxIncomingReadIndex.id.peerId.toInt64()
var maxIncomingReadIdNamespace: Int32 = maxIncomingReadIndex.id.namespace
var maxIncomingReadIdId: Int32 = maxIncomingReadIndex.id.id
var maxOutgoingReadTimestamp: Int32 = maxOutgoingReadIndex.timestamp
var maxOutgoingReadIdPeerId: Int64 = maxOutgoingReadIndex.id.peerId.toInt64()
var maxOutgoingReadIdNamespace: Int32 = maxOutgoingReadIndex.id.namespace
var maxOutgoingReadIdId: Int32 = maxOutgoingReadIndex.id.id
sharedBuffer.write(&maxIncomingReadTimestamp, offset: 0, length: 4)
sharedBuffer.write(&maxIncomingReadIdPeerId, offset: 0, length: 8)
sharedBuffer.write(&maxIncomingReadIdNamespace, offset: 0, length: 4)
sharedBuffer.write(&maxIncomingReadIdId, offset: 0, length: 4)
sharedBuffer.write(&maxOutgoingReadTimestamp, offset: 0, length: 4)
sharedBuffer.write(&maxOutgoingReadIdPeerId, offset: 0, length: 8)
sharedBuffer.write(&maxOutgoingReadIdNamespace, offset: 0, length: 4)
sharedBuffer.write(&maxOutgoingReadIdId, offset: 0, length: 4)
sharedBuffer.write(&count, offset: 0, length: 4)
var flags: Int32 = 0
if markedUnread {
flags |= 1 << 0
}
sharedBuffer.write(&flags, offset: 0, length: 4)
}
}
self.valueBox.set(self.table, key: self.key(id), value: sharedBuffer)
} else {
self.valueBox.remove(self.table, key: self.key(id), secure: false)
}
}
self.updatedInitialPeerReadStates.removeAll()
}
}
}

View File

@ -1,15 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "MurmurHash",
module_name = "MurmurHash",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/MurmurHash/Impl:MurMurHashObjC",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,22 +0,0 @@
objc_library(
name = "MurMurHashObjC",
enable_modules = True,
module_name = "MurMurHashObjC",
srcs = glob([
"Sources/**/*.m",
"Sources/**/*.h",
]),
hdrs = glob([
"PublicHeaders/**/*.h",
]),
includes = [
"PublicHeaders",
],
sdk_frameworks = [
"Foundation",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,12 +0,0 @@
#ifndef Postbox_MurMurHash32_h
#define Postbox_MurMurHash32_h
#import <stdint.h>
#import <Foundation/Foundation.h>
int32_t murMurHash32(void *bytes, int length);
int32_t murMurHash32Data(NSData *data);
int32_t murMurHashString32(const char *s);
NSString *postboxTransformedString(CFStringRef string, bool replaceWithTransliteratedVersion, bool appendTransliteratedVersion);
#endif

View File

@ -1,120 +0,0 @@
#import <MurMurHashObjC/MurMurHashObjC.h>
#include <stdlib.h>
#include <string.h>
#define FORCE_INLINE __attribute__((always_inline))
static inline uint32_t rotl32 ( uint32_t x, int8_t r )
{
return (x << r) | (x >> (32 - r));
}
#define ROTL32(x,y) rotl32(x,y)
static FORCE_INLINE uint32_t getblock ( const uint32_t * p, int i )
{
return p[i];
}
static FORCE_INLINE uint32_t fmix ( uint32_t h )
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
static void murMurHash32Impl(const void *key, int len, uint32_t seed, void *out)
{
const uint8_t * data = (const uint8_t*)key;
const int nblocks = len / 4;
uint32_t h1 = seed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
//----------
// body
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
for(int i = -nblocks; i; i++)
{
uint32_t k1 = getblock(blocks,i);
k1 *= c1;
k1 = ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
}
//----------
// tail
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
uint32_t k1 = 0;
switch(len & 3)
{
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h1 = fmix(h1);
*(uint32_t*)out = h1;
}
int32_t murMurHash32(void *bytes, int length)
{
int32_t result = 0;
murMurHash32Impl(bytes, length, -137723950, &result);
return result;
}
int32_t murMurHash32Data(NSData *data) {
return murMurHash32((void *)data.bytes, (int)data.length);
}
int32_t murMurHashString32(const char *s)
{
int32_t result = 0;
murMurHash32Impl(s, (int)strlen(s), -137723950, &result);
return result;
}
NSString *postboxTransformedString(CFStringRef string, bool replaceWithTransliteratedVersion, bool appendTransliteratedVersion) {
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:(__bridge NSString * _Nonnull)(string)];
CFStringTransform((CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, false);
if (replaceWithTransliteratedVersion || appendTransliteratedVersion) {
NSMutableString *transliteratedString = [[NSMutableString alloc] initWithString:mutableString];
CFStringTransform((CFMutableStringRef)transliteratedString, NULL, kCFStringTransformToLatin, false);
if (replaceWithTransliteratedVersion) {
return transliteratedString;
} else {
[mutableString appendString:@" "];
[mutableString appendString:transliteratedString];
}
}
return mutableString;
}

View File

@ -1,12 +0,0 @@
import Foundation
import MurMurHashObjC
public enum HashFunctions {
public static func murMurHash32(_ s: String) -> Int32 {
return murMurHashString32(s)
}
public static func murMurHash32(_ d: Data) -> Int32 {
return murMurHash32Data(d)
}
}

View File

@ -1,18 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PeerTable",
module_name = "PeerTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxCoding:PostboxCoding",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,100 +0,0 @@
import Foundation
import ValueBox
import PostboxDataTypes
import Table
import PostboxCoding
public protocol ReverseAssociatedPeerTable {
func removeReverseAssociation(target: PeerId, from: PeerId)
func addReverseAssociation(target: PeerId, from: PeerId)
}
public final class PeerTable: Table {
public static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
}
private let reverseAssociatedTable: ReverseAssociatedPeerTable?
private let sharedEncoder = PostboxEncoder()
private let sharedKey = ValueBoxKey(length: 8)
private var cachedPeers: [PeerId: Peer] = [:]
private var updatedInitialPeers: [PeerId: Peer?] = [:]
public init(valueBox: ValueBox, table: ValueBoxTable, reverseAssociatedTable: ReverseAssociatedPeerTable?) {
self.reverseAssociatedTable = reverseAssociatedTable
super.init(valueBox: valueBox, table: table)
}
private func key(_ id: PeerId) -> ValueBoxKey {
self.sharedKey.setInt64(0, value: id.toInt64())
return self.sharedKey
}
public func set(_ peer: Peer) {
let previous = self.get(peer.id)
self.cachedPeers[peer.id] = peer
if self.updatedInitialPeers[peer.id] == nil {
self.updatedInitialPeers[peer.id] = previous
}
}
public func get(_ id: PeerId) -> Peer? {
if let peer = self.cachedPeers[id] {
return peer
}
if let value = self.valueBox.get(self.table, key: self.key(id)) {
if let peer = PostboxDecoder(buffer: value).decodeRootObject() as? Peer {
self.cachedPeers[id] = peer
return peer
}
}
return nil
}
override public func clearMemoryCache() {
self.cachedPeers.removeAll()
assert(self.updatedInitialPeers.isEmpty)
}
public func transactionUpdatedPeers() -> [(Peer?, Peer)] {
var result: [(Peer?, Peer)] = []
for (peerId, initialPeer) in self.updatedInitialPeers {
if let peer = self.get(peerId) {
result.append((initialPeer, peer))
} else {
assertionFailure()
}
}
return result
}
override public func beforeCommit() {
if !self.updatedInitialPeers.isEmpty {
for (peerId, previousPeer) in self.updatedInitialPeers {
if let peer = self.cachedPeers[peerId] {
self.sharedEncoder.reset()
self.sharedEncoder.encodeRootObject(peer)
self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy())
let previousAssociation = previousPeer?.associatedPeerId
if previousAssociation != peer.associatedPeerId {
if let previousAssociation = previousAssociation {
self.reverseAssociatedTable?.removeReverseAssociation(target: previousAssociation, from: peerId)
}
if let associatedPeerId = peer.associatedPeerId {
self.reverseAssociatedTable?.addReverseAssociation(target: associatedPeerId, from: peerId)
}
}
} else {
assertionFailure()
}
}
self.updatedInitialPeers.removeAll()
}
}
}

View File

@ -1,16 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PostboxCoding",
module_name = "PostboxCoding",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/Buffers:Buffers",
"//submodules/Database/MurmurHash:MurmurHash",
],
visibility = [
"//visibility:public",
],
)

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PostboxDataTypes",
module_name = "PostboxDataTypes",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/PostboxCoding:PostboxCoding",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,86 +0,0 @@
import Foundation
import PostboxCoding
public enum ChatListTotalUnreadStateCategory: Int32 {
case filtered = 0
case raw = 1
}
public enum ChatListTotalUnreadStateStats: Int32 {
case messages = 0
case chats = 1
}
public struct ChatListTotalUnreadCounters: PostboxCoding, Equatable {
public var messageCount: Int32
public var chatCount: Int32
public init(messageCount: Int32, chatCount: Int32) {
self.messageCount = messageCount
self.chatCount = chatCount
}
public init(decoder: PostboxDecoder) {
self.messageCount = decoder.decodeInt32ForKey("m", orElse: 0)
self.chatCount = decoder.decodeInt32ForKey("c", orElse: 0)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.messageCount, forKey: "m")
encoder.encodeInt32(self.chatCount, forKey: "c")
}
}
public struct ChatListTotalUnreadState: PostboxCoding, Equatable {
public var absoluteCounters: [PeerSummaryCounterTags: ChatListTotalUnreadCounters]
public var filteredCounters: [PeerSummaryCounterTags: ChatListTotalUnreadCounters]
public init(absoluteCounters: [PeerSummaryCounterTags: ChatListTotalUnreadCounters], filteredCounters: [PeerSummaryCounterTags: ChatListTotalUnreadCounters]) {
self.absoluteCounters = absoluteCounters
self.filteredCounters = filteredCounters
}
public init(decoder: PostboxDecoder) {
self.absoluteCounters = decoder.decodeObjectDictionaryForKey("ad", keyDecoder: { decoder in
return PeerSummaryCounterTags(rawValue: decoder.decodeInt32ForKey("k", orElse: 0))
}, valueDecoder: { decoder in
return ChatListTotalUnreadCounters(decoder: decoder)
})
self.filteredCounters = decoder.decodeObjectDictionaryForKey("fd", keyDecoder: { decoder in
return PeerSummaryCounterTags(rawValue: decoder.decodeInt32ForKey("k", orElse: 0))
}, valueDecoder: { decoder in
return ChatListTotalUnreadCounters(decoder: decoder)
})
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObjectDictionary(self.absoluteCounters, forKey: "ad", keyEncoder: { key, encoder in
encoder.encodeInt32(key.rawValue, forKey: "k")
})
encoder.encodeObjectDictionary(self.filteredCounters, forKey: "fd", keyEncoder: { key, encoder in
encoder.encodeInt32(key.rawValue, forKey: "k")
})
}
public func count(for category: ChatListTotalUnreadStateCategory, in statsType: ChatListTotalUnreadStateStats, with tags: PeerSummaryCounterTags) -> Int32 {
let counters: [PeerSummaryCounterTags: ChatListTotalUnreadCounters]
switch category {
case .raw:
counters = self.absoluteCounters
case .filtered:
counters = self.filteredCounters
}
var result: Int32 = 0
for tag in tags {
if let category = counters[tag] {
switch statsType {
case .messages:
result = result &+ category.messageCount
case .chats:
result = result &+ category.chatCount
}
}
}
return result
}
}

View File

@ -1,27 +0,0 @@
import Foundation
public protocol MediaResourceId {
var uniqueId: String { get }
var hashValue: Int { get }
func isEqual(to: MediaResourceId) -> Bool
}
public struct WrappedMediaResourceId: Hashable {
public let id: MediaResourceId
public init(_ id: MediaResourceId) {
self.id = id
}
public static func ==(lhs: WrappedMediaResourceId, rhs: WrappedMediaResourceId) -> Bool {
return lhs.id.isEqual(to: rhs.id)
}
public var hashValue: Int {
return self.id.hashValue
}
}
public func anyHashableFromMediaResourceId(_ id: MediaResourceId) -> AnyHashable {
return AnyHashable(WrappedMediaResourceId(id))
}

View File

@ -1,280 +0,0 @@
import Foundation
import Buffers
public struct MessageId: Hashable, Comparable, CustomStringConvertible {
public typealias Namespace = Int32
public typealias Id = Int32
public let peerId: PeerId
public let namespace: Namespace
public let id: Id
public var description: String {
get {
return "\(namespace)_\(id)"
}
}
public init(peerId: PeerId, namespace: Namespace, id: Id) {
self.peerId = peerId
self.namespace = namespace
self.id = id
}
public init(_ buffer: ReadBuffer) {
var peerIdNamespaceValue: Int32 = 0
memcpy(&peerIdNamespaceValue, buffer.memory + buffer.offset, 4)
var peerIdIdValue: Int32 = 0
memcpy(&peerIdIdValue, buffer.memory + (buffer.offset + 4), 4)
self.peerId = PeerId(namespace: peerIdNamespaceValue, id: peerIdIdValue)
var namespaceValue: Int32 = 0
memcpy(&namespaceValue, buffer.memory + (buffer.offset + 8), 4)
self.namespace = namespaceValue
var idValue: Int32 = 0
memcpy(&idValue, buffer.memory + (buffer.offset + 12), 4)
self.id = idValue
buffer.offset += 16
}
public func encodeToBuffer(_ buffer: WriteBuffer) {
var peerIdNamespace = self.peerId.namespace
var peerIdId = self.peerId.id
var namespace = self.namespace
var id = self.id
buffer.write(&peerIdNamespace, offset: 0, length: 4);
buffer.write(&peerIdId, offset: 0, length: 4);
buffer.write(&namespace, offset: 0, length: 4);
buffer.write(&id, offset: 0, length: 4);
}
public static func encodeArrayToBuffer(_ array: [MessageId], buffer: WriteBuffer) {
var length: Int32 = Int32(array.count)
buffer.write(&length, offset: 0, length: 4)
for id in array {
id.encodeToBuffer(buffer)
}
}
public static func decodeArrayFromBuffer(_ buffer: ReadBuffer) -> [MessageId] {
var length: Int32 = 0
memcpy(&length, buffer.memory, 4)
buffer.offset += 4
var i = 0
var array: [MessageId] = []
while i < Int(length) {
array.append(MessageId(buffer))
i += 1
}
return array
}
public static func <(lhs: MessageId, rhs: MessageId) -> Bool {
if lhs.namespace == rhs.namespace {
if lhs.id == rhs.id {
return lhs.peerId < rhs.peerId
} else {
return lhs.id < rhs.id
}
} else {
return lhs.namespace < rhs.namespace
}
}
}
public struct ChatListIndex: Comparable, Hashable {
public let pinningIndex: UInt16?
public let messageIndex: MessageIndex
public init(pinningIndex: UInt16?, messageIndex: MessageIndex) {
self.pinningIndex = pinningIndex
self.messageIndex = messageIndex
}
public static func <(lhs: ChatListIndex, rhs: ChatListIndex) -> Bool {
if let lhsPinningIndex = lhs.pinningIndex, let rhsPinningIndex = rhs.pinningIndex {
if lhsPinningIndex > rhsPinningIndex {
return true
} else if lhsPinningIndex < rhsPinningIndex {
return false
}
} else if lhs.pinningIndex != nil {
return false
} else if rhs.pinningIndex != nil {
return true
}
return lhs.messageIndex < rhs.messageIndex
}
public var hashValue: Int {
return self.messageIndex.hashValue
}
public static var absoluteUpperBound: ChatListIndex {
return ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex.absoluteUpperBound())
}
public static var absoluteLowerBound: ChatListIndex {
return ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex.absoluteLowerBound())
}
public var predecessor: ChatListIndex {
return ChatListIndex(pinningIndex: self.pinningIndex, messageIndex: self.messageIndex.predecessor())
}
public var successor: ChatListIndex {
return ChatListIndex(pinningIndex: self.pinningIndex, messageIndex: self.messageIndex.successor())
}
}
public struct MessageTags: OptionSet, Sequence, Hashable {
public var rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let All = MessageTags(rawValue: 0xffffffff)
public var containsSingleElement: Bool {
var hasOne = false
for i in 0 ..< 31 {
let tag = (self.rawValue >> UInt32(i)) & 1
if tag != 0 {
if hasOne {
return false
} else {
hasOne = true
}
}
}
return hasOne
}
public func makeIterator() -> AnyIterator<MessageTags> {
var index = 0
return AnyIterator { () -> MessageTags? in
while index < 31 {
let currentTags = self.rawValue >> UInt32(index)
let tag = MessageTags(rawValue: 1 << UInt32(index))
index += 1
if currentTags == 0 {
break
}
if (currentTags & 1) != 0 {
return tag
}
}
return nil
}
}
}
public struct GlobalMessageTags: OptionSet, Sequence, Hashable {
public var rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
var isSingleTag: Bool {
let t = Int32(bitPattern: self.rawValue)
return t != 0 && t == (t & (-t))
}
public func makeIterator() -> AnyIterator<GlobalMessageTags> {
var index = 0
return AnyIterator { () -> GlobalMessageTags? in
while index < 31 {
let currentTags = self.rawValue >> UInt32(index)
let tag = GlobalMessageTags(rawValue: 1 << UInt32(index))
index += 1
if currentTags == 0 {
break
}
if (currentTags & 1) != 0 {
return tag
}
}
return nil
}
}
public var hashValue: Int {
return self.rawValue.hashValue
}
}
public struct LocalMessageTags: OptionSet, Sequence, Hashable {
public var rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
var isSingleTag: Bool {
let t = Int32(bitPattern: self.rawValue)
return t != 0 && t == (t & (-t))
}
public func makeIterator() -> AnyIterator<LocalMessageTags> {
var index = 0
return AnyIterator { () -> LocalMessageTags? in
while index < 31 {
let currentTags = self.rawValue >> UInt32(index)
let tag = LocalMessageTags(rawValue: 1 << UInt32(index))
index += 1
if currentTags == 0 {
break
}
if (currentTags & 1) != 0 {
return tag
}
}
return nil
}
}
public var hashValue: Int {
return self.rawValue.hashValue
}
}
public struct MessageFlags: OptionSet {
public var rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let Unsent = MessageFlags(rawValue: 1)
public static let Failed = MessageFlags(rawValue: 2)
public static let Incoming = MessageFlags(rawValue: 4)
public static let TopIndexable = MessageFlags(rawValue: 16)
public static let Sending = MessageFlags(rawValue: 32)
public static let CanBeGroupedIntoFeed = MessageFlags(rawValue: 64)
public static let WasScheduled = MessageFlags(rawValue: 128)
public static let CountedAsIncoming = MessageFlags(rawValue: 256)
public static let IsIncomingMask = MessageFlags([.Incoming, .CountedAsIncoming])
}

View File

@ -1,79 +0,0 @@
import Foundation
public struct MessageIndex: Comparable, Hashable {
public let id: MessageId
public let timestamp: Int32
public init(id: MessageId, timestamp: Int32) {
self.id = id
self.timestamp = timestamp
}
public func predecessor() -> MessageIndex {
if self.id.id != 0 {
return MessageIndex(id: MessageId(peerId: self.id.peerId, namespace: self.id.namespace, id: self.id.id - 1), timestamp: self.timestamp)
} else if self.id.namespace != 0 {
return MessageIndex(id: MessageId(peerId: self.id.peerId, namespace: self.id.namespace - 1, id: Int32.max - 1), timestamp: self.timestamp)
} else if self.timestamp != 0 {
return MessageIndex(id: MessageId(peerId: self.id.peerId, namespace: Int32(Int8.max) - 1, id: Int32.max - 1), timestamp: self.timestamp - 1)
} else {
return self
}
}
public func successor() -> MessageIndex {
return MessageIndex(id: MessageId(peerId: self.id.peerId, namespace: self.id.namespace, id: self.id.id == Int32.max ? self.id.id : (self.id.id + 1)), timestamp: self.timestamp)
}
public var hashValue: Int {
return self.id.hashValue
}
public static func absoluteUpperBound() -> MessageIndex {
return MessageIndex(id: MessageId(peerId: PeerId(namespace: Int32(Int8.max), id: Int32.max), namespace: Int32(Int8.max), id: Int32.max), timestamp: Int32.max)
}
public static func absoluteLowerBound() -> MessageIndex {
return MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 0), namespace: 0, id: 0), timestamp: 0)
}
public static func lowerBound(peerId: PeerId) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: 0)
}
public static func lowerBound(peerId: PeerId, namespace: MessageId.Namespace) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: 0), timestamp: 0)
}
public static func upperBound(peerId: PeerId) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: peerId, namespace: Int32(Int8.max), id: Int32.max), timestamp: Int32.max)
}
public static func upperBound(peerId: PeerId, namespace: MessageId.Namespace) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32.max), timestamp: Int32.max)
}
public static func upperBound(peerId: PeerId, timestamp: Int32, namespace: MessageId.Namespace) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32.max), timestamp: timestamp)
}
func withPeerId(_ peerId: PeerId) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: peerId, namespace: self.id.namespace, id: self.id.id), timestamp: self.timestamp)
}
func withNamespace(_ namespace: MessageId.Namespace) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: self.id.peerId, namespace: namespace, id: self.id.id), timestamp: self.timestamp)
}
public static func <(lhs: MessageIndex, rhs: MessageIndex) -> Bool {
if lhs.timestamp != rhs.timestamp {
return lhs.timestamp < rhs.timestamp
}
if lhs.id.namespace != rhs.id.namespace {
return lhs.id.namespace < rhs.id.namespace
}
return lhs.id.id < rhs.id.id
}
}

View File

@ -1,11 +0,0 @@
import Foundation
import Buffers
import PostboxCoding
public protocol Peer: class, PostboxCoding {
var id: PeerId { get }
var associatedPeerId: PeerId? { get }
var notificationSettingsPeerId: PeerId? { get }
func isEqual(_ other: Peer) -> Bool
}

View File

@ -1,23 +0,0 @@
import Foundation
public enum PeerGroupId: Hashable, Equatable, RawRepresentable {
case root
case group(Int32)
public var rawValue: Int32 {
switch self {
case .root:
return 0
case let .group(id):
return id
}
}
public init(rawValue: Int32) {
if rawValue == 0 {
self = .root
} else {
self = .group(rawValue)
}
}
}

View File

@ -1,89 +0,0 @@
import Foundation
import Buffers
public struct PeerId: Hashable, CustomStringConvertible, Comparable {
public typealias Namespace = Int32
public typealias Id = Int32
public let namespace: Namespace
public let id: Id
public init(namespace: Namespace, id: Id) {
self.namespace = namespace
self.id = id
}
public init(_ n: Int64) {
self.namespace = Int32((n >> 32) & 0x7fffffff)
self.id = Int32(bitPattern: UInt32(n & 0xffffffff))
}
public func toInt64() -> Int64 {
return (Int64(self.namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: self.id)))
}
public static func encodeArrayToBuffer(_ array: [PeerId], buffer: WriteBuffer) {
var length: Int32 = Int32(array.count)
buffer.write(&length, offset: 0, length: 4)
for id in array {
var value = id.toInt64()
buffer.write(&value, offset: 0, length: 8)
}
}
public static func decodeArrayFromBuffer(_ buffer: ReadBuffer) -> [PeerId] {
var length: Int32 = 0
memcpy(&length, buffer.memory, 4)
buffer.offset += 4
var i = 0
var array: [PeerId] = []
array.reserveCapacity(Int(length))
while i < Int(length) {
var value: Int64 = 0
buffer.read(&value, offset: 0, length: 8)
array.append(PeerId(value))
i += 1
}
return array
}
public var hashValue: Int {
get {
return Int(self.id)
}
}
public var description: String {
get {
return "\(namespace):\(id)"
}
}
public init(_ buffer: ReadBuffer) {
var namespace: Int32 = 0
var id: Int32 = 0
memcpy(&namespace, buffer.memory, 4)
self.namespace = namespace
memcpy(&id, buffer.memory + 4, 4)
self.id = id
}
public func encodeToBuffer(_ buffer: WriteBuffer) {
var namespace = self.namespace
var id = self.id
buffer.write(&namespace, offset: 0, length: 4);
buffer.write(&id, offset: 0, length: 4);
}
public static func <(lhs: PeerId, rhs: PeerId) -> Bool {
if lhs.namespace != rhs.namespace {
return lhs.namespace < rhs.namespace
}
if lhs.id != rhs.id {
return lhs.id < rhs.id
}
return false
}
}

View File

@ -1,152 +0,0 @@
public enum PeerReadState: Equatable, CustomStringConvertible {
case idBased(maxIncomingReadId: MessageId.Id, maxOutgoingReadId: MessageId.Id, maxKnownId: MessageId.Id, count: Int32, markedUnread: Bool)
case indexBased(maxIncomingReadIndex: MessageIndex, maxOutgoingReadIndex: MessageIndex, count: Int32, markedUnread: Bool)
public var count: Int32 {
switch self {
case let .idBased(_, _, _, count, _):
return count
case let .indexBased(_, _, count, _):
return count
}
}
public var maxKnownId: MessageId.Id? {
switch self {
case let .idBased(_, _, maxKnownId, _, _):
return maxKnownId
case .indexBased:
return nil
}
}
public var isUnread: Bool {
switch self {
case let .idBased(_, _, _, count, markedUnread):
return count > 0 || markedUnread
case let .indexBased(_, _, count, markedUnread):
return count > 0 || markedUnread
}
}
public var markedUnread: Bool {
switch self {
case let .idBased(_, _, _, _, markedUnread):
return markedUnread
case let .indexBased(_, _, _, markedUnread):
return markedUnread
}
}
public func withAddedCount(_ value: Int32) -> PeerReadState {
switch self {
case let .idBased(maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread):
return .idBased(maxIncomingReadId: maxIncomingReadId, maxOutgoingReadId: maxOutgoingReadId, maxKnownId: maxKnownId, count: count + value, markedUnread: markedUnread)
case let .indexBased(maxIncomingReadIndex, maxOutgoingReadIndex, count, markedUnread):
return .indexBased(maxIncomingReadIndex: maxIncomingReadIndex, maxOutgoingReadIndex: maxOutgoingReadIndex, count: count + value, markedUnread: markedUnread)
}
}
public var description: String {
switch self {
case let .idBased(maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread):
return "(PeerReadState maxIncomingReadId: \(maxIncomingReadId), maxOutgoingReadId: \(maxOutgoingReadId) maxKnownId: \(maxKnownId), count: \(count), markedUnread: \(markedUnread)"
case let .indexBased(maxIncomingReadIndex, maxOutgoingReadIndex, count, markedUnread):
return "(PeerReadState maxIncomingReadIndex: \(maxIncomingReadIndex), maxOutgoingReadIndex: \(maxOutgoingReadIndex), count: \(count), markedUnread: \(markedUnread)"
}
}
public func isIncomingMessageIndexRead(_ index: MessageIndex) -> Bool {
switch self {
case let .idBased(maxIncomingReadId, _, _, _, _):
return maxIncomingReadId >= index.id.id
case let .indexBased(maxIncomingReadIndex, _, _, _):
return maxIncomingReadIndex >= index
}
}
public func isOutgoingMessageIndexRead(_ index: MessageIndex) -> Bool {
switch self {
case let .idBased(_, maxOutgoingReadId, _, _, _):
return maxOutgoingReadId >= index.id.id
case let .indexBased(_, maxOutgoingReadIndex, _, _):
return maxOutgoingReadIndex >= index
}
}
}
public struct CombinedPeerReadState: Equatable {
public var states: [(MessageId.Namespace, PeerReadState)]
public init(states: [(MessageId.Namespace, PeerReadState)]) {
self.states = states
}
public var count: Int32 {
var result: Int32 = 0
for (_, state) in self.states {
result += state.count
}
return result
}
public var markedUnread: Bool {
for (_, state) in self.states {
if state.markedUnread {
return true
}
}
return false
}
public var isUnread: Bool {
for (_, state) in self.states {
if state.isUnread {
return true
}
}
return false
}
public static func ==(lhs: CombinedPeerReadState, rhs: CombinedPeerReadState) -> Bool {
if lhs.states.count != rhs.states.count {
return false
}
for (lhsNamespace, lhsState) in lhs.states {
var rhsFound = false
inner: for (rhsNamespace, rhsState) in rhs.states {
if rhsNamespace == lhsNamespace {
if lhsState != rhsState {
return false
}
rhsFound = true
break inner
}
}
if !rhsFound {
return false
}
}
return true
}
public func isOutgoingMessageIndexRead(_ index: MessageIndex) -> Bool {
for (namespace, readState) in self.states {
if namespace == index.id.namespace {
return readState.isOutgoingMessageIndexRead(index)
}
}
return false
}
public func isIncomingMessageIndexRead(_ index: MessageIndex) -> Bool {
for (namespace, readState) in self.states {
if namespace == index.id.namespace {
return readState.isIncomingMessageIndexRead(index)
}
}
return false
}
}

View File

@ -1,32 +0,0 @@
import Foundation
public struct PeerSummaryCounterTags: OptionSet, Sequence, Hashable {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public func makeIterator() -> AnyIterator<PeerSummaryCounterTags> {
var index = 0
return AnyIterator { () -> PeerSummaryCounterTags? in
while index < 31 {
let currentTags = self.rawValue >> UInt32(index)
let tag = PeerSummaryCounterTags(rawValue: 1 << UInt32(index))
index += 1
if currentTags == 0 {
break
}
if (currentTags & 1) != 0 {
return tag
}
}
return nil
}
}
}

View File

@ -1,18 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PreferencesTable",
module_name = "PreferencesTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxCoding:PostboxCoding",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,15 +0,0 @@
import Foundation
import PostboxCoding
import PostboxDataTypes
public protocol PreferencesEntry: PostboxCoding {
var relatedResources: [MediaResourceId] { get }
func isEqual(to: PreferencesEntry) -> Bool
}
public extension PreferencesEntry {
var relatedResources: [MediaResourceId] {
return []
}
}

View File

@ -1,73 +0,0 @@
import Foundation
import ValueBox
import Table
import PostboxCoding
public enum PreferencesOperation {
case update(ValueBoxKey, PreferencesEntry?)
}
private struct CachedEntry {
let entry: PreferencesEntry?
}
public final class PreferencesTable: Table {
private var cachedEntries: [ValueBoxKey: CachedEntry] = [:]
private var updatedEntryKeys = Set<ValueBoxKey>()
public static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false)
}
public func enumerateEntries(_ f: (PreferencesEntry) -> Bool) {
self.valueBox.scan(self.table, values: { _, value in
if let object = PostboxDecoder(buffer: value).decodeRootObject() as? PreferencesEntry {
return f(object)
} else {
return true
}
})
}
public func get(key: ValueBoxKey) -> PreferencesEntry? {
if let cached = self.cachedEntries[key] {
return cached.entry
} else {
if let value = self.valueBox.get(self.table, key: key), let object = PostboxDecoder(buffer: value).decodeRootObject() as? PreferencesEntry {
self.cachedEntries[key] = CachedEntry(entry: object)
return object
} else {
self.cachedEntries[key] = CachedEntry(entry: nil)
return nil
}
}
}
public func set(key: ValueBoxKey, value: PreferencesEntry?, operations: inout [PreferencesOperation]) {
self.cachedEntries[key] = CachedEntry(entry: value)
updatedEntryKeys.insert(key)
operations.append(.update(key, value))
}
override public func clearMemoryCache() {
assert(self.updatedEntryKeys.isEmpty)
}
override public func beforeCommit() {
if !self.updatedEntryKeys.isEmpty {
for key in self.updatedEntryKeys {
if let value = self.cachedEntries[key]?.entry {
let encoder = PostboxEncoder()
encoder.encodeRootObject(value)
withExtendedLifetime(encoder, {
self.valueBox.set(self.table, key: key, value: encoder.readBufferNoCopy())
})
} else {
self.valueBox.remove(self.table, key: key, secure: false)
}
}
self.updatedEntryKeys.removeAll()
}
}
}

View File

@ -1,15 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "Table",
module_name = "Table",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,18 +0,0 @@
import Foundation
import ValueBox
open class Table {
public final let valueBox: ValueBox
public final let table: ValueBoxTable
public init(valueBox: ValueBox, table: ValueBoxTable) {
self.valueBox = valueBox
self.table = table
}
open func clearMemoryCache() {
}
open func beforeCommit() {
}
}

View File

@ -1,17 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ValueBox",
module_name = "ValueBox",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/sqlcipher:sqlcipher",
"//submodules/Database/Buffers:Buffers",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,72 +0,0 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright (c) 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
#if os(macOS)
import sqlciphermac
#else
import sqlcipher
#endif
final class Database {
internal var handle: OpaquePointer? = nil
init?(logger: ValueBoxLogger, location: String) {
if location != ":memory:" {
let _ = open(location + "-guard", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)
}
let flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX
let res = sqlite3_open_v2(location, &self.handle, flags, nil)
if res != SQLITE_OK {
logger.log("sqlite3_open_v2: \(res)")
return nil
}
}
deinit {
sqlite3_close(self.handle)
} // sqlite3_close_v2 in Yosemite/iOS 8?
public func execute(_ SQL: String) -> Bool {
let res = sqlite3_exec(self.handle, SQL, nil, nil, nil)
if res == SQLITE_OK {
return true
} else {
if let error = sqlite3_errmsg(self.handle), let str = NSString(utf8String: error) {
print("SQL error \(res): \(str) on SQL")
} else {
print("SQL error \(res) on SQL")
}
return false
}
}
public func currentError() -> String? {
if let error = sqlite3_errmsg(self.handle), let str = NSString(utf8String: error) {
return "SQL error \(str)"
} else {
return nil
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +0,0 @@
import Foundation
import Buffers
public enum ValueBoxKeyType: Int32 {
case binary
case int64
}
public struct ValueBoxTable {
let id: Int32
let keyType: ValueBoxKeyType
let compactValuesOnCreation: Bool
public init(id: Int32, keyType: ValueBoxKeyType, compactValuesOnCreation: Bool) {
self.id = id
self.keyType = keyType
self.compactValuesOnCreation = compactValuesOnCreation
}
}
public struct ValueBoxFullTextTable {
let id: Int32
}
public struct ValueBoxEncryptionParameters {
public struct Key {
public let data: Data
public init?(data: Data) {
if data.count == 32 {
self.data = data
} else {
return nil
}
}
}
public struct Salt {
public let data: Data
public init?(data: Data) {
if data.count == 16 {
self.data = data
} else {
return nil
}
}
}
public let forceEncryptionIfNoSet: Bool
public let key: Key
public let salt: Salt
public init(forceEncryptionIfNoSet: Bool, key: Key, salt: Salt) {
self.forceEncryptionIfNoSet = forceEncryptionIfNoSet
self.key = key
self.salt = salt
}
}
public protocol ValueBox {
func begin()
func commit()
func checkpoint()
func beginStats()
func endStats()
func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, values: (ValueBoxKey, ReadBuffer) -> Bool, limit: Int)
func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, keys: (ValueBoxKey) -> Bool, limit: Int)
func scan(_ table: ValueBoxTable, values: (ValueBoxKey, ReadBuffer) -> Bool)
func scan(_ table: ValueBoxTable, keys: (ValueBoxKey) -> Bool)
func scanInt64(_ table: ValueBoxTable, values: (Int64, ReadBuffer) -> Bool)
func scanInt64(_ table: ValueBoxTable, keys: (Int64) -> Bool)
func get(_ table: ValueBoxTable, key: ValueBoxKey) -> ReadBuffer?
func read(_ table: ValueBoxTable, key: ValueBoxKey, _ process: (Int, (UnsafeMutableRawPointer, Int, Int) -> Void) -> Void)
func readWrite(_ table: ValueBoxTable, key: ValueBoxKey, _ process: (Int, (UnsafeMutableRawPointer, Int, Int) -> Void, (UnsafeRawPointer, Int, Int) -> Void) -> Void)
func exists(_ table: ValueBoxTable, key: ValueBoxKey) -> Bool
func set(_ table: ValueBoxTable, key: ValueBoxKey, value: MemoryBuffer)
func remove(_ table: ValueBoxTable, key: ValueBoxKey, secure: Bool)
func move(_ table: ValueBoxTable, from previousKey: ValueBoxKey, to updatedKey: ValueBoxKey)
func copy(fromTable: ValueBoxTable, fromKey: ValueBoxKey, toTable: ValueBoxTable, toKey: ValueBoxKey)
func removeRange(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey)
func fullTextSet(_ table: ValueBoxFullTextTable, collectionId: String, itemId: String, contents: String, tags: String)
func fullTextMatch(_ table: ValueBoxFullTextTable, collectionId: String?, query: String, tags: String?, values: (String, String) -> Bool)
func fullTextRemove(_ table: ValueBoxFullTextTable, itemId: String, secure: Bool)
func removeAllFromTable(_ table: ValueBoxTable)
func removeTable(_ table: ValueBoxTable)
func renameTable(_ table: ValueBoxTable, to toTable: ValueBoxTable)
func drop()
func count(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey) -> Int
func count(_ table: ValueBoxTable) -> Int
func exportEncrypted(to exportBasePath: String, encryptionParameters: ValueBoxEncryptionParameters)
}

View File

@ -1,252 +0,0 @@
import Foundation
import Buffers
private final class ValueBoxKeyImpl {
let memory: UnsafeMutableRawPointer
init(memory: UnsafeMutableRawPointer) {
self.memory = memory
}
deinit {
free(self.memory)
}
}
public struct ValueBoxKey: Equatable, Hashable, CustomStringConvertible, Comparable {
public let memory: UnsafeMutableRawPointer
public let length: Int
private let impl: ValueBoxKeyImpl
public init(length: Int) {
self.memory = malloc(length)!
self.length = length
self.impl = ValueBoxKeyImpl(memory: self.memory)
}
public init(_ value: String) {
let data = value.data(using: .utf8, allowLossyConversion: true) ?? Data()
self.memory = malloc(data.count)
self.length = data.count
self.impl = ValueBoxKeyImpl(memory: self.memory)
data.copyBytes(to: self.memory.assumingMemoryBound(to: UInt8.self), count: data.count)
}
public init(_ buffer: MemoryBuffer) {
self.memory = malloc(buffer.length)
self.length = buffer.length
self.impl = ValueBoxKeyImpl(memory: self.memory)
memcpy(self.memory, buffer.memory, buffer.length)
}
public func setInt32(_ offset: Int, value: Int32) {
var bigEndianValue = Int32(bigEndian: value)
memcpy(self.memory + offset, &bigEndianValue, 4)
}
public func setUInt32(_ offset: Int, value: UInt32) {
var bigEndianValue = UInt32(bigEndian: value)
memcpy(self.memory + offset, &bigEndianValue, 4)
}
public func setInt64(_ offset: Int, value: Int64) {
var bigEndianValue = Int64(bigEndian: value)
memcpy(self.memory + offset, &bigEndianValue, 8)
}
public func setInt8(_ offset: Int, value: Int8) {
var varValue = value
memcpy(self.memory + offset, &varValue, 1)
}
public func setUInt8(_ offset: Int, value: UInt8) {
var varValue = value
memcpy(self.memory + offset, &varValue, 1)
}
public func setUInt16(_ offset: Int, value: UInt16) {
var varValue = value
memcpy(self.memory + offset, &varValue, 2)
}
public func getInt32(_ offset: Int) -> Int32 {
var value: Int32 = 0
memcpy(&value, self.memory + offset, 4)
return Int32(bigEndian: value)
}
public func getUInt32(_ offset: Int) -> UInt32 {
var value: UInt32 = 0
memcpy(&value, self.memory + offset, 4)
return UInt32(bigEndian: value)
}
public func getInt64(_ offset: Int) -> Int64 {
var value: Int64 = 0
memcpy(&value, self.memory + offset, 8)
return Int64(bigEndian: value)
}
public func getInt8(_ offset: Int) -> Int8 {
var value: Int8 = 0
memcpy(&value, self.memory + offset, 1)
return value
}
public func getUInt8(_ offset: Int) -> UInt8 {
var value: UInt8 = 0
memcpy(&value, self.memory + offset, 1)
return value
}
public func getUInt16(_ offset: Int) -> UInt16 {
var value: UInt16 = 0
memcpy(&value, self.memory + offset, 2)
return value
}
public func prefix(_ length: Int) -> ValueBoxKey {
assert(length <= self.length, "length <= self.length")
let key = ValueBoxKey(length: length)
memcpy(key.memory, self.memory, length)
return key
}
public func isPrefix(to other: ValueBoxKey) -> Bool {
if self.length == 0 {
return true
} else if self.length <= other.length {
return memcmp(self.memory, other.memory, self.length) == 0
} else {
return false
}
}
public var reversed: ValueBoxKey {
let key = ValueBoxKey(length: self.length)
let keyMemory = key.memory.assumingMemoryBound(to: UInt8.self)
let selfMemory = self.memory.assumingMemoryBound(to: UInt8.self)
var i = self.length - 1
while i >= 0 {
keyMemory[i] = selfMemory[self.length - 1 - i]
i -= 1
}
return key
}
public var successor: ValueBoxKey {
let key = ValueBoxKey(length: self.length)
memcpy(key.memory, self.memory, self.length)
let memory = key.memory.assumingMemoryBound(to: UInt8.self)
var i = self.length - 1
while i >= 0 {
var byte = memory[i]
if byte != 0xff {
byte += 1
memory[i] = byte
break
} else {
byte = 0
memory[i] = byte
}
i -= 1
}
return key
}
public var predecessor: ValueBoxKey {
let key = ValueBoxKey(length: self.length)
memcpy(key.memory, self.memory, self.length)
let memory = key.memory.assumingMemoryBound(to: UInt8.self)
var i = self.length - 1
while i >= 0 {
var byte = memory[i]
if byte != 0x00 {
byte -= 1
memory[i] = byte
break
} else {
if i == 0 {
assert(self.length > 1)
let previousKey = ValueBoxKey(length: self.length - 1)
memcpy(previousKey.memory, self.memory, self.length - 1)
return previousKey
} else {
byte = 0xff
memory[i] = byte
}
}
i -= 1
}
return key
}
public var description: String {
let string = NSMutableString()
let memory = self.memory.assumingMemoryBound(to: UInt8.self)
for i in 0 ..< self.length {
let byte: Int = Int(memory[i])
string.appendFormat("%02x", byte)
}
return string as String
}
public var stringValue: String {
if let string = String(data: Data(bytes: self.memory, count: self.length), encoding: .utf8) {
return string
} else {
return "<unavailable>"
}
}
public func substringValue(_ range: Range<Int>) -> String? {
return String(data: Data(bytes: self.memory.advanced(by: range.lowerBound), count: range.count), encoding: .utf8)
}
public var hashValue: Int {
var hash = 37
let bytes = self.memory.assumingMemoryBound(to: Int8.self)
for i in 0 ..< self.length {
hash = (hash &* 54059) ^ (Int(bytes[i]) &* 76963)
}
return hash
}
public static func ==(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool {
return lhs.length == rhs.length && memcmp(lhs.memory, rhs.memory, lhs.length) == 0
}
public static func <(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool {
return mdb_cmp_memn(lhs.memory, lhs.length, rhs.memory, rhs.length) < 0
}
public func toMemoryBuffer() -> MemoryBuffer {
let data = malloc(self.length)!
memcpy(data, self.memory, self.length)
return MemoryBuffer(memory: data, capacity: self.length, length: self.length, freeWhenDone: true)
}
public static func +(lhs: ValueBoxKey, rhs: ValueBoxKey) -> ValueBoxKey {
let result = ValueBoxKey(length: lhs.length + rhs.length)
memcpy(result.memory, lhs.memory, lhs.length)
memcpy(result.memory.advanced(by: lhs.length), rhs.memory, rhs.length)
return result
}
}
private func mdb_cmp_memn(_ a_memory: UnsafeMutableRawPointer, _ a_length: Int, _ b_memory: UnsafeMutableRawPointer, _ b_length: Int) -> Int
{
var diff: Int = 0
var len_diff: Int = 0
var len: Int = 0
len = a_length
len_diff = a_length - b_length
if len_diff > 0 {
len = b_length
len_diff = 1
}
diff = Int(memcmp(a_memory, b_memory, len))
return diff != 0 ? diff : len_diff < 0 ? -1 : len_diff
}

View File

@ -1,5 +0,0 @@
import Foundation
public protocol ValueBoxLogger {
func log(_ what: String)
}

View File

@ -1125,11 +1125,37 @@ func debugRestoreState(basePath:String, name: String) {
private let sharedQueue = Queue(name: "org.telegram.postbox.Postbox")
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, isReadOnly: Bool) -> Signal<PostboxResult, NoError> {
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, isReadOnly: Bool, useCopy: Bool) -> Signal<PostboxResult, NoError> {
let queue = sharedQueue
return Signal { subscriber in
queue.async {
let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true, attributes: nil)
var tempDir: TempBoxDirectory?
let dbBasePath: String
if useCopy {
let directory = TempBox.shared.tempDirectory()
tempDir = directory
let originalBasePath = basePath + "/db"
if let originalFiles = try? FileManager.default.contentsOfDirectory(atPath: originalBasePath) {
do {
for fileName in originalFiles {
try FileManager.default.copyItem(atPath: originalBasePath + "/" + fileName, toPath: directory.path + "/" + fileName)
}
} catch let e {
postboxLog("openPostbox useCopy error: \(e)")
subscriber.putNext(.error)
return
}
} else {
subscriber.putNext(.error)
return
}
dbBasePath = directory.path
} else {
dbBasePath = basePath + "/db"
}
#if DEBUG
//debugSaveState(basePath: basePath, name: "previous1")
@ -1138,7 +1164,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
let startTime = CFAbsoluteTimeGetCurrent()
guard var valueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
guard var valueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
subscriber.putNext(.upgrading(progress))
}) else {
subscriber.putNext(.error)
@ -1161,7 +1187,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
postboxLog("Version \(userVersion) is newer than supported")
assertionFailure("Version \(userVersion) is newer than supported")
valueBox.drop()
guard let updatedValueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
subscriber.putNext(.upgrading(progress))
}) else {
subscriber.putNext(.error)
@ -1183,9 +1209,9 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
})
if let updatedPath = updatedPath {
valueBox.internalClose()
let _ = try? FileManager.default.removeItem(atPath: basePath + "/db")
let _ = try? FileManager.default.moveItem(atPath: updatedPath, toPath: basePath + "/db")
guard let updatedValueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
let _ = try? FileManager.default.removeItem(atPath: dbBasePath)
let _ = try? FileManager.default.moveItem(atPath: updatedPath, toPath: dbBasePath)
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
subscriber.putNext(.upgrading(progress))
}) else {
subscriber.putNext(.error)
@ -1199,7 +1225,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
assertionFailure("Couldn't find any upgrade for \(userVersion)")
postboxLog("Couldn't find any upgrade for \(userVersion)")
valueBox.drop()
guard let updatedValueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
subscriber.putNext(.upgrading(progress))
}) else {
subscriber.putNext(.error)
@ -1217,7 +1243,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
let endTime = CFAbsoluteTimeGetCurrent()
print("Postbox load took \((endTime - startTime) * 1000.0) ms")
subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox, timestampForAbsoluteTimeBasedOperations: timestampForAbsoluteTimeBasedOperations, isTemporary: isTemporary)))
subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox, timestampForAbsoluteTimeBasedOperations: timestampForAbsoluteTimeBasedOperations, isTemporary: isTemporary, tempDir: tempDir)))
subscriber.putCompletion()
break
}
@ -1232,6 +1258,7 @@ public final class Postbox {
public let seedConfiguration: SeedConfiguration
private let basePath: String
let valueBox: SqliteValueBox
private let tempDir: TempBoxDirectory?
private let ipcNotificationsDisposable = MetaDisposable()
@ -1370,7 +1397,7 @@ public final class Postbox {
var installedMessageActionsByPeerId: [PeerId: Bag<([StoreMessage], Transaction) -> Void>] = [:]
init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool) {
init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, tempDir: TempBoxDirectory?) {
assert(queue.isCurrent())
let startTime = CFAbsoluteTimeGetCurrent()
@ -1378,6 +1405,7 @@ public final class Postbox {
self.queue = queue
self.basePath = basePath
self.seedConfiguration = seedConfiguration
self.tempDir = tempDir
print("MediaBox path: \(self.basePath + "/media")")
@ -1585,7 +1613,9 @@ public final class Postbox {
}
deinit {
assert(true)
if let tempDir = self.tempDir {
TempBox.shared.dispose(tempDir)
}
}
private func takeNextViewId() -> Int {

View File

@ -7,7 +7,11 @@ private final class TempBoxFileContext {
var subscribers = Set<Int>()
var path: String {
return self.directory + "/" + self.fileName
if self.fileName.isEmpty {
return self.directory
} else {
return self.directory + "/" + self.fileName
}
}
init(directory: String, fileName: String) {
@ -34,6 +38,18 @@ public final class TempBoxFile {
}
}
public final class TempBoxDirectory {
fileprivate let key: TempBoxKey
fileprivate let id: Int
public let path: String
fileprivate init(key: TempBoxKey, id: Int, path: String) {
self.key = key
self.id = id
self.path = path
}
}
private final class TempBoxContexts {
private var nextId: Int = 0
private var contexts: [TempBoxKey: TempBoxFileContext] = [:]
@ -86,6 +102,23 @@ private final class TempBoxContexts {
return TempBoxFile(key: key, id: id, path: context.path)
}
func tempDirectory(basePath: String) -> TempBoxDirectory {
let id = self.nextId
self.nextId += 1
let key = TempBoxKey(path: nil, fileName: "", uniqueId: id)
let context: TempBoxFileContext
let dirName = "\(id)"
let dirPath = basePath + "/" + dirName
context = TempBoxFileContext(directory: dirPath, fileName: "")
self.contexts[key] = context
let _ = try? FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil)
context.subscribers.insert(id)
return TempBoxDirectory(key: key, id: id, path: context.path)
}
func dispose(_ file: TempBoxFile) -> [String] {
if let context = self.contexts[file.key] {
context.subscribers.remove(file.id)
@ -96,6 +129,17 @@ private final class TempBoxContexts {
}
return []
}
func dispose(_ directory: TempBoxDirectory) -> [String] {
if let context = self.contexts[directory.key] {
context.subscribers.remove(directory.id)
if context.subscribers.isEmpty {
self.contexts.removeValue(forKey: directory.key)
return [context.directory]
}
}
return []
}
}
private var sharedValue: TempBox?
@ -150,6 +194,12 @@ public final class TempBox {
}
}
public func tempDirectory() -> TempBoxDirectory {
return self.contexts.with { contexts in
return contexts.tempDirectory(basePath: self.currentBasePath)
}
}
public func dispose(_ file: TempBoxFile) {
let removePaths = self.contexts.with { contexts in
return contexts.dispose(file)
@ -162,4 +212,17 @@ public final class TempBox {
}
}
}
public func dispose(_ directory: TempBoxDirectory) {
let removePaths = self.contexts.with { contexts in
return contexts.dispose(directory)
}
if !removePaths.isEmpty {
DispatchQueue.global(qos: .background).async {
for path in removePaths {
let _ = try? FileManager.default.removeItem(atPath: path)
}
}
}
}
}

View File

@ -73,9 +73,9 @@ public enum AccountTransactionError {
case couldNotOpen
}
public func accountTransaction<T>(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, isReadOnly: Bool, transaction: @escaping (Postbox, Transaction) -> T) -> Signal<T, AccountTransactionError> {
public func accountTransaction<T>(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, isReadOnly: Bool, useCopy: Bool = false, transaction: @escaping (Postbox, Transaction) -> T) -> Signal<T, AccountTransactionError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: isReadOnly)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: isReadOnly, useCopy: useCopy)
return postbox
|> castError(AccountTransactionError.self)
|> mapToSignal { value -> Signal<T, AccountTransactionError> in

View File

@ -160,7 +160,7 @@ public enum AccountPreferenceEntriesResult {
public func accountPreferenceEntries(rootPath: String, id: AccountRecordId, keys: Set<ValueBoxKey>, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountPreferenceEntriesResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true, useCopy: false)
return postbox
|> mapToSignal { value -> Signal<AccountPreferenceEntriesResult, NoError> in
switch value {
@ -189,7 +189,7 @@ public enum AccountNoticeEntriesResult {
public func accountNoticeEntries(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountNoticeEntriesResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true, useCopy: false)
return postbox
|> mapToSignal { value -> Signal<AccountNoticeEntriesResult, NoError> in
switch value {
@ -212,7 +212,7 @@ public enum LegacyAccessChallengeDataResult {
public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<LegacyAccessChallengeDataResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: true, useCopy: false)
return postbox
|> mapToSignal { value -> Signal<LegacyAccessChallengeDataResult, NoError> in
switch value {
@ -231,7 +231,7 @@ public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecord
public func accountWithId(accountManager: AccountManager, networkArguments: NetworkInitializationArguments, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, supplementary: Bool, rootPath: String, beginWithTestingEnvironment: Bool, backupData: AccountBackupData?, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal<AccountResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false, useCopy: false)
return postbox
|> mapToSignal { result -> Signal<AccountResult, NoError> in

View File

@ -214,7 +214,7 @@ public func temporaryAccount(manager: AccountManager, rootPath: String, encrypti
return manager.allocatedTemporaryAccountId()
|> mapToSignal { id -> Signal<TemporaryAccount, NoError> in
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false)
return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: false, isReadOnly: false, useCopy: false)
|> mapToSignal { result -> Signal<TemporaryAccount, NoError> in
switch result {
case .upgrading:

View File

@ -842,13 +842,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}
}
}
if !isReplyThreadHead, !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && isAction {
/*if !isReplyThreadHead, !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && isAction {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { controller, f in
interfaceInteraction.deleteMessages(messages, controller, f)
})))
}
}*/
if data.messageActions.options.contains(.viewStickerPack) {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.StickerPack_ViewPack, icon: { theme in
@ -904,7 +904,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
clearCacheAsDelete = true
}
if !isReplyThreadHead, (!data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty || clearCacheAsDelete) && !isAction {
if !isReplyThreadHead, (!data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty || clearCacheAsDelete) {
var autoremoveDeadline: Int32?
for attribute in message.attributes {
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {