mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
0c06906352
@ -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",
|
||||
|
@ -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)
|
||||
}
|
||||
*/
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -1,4 +0,0 @@
|
||||
#ifndef Telegram_iOS_Telegram_Bridging_Header_h
|
||||
#define Telegram_iOS_Telegram_Bridging_Header_h
|
||||
|
||||
#endif
|
@ -65,20 +65,8 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String {
|
||||
return appGroupPath + "/telegram-data"
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 14.0, iOS 14.0, *)
|
||||
struct Provider: IntentTimelineProvider {
|
||||
public typealias Entry = SimpleEntry
|
||||
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
return SimpleEntry(date: Date(), contents: .recent)
|
||||
}
|
||||
|
||||
func getSnapshot(for configuration: SelectFriendsIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
let entry = SimpleEntry(date: Date(), contents: context.isPreview ? .preview : .peers(ParsedPeers(accountId: 0, peers: WidgetDataPeers(accountPeerId: 0, peers: [], updateTimestamp: 0))))
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(for configuration: SelectFriendsIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||
@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
|
||||
@ -118,7 +106,7 @@ struct Provider: IntentTimelineProvider {
|
||||
|
||||
var itemsByAccount: [Int64: [(Int64, Friend)]] = [:]
|
||||
var itemOrder: [(Int64, Int64)] = []
|
||||
if let friends = configuration.friends {
|
||||
if let friends = friends {
|
||||
for item in friends {
|
||||
guard let identifier = item.identifier else {
|
||||
continue
|
||||
@ -142,7 +130,7 @@ struct Provider: IntentTimelineProvider {
|
||||
|
||||
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
|
||||
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 []
|
||||
}
|
||||
@ -222,6 +210,23 @@ struct Provider: IntentTimelineProvider {
|
||||
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
|
||||
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
return SimpleEntry(date: Date(), contents: .recent)
|
||||
}
|
||||
|
||||
func getSnapshot(for configuration: SelectFriendsIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
let entry = SimpleEntry(date: Date(), contents: context.isPreview ? .preview : .peers(ParsedPeers(accountId: 0, peers: WidgetDataPeers(accountPeerId: 0, peers: [], updateTimestamp: 0))))
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(for configuration: SelectFriendsIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||
getCommonTimeline(friends: configuration.friends, in: context, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 14.0, iOS 14.0, *)
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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",
|
||||
],
|
||||
)
|
@ -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
|
@ -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;
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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",
|
||||
],
|
||||
)
|
@ -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
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
@ -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])
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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 []
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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() {
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
public protocol ValueBoxLogger {
|
||||
func log(_ what: String)
|
||||
}
|
@ -1125,12 +1125,38 @@ 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")
|
||||
//debugRestoreState(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 {
|
||||
|
@ -7,8 +7,12 @@ private final class TempBoxFileContext {
|
||||
var subscribers = Set<Int>()
|
||||
|
||||
var path: String {
|
||||
if self.fileName.isEmpty {
|
||||
return self.directory
|
||||
} else {
|
||||
return self.directory + "/" + self.fileName
|
||||
}
|
||||
}
|
||||
|
||||
init(directory: String, fileName: String) {
|
||||
self.directory = directory
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user