From 13f3951ddd60ce659d34dbc553e82bca6c0ec38b Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 16 Jul 2015 02:20:03 +0300 Subject: [PATCH] no message --- Postbox.xcodeproj/project.pbxproj | 4 + Postbox/MessageView.swift | 1 - Postbox/Peer.swift | 18 +- Postbox/PeerView.swift | 278 ++++++++++++++++++++++++++++++ Postbox/Postbox.swift | 101 +++++++++-- PostboxTests/PostboxTests.swift | 119 ++++++++++++- 6 files changed, 499 insertions(+), 22 deletions(-) create mode 100644 Postbox/PeerView.swift diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index ded107159b..ff36d79283 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ D0D224F21B4D6ABD0085E26D /* Functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D224ED1B4D6ABD0085E26D /* Functions.swift */; }; D0D224F31B4D6ABD0085E26D /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D224EE1B4D6ABD0085E26D /* Query.swift */; }; D0D224F41B4D6ABD0085E26D /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D224EF1B4D6ABD0085E26D /* Schema.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, ); }; }; D0E3A7561B28A7E300A402D9 /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E3A74A1B28A7E300A402D9 /* Postbox.framework */; }; D0E3A75D1B28A7E300A402D9 /* PostboxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A75C1B28A7E300A402D9 /* PostboxTests.swift */; }; @@ -71,6 +72,7 @@ D0D224ED1B4D6ABD0085E26D /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Functions.swift; path = submodules/sqlite.swift/SQLite/Functions.swift; sourceTree = SOURCE_ROOT; }; D0D224EE1B4D6ABD0085E26D /* Query.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Query.swift; path = submodules/sqlite.swift/SQLite/Query.swift; sourceTree = SOURCE_ROOT; }; D0D224EF1B4D6ABD0085E26D /* Schema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Schema.swift; path = submodules/sqlite.swift/SQLite/Schema.swift; sourceTree = SOURCE_ROOT; }; + D0D225251B4D84930085E26D /* PeerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerView.swift; sourceTree = ""; }; D0E3A74A1B28A7E300A402D9 /* Postbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Postbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0E3A74E1B28A7E300A402D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D0E3A74F1B28A7E300A402D9 /* Postbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Postbox.h; sourceTree = ""; }; @@ -180,6 +182,7 @@ D0E3A79D1B28B50400A402D9 /* Message.swift */, D0E3A7A11B28B7DC00A402D9 /* Media.swift */, D003E4E51B38DBDB00C22CBC /* MessageView.swift */, + D0D225251B4D84930085E26D /* PeerView.swift */, D0E3A7811B28ADD000A402D9 /* Postbox.swift */, D0E3A74D1B28A7E300A402D9 /* Supporting Files */, ); @@ -342,6 +345,7 @@ D07516791B2EC90400AE42E0 /* SQLite-Bridging.m in Sources */, D0D224F21B4D6ABD0085E26D /* Functions.swift in Sources */, D07516711B2EC7FE00AE42E0 /* Statement.swift in Sources */, + D0D225261B4D84930085E26D /* PeerView.swift in Sources */, D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */, D075166A1B2EC7FE00AE42E0 /* Database.swift in Sources */, D0D224F41B4D6ABD0085E26D /* Schema.swift in Sources */, diff --git a/Postbox/MessageView.swift b/Postbox/MessageView.swift index 173d734d2e..20c9fa6fee 100644 --- a/Postbox/MessageView.swift +++ b/Postbox/MessageView.swift @@ -175,7 +175,6 @@ public final class MutableMessageView: Printable { } self.later.removeAll(keepCapacity: true) - if anchorIndex + 1 < addedMessages.count { for namespace in self.namespaces { var i = anchorIndex + 1 diff --git a/Postbox/Peer.swift b/Postbox/Peer.swift index 2d1ab17b51..49491b027c 100644 --- a/Postbox/Peer.swift +++ b/Postbox/Peer.swift @@ -1,11 +1,11 @@ import Foundation -public struct PeerId: Hashable, Printable { +public struct PeerId: Hashable, Printable, Comparable { public typealias Namespace = Int32 public typealias Id = Int32 - let namespace: Namespace - let id: Id + public let namespace: Namespace + public let id: Id public init(namespace: Namespace, id: Id) { self.namespace = namespace @@ -53,6 +53,18 @@ public func ==(lhs: PeerId, rhs: PeerId) -> Bool { return lhs.id == rhs.id && lhs.namespace == rhs.namespace } +public func <(lhs: PeerId, rhs: PeerId) -> Bool { + if lhs.namespace != rhs.namespace { + return lhs.namespace < rhs.namespace + } + + if lhs.id != rhs.id { + return lhs.id < rhs.id + } + + return false +} + public protocol Peer: Coding { var id: PeerId { get } } diff --git a/Postbox/PeerView.swift b/Postbox/PeerView.swift new file mode 100644 index 0000000000..328c57497e --- /dev/null +++ b/Postbox/PeerView.swift @@ -0,0 +1,278 @@ +import Foundation + +public class PeerViewEntry { + public let peer: Peer + public let message: Message + + public init(peer: Peer, message: Message) { + self.peer = peer + self.message = message + } +} + +public struct PeerViewEntryIndex: Equatable, Comparable { + public let peerId: PeerId + public let messageIndex: MessageIndex + + public init(_ entry: PeerViewEntry) { + self.peerId = entry.peer.id + self.messageIndex = MessageIndex(entry.message) + } + + public init(peerId: PeerId, messageIndex: MessageIndex) { + self.peerId = peerId + self.messageIndex = messageIndex + } + + public func earlier() -> PeerViewEntryIndex { + return PeerViewEntryIndex(peerId: self.peerId, messageIndex: MessageIndex(id: MessageId(peerId: self.messageIndex.id.peerId, namespace: self.messageIndex.id.namespace, id: self.messageIndex.id.id - 1), timestamp: self.messageIndex.timestamp)) + } + + public func later() -> PeerViewEntryIndex { + return PeerViewEntryIndex(peerId: self.peerId, messageIndex: MessageIndex(id: MessageId(peerId: self.messageIndex.id.peerId, namespace: self.messageIndex.id.namespace, id: self.messageIndex.id.id + 1), timestamp: self.messageIndex.timestamp)) + } +} + +public func ==(lhs: PeerViewEntryIndex, rhs: PeerViewEntryIndex) -> Bool { + return lhs.peerId == rhs.peerId && lhs.messageIndex == rhs.messageIndex +} + +public func <(lhs: PeerViewEntryIndex, rhs: PeerViewEntryIndex) -> Bool { + if lhs.messageIndex != rhs.messageIndex { + return lhs.messageIndex < rhs.messageIndex + } + + return lhs.peerId < rhs.peerId +} + +public final class MutablePeerView: Printable { + public struct RemoveContext { + var invalidEarlier = false + var invalidLater = false + var removedEntries = false + } + + let tags: [Int32] + let count: Int + var earlier: PeerViewEntry? + var later: PeerViewEntry? + var entries: [PeerViewEntry] + + public init(tags: [Int32], count: Int, earlier: PeerViewEntry?, entries: [PeerViewEntry], later: PeerViewEntry?) { + self.tags = tags + self.count = count + self.earlier = earlier + self.entries = entries + self.later = later + } + + public func removeEntry(context: RemoveContext?, peerId: PeerId) -> RemoveContext { + var invalidationContext = context ?? RemoveContext() + + if let earlier = self.earlier { + if peerId == earlier.peer.id { + invalidationContext.invalidEarlier = true + } + } + + if let later = self.later { + if peerId == later.peer.id { + invalidationContext.invalidLater = true + } + } + + var i = 0 + while i < self.entries.count { + if self.entries[i].peer.id == peerId { + self.entries.removeAtIndex(i) + invalidationContext.removedEntries = true + break + } + i++ + } + + return invalidationContext + } + + public func addEntry(entry: PeerViewEntry) { + if self.entries.count == 0 { + self.entries.append(entry) + } else { + var first = PeerViewEntryIndex(self.entries[self.entries.count - 1]) + var last = PeerViewEntryIndex(self.entries[0]) + + let index = PeerViewEntryIndex(entry) + + var next: PeerViewEntryIndex? + if let later = self.later { + next = PeerViewEntryIndex(later) + } + + if index < last { + let earlierEntry = self.earlier + if earlierEntry == nil || PeerViewEntryIndex(earlierEntry!) < index { + if self.entries.count < self.count { + self.entries.insert(entry, atIndex: 0) + } else { + self.earlier = entry + } + } + } else if index > first { + if next != nil && index > next! { + let laterEntry = self.later + if laterEntry == nil || PeerViewEntryIndex(laterEntry!) > index { + if self.entries.count < self.count { + self.entries.append(entry) + } else { + self.later = entry + } + } + } else { + self.entries.append(entry) + if self.entries.count > self.count { + let earliest = self.entries[0] + self.earlier = earliest + self.entries.removeAtIndex(0) + } + } + } else if index != last && index != first { + var i = self.entries.count + while i >= 1 { + if PeerViewEntryIndex(self.entries[i - 1]) < index { + break + } + i-- + } + self.entries.insert(entry, atIndex: i) + if self.entries.count > self.count { + let earliest = self.entries[0] + self.earlier = earliest + self.entries.removeAtIndex(0) + } + } + } + } + + public func complete(context: RemoveContext, fetchEarlier: (PeerViewEntryIndex?, Int) -> [PeerViewEntry], fetchLater: (PeerViewEntryIndex?, Int) -> [PeerViewEntry]) { + if context.removedEntries && self.entries.count != self.count { + var addedEntries: [PeerViewEntry] = [] + + var latestAnchor: PeerViewEntryIndex? + + if self.entries.count != 0 { + latestAnchor = PeerViewEntryIndex(self.entries[self.entries.count - 1]) + } else if let later = self.later { + latestAnchor = PeerViewEntryIndex(later) + } + + if let later = self.later { + addedEntries += fetchLater(PeerViewEntryIndex(later).earlier(), self.count) + } + if let earlier = self.earlier { + addedEntries += fetchEarlier(PeerViewEntryIndex(earlier).later(), self.count) + } + + addedEntries += self.entries + addedEntries.sort({ PeerViewEntryIndex($0) < PeerViewEntryIndex($1) }) + + var i = addedEntries.count - 1 + while i >= 1 { + if PeerViewEntryIndex(addedEntries[i]) == PeerViewEntryIndex(addedEntries[i - 1]) { + addedEntries.removeAtIndex(i) + } + i-- + } + self.entries = [] + + var anchorIndex = addedEntries.count - 1 + if let latestAnchor = latestAnchor { + var i = addedEntries.count - 1 + while i >= 0 { + if PeerViewEntryIndex(addedEntries[i]) <= latestAnchor { + anchorIndex = i + break + } + i-- + } + } + + self.later = nil + if anchorIndex + 1 < addedEntries.count { + var i = anchorIndex + 1 + while i < addedEntries.count { + self.later = addedEntries[i] + break + } + } + + i = anchorIndex + while i >= 0 && i > anchorIndex - self.count { + self.entries.insert(addedEntries[i], atIndex: 0) + i-- + } + + self.earlier = nil + if anchorIndex - self.count >= 0 { + i = anchorIndex - self.count + while i >= 0 { + self.earlier = addedEntries[i] + break + } + } + } else { + var earlyIndex: PeerViewEntryIndex? + if self.entries.count != 0 { + earlyIndex = PeerViewEntryIndex(self.entries[0]) + } + + let earlierEntries = fetchEarlier(earlyIndex, 1) + if earlierEntries.count == 0 { + self.earlier = nil + } else { + self.earlier = earlierEntries[0] + } + + var lateIndex: PeerViewEntryIndex? + if self.entries.count != 0 { + lateIndex = PeerViewEntryIndex(self.entries[self.entries.count - 1]) + } + + let laterEntries = fetchLater(lateIndex, 1) + if laterEntries.count == 0 { + self.later = nil + } else { + self.later = laterEntries[0] + } + } + } + + public var description: String { + var string = "" + + if let earlier = self.earlier { + string += "more(" + string += "(p \(earlier.peer.id.namespace):\(earlier.peer.id.id), m \(earlier.message.id.namespace):\(earlier.message.id.id)—\(earlier.message.timestamp)" + string += ") " + } + + string += "[" + var first = true + for entry in self.entries { + if first { + first = false + } else { + string += ", " + } + string += "(p \(entry.peer.id.namespace):\(entry.peer.id.id), m \(entry.message.id.namespace):\(entry.message.id.id)—\(entry.message.timestamp))" + } + string += "]" + + if let later = self.later { + string += " more(" + string += "(p \(later.peer.id.namespace):\(later.peer.id.id), m \(later.message.id.namespace):\(later.message.id.id)—\(later.message.timestamp)" + string += ")" + } + + return string + } +} diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index 4572304d04..6018c1d352 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -3,6 +3,8 @@ import Foundation import SwiftSignalKit public final class Postbox { + static let PeerTagGeneral = 0 + public class Modifier { private unowned var postbox: Postbox @@ -20,7 +22,6 @@ public final class Postbox { } private let basePath: String - private let ownerId: PeerId private let messageNamespaces: [MessageId.Namespace] private let queue = SwiftSignalKit.Queue() @@ -28,9 +29,8 @@ public final class Postbox { private var peerMessageViews: [PeerId : Bag<(MutableMessageView, Pipe)>] = [:] - public init(basePath: String, ownerId: PeerId, messageNamespaces: [MessageId.Namespace]) { + public init(basePath: String, messageNamespaces: [MessageId.Namespace]) { self.basePath = basePath - self.ownerId = ownerId self.messageNamespaces = messageNamespaces self.openDatabase() } @@ -51,17 +51,12 @@ public final class Postbox { } private func createSchema() { - self.database.transaction() - //state self.database.execute("CREATE TABLE state (key INTEGER PRIMARY KEY, data BLOB)") //peer_messages self.database.execute("CREATE TABLE peer_messages (peerId INTEGER, namespace INTEGER, id INTEGER, data BLOB, associatedMediaIds BLOB, timestamp INTEGER, PRIMARY KEY(peerId, namespace, id))") - //peer_maxread - self.database.execute("CREATE TABLE peer_maxread (peerId INTEGER, namespace INTEGER, id INTEGER)") - //peer_media self.database.execute("CREATE TABLE peer_media (peerId INTEGER, mediaNamespace INTEGER, messageNamespace INTEGER, messageId INTEGER, PRIMARY KEY (peerId, mediaNamespace, messageNamespace, messageId))") self.database.execute("CREATE INDEX peer_media_peerId_messageNamespace_messageId ON peer_media (peerId, messageNamespace, messageId)") @@ -72,21 +67,97 @@ public final class Postbox { //media_cleanup self.database.execute("CREATE TABLE media_cleanup (namespace INTEGER, id INTEGER, data BLOB, PRIMARY KEY(namespace, id))") - //messages_readstate - self.database.execute("CREATE TABLE messages_readstate (peerId INTEGER, namespace INTEGER, maxUnreadIncomingId INTEGER, maxUnreadOutgoingId INTEGER, incomingUnreadCount INTEGER, PRIMARY KEY (peerId, namespace))") - - //top_messages - self.database.execute("CREATE TABLE top_messages (peerId INTEGER PRIMARY KEY, timestamp INTEGER)") + //peer_entries + self.database.execute("CREATE TABLE peer_entries (entry BLOB)") + self.database.execute("CREATE INDEX peer_entries_entry on peer_entries (entry)") //peers self.database.execute("CREATE TABLE peers (peerId INTEGER PRIMARY KEY, data BLOB)") + } + + public func _testBlobsPrepare(range: Int) { + self.database.execute("CREATE TABLE test_blobs (number INTEGER, data BLOB)") + self.database.execute("CREATE INDEX test_blobs_index_data ON test_blobs (data)") - //user_statuses - self.database.execute("CREATE TABLE user_statuses (id INTEGER PRIMARY KEY, data BLOB)") + self.database.transaction() + let insert = self.database.prepare("INSERT INTO test_blobs (number, data) VALUES (?, ?)") + for i in 0 ..< range { + var value = Int32(bigEndian: Int32(i)) + let blob = Blob(bytes: &value, length: 4) + insert.run(Int64(i), blob) + } self.database.commit() } + public func _testBlobsTest(range: Int) { + let select = self.database.prepare("SELECT number FROM test_blobs WHERE data < ? ORDER BY data DESC LIMIT 1") + + for i in 1 ..< 1000 { + var value = 1 + Int32(arc4random_uniform(UInt32(range - 1))) + var binary = Int32(bigEndian: value) + var found = false + for row in select.run(Blob(bytes: &binary, length: 4)) { + found = true + let selected = Int32(row[0] as! Int64) + if selected != value - 1 { + assertionFailure("invalid value \(selected) != \(value - 1)") + } + break + } + if !found { + assertionFailure("value not found for \(value - 1)") + } + } + } + + private class func peerViewEntryIndexForBlob(blob: Blob) -> PeerViewEntryIndex { + let buffer = ReadBuffer(memory: UnsafeMutablePointer(blob.data.bytes), length: blob.data.length, freeWhenDone: false) + var offset: Int = 0 + + var timestamp: Int32 = 0 + buffer.read(×tamp, offset: offset, length: 4) + timestamp = Int32(bigEndian: timestamp) + offset += 4 + + var namespace: Int32 = 0 + buffer.read(&namespace, offset: offset, length: 4) + namespace = Int32(bigEndian: namespace) + offset += 4 + + var id: Int32 = 0 + buffer.read(&id, offset: offset, length: 4) + id = Int32(bigEndian: id) + offset += 4 + + var peerIdRepresentation: Int64 = 0 + buffer.read(&peerIdRepresentation, offset: offset, length: 8) + peerIdRepresentation = Int64(bigEndian: peerIdRepresentation) + offset += 8 + + let peerId = PeerId(peerIdRepresentation) + + return PeerViewEntryIndex(peerId: peerId, messageIndex: MessageIndex(id: MessageId(peerId:peerId, namespace: namespace, id: id), timestamp: timestamp)) + } + + private class func blobForPeerViewEntryIndex(index: PeerViewEntryIndex) -> Blob { + let buffer = WriteBuffer() + + var timestamp = Int32(bigEndian: index.messageIndex.timestamp) + buffer.write(×tamp, offset: 0, length: 4) + + var namespace = Int32(bigEndian: index.messageIndex.id.namespace) + buffer.write(&namespace, offset: 0, length: 4) + + var id = Int32(bigEndian: index.messageIndex.id.id) + buffer.write(&id, offset: 0, length: 4) + + var peerIdRepresentation = Int64(bigEndian: index.peerId.toInt64()) + buffer.write(&peerIdRepresentation, offset: 0, length: 8) + + return Blob(data: buffer.makeData()) + } + private class func messageIdsGroupedByNamespace(ids: [MessageId]) -> [MessageId.Namespace : [MessageId]] { var grouped: [MessageId.Namespace : [MessageId]] = [:] diff --git a/PostboxTests/PostboxTests.swift b/PostboxTests/PostboxTests.swift index e7369d36cc..957984c544 100644 --- a/PostboxTests/PostboxTests.swift +++ b/PostboxTests/PostboxTests.swift @@ -16,6 +16,22 @@ enum TestMediaNamespace: MediaId.Namespace { case Test = 0 } +class TestPeer: Peer { + var id: PeerId + + init(id: PeerId) { + self.id = id + } + + required init(decoder: Decoder) { + self.id = PeerId(decoder.decodeInt64ForKey("id")) + } + + func encode(encoder: Encoder) { + encoder.encodeInt64(self.id.toInt64(), forKey: "id") + } +} + class TestMessage: Message { var id: MessageId var authorId: PeerId @@ -97,7 +113,7 @@ class PostboxTests: XCTestCase { let basePath = "/tmp/postboxtest" NSFileManager.defaultManager().removeItemAtPath(basePath, error: nil) - let postbox = Postbox(basePath: basePath, ownerId: ownerId, messageNamespaces: [messageNamespace]) + let postbox = Postbox(basePath: basePath, messageNamespaces: [messageNamespace]) postbox.modify { state in let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1)) for i in 0 ..< 10 { @@ -586,7 +602,7 @@ class PostboxTests: XCTestCase { let basePath = "/tmp/postboxtest" NSFileManager.defaultManager().removeItemAtPath(basePath, error: nil) - let postbox = Postbox(basePath: basePath, ownerId: ownerId, messageNamespaces: [messageNamespace]) + let postbox = Postbox(basePath: basePath, messageNamespaces: [messageNamespace]) postbox.modify { state in let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1)) @@ -626,7 +642,7 @@ class PostboxTests: XCTestCase { let basePath = "/tmp/postboxtest" NSFileManager.defaultManager().removeItemAtPath(basePath, error: nil) - let postbox = Postbox(basePath: basePath, ownerId: ownerId, messageNamespaces: [messageNamespaceCloud, messageNamespaceLocal]) + let postbox = Postbox(basePath: basePath, messageNamespaces: [messageNamespaceCloud, messageNamespaceLocal]) postbox.modify { state in let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1)) @@ -660,4 +676,101 @@ class PostboxTests: XCTestCase { postbox._sync() } + + func testPeerView() { + let view = MutablePeerView(tags: [], count: 3, earlier: nil, entries: [], later: nil) + let messageNamespaceCloud = TestMessageNamespace.Cloud.rawValue + let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000) + + var entries: [PeerViewEntry] = [] + + func print(entries: [PeerViewEntry]) { + var string = "" + string += "[" + var first = true + for entry in entries { + if first { + first = false + } else { + string += ", " + } + string += "(p \(entry.peer.id.namespace):\(entry.peer.id.id), m \(entry.message.id.namespace):\(entry.message.id.id)—\(entry.message.timestamp))" + } + string += "]" + println("\(string)") + } + + func add(entry: PeerViewEntry) { + entries.append(entry) + entries.sort({ PeerViewEntryIndex($0) < PeerViewEntryIndex($1) }) + + view.addEntry(entry) + + println("\n\(view)") + print(entries) + } + + func remove(peerId: PeerId, context: MutablePeerView.RemoveContext? = nil) -> MutablePeerView.RemoveContext { + entries = entries.filter({ $0.peer.id != peerId }) + return view.removeEntry(context, peerId: peerId) + } + + func fetchEarlier(entries: [PeerViewEntry])(index: PeerViewEntryIndex?, count: Int) -> [PeerViewEntry] { + var filtered: [PeerViewEntry] = [] + var i = entries.count - 1 + while i >= 0 && filtered.count < count { + if index == nil || PeerViewEntryIndex(entries[i]) < index! { + filtered.append(entries[i]) + } + i-- + } + + return filtered + } + + func fetchLater(entries: [PeerViewEntry])(index: PeerViewEntryIndex?, count: Int) -> [PeerViewEntry] { + var filtered: [PeerViewEntry] = [] + var i = 0 + while i < entries.count && filtered.count < count { + if index == nil || PeerViewEntryIndex(entries[i]) > index! { + filtered.append(entries[i]) + } + i++ + } + + return filtered + } + + func complete(context: MutablePeerView.RemoveContext) { + view.complete(context, fetchEarlier: fetchEarlier(entries), fetchLater: fetchLater(entries)) + } + + println("\(view)") + + for i in 1 ..< 10 { + let messageId = MessageId(peerId: otherId, namespace: messageNamespaceCloud, id: Int32(i * 2 * 100)) + let message = TestMessage(id: messageId, authorId: otherId, date: Int32(i * 2 * 100), text: "\(i)", referencedMediaIds: []) + + add(PeerViewEntry(peer: TestPeer(id: PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(i * 2))), message: message)) + } + + if true { + let i = 15 + let messageId = MessageId(peerId: otherId, namespace: messageNamespaceCloud, id: Int32(i * 100)) + let message = TestMessage(id: messageId, authorId: otherId, date: Int32(i * 100), text: "\(i)", referencedMediaIds: []) + + add(PeerViewEntry(peer: TestPeer(id: PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(i))), message: message)) + } + + if true { + var context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(15))) + context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(14)), context: context) + context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(16)), context: context) + context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(18)), context: context) + println("\n\(view)") + print(entries) + complete(context) + println("\(view)") + } + } }