Calculate local unread count before applying notification

This commit is contained in:
Peter 2019-10-21 23:27:06 +04:00
parent 10692a323e
commit c83a2364ac
40 changed files with 6091 additions and 101 deletions

7
BUCK
View File

@ -339,10 +339,13 @@ apple_binary(
deps = [
"//submodules/BuildConfig:BuildConfig",
"//submodules/MtProtoKit:MtProtoKit#shared",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/EncryptionProvider:EncryptionProvider",
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
"//submodules/Database/MessageHistoryReadStateTable:MessageHistoryReadStateTable",
"//submodules/Database/MessageHistoryMetadataTable:MessageHistoryMetadataTable",
"//submodules/sqlcipher:sqlcipher",
#"//submodules/Postbox:Postbox#shared",
#"//submodules/SyncCore:SyncCore#shared",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -1,13 +1,6 @@
#ifndef NotificationService_BridgingHeader_h
#define NotificationService_BridgingHeader_h
#import <Foundation/Foundation.h>
#import <BuildConfig/BuildConfig.h>
@protocol SyncProvider <NSObject>
- (void)addIncomingMessageWithRootPath:(NSString * _Nonnull)rootPath accountId:(int64_t)accountId encryptionParameters:(DeviceSpecificEncryptionParameters * _Nonnull)encryptionParameters peerId:(int64_t)peerId messageId:(int32_t)messageId completion:(void (^)(int32_t))completion;
@end
#import "NotificationService.h"
#endif

View File

@ -1,9 +1,16 @@
#import <Foundation/Foundation.h>
#import <UserNotifications/UserNotifications.h>
#import <BuildConfig/BuildConfig.h>
NS_ASSUME_NONNULL_BEGIN
@interface NotificationService : UNNotificationServiceExtension
@interface NotificationServiceImpl : NSObject
- (instancetype)initWithCountIncomingMessage:(void (^)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t))countIncomingMessage;
- (void)updateUnreadCount:(int32_t)unreadCount;
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler;
- (void)serviceExtensionTimeWillExpire;
@end

View File

@ -1,5 +1,7 @@
#import "NotificationService.h"
#import <mach/mach.h>
#import <UIKit/UIKit.h>
#import <BuildConfig/BuildConfig.h>
@ -34,47 +36,43 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
return (((int64_t)(namespace)) << 32) | ((int64_t)((uint64_t)((uint32_t)value)));
}
@interface ParsedNotificationMessage : NSObject
@property (nonatomic, readonly) int64_t accountId;
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
@end
@implementation ParsedNotificationMessage
- (instancetype)initWithAccountId:(int64_t)accountId peerId:(int64_t)peerId messageId:(int64_t)messageId {
self = [super init];
if (self != nil) {
_accountId = accountId;
_peerId = peerId;
_messageId = messageId;
static void reportMemory() {
struct task_basic_info info;
mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS) {
NSLog(@"Memory in use (in bytes): %lu", info.resident_size);
NSLog(@"Memory in use (in MiB): %f", ((CGFloat)info.resident_size / 1048576));
} else {
NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
}
return self;
}
@end
@interface NotificationService () {
@interface NotificationServiceImpl () {
void (^_countIncomingMessage)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t);
NSString * _Nullable _rootPath;
DeviceSpecificEncryptionParameters * _Nullable _deviceSpecificEncryptionParameters;
NSString * _Nullable _baseAppBundleId;
void (^_contentHandler)(UNNotificationContent *);
UNMutableNotificationContent * _Nullable _bestAttemptContent;
void (^_cancelFetch)(void);
ParsedNotificationMessage * _Nullable _parsedMessage;
NSNumber * _Nullable _updatedUnreadCount;
bool _contentReady;
}
@end
@implementation NotificationService
@implementation NotificationServiceImpl
- (instancetype)init {
- (instancetype)initWithCountIncomingMessage:(void (^)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t))countIncomingMessage {
self = [super init];
if (self != nil) {
reportMemory();
_countIncomingMessage = [countIncomingMessage copy];
NSString *appBundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
NSRange lastDotRange = [appBundleIdentifier rangeOfString:@"." options:NSBackwardsSearch];
if (lastDotRange.location != NSNotFound) {
@ -85,6 +83,9 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
if (appGroupUrl != nil) {
NSString *rootPath = [[appGroupUrl path] stringByAppendingPathComponent:@"telegram-data"];
_rootPath = rootPath;
if (rootPath != nil) {
_deviceSpecificEncryptionParameters = [BuildConfig deviceSpecificEncryptionParameters:rootPath baseAppBundleId:_baseAppBundleId];
}
} else {
NSAssert(false, @"appGroupUrl == nil");
}
@ -97,6 +98,7 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
- (void)completeWithBestAttemptContent {
_contentReady = true;
//_updatedUnreadCount = @(-1);
if (_contentReady && _updatedUnreadCount) {
[self _internalComplete];
}
@ -110,6 +112,8 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
}
- (void)_internalComplete {
reportMemory();
#ifdef __IPHONE_13_0
if (_baseAppBundleId != nil) {
BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:[_baseAppBundleId stringByAppendingString:@".refresh"]];
@ -193,16 +197,9 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
peerId = makePeerId(PeerNamespaceCloudChannel, [channelIdString intValue]);
}
_parsedMessage = [[ParsedNotificationMessage alloc] initWithAccountId:account.accountId peerId:peerId messageId:messageId];
__weak NotificationService *weakSelf = self;
[self addUnreadMessage:_rootPath accountId:account.accountId encryptionParameters:nil peerId:peerId messageId:messageId completion:^(int32_t badge) {
__strong NotificationService *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
[strongSelf updateUnreadCount:badge];
}];
if (_countIncomingMessage && _deviceSpecificEncryptionParameters) {
_countIncomingMessage(_rootPath, account.accountId, _deviceSpecificEncryptionParameters, peerId, messageId);
}
NSString *silentString = decryptedPayload[@"silent"];
if ([silentString isKindOfClass:[NSString class]]) {
@ -375,10 +372,10 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
} else {
BuildConfig *buildConfig = [[BuildConfig alloc] initWithBaseAppBundleId:_baseAppBundleId];
__weak NotificationService *weakSelf = self;
__weak typeof(self) weakSelf = self;
_cancelFetch = fetchImage(buildConfig, accountInfos.proxy, account, inputFileLocation, fileDatacenterId, ^(NSData * _Nullable data) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong NotificationService *strongSelf = weakSelf;
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
@ -431,11 +428,4 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
}
}
- (void)addUnreadMessage:(NSString * _Nonnull)rootPath accountId:(int64_t)accountId encryptionParameters:(DeviceSpecificEncryptionParameters * _Nonnull)encryptionParameters peerId:(int64_t)peerId messageId:(int32_t)messageId completion:(void (^)(int32_t))completion {
if (completion) {
completion(-1);
}
}
@end

View File

@ -0,0 +1,31 @@
import Foundation
import UserNotifications
@available(iOSApplicationExtension 10.0, *)
@objc(NotificationService)
final class NotificationService: UNNotificationServiceExtension {
private let impl: NotificationServiceImpl
override init() {
var completion: ((Int32) -> Void)?
self.impl = NotificationServiceImpl(countIncomingMessage: { rootPath, accountId, encryptionParameters, peerId, messageId in
SyncProviderImpl().addIncomingMessage(withRootPath: rootPath, accountId: accountId, encryptionParameters: encryptionParameters, peerId: peerId, messageId: messageId, completion: { count in
completion?(count)
})
})
super.init()
completion = { [weak self] count in
self?.impl.updateUnreadCount(count)
}
}
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.impl.didReceive(request, withContentHandler: contentHandler)
}
override func serviceExtensionTimeWillExpire() {
self.impl.serviceExtensionTimeWillExpire()
}
}

View File

@ -1,4 +1,3 @@
#import <Foundation/Foundation.h>
#import <sqlcipher/sqlcipher.h>

View File

@ -1 +1,3 @@
#import "Sync.h"
#import "Sync.h"
//#import <sqlcipher/sqlcipher.h>

View File

@ -1,32 +1,95 @@
//import SwiftSignalKit
//import Postbox
//import SyncCore
//import BuildConfig
import Foundation
import SwiftSignalKit
import ValueBox
import MessageHistoryReadStateTable
import MessageHistoryMetadataTable
import PostboxDataTypes
@objc(SyncProviderImpl)
final class SyncProviderImpl: NSObject {
private func accountRecordIdPathName(_ id: Int64) -> String {
return "account-\(UInt64(bitPattern: id))"
}
/*@objc(SyncProviderImpl)
final class SyncProviderImpl: NSObject, SyncProvider {
func addIncomingMessage(withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: ((Int32) -> Void)!) {
let _ = (addIncomingMessageImpl(rootPath: rootPath, accountId: accountId, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: encryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: encryptionParameters.salt)!), peerId: peerId, messageId: messageId)
|> deliverOnMainQueue).start(next: { result in
completion(Int32(clamping: result))
})
private final class ValueBoxLoggerImpl: ValueBoxLogger {
func log(_ what: String) {
print("ValueBox: \(what)")
}
}
private func addIncomingMessageImpl(rootPath: String, accountId: Int64, encryptionParameters: ValueBoxEncryptionParameters, peerId: Int64, messageId: Int32) -> Signal<Int, NoError> {
return accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, transaction: { transaction -> Int in
transaction.countIncomingMessage(id: MessageId(peerId: PeerId(peerId), namespace: Namespaces.Message.Cloud, id: messageId))
let totalUnreadState = transaction.getTotalUnreadState()
let totalCount = totalUnreadState.count(for: .filtered, in: .chats, with: [
.regularChatsAndPrivateGroups,
.publicGroups,
.channels
])
return Int(totalCount)
})
private extension PeerSummaryCounterTags {
static let regularChatsAndPrivateGroups = PeerSummaryCounterTags(rawValue: 1 << 0)
static let publicGroups = PeerSummaryCounterTags(rawValue: 1 << 1)
static let channels = PeerSummaryCounterTags(rawValue: 1 << 2)
}
private struct Namespaces {
struct Message {
static let Cloud: Int32 = 0
}
struct Peer {
static let CloudUser: Int32 = 0
static let CloudGroup: Int32 = 1
static let CloudChannel: Int32 = 2
static let SecretChat: Int32 = 3
}
}
final class SyncProviderImpl {
func addIncomingMessage(withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) {
Queue.mainQueue().async {
let basePath = rootPath + "/" + accountRecordIdPathName(accountId) + "/postbox"
let valueBox = SqliteValueBox(basePath: basePath + "/db", queue: Queue.mainQueue(), logger: ValueBoxLoggerImpl(), encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: encryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: encryptionParameters.salt)!), disableCache: true, upgradeProgress: { _ in
})
let metadataTable = MessageHistoryMetadataTable(valueBox: valueBox, table: MessageHistoryMetadataTable.tableSpec(10))
let readStateTable = MessageHistoryReadStateTable(valueBox: valueBox, table: MessageHistoryReadStateTable.tableSpec(14), defaultMessageNamespaceReadStates: [:])
let peerId = PeerId(peerId)
let initialCombinedState = readStateTable.getCombinedState(peerId)
let (combinedState, _) = readStateTable.addIncomingMessages(peerId, indices: Set([MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: messageId), timestamp: 1)]))
if let combinedState = combinedState {
let initialCount = initialCombinedState?.count ?? 0
let updatedCount = combinedState.count
let deltaCount = max(0, updatedCount - initialCount)
let tag: PeerSummaryCounterTags
if peerId.namespace == Namespaces.Peer.CloudChannel {
tag = .channels
} else {
tag = .regularChatsAndPrivateGroups
}
var totalCount: Int32 = -1
var totalUnreadState = metadataTable.getChatListTotalUnreadState()
if var counters = totalUnreadState.absoluteCounters[tag] {
if initialCount == 0 && updatedCount > 0 {
counters.chatCount += 1
}
counters.messageCount += deltaCount
totalUnreadState.absoluteCounters[tag] = counters
}
if var counters = totalUnreadState.filteredCounters[tag] {
if initialCount == 0 && updatedCount > 0 {
counters.chatCount += 1
}
counters.messageCount += deltaCount
totalUnreadState.filteredCounters[tag] = counters
}
totalCount = totalUnreadState.count(for: .filtered, in: .messages, with: [.channels, .publicGroups, .regularChatsAndPrivateGroups])
metadataTable.setChatListTotalUnreadState(totalUnreadState)
metadataTable.setShouldReindexUnreadCounts(value: true)
metadataTable.beforeCommit()
readStateTable.beforeCommit()
completion(totalCount)
} else {
completion(-1)
}
}
}
}
*/

View File

@ -0,0 +1,13 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "Buffers",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

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

View File

@ -0,0 +1,17 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "MessageHistoryMetadataTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
"//submodules/Database/PostboxCoding:PostboxCoding",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

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

View File

@ -0,0 +1,16 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "MessageHistoryReadStateTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

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

View File

@ -0,0 +1,20 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "MurmurHash",
srcs = glob([
"Sources/**/*.swift",
"Sources/**/*.m",
]),
headers = glob([
"Sources/**/*.h",
]),
exported_headers = glob([
"Sources/**/*.h",
]),
deps = [
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

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

View File

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

View File

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

View File

@ -0,0 +1,15 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "PostboxCoding",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/Buffers:Buffers",
"//submodules/Database/MurmurHash:MurmurHash",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "PostboxDataTypes",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/PostboxCoding:PostboxCoding",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,14 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "Table",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

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

View File

@ -0,0 +1,16 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "ValueBox",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/sqlcipher:sqlcipher",
"//submodules/Database/Buffers:Buffers",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
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)
func removeAllFromTable(_ table: ValueBoxTable)
func removeTable(_ table: ValueBoxTable)
func renameTable(_ table: ValueBoxTable, to toTable: ValueBoxTable)
func drop()
func count(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey) -> Int
func count(_ table: ValueBoxTable) -> Int
func exportEncrypted(to exportBasePath: String, encryptionParameters: ValueBoxEncryptionParameters)
}

View File

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

View File

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

View File

@ -644,14 +644,10 @@ final class ChatListIndexTable: Table {
if peerId.namespace == Int32.max {
return
}
/*guard let peer = postbox.peerTable.get(peerId) else {
return
}*/
guard let combinedState = postbox.readStateTable.getCombinedState(peerId) else {
return
}
/*let notificationPeerId: PeerId = peer.associatedPeerId ?? peerId
let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId)*/
let inclusion = self.get(peerId: peerId)
if let (inclusionGroupId, _) = inclusion.includedIndex(peerId: peerId), inclusionGroupId == groupId {
for (namespace, state) in combinedState.states {

View File

@ -77,6 +77,16 @@ public struct PeerGroupUnreadCountersCombinedSummary: PostboxCoding, Equatable {
}
}
public enum ChatListTotalUnreadStateCategory: Int32 {
case filtered = 0
case raw = 1
}
public enum ChatListTotalUnreadStateStats: Int32 {
case messages = 0
case chats = 1
}
public struct ChatListTotalUnreadState: PostboxCoding, Equatable {
public var absoluteCounters: [PeerSummaryCounterTags: ChatListTotalUnreadCounters]
public var filteredCounters: [PeerSummaryCounterTags: ChatListTotalUnreadCounters]

View File

@ -32,16 +32,6 @@ public struct ChatListTotalUnreadCounters: PostboxCoding, Equatable {
}
}
public enum ChatListTotalUnreadStateCategory: Int32 {
case filtered = 0
case raw = 1
}
public enum ChatListTotalUnreadStateStats: Int32 {
case messages = 0
case chats = 1
}
private struct InitializedChatListKey: Hashable {
let groupId: PeerGroupId
}

View File

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