mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-19 04:00:54 +00:00
no message
This commit is contained in:
parent
6889f59981
commit
7f5890f459
@ -22,7 +22,8 @@
|
|||||||
D07516771B2EC90400AE42E0 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516741B2EC90400AE42E0 /* fts3_tokenizer.h */; };
|
D07516771B2EC90400AE42E0 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516741B2EC90400AE42E0 /* fts3_tokenizer.h */; };
|
||||||
D07516781B2EC90400AE42E0 /* SQLite-Bridging.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516751B2EC90400AE42E0 /* SQLite-Bridging.h */; };
|
D07516781B2EC90400AE42E0 /* SQLite-Bridging.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516751B2EC90400AE42E0 /* SQLite-Bridging.h */; };
|
||||||
D07516791B2EC90400AE42E0 /* SQLite-Bridging.m in Sources */ = {isa = PBXBuildFile; fileRef = D07516761B2EC90400AE42E0 /* SQLite-Bridging.m */; };
|
D07516791B2EC90400AE42E0 /* SQLite-Bridging.m in Sources */ = {isa = PBXBuildFile; fileRef = D07516761B2EC90400AE42E0 /* SQLite-Bridging.m */; };
|
||||||
D07FC7D31B4A3D6B0010B3F7 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D07FC7D21B4A3D6B0010B3F7 /* SwiftSignalKit.framework */; };
|
D0B76BE71B66639F0095CF45 /* DeferredString.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B76BE61B66639F0095CF45 /* DeferredString.swift */; };
|
||||||
|
D0C07F6A1B67DB4800966E43 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */; };
|
||||||
D0D224F21B4D6ABD0085E26D /* Functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D224ED1B4D6ABD0085E26D /* Functions.swift */; };
|
D0D224F21B4D6ABD0085E26D /* Functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D224ED1B4D6ABD0085E26D /* Functions.swift */; };
|
||||||
D0D225261B4D84930085E26D /* PeerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D225251B4D84930085E26D /* PeerView.swift */; };
|
D0D225261B4D84930085E26D /* PeerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D225251B4D84930085E26D /* PeerView.swift */; };
|
||||||
D0E3A7501B28A7E300A402D9 /* Postbox.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E3A74F1B28A7E300A402D9 /* Postbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
D0E3A7501B28A7E300A402D9 /* Postbox.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E3A74F1B28A7E300A402D9 /* Postbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
@ -62,7 +63,8 @@
|
|||||||
D07516741B2EC90400AE42E0 /* fts3_tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fts3_tokenizer.h; sourceTree = "<group>"; };
|
D07516741B2EC90400AE42E0 /* fts3_tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fts3_tokenizer.h; sourceTree = "<group>"; };
|
||||||
D07516751B2EC90400AE42E0 /* SQLite-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SQLite-Bridging.h"; sourceTree = "<group>"; };
|
D07516751B2EC90400AE42E0 /* SQLite-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SQLite-Bridging.h"; sourceTree = "<group>"; };
|
||||||
D07516761B2EC90400AE42E0 /* SQLite-Bridging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SQLite-Bridging.m"; sourceTree = "<group>"; };
|
D07516761B2EC90400AE42E0 /* SQLite-Bridging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SQLite-Bridging.m"; sourceTree = "<group>"; };
|
||||||
D07FC7D21B4A3D6B0010B3F7 /* SwiftSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftSignalKit.framework; path = "../SSignalKit/build/Debug-iphoneos/SwiftSignalKit.framework"; sourceTree = "<group>"; };
|
D0B76BE61B66639F0095CF45 /* DeferredString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeferredString.swift; sourceTree = "<group>"; };
|
||||||
|
D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftSignalKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-gbpsmqzuwcmmxadrqcwyrluaftwp/Build/Products/Debug-iphoneos/SwiftSignalKit.framework"; sourceTree = "<group>"; };
|
||||||
D0D224ED1B4D6ABD0085E26D /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Functions.swift; path = submodules/sqlite.swift/SQLite/Functions.swift; sourceTree = SOURCE_ROOT; };
|
D0D224ED1B4D6ABD0085E26D /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Functions.swift; path = submodules/sqlite.swift/SQLite/Functions.swift; sourceTree = SOURCE_ROOT; };
|
||||||
D0D225251B4D84930085E26D /* PeerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerView.swift; sourceTree = "<group>"; };
|
D0D225251B4D84930085E26D /* PeerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerView.swift; sourceTree = "<group>"; };
|
||||||
D0E3A74A1B28A7E300A402D9 /* Postbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Postbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D0E3A74A1B28A7E300A402D9 /* Postbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Postbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -83,6 +85,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D0C07F6A1B67DB4800966E43 /* SwiftSignalKit.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -90,7 +93,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
D07FC7D31B4A3D6B0010B3F7 /* SwiftSignalKit.framework in Frameworks */,
|
|
||||||
D0E3A7561B28A7E300A402D9 /* Postbox.framework in Frameworks */,
|
D0E3A7561B28A7E300A402D9 /* Postbox.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -142,7 +144,7 @@
|
|||||||
D0E3A7401B28A7E300A402D9 = {
|
D0E3A7401B28A7E300A402D9 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D07FC7D21B4A3D6B0010B3F7 /* SwiftSignalKit.framework */,
|
D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */,
|
||||||
D07516491B2D9E2500AE42E0 /* Postbox.xcconfig */,
|
D07516491B2D9E2500AE42E0 /* Postbox.xcconfig */,
|
||||||
D0E3A74C1B28A7E300A402D9 /* Postbox */,
|
D0E3A74C1B28A7E300A402D9 /* Postbox */,
|
||||||
D0E3A7591B28A7E300A402D9 /* PostboxTests */,
|
D0E3A7591B28A7E300A402D9 /* PostboxTests */,
|
||||||
@ -166,6 +168,7 @@
|
|||||||
D075163D1B2D9CEF00AE42E0 /* PostboxPrivate */,
|
D075163D1B2D9CEF00AE42E0 /* PostboxPrivate */,
|
||||||
D07515FC1B2C44A200AE42E0 /* thirdparty */,
|
D07515FC1B2C44A200AE42E0 /* thirdparty */,
|
||||||
D0E3A7871B28AE9C00A402D9 /* Coding.swift */,
|
D0E3A7871B28AE9C00A402D9 /* Coding.swift */,
|
||||||
|
D0B76BE61B66639F0095CF45 /* DeferredString.swift */,
|
||||||
D0E3A7831B28AE0900A402D9 /* Peer.swift */,
|
D0E3A7831B28AE0900A402D9 /* Peer.swift */,
|
||||||
D0E3A79D1B28B50400A402D9 /* Message.swift */,
|
D0E3A79D1B28B50400A402D9 /* Message.swift */,
|
||||||
D0E3A7A11B28B7DC00A402D9 /* Media.swift */,
|
D0E3A7A11B28B7DC00A402D9 /* Media.swift */,
|
||||||
@ -334,6 +337,7 @@
|
|||||||
D07516711B2EC7FE00AE42E0 /* Statement.swift in Sources */,
|
D07516711B2EC7FE00AE42E0 /* Statement.swift in Sources */,
|
||||||
D0D225261B4D84930085E26D /* PeerView.swift in Sources */,
|
D0D225261B4D84930085E26D /* PeerView.swift in Sources */,
|
||||||
D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */,
|
D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */,
|
||||||
|
D0B76BE71B66639F0095CF45 /* DeferredString.swift in Sources */,
|
||||||
D075166A1B2EC7FE00AE42E0 /* Database.swift in Sources */,
|
D075166A1B2EC7FE00AE42E0 /* Database.swift in Sources */,
|
||||||
D07516441B2D9CEF00AE42E0 /* sqlite3.c in Sources */,
|
D07516441B2D9CEF00AE42E0 /* sqlite3.c in Sources */,
|
||||||
);
|
);
|
||||||
@ -459,6 +463,7 @@
|
|||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"/Users/peter/Documents/PostBoxTest/submodules/SSignalKit/build/Debug-iphoneos",
|
"/Users/peter/Documents/PostBoxTest/submodules/SSignalKit/build/Debug-iphoneos",
|
||||||
@ -487,6 +492,7 @@
|
|||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"/Users/peter/Documents/PostBoxTest/submodules/SSignalKit/build/Debug-iphoneos",
|
"/Users/peter/Documents/PostBoxTest/submodules/SSignalKit/build/Debug-iphoneos",
|
||||||
@ -503,6 +509,7 @@
|
|||||||
);
|
);
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
@ -136,6 +136,13 @@ public final class Encoder {
|
|||||||
self.buffer.write(UnsafePointer<Void>(key), offset: 0, length: Int(length))
|
self.buffer.write(UnsafePointer<Void>(key), offset: 0, length: Int(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func encodeKey(key: Int8) {
|
||||||
|
var length: Int8 = 1
|
||||||
|
self.buffer.write(&length, offset: 0, length: 1)
|
||||||
|
var keyValue = key
|
||||||
|
self.buffer.write(&keyValue, offset: 0, length: Int(length))
|
||||||
|
}
|
||||||
|
|
||||||
public func encodeInt32(value: Int32, forKey key: UnsafePointer<Int8>) {
|
public func encodeInt32(value: Int32, forKey key: UnsafePointer<Int8>) {
|
||||||
self.encodeKey(key)
|
self.encodeKey(key)
|
||||||
var type: Int8 = ValueType.Int32.rawValue
|
var type: Int8 = ValueType.Int32.rawValue
|
||||||
@ -144,6 +151,14 @@ public final class Encoder {
|
|||||||
self.buffer.write(&v, offset: 0, length: 4)
|
self.buffer.write(&v, offset: 0, length: 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func encodeInt32(value: Int32, forKey key: Int8) {
|
||||||
|
self.encodeKey(key)
|
||||||
|
var type: Int8 = ValueType.Int32.rawValue
|
||||||
|
self.buffer.write(&type, offset: 0, length: 1)
|
||||||
|
var v = value
|
||||||
|
self.buffer.write(&v, offset: 0, length: 4)
|
||||||
|
}
|
||||||
|
|
||||||
public func encodeInt64(value: Int64, forKey key: UnsafePointer<Int8>) {
|
public func encodeInt64(value: Int64, forKey key: UnsafePointer<Int8>) {
|
||||||
self.encodeKey(key)
|
self.encodeKey(key)
|
||||||
var type: Int8 = ValueType.Int64.rawValue
|
var type: Int8 = ValueType.Int64.rawValue
|
||||||
@ -152,6 +167,14 @@ public final class Encoder {
|
|||||||
self.buffer.write(&v, offset: 0, length: 8)
|
self.buffer.write(&v, offset: 0, length: 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func encodeInt64(value: Int64, forKey key: Int8) {
|
||||||
|
self.encodeKey(key)
|
||||||
|
var type: Int8 = ValueType.Int64.rawValue
|
||||||
|
self.buffer.write(&type, offset: 0, length: 1)
|
||||||
|
var v = value
|
||||||
|
self.buffer.write(&v, offset: 0, length: 8)
|
||||||
|
}
|
||||||
|
|
||||||
public func encodeBool(value: Bool, forKey key: UnsafePointer<Int8>) {
|
public func encodeBool(value: Bool, forKey key: UnsafePointer<Int8>) {
|
||||||
self.encodeKey(key)
|
self.encodeKey(key)
|
||||||
var type: Int8 = ValueType.Bool.rawValue
|
var type: Int8 = ValueType.Bool.rawValue
|
||||||
@ -160,6 +183,14 @@ public final class Encoder {
|
|||||||
self.buffer.write(&v, offset: 0, length: 1)
|
self.buffer.write(&v, offset: 0, length: 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func encodeBool(value: Bool, forKey key: Int8) {
|
||||||
|
self.encodeKey(key)
|
||||||
|
var type: Int8 = ValueType.Bool.rawValue
|
||||||
|
self.buffer.write(&type, offset: 0, length: 1)
|
||||||
|
var v: Int8 = value ? 1 : 0
|
||||||
|
self.buffer.write(&v, offset: 0, length: 1)
|
||||||
|
}
|
||||||
|
|
||||||
public func encodeDouble(value: Double, forKey key: UnsafePointer<Int8>) {
|
public func encodeDouble(value: Double, forKey key: UnsafePointer<Int8>) {
|
||||||
self.encodeKey(key)
|
self.encodeKey(key)
|
||||||
var type: Int8 = ValueType.Double.rawValue
|
var type: Int8 = ValueType.Double.rawValue
|
||||||
@ -168,6 +199,14 @@ public final class Encoder {
|
|||||||
self.buffer.write(&v, offset: 0, length: 8)
|
self.buffer.write(&v, offset: 0, length: 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func encodeDouble(value: Double, forKey key: Int8) {
|
||||||
|
self.encodeKey(key)
|
||||||
|
var type: Int8 = ValueType.Double.rawValue
|
||||||
|
self.buffer.write(&type, offset: 0, length: 1)
|
||||||
|
var v = value
|
||||||
|
self.buffer.write(&v, offset: 0, length: 8)
|
||||||
|
}
|
||||||
|
|
||||||
public func encodeString(value: String, forKey key: UnsafePointer<Int8>) {
|
public func encodeString(value: String, forKey key: UnsafePointer<Int8>) {
|
||||||
self.encodeKey(key)
|
self.encodeKey(key)
|
||||||
var type: Int8 = ValueType.String.rawValue
|
var type: Int8 = ValueType.String.rawValue
|
||||||
@ -178,6 +217,26 @@ public final class Encoder {
|
|||||||
self.buffer.write(data.bytes, offset: 0, length: Int(length))
|
self.buffer.write(data.bytes, offset: 0, length: Int(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func encodeString(value: DeferredString, forKey key: UnsafePointer<Int8>) {
|
||||||
|
self.encodeKey(key)
|
||||||
|
var type: Int8 = ValueType.String.rawValue
|
||||||
|
self.buffer.write(&type, offset: 0, length: 1)
|
||||||
|
let data = value.data
|
||||||
|
var length: Int32 = Int32(data.length)
|
||||||
|
self.buffer.write(&length, offset: 0, length: 4)
|
||||||
|
self.buffer.write(data.bytes, offset: 0, length: Int(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encodeString(value: String, forKey key: Int8) {
|
||||||
|
self.encodeKey(key)
|
||||||
|
var type: Int8 = ValueType.String.rawValue
|
||||||
|
self.buffer.write(&type, offset: 0, length: 1)
|
||||||
|
let data = value.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
|
||||||
|
var length: Int32 = Int32(data.length)
|
||||||
|
self.buffer.write(&length, offset: 0, length: 4)
|
||||||
|
self.buffer.write(data.bytes, offset: 0, length: Int(length))
|
||||||
|
}
|
||||||
|
|
||||||
public func encodeRootObject(value: Coding) {
|
public func encodeRootObject(value: Coding) {
|
||||||
self.encodeObject(value, forKey: "_")
|
self.encodeObject(value, forKey: "_")
|
||||||
}
|
}
|
||||||
@ -278,6 +337,8 @@ public final class Encoder {
|
|||||||
self.buffer.write(&bytesLength, offset: 0, length: 4)
|
self.buffer.write(&bytesLength, offset: 0, length: 4)
|
||||||
self.buffer.write(bytes.memory, offset: 0, length: bytes.offset)
|
self.buffer.write(bytes.memory, offset: 0, length: bytes.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public let sharedWriteBuffer = WriteBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class Decoder {
|
public final class Decoder {
|
||||||
@ -376,6 +437,37 @@ public final class Decoder {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class func positionOnKey(bytes: UnsafePointer<Int8>, inout offset: Int, maxOffset: Int, length: Int, key: Int16, valueType: ValueType) -> Bool
|
||||||
|
{
|
||||||
|
var keyValue = key
|
||||||
|
let startOffset = offset
|
||||||
|
|
||||||
|
let keyLength: Int = 2
|
||||||
|
while (offset < maxOffset)
|
||||||
|
{
|
||||||
|
let readKeyLength = bytes[offset]
|
||||||
|
offset += 1
|
||||||
|
offset += Int(readKeyLength)
|
||||||
|
|
||||||
|
let readValueType = bytes[offset]
|
||||||
|
offset += 1
|
||||||
|
|
||||||
|
if readValueType != valueType.rawValue || keyLength != Int(readKeyLength) || memcmp(bytes + (offset - Int(readKeyLength) - 1), &keyValue, keyLength) != 0 {
|
||||||
|
skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!)
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startOffset != 0)
|
||||||
|
{
|
||||||
|
offset = 0
|
||||||
|
return positionOnKey(bytes, offset: &offset, maxOffset: startOffset, length: length, key: key, valueType: valueType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
public func decodeInt32ForKey(key: UnsafePointer<Int8>) -> Int32 {
|
public func decodeInt32ForKey(key: UnsafePointer<Int8>) -> Int32 {
|
||||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) {
|
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) {
|
||||||
var value: Int32 = 0
|
var value: Int32 = 0
|
||||||
@ -433,6 +525,43 @@ public final class Decoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func decodeStringForKey(key: UnsafePointer<Int8>) -> String? {
|
||||||
|
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||||
|
var length: Int32 = 0
|
||||||
|
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||||
|
let data = NSData(bytes: self.buffer.memory + (self.buffer.offset + 4), length: Int(length))
|
||||||
|
self.buffer.offset += 4 + Int(length)
|
||||||
|
let value = NSString(data: data, encoding: NSUTF8StringEncoding)
|
||||||
|
return value as? String
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decodeStringForKey(key: UnsafePointer<Int8>) -> DeferredString {
|
||||||
|
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||||
|
var length: Int32 = 0
|
||||||
|
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||||
|
let data = NSData(bytes: self.buffer.memory + (self.buffer.offset + 4), length: Int(length))
|
||||||
|
self.buffer.offset += 4 + Int(length)
|
||||||
|
return DeferredStringValue(data)
|
||||||
|
} else {
|
||||||
|
return DeferredStringValue("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decodeStringForKey(key: UnsafePointer<Int8>) -> DeferredString? {
|
||||||
|
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||||
|
var length: Int32 = 0
|
||||||
|
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||||
|
let data = NSData(bytes: self.buffer.memory + (self.buffer.offset + 4), length: Int(length))
|
||||||
|
self.buffer.offset += 4 + Int(length)
|
||||||
|
return DeferredStringValue(data)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func decodeRootObject() -> Coding? {
|
public func decodeRootObject() -> Coding? {
|
||||||
return self.decodeObjectForKey("_")
|
return self.decodeObjectForKey("_")
|
||||||
}
|
}
|
||||||
|
124
Postbox/DeferredString.swift
Normal file
124
Postbox/DeferredString.swift
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol DeferredString {
|
||||||
|
var data: NSData { get }
|
||||||
|
var string: String { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class DeferredStringImpl {
|
||||||
|
var string: String!
|
||||||
|
var data: NSData!
|
||||||
|
var lock: OSSpinLock = 0
|
||||||
|
|
||||||
|
init(string: String!, data: NSData!) {
|
||||||
|
self.string = string
|
||||||
|
self.data = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DeferredStringValue: DeferredString, CustomStringConvertible, StringLiteralConvertible {
|
||||||
|
private var impl: DeferredStringImpl
|
||||||
|
|
||||||
|
public typealias ExtendedGraphemeClusterLiteralType = StringLiteralType
|
||||||
|
public typealias UnicodeScalarLiteralType = StringLiteralType
|
||||||
|
|
||||||
|
public init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
|
||||||
|
self.impl = DeferredStringImpl(string: "\(value)", data: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
|
||||||
|
self.impl = DeferredStringImpl(string: value, data: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(stringLiteral value: StringLiteralType) {
|
||||||
|
self.impl = DeferredStringImpl(string: value, data: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ string: String) {
|
||||||
|
self.impl = DeferredStringImpl(string: string, data: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ data: NSData) {
|
||||||
|
self.impl = DeferredStringImpl(string: nil, data: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return self.string
|
||||||
|
}
|
||||||
|
|
||||||
|
public var data: NSData {
|
||||||
|
if isUniquelyReferencedNonObjC(&self.impl) {
|
||||||
|
let value: NSData
|
||||||
|
|
||||||
|
if impl.data != nil {
|
||||||
|
value = impl.data
|
||||||
|
} else if impl.string != nil {
|
||||||
|
value = impl.string.dataUsingEncoding(NSUTF8StringEncoding) ?? NSData()
|
||||||
|
impl.data = value
|
||||||
|
} else {
|
||||||
|
value = NSData()
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
let value: NSData
|
||||||
|
|
||||||
|
OSSpinLockLock(&impl.lock)
|
||||||
|
if impl.data != nil {
|
||||||
|
value = impl.data
|
||||||
|
} else if impl.string != nil {
|
||||||
|
value = impl.string.dataUsingEncoding(NSUTF8StringEncoding) ?? NSData()
|
||||||
|
impl.data = value
|
||||||
|
} else {
|
||||||
|
value = NSData()
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&impl.lock)
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var string: String {
|
||||||
|
if isUniquelyReferencedNonObjC(&self.impl) {
|
||||||
|
let value: String
|
||||||
|
|
||||||
|
if impl.string != nil {
|
||||||
|
value = impl.string
|
||||||
|
} else if impl.data != nil {
|
||||||
|
let fromData = NSString(data: impl.data, encoding: NSUTF8StringEncoding)
|
||||||
|
if fromData == nil {
|
||||||
|
value = ""
|
||||||
|
impl.string = value
|
||||||
|
} else {
|
||||||
|
value = fromData as! String
|
||||||
|
impl.string = value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
let value: String
|
||||||
|
|
||||||
|
OSSpinLockLock(&impl.lock)
|
||||||
|
if impl.string != nil {
|
||||||
|
value = impl.string
|
||||||
|
} else if impl.data != nil {
|
||||||
|
let fromData = NSString(data: impl.data, encoding: NSUTF8StringEncoding)
|
||||||
|
if fromData == nil {
|
||||||
|
value = ""
|
||||||
|
impl.string = value
|
||||||
|
} else {
|
||||||
|
value = fromData as! String
|
||||||
|
impl.string = value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = ""
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&impl.lock)
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -110,5 +110,28 @@ public protocol Message: Coding {
|
|||||||
var id: MessageId { get }
|
var id: MessageId { get }
|
||||||
var timestamp: Int32 { get }
|
var timestamp: Int32 { get }
|
||||||
var text: String { get }
|
var text: String { get }
|
||||||
var referencedMediaIds: [MediaId] { get }
|
var mediaIds: [MediaId] { get }
|
||||||
|
var peerIds: [PeerId] { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RenderedMessage {
|
||||||
|
public let message: Message
|
||||||
|
|
||||||
|
internal let incomplete: Bool
|
||||||
|
public let peers: [Peer]
|
||||||
|
public let media: [Media]
|
||||||
|
|
||||||
|
internal init(message: Message) {
|
||||||
|
self.message = message
|
||||||
|
self.peers = []
|
||||||
|
self.media = []
|
||||||
|
self.incomplete = true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init(message: Message, peers: [Peer], media: [Media]) {
|
||||||
|
self.message = message
|
||||||
|
self.peers = peers
|
||||||
|
self.media = media
|
||||||
|
self.incomplete = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,11 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
|
|
||||||
let namespaces: [MessageId.Namespace]
|
let namespaces: [MessageId.Namespace]
|
||||||
let count: Int
|
let count: Int
|
||||||
var earlier: [MessageId.Namespace : Message] = [:]
|
var earlier: [MessageId.Namespace : RenderedMessage] = [:]
|
||||||
var later: [MessageId.Namespace : Message] = [:]
|
var later: [MessageId.Namespace : RenderedMessage] = [:]
|
||||||
var messages: [Message]
|
var messages: [RenderedMessage]
|
||||||
|
|
||||||
public init(namespaces: [MessageId.Namespace], count: Int, earlier: [MessageId.Namespace : Message], messages: [Message], later: [MessageId.Namespace : Message]) {
|
public init(namespaces: [MessageId.Namespace], count: Int, earlier: [MessageId.Namespace : RenderedMessage], messages: [RenderedMessage], later: [MessageId.Namespace : RenderedMessage]) {
|
||||||
self.namespaces = namespaces
|
self.namespaces = namespaces
|
||||||
self.count = count
|
self.count = count
|
||||||
self.earlier = earlier
|
self.earlier = earlier
|
||||||
@ -31,56 +31,60 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
self.messages = messages
|
self.messages = messages
|
||||||
}
|
}
|
||||||
|
|
||||||
public func add(message: Message) {
|
public func add(message: RenderedMessage) -> Bool {
|
||||||
if self.messages.count == 0 {
|
if self.messages.count == 0 {
|
||||||
self.messages.append(message)
|
self.messages.append(message)
|
||||||
|
return true
|
||||||
} else {
|
} else {
|
||||||
let first = MessageIndex(self.messages[self.messages.count - 1])
|
let first = MessageIndex(self.messages[self.messages.count - 1].message)
|
||||||
let last = MessageIndex(self.messages[0])
|
let last = MessageIndex(self.messages[0].message)
|
||||||
|
|
||||||
var next: MessageIndex?
|
var next: MessageIndex?
|
||||||
for namespace in self.namespaces {
|
for namespace in self.namespaces {
|
||||||
if let message = later[namespace] {
|
if let message = later[namespace] {
|
||||||
let messageIndex = MessageIndex(message)
|
let messageIndex = MessageIndex(message.message)
|
||||||
if next == nil || messageIndex < next! {
|
if next == nil || messageIndex < next! {
|
||||||
next = messageIndex
|
next = messageIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = MessageIndex(message)
|
let index = MessageIndex(message.message)
|
||||||
|
|
||||||
if index < last {
|
if index < last {
|
||||||
let earlierMessage = self.earlier[message.id.namespace]
|
let earlierMessage = self.earlier[message.message.id.namespace]
|
||||||
if earlierMessage == nil || earlierMessage!.id.id < message.id.id {
|
if earlierMessage == nil || earlierMessage!.message.id.id < message.message.id.id {
|
||||||
if self.messages.count < self.count {
|
if self.messages.count < self.count {
|
||||||
self.messages.insert(message, atIndex: 0)
|
self.messages.insert(message, atIndex: 0)
|
||||||
} else {
|
} else {
|
||||||
self.earlier[message.id.namespace] = message
|
self.earlier[message.message.id.namespace] = message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
} else if index > first {
|
} else if index > first {
|
||||||
if next != nil && index > next! {
|
if next != nil && index > next! {
|
||||||
let laterMessage = self.later[message.id.namespace]
|
let laterMessage = self.later[message.message.id.namespace]
|
||||||
if laterMessage == nil || laterMessage!.id.id > message.id.id {
|
if laterMessage == nil || laterMessage!.message.id.id > message.message.id.id {
|
||||||
if self.messages.count < self.count {
|
if self.messages.count < self.count {
|
||||||
self.messages.append(message)
|
self.messages.append(message)
|
||||||
} else {
|
} else {
|
||||||
self.later[message.id.namespace] = message
|
self.later[message.message.id.namespace] = message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.messages.append(message)
|
self.messages.append(message)
|
||||||
if self.messages.count > self.count {
|
if self.messages.count > self.count {
|
||||||
let earliest = self.messages[0]
|
let earliest = self.messages[0]
|
||||||
self.earlier[earliest.id.namespace] = earliest
|
self.earlier[earliest.message.id.namespace] = earliest
|
||||||
self.messages.removeAtIndex(0)
|
self.messages.removeAtIndex(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
} else if index != last && index != first {
|
} else if index != last && index != first {
|
||||||
var i = self.messages.count
|
var i = self.messages.count
|
||||||
while i >= 1 {
|
while i >= 1 {
|
||||||
if MessageIndex(self.messages[i - 1]) < index {
|
if MessageIndex(self.messages[i - 1].message) < index {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
@ -88,9 +92,12 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
self.messages.insert(message, atIndex: i)
|
self.messages.insert(message, atIndex: i)
|
||||||
if self.messages.count > self.count {
|
if self.messages.count > self.count {
|
||||||
let earliest = self.messages[0]
|
let earliest = self.messages[0]
|
||||||
self.earlier[earliest.id.namespace] = earliest
|
self.earlier[earliest.message.id.namespace] = earliest
|
||||||
self.messages.removeAtIndex(0)
|
self.messages.removeAtIndex(0)
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,21 +109,21 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (_, message) in self.earlier {
|
for (_, message) in self.earlier {
|
||||||
if ids.contains(message.id) {
|
if ids.contains(message.message.id) {
|
||||||
updatedContext.invalidEarlier.insert(message.id.namespace)
|
updatedContext.invalidEarlier.insert(message.message.id.namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, message) in self.later {
|
for (_, message) in self.later {
|
||||||
if ids.contains(message.id) {
|
if ids.contains(message.message.id) {
|
||||||
updatedContext.invalidLater.insert(message.id.namespace)
|
updatedContext.invalidLater.insert(message.message.id.namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.messages.count != 0 {
|
if self.messages.count != 0 {
|
||||||
var i = self.messages.count - 1
|
var i = self.messages.count - 1
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
if ids.contains(self.messages[i].id) {
|
if ids.contains(self.messages[i].message.id) {
|
||||||
self.messages.removeAtIndex(i)
|
self.messages.removeAtIndex(i)
|
||||||
updatedContext.removedMessages = true
|
updatedContext.removedMessages = true
|
||||||
}
|
}
|
||||||
@ -127,19 +134,18 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
return updatedContext
|
return updatedContext
|
||||||
}
|
}
|
||||||
|
|
||||||
public func complete(context: RemoveContext, fetchEarlier: (MessageId.Namespace, MessageId.Id?, Int) -> [Message], fetchLater: (MessageId.Namespace, MessageId.Id?, Int) -> [Message]) {
|
public func complete(context: RemoveContext, fetchEarlier: (MessageId.Namespace, MessageId.Id?, Int) -> [RenderedMessage], fetchLater: (MessageId.Namespace, MessageId.Id?, Int) -> [RenderedMessage]) {
|
||||||
if context.removedMessages {
|
if context.removedMessages {
|
||||||
var addedMessages: [Message] = []
|
var addedMessages: [RenderedMessage] = []
|
||||||
|
|
||||||
var latestAnchor: MessageIndex?
|
var latestAnchor: MessageIndex?
|
||||||
if let lastMessage = self.messages.last {
|
if let lastMessage = self.messages.last {
|
||||||
latestAnchor = MessageIndex(lastMessage)
|
latestAnchor = MessageIndex(lastMessage.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if latestAnchor == nil {
|
if latestAnchor == nil {
|
||||||
var laterMessages: [Message] = []
|
|
||||||
for (_, message) in self.later {
|
for (_, message) in self.later {
|
||||||
let messageIndex = MessageIndex(message)
|
let messageIndex = MessageIndex(message.message)
|
||||||
if latestAnchor == nil || latestAnchor! > messageIndex {
|
if latestAnchor == nil || latestAnchor! > messageIndex {
|
||||||
latestAnchor = messageIndex
|
latestAnchor = messageIndex
|
||||||
}
|
}
|
||||||
@ -148,18 +154,18 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
|
|
||||||
for namespace in self.namespaces {
|
for namespace in self.namespaces {
|
||||||
if let later = self.later[namespace] {
|
if let later = self.later[namespace] {
|
||||||
addedMessages += fetchLater(namespace, later.id.id - 1, self.count)
|
addedMessages += fetchLater(namespace, later.message.id.id - 1, self.count)
|
||||||
}
|
}
|
||||||
if let earlier = self.earlier[namespace] {
|
if let earlier = self.earlier[namespace] {
|
||||||
addedMessages += fetchEarlier(namespace, earlier.id.id + 1, self.count)
|
addedMessages += fetchEarlier(namespace, earlier.message.id.id + 1, self.count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addedMessages += self.messages
|
addedMessages += self.messages
|
||||||
addedMessages.sortInPlace({ MessageIndex($0) < MessageIndex($1) })
|
addedMessages.sortInPlace({ MessageIndex($0.message) < MessageIndex($1.message) })
|
||||||
var i = addedMessages.count - 1
|
var i = addedMessages.count - 1
|
||||||
while i >= 1 {
|
while i >= 1 {
|
||||||
if addedMessages[i].id == addedMessages[i - 1].id {
|
if addedMessages[i].message.id == addedMessages[i - 1].message.id {
|
||||||
addedMessages.removeAtIndex(i)
|
addedMessages.removeAtIndex(i)
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
@ -170,7 +176,7 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
if let latestAnchor = latestAnchor {
|
if let latestAnchor = latestAnchor {
|
||||||
var i = addedMessages.count - 1
|
var i = addedMessages.count - 1
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
if MessageIndex(addedMessages[i]) <= latestAnchor {
|
if MessageIndex(addedMessages[i].message) <= latestAnchor {
|
||||||
anchorIndex = i
|
anchorIndex = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -183,7 +189,7 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
for namespace in self.namespaces {
|
for namespace in self.namespaces {
|
||||||
var i = anchorIndex + 1
|
var i = anchorIndex + 1
|
||||||
while i < addedMessages.count {
|
while i < addedMessages.count {
|
||||||
if addedMessages[i].id.namespace == namespace {
|
if addedMessages[i].message.id.namespace == namespace {
|
||||||
self.later[namespace] = addedMessages[i]
|
self.later[namespace] = addedMessages[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -203,7 +209,7 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
for namespace in self.namespaces {
|
for namespace in self.namespaces {
|
||||||
i = anchorIndex - self.count
|
i = anchorIndex - self.count
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
if addedMessages[i].id.namespace == namespace {
|
if addedMessages[i].message.id.namespace == namespace {
|
||||||
self.earlier[namespace] = addedMessages[i]
|
self.earlier[namespace] = addedMessages[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -217,8 +223,8 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
var earlyId: MessageId.Id?
|
var earlyId: MessageId.Id?
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < self.messages.count {
|
while i < self.messages.count {
|
||||||
if self.messages[i].id.namespace == namespace {
|
if self.messages[i].message.id.namespace == namespace {
|
||||||
earlyId = self.messages[i].id.id
|
earlyId = self.messages[i].message.id.id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
@ -236,8 +242,8 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
var lateId: MessageId.Id?
|
var lateId: MessageId.Id?
|
||||||
var i = self.messages.count - 1
|
var i = self.messages.count - 1
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
if self.messages[i].id.namespace == namespace {
|
if self.messages[i].message.id.namespace == namespace {
|
||||||
lateId = self.messages[i].id.id
|
lateId = self.messages[i].message.id.id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
@ -253,6 +259,56 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func incompleteMessages() -> [Message] {
|
||||||
|
var result: [Message] = []
|
||||||
|
|
||||||
|
for (_, message) in self.earlier {
|
||||||
|
if message.incomplete {
|
||||||
|
result.append(message.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (_, message) in self.later {
|
||||||
|
if message.incomplete {
|
||||||
|
result.append(message.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for message in self.messages {
|
||||||
|
if message.incomplete {
|
||||||
|
result.append(message.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func completeMessages(messages: [MessageId : RenderedMessage]) {
|
||||||
|
var earlier = self.earlier
|
||||||
|
for (namespace, message) in self.earlier {
|
||||||
|
if let message = messages[message.message.id] {
|
||||||
|
earlier[namespace] = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.earlier = earlier
|
||||||
|
|
||||||
|
var later = self.later
|
||||||
|
for (namespace, message) in self.later {
|
||||||
|
if let message = messages[message.message.id] {
|
||||||
|
later[namespace] = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.later = later
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while i < self.messages.count {
|
||||||
|
if let message = messages[self.messages[i].message.id] {
|
||||||
|
self.messages[i] = message
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
var string = ""
|
var string = ""
|
||||||
string += "...("
|
string += "...("
|
||||||
@ -264,7 +320,7 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
} else {
|
} else {
|
||||||
string += ", "
|
string += ", "
|
||||||
}
|
}
|
||||||
string += "\(namespace): \(value.id.id)—\(value.timestamp)"
|
string += "\(namespace): \(value.message.id.id)—\(value.message.timestamp)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string += ") —— "
|
string += ") —— "
|
||||||
@ -277,7 +333,7 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
} else {
|
} else {
|
||||||
string += ", "
|
string += ", "
|
||||||
}
|
}
|
||||||
string += "\(message.id.namespace): \(message.id.id)—\(message.timestamp)"
|
string += "\(message.message.id.namespace): \(message.message.id.id)—\(message.message.timestamp)"
|
||||||
}
|
}
|
||||||
string += "]"
|
string += "]"
|
||||||
|
|
||||||
@ -290,7 +346,7 @@ public final class MutableMessageView: CustomStringConvertible {
|
|||||||
} else {
|
} else {
|
||||||
string += ", "
|
string += ", "
|
||||||
}
|
}
|
||||||
string += "\(namespace): \(value.id.id)—\(value.timestamp)"
|
string += "\(namespace): \(value.message.id.id)—\(value.message.timestamp)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string += ")..."
|
string += ")..."
|
||||||
@ -304,7 +360,7 @@ public final class MessageView: CustomStringConvertible {
|
|||||||
private let earlierIds: [MessageIndex]
|
private let earlierIds: [MessageIndex]
|
||||||
public let hasLater: Bool
|
public let hasLater: Bool
|
||||||
private let laterIds: [MessageIndex]
|
private let laterIds: [MessageIndex]
|
||||||
public let messages: [Message]
|
public let messages: [RenderedMessage]
|
||||||
|
|
||||||
init(_ mutableView: MutableMessageView) {
|
init(_ mutableView: MutableMessageView) {
|
||||||
self.hasEarlier = mutableView.earlier.count != 0
|
self.hasEarlier = mutableView.earlier.count != 0
|
||||||
@ -313,13 +369,13 @@ public final class MessageView: CustomStringConvertible {
|
|||||||
|
|
||||||
var earlierIds: [MessageIndex] = []
|
var earlierIds: [MessageIndex] = []
|
||||||
for (_, message) in mutableView.earlier {
|
for (_, message) in mutableView.earlier {
|
||||||
earlierIds.append(MessageIndex(message))
|
earlierIds.append(MessageIndex(message.message))
|
||||||
}
|
}
|
||||||
self.earlierIds = earlierIds
|
self.earlierIds = earlierIds
|
||||||
|
|
||||||
var laterIds: [MessageIndex] = []
|
var laterIds: [MessageIndex] = []
|
||||||
for (_, message) in mutableView.later {
|
for (_, message) in mutableView.later {
|
||||||
laterIds.append(MessageIndex(message))
|
laterIds.append(MessageIndex(message.message))
|
||||||
}
|
}
|
||||||
self.laterIds = laterIds
|
self.laterIds = laterIds
|
||||||
}
|
}
|
||||||
@ -347,7 +403,7 @@ public final class MessageView: CustomStringConvertible {
|
|||||||
} else {
|
} else {
|
||||||
string += ", "
|
string += ", "
|
||||||
}
|
}
|
||||||
string += "\(message.id.namespace): \(message.id.id)—\(message.timestamp)"
|
string += "\(message.message.id.namespace): \(message.message.id.id)—\(message.message.timestamp)"
|
||||||
}
|
}
|
||||||
string += "]"
|
string += "]"
|
||||||
if self.hasLater {
|
if self.hasLater {
|
||||||
|
@ -21,6 +21,30 @@ public struct PeerId: Hashable, CustomStringConvertible, Comparable {
|
|||||||
return (Int64(self.namespace) << 32) | Int64(self.id)
|
return (Int64(self.namespace) << 32) | Int64(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] = []
|
||||||
|
while i < Int(length) {
|
||||||
|
var value: Int64 = 0
|
||||||
|
buffer.read(&value, offset: 0, length: 8)
|
||||||
|
array.append(PeerId(value))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
public var hashValue: Int {
|
public var hashValue: Int {
|
||||||
get {
|
get {
|
||||||
return Int(self.id)
|
return Int(self.id)
|
||||||
@ -67,4 +91,6 @@ public func <(lhs: PeerId, rhs: PeerId) -> Bool {
|
|||||||
|
|
||||||
public protocol Peer: Coding {
|
public protocol Peer: Coding {
|
||||||
var id: PeerId { get }
|
var id: PeerId { get }
|
||||||
|
|
||||||
|
func equalsTo(other: Peer) -> Bool
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class PeerViewEntry {
|
public final class PeerViewEntry {
|
||||||
public let peerId: PeerId
|
public let peerId: PeerId
|
||||||
public let peer: Peer?
|
public let peer: Peer?
|
||||||
public let message: Message
|
public let message: RenderedMessage
|
||||||
|
|
||||||
public init(peer: Peer, message: Message) {
|
public init(peer: Peer, message: RenderedMessage) {
|
||||||
self.peerId = peer.id
|
self.peerId = peer.id
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.message = message
|
self.message = message
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(peerId: PeerId, message: Message) {
|
public init(peerId: PeerId, message: RenderedMessage) {
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.peer = nil
|
self.peer = nil
|
||||||
self.message = message
|
self.message = message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private init(peer: Peer?, peerId: PeerId, message: RenderedMessage) {
|
||||||
|
self.peer = peer
|
||||||
|
self.peerId = peerId
|
||||||
|
self.message = message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PeerViewEntryIndex: Equatable, Comparable {
|
public struct PeerViewEntryIndex: Equatable, Comparable {
|
||||||
@ -24,7 +30,7 @@ public struct PeerViewEntryIndex: Equatable, Comparable {
|
|||||||
|
|
||||||
public init(_ entry: PeerViewEntry) {
|
public init(_ entry: PeerViewEntry) {
|
||||||
self.peerId = entry.peerId
|
self.peerId = entry.peerId
|
||||||
self.messageIndex = MessageIndex(entry.message)
|
self.messageIndex = MessageIndex(entry.message.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(peerId: PeerId, messageIndex: MessageIndex) {
|
public init(peerId: PeerId, messageIndex: MessageIndex) {
|
||||||
@ -252,12 +258,85 @@ public final class MutablePeerView: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updatePeers(peers: [PeerId : Peer]) -> Bool {
|
||||||
|
var updated = false
|
||||||
|
|
||||||
|
if let earlier = self.earlier {
|
||||||
|
if let peer = peers[earlier.peerId] {
|
||||||
|
self.earlier = PeerViewEntry(peer: peer, message: earlier.message)
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let later = self.later {
|
||||||
|
if let peer = peers[later.peerId] {
|
||||||
|
self.later = PeerViewEntry(peer: peer, message: later.message)
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while i < self.entries.count {
|
||||||
|
if let peer = peers[self.entries[i].peerId] {
|
||||||
|
self.entries[i] = PeerViewEntry(peer: peer, message: self.entries[i].message)
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
|
public func incompleteMessages() -> [Message] {
|
||||||
|
var result: [Message] = []
|
||||||
|
|
||||||
|
if let earlier = self.earlier {
|
||||||
|
if earlier.message.incomplete {
|
||||||
|
result.append(earlier.message.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let later = self.later {
|
||||||
|
if later.message.incomplete {
|
||||||
|
result.append(later.message.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in self.entries {
|
||||||
|
if entry.message.incomplete {
|
||||||
|
result.append(entry.message.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func completeMessages(messages: [MessageId : RenderedMessage]) {
|
||||||
|
if let earlier = self.earlier {
|
||||||
|
if let message = messages[earlier.message.message.id] {
|
||||||
|
self.earlier = PeerViewEntry(peer: earlier.peer, peerId: earlier.peerId, message: message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let later = self.later {
|
||||||
|
if let message = messages[later.message.message.id] {
|
||||||
|
self.later = PeerViewEntry(peer: later.peer, peerId: later.peerId, message: message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while i < self.entries.count {
|
||||||
|
if let message = messages[self.entries[i].message.message.id] {
|
||||||
|
self.entries[i] = PeerViewEntry(peer: self.entries[i].peer, peerId: self.entries[i].peerId, message: message)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
var string = ""
|
var string = ""
|
||||||
|
|
||||||
if let earlier = self.earlier {
|
if let earlier = self.earlier {
|
||||||
string += "more("
|
string += "more("
|
||||||
string += "(p \(earlier.peerId.namespace):\(earlier.peerId.id), m \(earlier.message.id.namespace):\(earlier.message.id.id)—\(earlier.message.timestamp)"
|
string += "(p \(earlier.peerId.namespace):\(earlier.peerId.id), m \(earlier.message.message.id.namespace):\(earlier.message.message.id.id)—\(earlier.message.message.timestamp)"
|
||||||
string += ") "
|
string += ") "
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,13 +348,13 @@ public final class MutablePeerView: CustomStringConvertible {
|
|||||||
} else {
|
} else {
|
||||||
string += ", "
|
string += ", "
|
||||||
}
|
}
|
||||||
string += "(p \(entry.peerId.namespace):\(entry.peerId.id), m \(entry.message.id.namespace):\(entry.message.id.id)—\(entry.message.timestamp))"
|
string += "(p \(entry.peerId.namespace):\(entry.peerId.id), m \(entry.message.message.id.namespace):\(entry.message.message.id.id)—\(entry.message.message.timestamp))"
|
||||||
}
|
}
|
||||||
string += "]"
|
string += "]"
|
||||||
|
|
||||||
if let later = self.later {
|
if let later = self.later {
|
||||||
string += " more("
|
string += " more("
|
||||||
string += "(p \(later.peerId.namespace):\(later.peerId), m \(later.message.id.namespace):\(later.message.id.id)—\(later.message.timestamp)"
|
string += "(p \(later.peerId.namespace):\(later.peerId), m \(later.message.message.id.namespace):\(later.message.message.id.id)—\(later.message.message.timestamp)"
|
||||||
string += ")"
|
string += ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +400,7 @@ public final class PeerView: CustomStringConvertible {
|
|||||||
} else {
|
} else {
|
||||||
string += ", "
|
string += ", "
|
||||||
}
|
}
|
||||||
string += "(p \(entry.peerId.namespace):\(entry.peerId.id), m \(entry.message.id.namespace):\(entry.message.id.id)—\(entry.message.timestamp))"
|
string += "(p \(entry.peerId.namespace):\(entry.peerId.id), m \(entry.message.message.id.namespace):\(entry.message.message.id.id)—\(entry.message.message.timestamp))"
|
||||||
}
|
}
|
||||||
string += "]"
|
string += "]"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ public protocol PostboxState: Coding {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Modifier<State: PostboxState> {
|
public final class Modifier<State: PostboxState> {
|
||||||
private weak var postbox: Postbox<State>?
|
private weak var postbox: Postbox<State>?
|
||||||
|
|
||||||
private init(postbox: Postbox<State>) {
|
private init(postbox: Postbox<State>) {
|
||||||
@ -28,6 +28,10 @@ public class Modifier<State: PostboxState> {
|
|||||||
public func setState(state: State) {
|
public func setState(state: State) {
|
||||||
self.postbox?.setState(state)
|
self.postbox?.setState(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updatePeers(peers: [Peer], update: (Peer, Peer) -> Peer) {
|
||||||
|
self.postbox?.updatePeers(peers, update: update)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class Postbox<State: PostboxState> {
|
public final class Postbox<State: PostboxState> {
|
||||||
@ -38,7 +42,10 @@ public final class Postbox<State: PostboxState> {
|
|||||||
private var database: Database!
|
private var database: Database!
|
||||||
|
|
||||||
private var peerMessageViews: [PeerId : Bag<(MutableMessageView, Pipe<MessageView>)>] = [:]
|
private var peerMessageViews: [PeerId : Bag<(MutableMessageView, Pipe<MessageView>)>] = [:]
|
||||||
|
private var deferredMessageViewsToUpdate: [(MutableMessageView, Pipe<MessageView>)] = []
|
||||||
private var peerViews: Bag<(MutablePeerView, Pipe<PeerView>)> = Bag()
|
private var peerViews: Bag<(MutablePeerView, Pipe<PeerView>)> = Bag()
|
||||||
|
private var deferredPeerViewsToUpdate: [(MutablePeerView, Pipe<PeerView>)] = []
|
||||||
|
|
||||||
private var statePipe: Pipe<State> = Pipe()
|
private var statePipe: Pipe<State> = Pipe()
|
||||||
|
|
||||||
public init(basePath: String, messageNamespaces: [MessageId.Namespace]) {
|
public init(basePath: String, messageNamespaces: [MessageId.Namespace]) {
|
||||||
@ -48,21 +55,45 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func openDatabase() {
|
private func openDatabase() {
|
||||||
do {
|
|
||||||
try NSFileManager.defaultManager().createDirectoryAtPath(basePath, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
} catch _ {
|
|
||||||
}
|
|
||||||
self.database = Database(basePath.stringByAppendingPathComponent("db"))
|
|
||||||
|
|
||||||
self.queue.dispatch {
|
self.queue.dispatch {
|
||||||
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
|
do {
|
||||||
|
try NSFileManager.defaultManager().createDirectoryAtPath(self.basePath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch _ {
|
||||||
|
}
|
||||||
|
self.database = Database(self.basePath.stringByAppendingPathComponent("db"))
|
||||||
|
|
||||||
let result = self.database.scalar("PRAGMA user_version") as! Int64
|
let result = self.database.scalar("PRAGMA user_version") as! Int64
|
||||||
if result == 1 {
|
let version: Int64 = 7
|
||||||
|
if result == version {
|
||||||
print("(Postbox schema version \(result))")
|
print("(Postbox schema version \(result))")
|
||||||
} else {
|
} else {
|
||||||
|
if result != 0 {
|
||||||
|
print("(Postbox migrating to version \(version))")
|
||||||
|
do {
|
||||||
|
try NSFileManager.defaultManager().removeItemAtPath(self.basePath)
|
||||||
|
try NSFileManager.defaultManager().createDirectoryAtPath(self.basePath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch (_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
self.database = Database(self.basePath.stringByAppendingPathComponent("db"))
|
||||||
|
}
|
||||||
print("(Postbox creating schema)")
|
print("(Postbox creating schema)")
|
||||||
self.createSchema()
|
self.createSchema()
|
||||||
self.database.execute("PRAGMA user_version = 1")
|
self.database.execute("PRAGMA user_version = \(version)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.database.adjustChunkSize()
|
||||||
|
self.database.execute("PRAGMA page_size=1024")
|
||||||
|
self.database.execute("PRAGMA cache_size=-2097152")
|
||||||
|
self.database.execute("PRAGMA synchronous=NORMAL")
|
||||||
|
self.database.execute("PRAGMA journal_mode=truncate")
|
||||||
|
self.database.execute("PRAGMA temp_store=MEMORY")
|
||||||
|
//self.database.execute("PRAGMA wal_autocheckpoint=32")
|
||||||
|
//self.database.execute("PRAGMA journal_size_limit=1536")
|
||||||
|
|
||||||
|
print("(Postbox initialization took \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +123,7 @@ public final class Postbox<State: PostboxState> {
|
|||||||
self.database.execute("CREATE INDEX peer_entries_entry on peer_entries (entry)")
|
self.database.execute("CREATE INDEX peer_entries_entry on peer_entries (entry)")
|
||||||
|
|
||||||
//peers
|
//peers
|
||||||
self.database.execute("CREATE TABLE peers (peerId INTEGER PRIMARY KEY, data BLOB)")
|
self.database.execute("CREATE TABLE peers (id INTEGER PRIMARY KEY, data BLOB)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private class func peerViewEntryIndexForBlob(blob: Blob) -> PeerViewEntryIndex {
|
private class func peerViewEntryIndexForBlob(blob: Blob) -> PeerViewEntryIndex {
|
||||||
@ -197,14 +228,23 @@ public final class Postbox<State: PostboxState> {
|
|||||||
return grouped
|
return grouped
|
||||||
}
|
}
|
||||||
|
|
||||||
private class func messagesGroupedByPeerId(messages: [Message]) -> [PeerId : [Message]] {
|
private class func messagesGroupedByPeerId(messages: [Message]) -> [(PeerId, [Message])] {
|
||||||
var grouped: [PeerId : [Message]] = [:]
|
var grouped: [(PeerId, [Message])] = []
|
||||||
|
|
||||||
for message in messages {
|
for message in messages {
|
||||||
if grouped[message.id.peerId] != nil {
|
var i = 0
|
||||||
grouped[message.id.peerId]!.append(message)
|
let count = grouped.count
|
||||||
} else {
|
var found = false
|
||||||
grouped[message.id.peerId] = [message]
|
while i < count {
|
||||||
|
if grouped[i].0 == message.id.peerId {
|
||||||
|
grouped[i].1.append(message)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
grouped.append((message.id.peerId, [message]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,8 +365,12 @@ public final class Postbox<State: PostboxState> {
|
|||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var cachedState: State?
|
||||||
|
|
||||||
private func setState(state: State) {
|
private func setState(state: State) {
|
||||||
self.queue.dispatch {
|
self.queue.dispatch {
|
||||||
|
self.cachedState = state
|
||||||
|
|
||||||
let encoder = Encoder()
|
let encoder = Encoder()
|
||||||
encoder.encodeRootObject(state)
|
encoder.encodeRootObject(state)
|
||||||
let blob = Blob(data: encoder.makeData())
|
let blob = Blob(data: encoder.makeData())
|
||||||
@ -337,16 +381,21 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func getState() -> State? {
|
private func getState() -> State? {
|
||||||
for row in self.database.prepareCached("SELECT data FROM state WHERE id = ?").run(Int64(0)) {
|
if let cachedState = self.cachedState {
|
||||||
let data = (row[0] as! Blob).data
|
return cachedState
|
||||||
let buffer = ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false)
|
} else {
|
||||||
let decoder = Decoder(buffer: buffer)
|
for row in self.database.prepareCached("SELECT data FROM state WHERE id = ?").run(Int64(0)) {
|
||||||
if let state = decoder.decodeRootObject() as? State {
|
let data = (row[0] as! Blob).data
|
||||||
return state
|
let buffer = ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false)
|
||||||
|
let decoder = Decoder(buffer: buffer)
|
||||||
|
if let state = decoder.decodeRootObject() as? State {
|
||||||
|
self.cachedState = state
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func state() -> Signal<State?, NoError> {
|
public func state() -> Signal<State?, NoError> {
|
||||||
@ -394,11 +443,11 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func addMessages(messages: [Message], medias: [Media]) {
|
private func addMessages(messages: [Message], medias: [Media]) {
|
||||||
let messageInsertStatement = self.database.prepare("INSERT INTO peer_messages (peerId, namespace, id, data, associatedMediaIds, timestamp) VALUES (?, ?, ?, ?, ?, ?)")
|
let messageInsertStatement = self.database.prepareCached("INSERT INTO peer_messages (peerId, namespace, id, data, associatedMediaIds, timestamp) VALUES (?, ?, ?, ?, ?, ?)")
|
||||||
let peerMediaInsertStatement = self.database.prepare("INSERT INTO peer_media (peerId, mediaNamespace, messageNamespace, messageId) VALUES (?, ?, ?, ?)")
|
let peerMediaInsertStatement = self.database.prepareCached("INSERT INTO peer_media (peerId, mediaNamespace, messageNamespace, messageId) VALUES (?, ?, ?, ?)")
|
||||||
let mediaInsertStatement = self.database.prepare("INSERT INTO media (namespace, id, data, associatedMessageIds) VALUES (?, ?, ?, ?)")
|
let mediaInsertStatement = self.database.prepareCached("INSERT INTO media (namespace, id, data, associatedMessageIds) VALUES (?, ?, ?, ?)")
|
||||||
let referencedMessageIdsStatement = self.database.prepare("SELECT associatedMessageIds FROM media WHERE namespace = ? AND id = ?")
|
let referencedMessageIdsStatement = self.database.prepareCached("SELECT associatedMessageIds FROM media WHERE namespace = ? AND id = ?")
|
||||||
let updateReferencedMessageIdsStatement = self.database.prepare("UPDATE media SET associatedMessageIds = ? WHERE namespace = ? AND id = ?")
|
let updateReferencedMessageIdsStatement = self.database.prepareCached("UPDATE media SET associatedMessageIds = ? WHERE namespace = ? AND id = ?")
|
||||||
|
|
||||||
let encoder = Encoder()
|
let encoder = Encoder()
|
||||||
|
|
||||||
@ -437,18 +486,12 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let relatedViews = self.peerMessageViews[peerId] ?? Bag()
|
|
||||||
|
|
||||||
for message in peerMessages {
|
for message in peerMessages {
|
||||||
if existingMessageIds.contains(message.id) {
|
if existingMessageIds.contains(message.id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
existingMessageIds.insert(message.id)
|
existingMessageIds.insert(message.id)
|
||||||
|
|
||||||
for record in relatedViews.copyItems() {
|
|
||||||
record.0.add(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = MessageIndex(message)
|
let index = MessageIndex(message)
|
||||||
if maxMessage == nil || index > maxMessage!.0 {
|
if maxMessage == nil || index > maxMessage!.0 {
|
||||||
maxMessage = (index, message)
|
maxMessage = (index, message)
|
||||||
@ -458,8 +501,8 @@ public final class Postbox<State: PostboxState> {
|
|||||||
encoder.encodeRootObject(message)
|
encoder.encodeRootObject(message)
|
||||||
let messageBlob = Blob(data: encoder.makeData())
|
let messageBlob = Blob(data: encoder.makeData())
|
||||||
|
|
||||||
let referencedMediaIdsMediaIdsBlob = Postbox.blobForMediaIds(message.referencedMediaIds)
|
let referencedMediaIdsMediaIdsBlob = Postbox.blobForMediaIds(message.mediaIds)
|
||||||
for id in message.referencedMediaIds {
|
for id in message.mediaIds {
|
||||||
if messageIdsByMediaId[id] != nil {
|
if messageIdsByMediaId[id] != nil {
|
||||||
messageIdsByMediaId[id]!.append(message.id)
|
messageIdsByMediaId[id]!.append(message.id)
|
||||||
} else {
|
} else {
|
||||||
@ -469,17 +512,28 @@ public final class Postbox<State: PostboxState> {
|
|||||||
|
|
||||||
messageInsertStatement.run(peerId.toInt64(), Int64(message.id.namespace), Int64(message.id.id), messageBlob, referencedMediaIdsMediaIdsBlob, Int64(message.timestamp))
|
messageInsertStatement.run(peerId.toInt64(), Int64(message.id.namespace), Int64(message.id.id), messageBlob, referencedMediaIdsMediaIdsBlob, Int64(message.timestamp))
|
||||||
|
|
||||||
for id in message.referencedMediaIds {
|
for id in message.mediaIds {
|
||||||
peerMediaInsertStatement.run(peerId.toInt64(), Int64(id.namespace), Int64(message.id.namespace), Int64(message.id.id))
|
peerMediaInsertStatement.run(peerId.toInt64(), Int64(id.namespace), Int64(message.id.namespace), Int64(message.id.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for record in relatedViews.copyItems() {
|
if let relatedViews = self.peerMessageViews[peerId] {
|
||||||
record.1.putNext(MessageView(record.0))
|
for record in relatedViews.copyItems() {
|
||||||
|
var updated = false
|
||||||
|
for message in peerMessages {
|
||||||
|
if record.0.add(RenderedMessage(message: message)) {
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if updated {
|
||||||
|
self.deferMessageViewUpdate(record.0, pipe: record.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let maxMessage = maxMessage {
|
if let maxMessage = maxMessage {
|
||||||
self.updatePeerEntry(peerId, message: maxMessage.1)
|
self.updatePeerEntry(peerId, message: RenderedMessage(message: maxMessage.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,56 +639,197 @@ public final class Postbox<State: PostboxState> {
|
|||||||
return grouped
|
return grouped
|
||||||
}
|
}
|
||||||
|
|
||||||
private func peerWithId(peerId: PeerId) -> Peer? {
|
private func mediaWithIds(ids: [MediaId]) -> [MediaId : Media] {
|
||||||
for row in self.database.prepare("SELECT data FROM peers WHERE peerId = ?").run(peerId.toInt64()) {
|
if ids.count == 0 {
|
||||||
let data = (row[0] as! Blob).data
|
return [:]
|
||||||
let decoder = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false))
|
} else {
|
||||||
if let peer = decoder.decodeRootObject() as? Peer {
|
let select = self.database.prepareCached("SELECT data FROM media WHERE namespace = ? AND id = ?")
|
||||||
return peer
|
var result: [MediaId : Media] = [:]
|
||||||
} else {
|
|
||||||
print("(PostBox: can't decode peer)")
|
for id in ids {
|
||||||
|
for row in select.run(Int64(id.namespace), id.id) {
|
||||||
|
let blob = row[0] as! Blob
|
||||||
|
if let media = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer<Void>(blob.data.bytes), length: blob.data.length, freeWhenDone: false)) as? Media {
|
||||||
|
result[media.id] = media
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cachedPeers: [PeerId : Peer] = [:]
|
||||||
|
|
||||||
private func updatePeerEntry(peerId: PeerId, message: Message?, replace: Bool = false) {
|
private func peerWithId(peerId: PeerId) -> Peer? {
|
||||||
|
if let cachedPeer = cachedPeers[peerId] {
|
||||||
|
return cachedPeer
|
||||||
|
} else {
|
||||||
|
for row in self.database.prepareCached("SELECT data FROM peers WHERE id = ?").run(peerId.toInt64()) {
|
||||||
|
let data = (row[0] as! Blob).data
|
||||||
|
let decoder = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false))
|
||||||
|
if let peer = decoder.decodeRootObject() as? Peer {
|
||||||
|
cachedPeers[peer.id] = peer
|
||||||
|
return peer
|
||||||
|
} else {
|
||||||
|
print("(PostBox: can't decode peer)")
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func peersWithIds(ids: [PeerId]) -> [PeerId : Peer] {
|
||||||
|
if ids.count == 0 {
|
||||||
|
return [:]
|
||||||
|
} else {
|
||||||
|
var remainingIds: [PeerId] = []
|
||||||
|
|
||||||
|
var peers: [PeerId : Peer] = [:]
|
||||||
|
|
||||||
|
for id in ids {
|
||||||
|
if let cachedPeer = cachedPeers[id] {
|
||||||
|
peers[id] = cachedPeer
|
||||||
|
} else {
|
||||||
|
remainingIds.append(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if remainingIds.count != 0 {
|
||||||
|
let rows: Statement
|
||||||
|
if ids.count == 1 {
|
||||||
|
rows = self.database.prepareCached("SELECT data FROM peers WHERE id = ?").run(ids[0].toInt64())
|
||||||
|
} else if ids.count == 2 {
|
||||||
|
rows = self.database.prepareCached("SELECT data FROM peers WHERE id IN (?, ?)").run(ids[0].toInt64(), ids[1].toInt64())
|
||||||
|
} else {
|
||||||
|
var query = "SELECT data FROM peers WHERE id IN ("
|
||||||
|
var first = true
|
||||||
|
for id in ids {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
query += "\(id.toInt64())"
|
||||||
|
} else {
|
||||||
|
query += ",\(id.toInt64())"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query += ")"
|
||||||
|
rows = self.database.prepare(query).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in rows {
|
||||||
|
let blob = row[0] as! Blob
|
||||||
|
if let peer = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer<Void>(blob.data.bytes), length: blob.data.length, freeWhenDone: false)).decodeRootObject() as? Peer {
|
||||||
|
self.cachedPeers[peer.id] = peer
|
||||||
|
peers[peer.id] = peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deferPeerViewUpdate(view: MutablePeerView, pipe: Pipe<PeerView>) {
|
||||||
|
var i = 0
|
||||||
|
var found = false
|
||||||
|
while i < self.deferredPeerViewsToUpdate.count {
|
||||||
|
if self.deferredPeerViewsToUpdate[i].1 === pipe {
|
||||||
|
self.deferredPeerViewsToUpdate[i] = (view, pipe)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
self.deferredPeerViewsToUpdate.append((view, pipe))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deferMessageViewUpdate(view: MutableMessageView, pipe: Pipe<MessageView>) {
|
||||||
|
var i = 0
|
||||||
|
var found = false
|
||||||
|
while i < self.deferredPeerViewsToUpdate.count {
|
||||||
|
if self.deferredMessageViewsToUpdate[i].1 === pipe {
|
||||||
|
self.deferredMessageViewsToUpdate[i] = (view, pipe)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
self.deferredMessageViewsToUpdate.append((view, pipe))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func performDeferredUpdates() {
|
||||||
|
let deferredPeerViewsToUpdate = self.deferredPeerViewsToUpdate
|
||||||
|
self.deferredPeerViewsToUpdate.removeAll()
|
||||||
|
|
||||||
|
for entry in deferredPeerViewsToUpdate {
|
||||||
|
let viewRenderedMessages = self.renderedMessages(entry.0.incompleteMessages())
|
||||||
|
if viewRenderedMessages.count != 0 {
|
||||||
|
var viewRenderedMessagesDict: [MessageId : RenderedMessage] = [:]
|
||||||
|
for message in viewRenderedMessages {
|
||||||
|
viewRenderedMessagesDict[message.message.id] = message
|
||||||
|
}
|
||||||
|
entry.0.completeMessages(viewRenderedMessagesDict)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.1.putNext(PeerView(entry.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
let deferredMessageViewsToUpdate = self.deferredMessageViewsToUpdate
|
||||||
|
self.deferredMessageViewsToUpdate.removeAll()
|
||||||
|
|
||||||
|
for entry in deferredMessageViewsToUpdate {
|
||||||
|
let viewRenderedMessages = self.renderedMessages(entry.0.incompleteMessages())
|
||||||
|
if viewRenderedMessages.count != 0 {
|
||||||
|
var viewRenderedMessagesDict: [MessageId : RenderedMessage] = [:]
|
||||||
|
for message in viewRenderedMessages {
|
||||||
|
viewRenderedMessagesDict[message.message.id] = message
|
||||||
|
}
|
||||||
|
entry.0.completeMessages(viewRenderedMessagesDict)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.1.putNext(MessageView(entry.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updatePeerEntry(peerId: PeerId, message: RenderedMessage?, replace: Bool = false) {
|
||||||
var currentIndex: PeerViewEntryIndex?
|
var currentIndex: PeerViewEntryIndex?
|
||||||
for row in self.database.prepare("SELECT entry FROM peer_entries WHERE peerId = ?").run(peerId.toInt64()) {
|
for row in self.database.prepareCached("SELECT entry FROM peer_entries WHERE peerId = ?").run(peerId.toInt64()) {
|
||||||
currentIndex = Postbox.peerViewEntryIndexForBlob(row[0] as! Blob)
|
currentIndex = Postbox.peerViewEntryIndexForBlob(row[0] as! Blob)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedPeerMessage: Message?
|
var updatedPeerMessage: RenderedMessage?
|
||||||
|
|
||||||
if let currentIndex = currentIndex {
|
if let currentIndex = currentIndex {
|
||||||
if let message = message {
|
if let message = message {
|
||||||
let messageIndex = MessageIndex(message)
|
let messageIndex = MessageIndex(message.message)
|
||||||
if replace || currentIndex.messageIndex < messageIndex {
|
if replace || currentIndex.messageIndex < messageIndex {
|
||||||
let updatedIndex = PeerViewEntryIndex(peerId: peerId, messageIndex: messageIndex)
|
let updatedIndex = PeerViewEntryIndex(peerId: peerId, messageIndex: messageIndex)
|
||||||
updatedPeerMessage = message
|
updatedPeerMessage = message
|
||||||
let updatedBlob = Postbox.blobForPeerViewEntryIndex(updatedIndex)
|
let updatedBlob = Postbox.blobForPeerViewEntryIndex(updatedIndex)
|
||||||
self.database.prepare("UPDATE peer_entries SET entry = ? WHERE peerId = ?").run(updatedBlob, peerId.toInt64())
|
self.database.prepareCached("UPDATE peer_entries SET entry = ? WHERE peerId = ?").run(updatedBlob, peerId.toInt64())
|
||||||
}
|
}
|
||||||
} else if replace {
|
} else if replace {
|
||||||
//TODO: remove?
|
//TODO: remove?
|
||||||
}
|
}
|
||||||
} else if let message = message {
|
} else if let message = message {
|
||||||
updatedPeerMessage = message
|
updatedPeerMessage = message
|
||||||
let updatedIndex = PeerViewEntryIndex(peerId: peerId, messageIndex: MessageIndex(message))
|
let updatedIndex = PeerViewEntryIndex(peerId: peerId, messageIndex: MessageIndex(message.message))
|
||||||
let updatedBlob = Postbox.blobForPeerViewEntryIndex(updatedIndex)
|
let updatedBlob = Postbox.blobForPeerViewEntryIndex(updatedIndex)
|
||||||
self.database.prepare("INSERT INTO peer_entries (peerId, entry) VALUES (?, ?)").run(peerId.toInt64(), updatedBlob)
|
self.database.prepareCached("INSERT INTO peer_entries (peerId, entry) VALUES (?, ?)").run(peerId.toInt64(), updatedBlob)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let updatedPeerMessage = updatedPeerMessage {
|
if let updatedPeerMessage = updatedPeerMessage {
|
||||||
var peer: Peer?
|
var peer: Peer?
|
||||||
for (view, sink) in self.peerViews.copyItems() {
|
for (view, pipe) in self.peerViews.copyItems() {
|
||||||
|
|
||||||
if peer == nil {
|
if peer == nil {
|
||||||
for entry in view.entries {
|
for entry in view.entries {
|
||||||
if entry.peerId == peerId {
|
if entry.peerId == peerId {
|
||||||
@ -659,7 +854,7 @@ public final class Postbox<State: PostboxState> {
|
|||||||
view.addEntry(entry)
|
view.addEntry(entry)
|
||||||
view.complete(context, fetchEarlier: self.fetchPeerEntriesRelative(true), fetchLater: self.fetchPeerEntriesRelative(false))
|
view.complete(context, fetchEarlier: self.fetchPeerEntriesRelative(true), fetchLater: self.fetchPeerEntriesRelative(false))
|
||||||
|
|
||||||
sink.putNext(PeerView(view))
|
self.deferPeerViewUpdate(view, pipe: pipe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -671,11 +866,11 @@ public final class Postbox<State: PostboxState> {
|
|||||||
|
|
||||||
for (peerId, messageIds) in Postbox.messageIdsGroupedByPeerId(ids) {
|
for (peerId, messageIds) in Postbox.messageIdsGroupedByPeerId(ids) {
|
||||||
if let relatedViews = self.peerMessageViews[peerId] {
|
if let relatedViews = self.peerMessageViews[peerId] {
|
||||||
for (view, sink) in relatedViews.copyItems() {
|
for (view, pipe) in relatedViews.copyItems() {
|
||||||
let context = view.remove(Set<MessageId>(messageIds))
|
let context = view.remove(Set<MessageId>(messageIds))
|
||||||
if !context.empty() {
|
if !context.empty() {
|
||||||
view.complete(context, fetchEarlier: self.fetchMessagesRelative(peerId, earlier: true), fetchLater: self.fetchMessagesRelative(peerId, earlier: false))
|
view.complete(context, fetchEarlier: self.fetchMessagesRelative(peerId, earlier: true), fetchLater: self.fetchMessagesRelative(peerId, earlier: false))
|
||||||
sink.putNext(MessageView(view))
|
self.deferMessageViewUpdate(view, pipe: pipe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -735,16 +930,16 @@ public final class Postbox<State: PostboxState> {
|
|||||||
updatedMessageIdsByMediaId[mediaId] = messageIds
|
updatedMessageIdsByMediaId[mediaId] = messageIds
|
||||||
|
|
||||||
if messageIds.count == 0 {
|
if messageIds.count == 0 {
|
||||||
self.database.prepare("INSERT OR IGNORE INTO media_cleanup (namespace, id, data) VALUES (?, ?, ?)").run(Int64(namespace), mediaId.id, row[1] as! Blob)
|
self.database.prepareCached("INSERT OR IGNORE INTO media_cleanup (namespace, id, data) VALUES (?, ?, ?)").run(Int64(namespace), mediaId.id, row[1] as! Blob)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (mediaId, messageIds) in updatedMessageIdsByMediaId {
|
for (mediaId, messageIds) in updatedMessageIdsByMediaId {
|
||||||
if messageIds.count == 0 {
|
if messageIds.count == 0 {
|
||||||
self.database.prepare("DELETE FROM media WHERE namespace = ? AND id = ?").run(Int64(mediaId.namespace), mediaId.id)
|
self.database.prepareCached("DELETE FROM media WHERE namespace = ? AND id = ?").run(Int64(mediaId.namespace), mediaId.id)
|
||||||
} else {
|
} else {
|
||||||
self.database.prepare("UPDATE media SET associatedMessageIds = ? WHERE namespace = ? AND id = ?").run(Postbox.blobForMessageIds(messageIds), Int64(mediaId.namespace), mediaId.id)
|
self.database.prepareCached("UPDATE media SET associatedMessageIds = ? WHERE namespace = ? AND id = ?").run(Postbox.blobForMessageIds(messageIds), Int64(mediaId.namespace), mediaId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,13 +950,79 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updatePeers(peers: [Peer], update: (Peer, Peer) -> Peer) {
|
||||||
|
if peers.count == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if peers.count == -1 {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var peerIds: [PeerId] = []
|
||||||
|
for peer in peers {
|
||||||
|
peerIds.append(peer.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPeers = self.peersWithIds(peerIds)
|
||||||
|
|
||||||
|
let updatePeer = self.database.prepareCached("UPDATE peers SET data = ? WHERE id = ?")
|
||||||
|
let insertPeer = self.database.prepareCached("INSERT INTO peers (id, data) VALUES (?, ?)")
|
||||||
|
let encoder = Encoder()
|
||||||
|
|
||||||
|
var updatedPeers: [PeerId : Peer] = [:]
|
||||||
|
|
||||||
|
for updatedPeer in peers {
|
||||||
|
let currentPeer = currentPeers[updatedPeer.id]
|
||||||
|
|
||||||
|
var finalPeer = updatedPeer
|
||||||
|
if let currentPeer = currentPeer {
|
||||||
|
finalPeer = update(currentPeer, updatedPeer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentPeer == nil || !finalPeer.equalsTo(currentPeer!) {
|
||||||
|
updatedPeers[finalPeer.id] = finalPeer
|
||||||
|
self.cachedPeers[finalPeer.id] = finalPeer
|
||||||
|
|
||||||
|
encoder.reset()
|
||||||
|
encoder.encodeRootObject(finalPeer)
|
||||||
|
|
||||||
|
if currentPeer != nil {
|
||||||
|
updatePeer.run(Blob(data: encoder.makeData()), finalPeer.id.toInt64())
|
||||||
|
} else {
|
||||||
|
insertPeer.run(finalPeer.id.toInt64(), Blob(data: encoder.makeData()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for record in self.peerViews.copyItems() {
|
||||||
|
if record.0.updatePeers(updatedPeers) {
|
||||||
|
deferPeerViewUpdate(record.0, pipe: record.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func modify<T>(f: Modifier<State> -> T) -> Signal<T, NoError> {
|
public func modify<T>(f: Modifier<State> -> T) -> Signal<T, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
self.queue.dispatch {
|
self.queue.dispatch {
|
||||||
|
//#if DEBUG
|
||||||
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
//#endif
|
||||||
|
|
||||||
self.database.transaction()
|
self.database.transaction()
|
||||||
let result = f(Modifier(postbox: self))
|
let result = f(Modifier(postbox: self))
|
||||||
|
//print("(Postbox modify took \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms)")
|
||||||
|
//#if DEBUG
|
||||||
|
//startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
//#endif
|
||||||
self.database.commit()
|
self.database.commit()
|
||||||
|
|
||||||
|
//#if DEBUG
|
||||||
|
print("(Postbox commit took \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms)")
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
self.performDeferredUpdates()
|
||||||
|
|
||||||
subscriber.putNext(result)
|
subscriber.putNext(result)
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}
|
}
|
||||||
@ -772,7 +1033,7 @@ public final class Postbox<State: PostboxState> {
|
|||||||
private func findAdjacentMessageIds(peerId: PeerId, namespace: MessageId.Namespace, index: MessageIndex) -> (MessageId.Id?, MessageId.Id?) {
|
private func findAdjacentMessageIds(peerId: PeerId, namespace: MessageId.Namespace, index: MessageIndex) -> (MessageId.Id?, MessageId.Id?) {
|
||||||
var minId: MessageId.Id?
|
var minId: MessageId.Id?
|
||||||
var maxId: MessageId.Id?
|
var maxId: MessageId.Id?
|
||||||
for row in self.database.prepare("SELECT MIN(id), MAX(id) FROM peer_messages WHERE peerId = ? AND namespace = ?").run(peerId.toInt64(), Int64(namespace)) {
|
for row in self.database.prepareCached("SELECT MIN(id), MAX(id) FROM peer_messages WHERE peerId = ? AND namespace = ?").run(peerId.toInt64(), Int64(namespace)) {
|
||||||
minId = MessageId.Id(row[0] as! Int64)
|
minId = MessageId.Id(row[0] as! Int64)
|
||||||
maxId = MessageId.Id(row[1] as! Int64)
|
maxId = MessageId.Id(row[1] as! Int64)
|
||||||
}
|
}
|
||||||
@ -780,7 +1041,7 @@ public final class Postbox<State: PostboxState> {
|
|||||||
if let minId = minId, maxId = maxId {
|
if let minId = minId, maxId = maxId {
|
||||||
var minTimestamp: Int32!
|
var minTimestamp: Int32!
|
||||||
var maxTimestamp: Int32!
|
var maxTimestamp: Int32!
|
||||||
for row in self.database.prepare("SELECT id, timestamp FROM peer_messages WHERE peerId = ? AND namespace = ? AND id IN (?, ?)").run(peerId.toInt64(), Int64(namespace), Int64(minId), Int64(maxId)) {
|
for row in self.database.prepareCached("SELECT id, timestamp FROM peer_messages WHERE peerId = ? AND namespace = ? AND id IN (?, ?)").run(peerId.toInt64(), Int64(namespace), Int64(minId), Int64(maxId)) {
|
||||||
let id = Int32(row[0] as! Int64)
|
let id = Int32(row[0] as! Int64)
|
||||||
let timestamp = Int32(row[1] as! Int64)
|
let timestamp = Int32(row[1] as! Int64)
|
||||||
if id == minId {
|
if id == minId {
|
||||||
@ -790,8 +1051,8 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let earlierMidStatement = self.database.prepare("SELECT id, timestamp FROM peer_messages WHERE peerId = ? AND namespace = ? AND id <= ? LIMIT 1")
|
let earlierMidStatement = self.database.prepareCached("SELECT id, timestamp FROM peer_messages WHERE peerId = ? AND namespace = ? AND id <= ? LIMIT 1")
|
||||||
let laterMidStatement = self.database.prepare("SELECT id, timestamp FROM peer_messages WHERE peerId = ? AND namespace = ? AND id >= ? LIMIT 1")
|
let laterMidStatement = self.database.prepareCached("SELECT id, timestamp FROM peer_messages WHERE peerId = ? AND namespace = ? AND id >= ? LIMIT 1")
|
||||||
|
|
||||||
func lowerBound(timestamp: Int32) -> MessageId.Id? {
|
func lowerBound(timestamp: Int32) -> MessageId.Id? {
|
||||||
var leftId = minId
|
var leftId = minId
|
||||||
@ -883,16 +1144,16 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchMessagesAround(peerId: PeerId, anchorId: MessageId, count: Int) -> ([Message], [MessageId.Namespace : Message], [MessageId.Namespace : Message]) {
|
private func fetchMessagesAround(peerId: PeerId, anchorId: MessageId, count: Int) -> ([RenderedMessage], [MessageId.Namespace : RenderedMessage], [MessageId.Namespace : RenderedMessage]) {
|
||||||
var messages: [Message] = []
|
var messages: [RenderedMessage] = []
|
||||||
|
|
||||||
messages += self.fetchMessagesRelative(peerId, earlier: true)(namespace: anchorId.namespace, id: anchorId.id, count: count + 1)
|
messages += self.fetchMessagesRelative(peerId, earlier: true)(namespace: anchorId.namespace, id: anchorId.id, count: count + 1)
|
||||||
messages += self.fetchMessagesRelative(peerId, earlier: false)(namespace: anchorId.namespace, id: anchorId.id - 1, count: count + 1)
|
messages += self.fetchMessagesRelative(peerId, earlier: false)(namespace: anchorId.namespace, id: anchorId.id - 1, count: count + 1)
|
||||||
|
|
||||||
messages.sortInPlace({ MessageIndex($0) < MessageIndex($1) })
|
messages.sortInPlace({ MessageIndex($0.message) < MessageIndex($1.message) })
|
||||||
var i = messages.count - 1
|
var i = messages.count - 1
|
||||||
while i >= 1 {
|
while i >= 1 {
|
||||||
if messages[i].id == messages[i - 1].id {
|
if messages[i].message.id == messages[i - 1].message.id {
|
||||||
messages.removeAtIndex(i)
|
messages.removeAtIndex(i)
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
@ -903,19 +1164,19 @@ public final class Postbox<State: PostboxState> {
|
|||||||
} else {
|
} else {
|
||||||
var index: MessageIndex!
|
var index: MessageIndex!
|
||||||
for message in messages {
|
for message in messages {
|
||||||
if message.id == anchorId {
|
if message.message.id == anchorId {
|
||||||
index = MessageIndex(message)
|
index = MessageIndex(message.message)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if index == nil {
|
if index == nil {
|
||||||
var closestId: MessageId.Id = messages[0].id.id
|
var closestId: MessageId.Id = messages[0].message.id.id
|
||||||
var closestDistance = abs(closestId - anchorId.id)
|
var closestDistance = abs(closestId - anchorId.id)
|
||||||
let closestTimestamp: Int32 = messages[0].timestamp
|
let closestTimestamp: Int32 = messages[0].message.timestamp
|
||||||
for message in messages {
|
for message in messages {
|
||||||
if abs(message.id.id - anchorId.id) < closestDistance {
|
if abs(message.message.id.id - anchorId.id) < closestDistance {
|
||||||
closestId = message.id.id
|
closestId = message.message.id.id
|
||||||
closestDistance = abs(message.id.id - anchorId.id)
|
closestDistance = abs(message.message.id.id - anchorId.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index = MessageIndex(id: MessageId(peerId: peerId, namespace: anchorId.namespace, id: closestId), timestamp: closestTimestamp)
|
index = MessageIndex(id: MessageId(peerId: peerId, namespace: anchorId.namespace, id: closestId), timestamp: closestTimestamp)
|
||||||
@ -933,10 +1194,10 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.sortInPlace({ MessageIndex($0) < MessageIndex($1) })
|
messages.sortInPlace({ MessageIndex($0.message) < MessageIndex($1.message) })
|
||||||
var i = messages.count - 1
|
var i = messages.count - 1
|
||||||
while i >= 1 {
|
while i >= 1 {
|
||||||
if messages[i].id == messages[i - 1].id {
|
if messages[i].message.id == messages[i - 1].message.id {
|
||||||
messages.removeAtIndex(i)
|
messages.removeAtIndex(i)
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
@ -945,16 +1206,16 @@ public final class Postbox<State: PostboxState> {
|
|||||||
var anchorIndex = messages.count / 2
|
var anchorIndex = messages.count / 2
|
||||||
i = 0
|
i = 0
|
||||||
while i < messages.count {
|
while i < messages.count {
|
||||||
if messages[i].id == index.id {
|
if messages[i].message.id == index.id {
|
||||||
anchorIndex = i
|
anchorIndex = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
var filteredMessages: [Message] = []
|
var filteredMessages: [RenderedMessage] = []
|
||||||
var earlier: [MessageId.Namespace : Message] = [:]
|
var earlier: [MessageId.Namespace : RenderedMessage] = [:]
|
||||||
var later: [MessageId.Namespace : Message] = [:]
|
var later: [MessageId.Namespace : RenderedMessage] = [:]
|
||||||
|
|
||||||
i = anchorIndex
|
i = anchorIndex
|
||||||
var j = anchorIndex - 1
|
var j = anchorIndex - 1
|
||||||
@ -977,32 +1238,32 @@ public final class Postbox<State: PostboxState> {
|
|||||||
|
|
||||||
i = leftIndex - 1
|
i = leftIndex - 1
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
if earlier[messages[i].id.namespace] == nil {
|
if earlier[messages[i].message.id.namespace] == nil {
|
||||||
earlier[messages[i].id.namespace] = messages[i]
|
earlier[messages[i].message.id.namespace] = messages[i]
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
|
|
||||||
i = rightIndex + 1
|
i = rightIndex + 1
|
||||||
while i < messages.count {
|
while i < messages.count {
|
||||||
if later[messages[i].id.namespace] == nil {
|
if later[messages[i].message.id.namespace] == nil {
|
||||||
later[messages[i].id.namespace] = messages[i]
|
later[messages[i].message.id.namespace] = messages[i]
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredMessages.sortInPlace({ MessageIndex($0) < MessageIndex($1) })
|
filteredMessages.sortInPlace({ MessageIndex($0.message) < MessageIndex($1.message) })
|
||||||
|
|
||||||
return (filteredMessages, earlier, later)
|
return (filteredMessages, earlier, later)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchMessagesRelative(peerId: PeerId, earlier: Bool)(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
private func fetchMessagesRelative(peerId: PeerId, earlier: Bool)(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [RenderedMessage] {
|
||||||
var messages: [Message] = []
|
var messages: [Message] = []
|
||||||
|
|
||||||
let sign = earlier ? "<" : ">"
|
let sign = earlier ? "<" : ">"
|
||||||
let order = earlier ? "DESC" : "ASC"
|
let order = earlier ? "DESC" : "ASC"
|
||||||
let statement = self.database.prepare("SELECT data, associatedMediaIds FROM peer_messages WHERE peerId = ? AND namespace = ? AND id \(sign) ? ORDER BY id \(order) LIMIT \(count)")
|
let statement = self.database.prepareCached("SELECT data, associatedMediaIds FROM peer_messages WHERE peerId = ? AND namespace = ? AND id \(sign) ? ORDER BY id \(order) LIMIT ?")
|
||||||
let bound: Int64
|
let bound: Int64
|
||||||
if let id = id {
|
if let id = id {
|
||||||
bound = Int64(id)
|
bound = Int64(id)
|
||||||
@ -1012,7 +1273,7 @@ public final class Postbox<State: PostboxState> {
|
|||||||
bound = Int64(Int32.min)
|
bound = Int64(Int32.min)
|
||||||
}
|
}
|
||||||
|
|
||||||
for row in statement.run(Int64(peerId.toInt64()), Int64(namespace), bound) {
|
for row in statement.run(Int64(peerId.toInt64()), Int64(namespace), bound, Int64(count)) {
|
||||||
let data = (row[0] as! Blob).data
|
let data = (row[0] as! Blob).data
|
||||||
let decoder = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false))
|
let decoder = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false))
|
||||||
if let message = decoder.decodeRootObject() as? Message {
|
if let message = decoder.decodeRootObject() as? Message {
|
||||||
@ -1022,7 +1283,7 @@ public final class Postbox<State: PostboxState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return messages
|
return self.renderedMessages(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchPeerEntryIndicesRelative(earlier: Bool)(index: PeerViewEntryIndex?, count: Int) -> [PeerViewEntryIndex] {
|
private func fetchPeerEntryIndicesRelative(earlier: Bool)(index: PeerViewEntryIndex?, count: Int) -> [PeerViewEntryIndex] {
|
||||||
@ -1049,12 +1310,12 @@ public final class Postbox<State: PostboxState> {
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
private func messageForPeer(peerId: PeerId, id: MessageId) -> Message? {
|
private func messageForPeer(peerId: PeerId, id: MessageId) -> RenderedMessage? {
|
||||||
for row in self.database.prepareCached("SELECT data, associatedMediaIds FROM peer_messages WHERE peerId = ? AND namespace = ? AND id = ?").run(peerId.toInt64(), Int64(id.namespace), Int64(id.id)) {
|
for row in self.database.prepareCached("SELECT data, associatedMediaIds FROM peer_messages WHERE peerId = ? AND namespace = ? AND id = ?").run(peerId.toInt64(), Int64(id.namespace), Int64(id.id)) {
|
||||||
let data = (row[0] as! Blob).data
|
let data = (row[0] as! Blob).data
|
||||||
let decoder = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false))
|
let decoder = Decoder(buffer: ReadBuffer(memory: UnsafeMutablePointer(data.bytes), length: data.length, freeWhenDone: false))
|
||||||
if let message = decoder.decodeRootObject() as? Message {
|
if let message = decoder.decodeRootObject() as? Message {
|
||||||
return message
|
return self.renderedMessages([message]).first
|
||||||
} else {
|
} else {
|
||||||
print("(PostBox: can't decode message)")
|
print("(PostBox: can't decode message)")
|
||||||
}
|
}
|
||||||
@ -1098,38 +1359,98 @@ public final class Postbox<State: PostboxState> {
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchMessagesTail(peerId: PeerId, count: Int) -> [Message] {
|
private func renderedMessages(messages: [Message]) -> [RenderedMessage] {
|
||||||
var messages: [Message] = []
|
if messages.count == 0 {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
var peerIds = Set<PeerId>()
|
||||||
|
var mediaIds = Set<MediaId>()
|
||||||
|
|
||||||
|
for message in messages {
|
||||||
|
for peerId in message.peerIds {
|
||||||
|
peerIds.insert(peerId)
|
||||||
|
}
|
||||||
|
for mediaId in message.mediaIds {
|
||||||
|
mediaIds.insert(mediaId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayPeerIds: [PeerId] = []
|
||||||
|
for id in peerIds {
|
||||||
|
arrayPeerIds.append(id)
|
||||||
|
}
|
||||||
|
let peers = self.peersWithIds(arrayPeerIds)
|
||||||
|
|
||||||
|
var arrayMediaIds: [MediaId] = []
|
||||||
|
for id in mediaIds {
|
||||||
|
arrayMediaIds.append(id)
|
||||||
|
}
|
||||||
|
let medias = self.mediaWithIds(arrayMediaIds)
|
||||||
|
|
||||||
|
var result: [RenderedMessage] = []
|
||||||
|
|
||||||
|
for message in messages {
|
||||||
|
if message.peerIds.count == 0 && message.mediaIds.count == 0 {
|
||||||
|
result.append(RenderedMessage(message: message, peers: [], media: []))
|
||||||
|
} else {
|
||||||
|
var messagePeers: [Peer] = []
|
||||||
|
for id in message.peerIds {
|
||||||
|
if let peer = peers[id] {
|
||||||
|
messagePeers.append(peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageMedia: [Media] = []
|
||||||
|
for id in message.mediaIds {
|
||||||
|
if let media = medias[id] {
|
||||||
|
messageMedia.append(media)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(RenderedMessage(message: message, peers: messagePeers, media: messageMedia))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private func fetchMessagesTail(peerId: PeerId, count: Int) -> [RenderedMessage] {
|
||||||
|
var messages: [RenderedMessage] = []
|
||||||
|
|
||||||
for namespace in self.messageNamespaces {
|
for namespace in self.messageNamespaces {
|
||||||
messages += self.fetchMessagesRelative(peerId, earlier: true)(namespace: namespace, id: nil, count: count)
|
messages += self.fetchMessagesRelative(peerId, earlier: true)(namespace: namespace, id: nil, count: count)
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.sortInPlace({ MessageIndex($0) < MessageIndex($1)})
|
messages.sortInPlace({ MessageIndex($0.message) < MessageIndex($1.message)})
|
||||||
|
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
public func tailMessageViewForPeerId(peerId: PeerId, count: Int) -> Signal<MessageView, NoError> {
|
public func tailMessageViewForPeerId(peerId: PeerId, count: Int) -> Signal<MessageView, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
self.queue.dispatch {
|
self.queue.dispatch {
|
||||||
let tail = self.fetchMessagesTail(peerId, count: count + 1)
|
let tail = self.fetchMessagesTail(peerId, count: count + 1)
|
||||||
|
|
||||||
var messages: [Message] = []
|
print("tailMessageViewForPeerId fetch: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
|
||||||
|
|
||||||
|
var messages: [RenderedMessage] = []
|
||||||
var i = tail.count - 1
|
var i = tail.count - 1
|
||||||
while i >= 0 && i >= tail.count - count {
|
while i >= 0 && i >= tail.count - count {
|
||||||
messages.insert(tail[i], atIndex: 0)
|
messages.insert(tail[i], atIndex: 0)
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
|
|
||||||
var earlier: [MessageId.Namespace : Message] = [:]
|
var earlier: [MessageId.Namespace : RenderedMessage] = [:]
|
||||||
|
|
||||||
for namespace in self.messageNamespaces {
|
for namespace in self.messageNamespaces {
|
||||||
var i = tail.count - count - 1
|
var i = tail.count - count - 1
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
if tail[i].id.namespace == namespace {
|
if tail[i].message.id.namespace == namespace {
|
||||||
earlier[namespace] = tail[i]
|
earlier[namespace] = tail[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1184,19 +1505,19 @@ public final class Postbox<State: PostboxState> {
|
|||||||
if around.0.count == 0 {
|
if around.0.count == 0 {
|
||||||
let tail = self.fetchMessagesTail(peerId, count: count + 1)
|
let tail = self.fetchMessagesTail(peerId, count: count + 1)
|
||||||
|
|
||||||
var messages: [Message] = []
|
var messages: [RenderedMessage] = []
|
||||||
var i = tail.count - 1
|
var i = tail.count - 1
|
||||||
while i >= 0 && i >= tail.count - count {
|
while i >= 0 && i >= tail.count - count {
|
||||||
messages.insert(tail[i], atIndex: 0)
|
messages.insert(tail[i], atIndex: 0)
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
|
|
||||||
var earlier: [MessageId.Namespace : Message] = [:]
|
var earlier: [MessageId.Namespace : RenderedMessage] = [:]
|
||||||
|
|
||||||
for namespace in self.messageNamespaces {
|
for namespace in self.messageNamespaces {
|
||||||
var i = tail.count - count - 1
|
var i = tail.count - count - 1
|
||||||
while i >= 0 {
|
while i >= 0 {
|
||||||
if tail[i].id.namespace == namespace {
|
if tail[i].message.id.namespace == namespace {
|
||||||
earlier[namespace] = tail[i]
|
earlier[namespace] = tail[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1249,7 +1570,12 @@ public final class Postbox<State: PostboxState> {
|
|||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
self.queue.dispatch {
|
self.queue.dispatch {
|
||||||
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
let tail = self.fetchPeerEntriesRelative(true)(index: nil, count: count + 1)
|
let tail = self.fetchPeerEntriesRelative(true)(index: nil, count: count + 1)
|
||||||
|
self.fetchPeerEntriesRelative(true)(index: nil, count: count + 1)
|
||||||
|
|
||||||
|
print("(Postbox fetchPeerEntriesRelative took \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms)")
|
||||||
|
|
||||||
var entries: [PeerViewEntry] = []
|
var entries: [PeerViewEntry] = []
|
||||||
var i = tail.count - 1
|
var i = tail.count - 1
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e53267d7394c6df648d588bebc4db10dc853c75d
|
Subproject commit b0b93692fdac68e8ffa71e11b23c95b3e0f8b256
|
Loading…
x
Reference in New Issue
Block a user