This commit is contained in:
overtake 2019-05-18 23:10:00 +02:00
commit 3f2f5a82f3
9 changed files with 627 additions and 288 deletions

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0E3A7491B28A7E300A402D9"
BuildableName = "Postbox.framework"
BlueprintName = "Postbox"
ReferencedContainer = "container:Postbox.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "DebugHockeyapp"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0E3A7541B28A7E300A402D9"
BuildableName = "PostboxTests.xctest"
BlueprintName = "PostboxTests"
ReferencedContainer = "container:Postbox.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0E3A7491B28A7E300A402D9"
BuildableName = "Postbox.framework"
BlueprintName = "Postbox"
ReferencedContainer = "container:Postbox.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "DebugHockeyapp"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0E3A7491B28A7E300A402D9"
BuildableName = "Postbox.framework"
BlueprintName = "Postbox"
ReferencedContainer = "container:Postbox.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "ReleaseHockeyapp"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0E3A7491B28A7E300A402D9"
BuildableName = "Postbox.framework"
BlueprintName = "Postbox"
ReferencedContainer = "container:Postbox.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "DebugHockeyapp">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "ReleaseHockeyapp"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "DebugHockeyapp"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0E3A7541B28A7E300A402D9"
BuildableName = "PostboxTests.xctest"
BlueprintName = "PostboxTests"
ReferencedContainer = "container:Postbox.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "DebugHockeyapp"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "ReleaseHockeyapp"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "DebugHockeyapp">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "ReleaseHockeyapp"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -28,9 +28,11 @@ public final class Database {
internal var handle: OpaquePointer? = nil
public init?(_ location: String) {
let _ = open(location + "-guard", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)
if location != ":memory:" {
let _ = open(location + "-guard", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)
}
let flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX
let res = sqlite3_open_v2(location.description, &self.handle, flags, nil)
let res = sqlite3_open_v2(location, &self.handle, flags, nil)
if res != SQLITE_OK {
postboxLog("sqlite3_open_v2: \(res)")
return nil

View File

@ -253,12 +253,12 @@ final class MutableMessageHistoryView {
self.topTaggedMessages = topTaggedMessages
self.additionalDatas = additionalDatas
self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, statistics: self.orderStatistics, limit: count + 2, locations: peerIds)
self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds)
if case let .loading(loadingState) = self.state {
let sampledState = loadingState.checkAndSample(postbox: postbox)
switch sampledState {
case let .ready(anchor, holes):
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: self.orderStatistics, limit: count + 2, locations: peerIds, postbox: postbox, holes: holes))
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds, postbox: postbox, holes: holes))
self.sampledState = self.state.sample(postbox: postbox)
case .loadHole:
break
@ -270,12 +270,12 @@ final class MutableMessageHistoryView {
}
private func reset(postbox: Postbox) {
self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds)
self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds)
if case let .loading(loadingState) = self.state {
let sampledState = loadingState.checkAndSample(postbox: postbox)
switch sampledState {
case let .ready(anchor, holes):
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds, postbox: postbox, holes: holes))
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes))
case .loadHole:
break
}
@ -284,7 +284,7 @@ final class MutableMessageHistoryView {
let sampledState = loadingState.checkAndSample(postbox: postbox)
switch sampledState {
case let .ready(anchor, holes):
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds, postbox: postbox, holes: holes))
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes))
case .loadHole:
break
}
@ -381,18 +381,18 @@ final class MutableMessageHistoryView {
switch operation {
case let .InsertMessage(message):
if unwrappedTag.isEmpty || message.tags.contains(unwrappedTag) {
if loadedState.add(postbox: postbox, entry: .IntermediateMessageEntry(message, nil, nil)) {
if loadedState.add(entry: .IntermediateMessageEntry(message, nil, nil)) {
hasChanges = true
}
}
case let .Remove(indicesAndTags):
for (index, _) in indicesAndTags {
if loadedState.remove(postbox: postbox, index: index) {
if loadedState.remove(index: index) {
hasChanges = true
}
}
case let .UpdateEmbeddedMedia(index, buffer):
if loadedState.updateEmbeddedMedia(postbox: postbox, index: index, buffer: buffer) {
if loadedState.updateEmbeddedMedia(index: index, buffer: buffer) {
hasChanges = true
}
case let .UpdateGroupInfos(groupInfos):
@ -455,7 +455,7 @@ final class MutableMessageHistoryView {
let sampledState = loadingState.checkAndSample(postbox: postbox)
switch sampledState {
case let .ready(anchor, holes):
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds, postbox: postbox, holes: holes))
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes))
case .loadHole:
break
}
@ -745,15 +745,17 @@ public final class MessageHistoryView {
assert(Set(entries.map({ $0.message.stableId })).count == entries.count)
if !entries.isEmpty {
let anchorIndex = binaryIndexOrLower(entries, state.anchor)
let lowerCount = mutableView.fillCount / 2 + 1
let upperCount = mutableView.fillCount / 2
if anchorIndex >= 0 && entries.count - anchorIndex >= upperCount {
let lowerOrEqualThanAnchorCount = anchorIndex + 1
let higherThanAnchorCount = entries.count - anchorIndex - 1
if higherThanAnchorCount > mutableView.fillCount {
self.laterId = entries[entries.count - 1].index
entries.removeLast()
} else {
self.laterId = nil
}
if anchorIndex >= lowerCount {
if lowerOrEqualThanAnchorCount > mutableView.fillCount {
self.earlierId = entries[0].index
entries.removeFirst()
} else {

View File

@ -165,13 +165,12 @@ private func binaryIndexOrLower(_ inputArr: [MutableMessageHistoryEntry], _ sear
return hi
}
private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], anchor: HistoryViewAnchor, limit: Int) -> [(PeerIdAndNamespace, Int)] {
private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], anchor: HistoryViewAnchor, halfLimit: Int) -> (lowerOrAtAnchor:[(PeerIdAndNamespace, Int)], higherThanAnchor: [(PeerIdAndNamespace, Int)]) {
var previousAnchorIndices: [PeerIdAndNamespace: Int] = [:]
var nextAnchorIndices: [PeerIdAndNamespace: Int] = [:]
for (space, items) in orderedEntriesBySpace {
let index = binaryIndexOrLower(items.entries, anchor)
previousAnchorIndices[space] = index
nextAnchorIndices[space] = index + 1
previousAnchorIndices[space] = items.lowerOrAtAnchor.count - 1
nextAnchorIndices[space] = 0
}
var backwardsResult: [(PeerIdAndNamespace, Int)] = []
@ -182,7 +181,7 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
for (space, value) in previousAnchorIndices {
if value != -1 {
if let minSpaceValue = minSpace {
if orderedEntriesBySpace[space]!.entries[value].index > orderedEntriesBySpace[minSpaceValue]!.entries[previousAnchorIndices[minSpaceValue]!].index {
if orderedEntriesBySpace[space]!.lowerOrAtAnchor[value].index > orderedEntriesBySpace[minSpaceValue]!.lowerOrAtAnchor[previousAnchorIndices[minSpaceValue]!].index {
minSpace = space
}
} else {
@ -193,16 +192,22 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
if let minSpace = minSpace {
backwardsResult.append((minSpace, previousAnchorIndices[minSpace]!))
previousAnchorIndices[minSpace]! -= 1
if (result.count + backwardsResult.count) == limit {
if backwardsResult.count == halfLimit {
break
}
}
if minSpace == nil {
break
}
}
while true {
var maxSpace: PeerIdAndNamespace?
for (space, value) in nextAnchorIndices {
if value != orderedEntriesBySpace[space]!.entries.count {
if value != orderedEntriesBySpace[space]!.higherThanAnchor.count {
if let maxSpaceValue = maxSpace {
if orderedEntriesBySpace[space]!.entries[value].index < orderedEntriesBySpace[maxSpaceValue]!.entries[nextAnchorIndices[maxSpaceValue]!].index {
if orderedEntriesBySpace[space]!.higherThanAnchor[value].index < orderedEntriesBySpace[maxSpaceValue]!.higherThanAnchor[nextAnchorIndices[maxSpaceValue]!].index {
maxSpace = space
}
} else {
@ -213,16 +218,16 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
if let maxSpace = maxSpace {
result.append((maxSpace, nextAnchorIndices[maxSpace]!))
nextAnchorIndices[maxSpace]! += 1
if (result.count + backwardsResult.count) == limit {
if result.count == halfLimit {
break
}
}
if minSpace == nil && maxSpace == nil {
if maxSpace == nil {
break
}
}
return backwardsResult.reversed() + result
return (backwardsResult.reversed(), result)
}
struct SampledHistoryViewHole: Equatable {
@ -274,9 +279,9 @@ private func isIndex(index: MessageIndex, closerTo anchor: HistoryViewAnchor, th
}
}
private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], holes: HistoryViewHoles, anchor: HistoryViewAnchor, tag: MessageTags?) -> (clipRanges: [ClosedRange<MessageIndex>], sampledHole: SampledHistoryViewHole?) {
private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], holes: HistoryViewHoles, anchor: HistoryViewAnchor, tag: MessageTags?, halfLimit: Int) -> (clipRanges: [ClosedRange<MessageIndex>], sampledHole: SampledHistoryViewHole?) {
var clipRanges: [ClosedRange<MessageIndex>] = []
var sampledHole: (itemIndex: Int?, hole: SampledHistoryViewHole)?
var sampledHole: (distanceFromAnchor: Int?, hole: SampledHistoryViewHole)?
for (space, indices) in holes.holesBySpace {
if indices.isEmpty {
@ -292,7 +297,7 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
}
}
}
guard let items = orderedEntriesBySpace[space], !items.entries.isEmpty else {
guard let items = orderedEntriesBySpace[space], (!items.lowerOrAtAnchor.isEmpty || !items.higherThanAnchor.isEmpty) else {
let holeBounds: (startId: MessageId.Id, endId: MessageId.Id)
switch anchor {
case .lowerBound:
@ -307,11 +312,176 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
continue
}
}
guard let bounds = items.bounds else {
assertionFailure("A non-empty entry list should have non-nil bounds")
continue
for item in items.lowerOrAtAnchor {
if item.index.id.id == 76891 {
assert(true)
}
}
let anchorIndex = binaryIndexOrLower(items.entries, anchor)
for item in items.higherThanAnchor {
if item.index.id.id == 76891 {
assert(true)
}
}
var lowerOrAtAnchorHole: (distanceFromAnchor: Int, hole: SampledHistoryViewHole)?
for i in (-1 ..< items.lowerOrAtAnchor.count).reversed() {
let startingMessageId: MessageId.Id
if items.higherThanAnchor.isEmpty {
startingMessageId = Int32.max - 1
} else {
startingMessageId = items.higherThanAnchor[0].index.id.id
}
let currentMessageId: MessageId.Id
if i == -1 {
if items.lowerOrAtAnchor.count >= halfLimit {
break
}
currentMessageId = 1
} else {
currentMessageId = items.lowerOrAtAnchor[i].index.id.id
}
let range: ClosedRange<Int> = Int(currentMessageId) ... Int(startingMessageId)
if indices.intersects(integersIn: range) {
let holeStartIndex: Int
if let value = indices.integerLessThanOrEqualTo(Int(startingMessageId)) {
holeStartIndex = value
} else {
holeStartIndex = indices[indices.endIndex]
}
lowerOrAtAnchorHole = (items.lowerOrAtAnchor.count - i, SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, indices: indices, startId: Int32(holeStartIndex), endId: 1))
if i == -1 {
if items.lowerOrAtAnchor.count == 0 {
if items.higherThanAnchor.count == 0 {
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
} else {
let clipIndex = items.higherThanAnchor[0].index.predecessor()
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
}
} else {
let clipIndex = items.lowerOrAtAnchor[0].index.predecessor()
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
}
} else {
if i == items.lowerOrAtAnchor.count - 1 {
if items.higherThanAnchor.count == 0 {
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
} else {
let clipIndex = items.higherThanAnchor[0].index.predecessor()
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
}
} else {
let clipIndex: MessageIndex
if indices.contains(Int(items.lowerOrAtAnchor[i + 1].index.id.id)) {
clipIndex = items.lowerOrAtAnchor[i + 1].index
} else {
clipIndex = items.lowerOrAtAnchor[i + 1].index.predecessor()
}
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
}
}
break
}
}
var higherThanAnchorHole: (distanceFromAnchor: Int, hole: SampledHistoryViewHole)?
for i in (0 ..< items.higherThanAnchor.count + 1) {
let startingMessageId: MessageId.Id
if items.lowerOrAtAnchor.isEmpty {
startingMessageId = 1
} else {
startingMessageId = items.lowerOrAtAnchor[items.lowerOrAtAnchor.count - 1].index.id.id
}
let currentMessageId: MessageId.Id
if i == items.higherThanAnchor.count {
if items.higherThanAnchor.count >= halfLimit {
break
}
currentMessageId = Int32.max - 1
} else {
currentMessageId = items.higherThanAnchor[i].index.id.id
}
let range: ClosedRange<Int> = Int(startingMessageId) ... Int(currentMessageId)
if indices.intersects(integersIn: range) {
let holeStartIndex: Int
if let value = indices.integerGreaterThanOrEqualTo(Int(startingMessageId)) {
holeStartIndex = value
} else {
holeStartIndex = indices[indices.startIndex]
}
higherThanAnchorHole = (i, SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, indices: indices, startId: Int32(holeStartIndex), endId: Int32.max - 1))
if i == items.higherThanAnchor.count {
if items.higherThanAnchor.count == 0 {
if items.lowerOrAtAnchor.count == 0 {
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
} else {
let clipIndex = items.lowerOrAtAnchor[items.lowerOrAtAnchor.count - 1].index.successor()
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
}
} else {
let clipIndex = items.higherThanAnchor[items.higherThanAnchor.count - 1].index.successor()
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
}
} else {
if i == 0 {
if items.lowerOrAtAnchor.count == 0 {
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
} else {
let clipIndex = items.lowerOrAtAnchor[items.lowerOrAtAnchor.count - 1].index.successor()
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
}
} else {
let clipIndex: MessageIndex
if indices.contains(Int(items.higherThanAnchor[i - 1].index.id.id)) {
clipIndex = items.higherThanAnchor[i - 1].index
} else {
clipIndex = items.higherThanAnchor[i - 1].index.successor()
}
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
}
}
break
}
}
var chosenHole: (distanceFromAnchor: Int, hole: SampledHistoryViewHole)?
if let lowerOrAtAnchorHole = lowerOrAtAnchorHole, let higherThanAnchorHole = higherThanAnchorHole {
if items.lowerOrAtAnchor.isEmpty != items.higherThanAnchor.isEmpty {
if !items.lowerOrAtAnchor.isEmpty {
chosenHole = lowerOrAtAnchorHole
} else {
chosenHole = higherThanAnchorHole
}
} else {
if lowerOrAtAnchorHole.distanceFromAnchor < higherThanAnchorHole.distanceFromAnchor {
chosenHole = lowerOrAtAnchorHole
} else {
chosenHole = higherThanAnchorHole
}
}
} else if let lowerOrAtAnchorHole = lowerOrAtAnchorHole {
chosenHole = lowerOrAtAnchorHole
} else if let higherThanAnchorHole = higherThanAnchorHole {
chosenHole = higherThanAnchorHole
}
if let chosenHole = chosenHole {
if let current = sampledHole {
if let distance = current.distanceFromAnchor {
if chosenHole.distanceFromAnchor < distance {
sampledHole = (chosenHole.distanceFromAnchor, chosenHole.hole)
}
}
} else {
sampledHole = (chosenHole.distanceFromAnchor, chosenHole.hole)
}
}
/*let anchorIndex = binaryIndexOrLower(items.entries, anchor)
let anchorStartingMessageId: MessageId.Id
if anchorIndex == -1 {
anchorStartingMessageId = 1
@ -406,7 +576,7 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
}
}
higherDirectionIndex += 1
}
}*/
}
return (clipRanges, sampledHole?.hole)
}
@ -443,8 +613,61 @@ struct HistoryViewHoles {
}
struct OrderedHistoryViewEntries {
var entries: [MutableMessageHistoryEntry]
var bounds: (lower: MessageIndex, upper: MessageIndex)?
var lowerOrAtAnchor: [MutableMessageHistoryEntry]
var higherThanAnchor: [MutableMessageHistoryEntry]
func find(index: MessageIndex) -> MutableMessageHistoryEntry? {
if let entryIndex = binarySearch(self.lowerOrAtAnchor, extract: { $0.index }, searchItem: index) {
return self.lowerOrAtAnchor[entryIndex]
} else if let entryIndex = binarySearch(self.higherThanAnchor, extract: { $0.index }, searchItem: index) {
return self.higherThanAnchor[entryIndex]
} else {
return nil
}
}
mutating func mutableScan(_ f: (MutableMessageHistoryEntry) -> MutableMessageHistoryEntry?) -> Bool {
for i in 0 ..< self.lowerOrAtAnchor.count {
if let updated = f(self.lowerOrAtAnchor[i]) {
self.lowerOrAtAnchor[i] = updated
return true
}
}
for i in 0 ..< self.higherThanAnchor.count {
if let updated = f(self.higherThanAnchor[i]) {
self.higherThanAnchor[i] = updated
return true
}
}
return false
}
mutating func update(index: MessageIndex, _ f: (MutableMessageHistoryEntry) -> MutableMessageHistoryEntry?) -> Bool {
if let entryIndex = binarySearch(self.lowerOrAtAnchor, extract: { $0.index }, searchItem: index) {
if let updated = f(self.lowerOrAtAnchor[entryIndex]) {
self.lowerOrAtAnchor[entryIndex] = updated
return true
}
} else if let entryIndex = binarySearch(self.higherThanAnchor, extract: { $0.index }, searchItem: index) {
if let updated = f(self.higherThanAnchor[entryIndex]) {
self.higherThanAnchor[entryIndex] = updated
return true
}
}
return false
}
mutating func remove(index: MessageIndex) -> Bool {
if let entryIndex = binarySearch(self.lowerOrAtAnchor, extract: { $0.index }, searchItem: index) {
self.lowerOrAtAnchor.remove(at: entryIndex)
return true
} else if let entryIndex = binarySearch(self.higherThanAnchor, extract: { $0.index }, searchItem: index) {
self.higherThanAnchor.remove(at: entryIndex)
return true
} else {
return false
}
}
}
struct HistoryViewLoadedSample {
@ -459,17 +682,17 @@ final class HistoryViewLoadedState {
let anchor: HistoryViewAnchor
let tag: MessageTags?
let statistics: MessageHistoryViewOrderStatistics
let limit: Int
let halfLimit: Int
var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries]
var holes: HistoryViewHoles
var spacesWithRemovals = Set<PeerIdAndNamespace>()
init(anchor: HistoryViewAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, limit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) {
precondition(limit >= 3)
init(anchor: HistoryViewAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) {
precondition(halfLimit >= 3)
self.anchor = anchor
self.tag = tag
self.statistics = statistics
self.limit = limit
self.halfLimit = halfLimit
self.orderedEntriesBySpace = [:]
self.holes = holes
@ -509,56 +732,43 @@ final class HistoryViewLoadedState {
anchorIndex = upperBound
}
var lowerMessages: [MutableMessageHistoryEntry] = []
var higherMessages: [MutableMessageHistoryEntry] = []
var lowerOrAtAnchorMessages: [MutableMessageHistoryEntry] = []
var higherThanAnchorMessages: [MutableMessageHistoryEntry] = []
if let currentEntries = self.orderedEntriesBySpace[space], !currentEntries.entries.isEmpty {
let index = binaryIndexOrLower(currentEntries.entries, self.anchor)
if index >= 0 {
lowerMessages = Array(currentEntries.entries[0 ... index].reversed())
}
if index < currentEntries.entries.count {
higherMessages = Array(currentEntries.entries[index + 1 ..< currentEntries.entries.count])
}
if let currentEntries = self.orderedEntriesBySpace[space] {
lowerOrAtAnchorMessages = currentEntries.lowerOrAtAnchor.reversed()
higherThanAnchorMessages = currentEntries.higherThanAnchor
}
func mapEntry(_ message: IntermediateMessage) -> MutableMessageHistoryEntry {
return .IntermediateMessageEntry(message, nil, nil)
}
if lowerMessages.count < self.limit / 2 + 1 {
if lowerOrAtAnchorMessages.count < self.halfLimit {
let nextLowerIndex: (index: MessageIndex, includeFrom: Bool)
if let lastMessage = lowerMessages.last {
if let lastMessage = lowerOrAtAnchorMessages.last {
nextLowerIndex = (lastMessage.index, false)
} else {
nextLowerIndex = (anchorIndex, true)
}
lowerMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, limit: self.limit / 2 + 1 - lowerMessages.count).map(mapEntry))
lowerOrAtAnchorMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, limit: self.halfLimit - lowerOrAtAnchorMessages.count).map(mapEntry))
}
if higherMessages.count < self.limit - lowerMessages.count {
if higherThanAnchorMessages.count < self.halfLimit {
let nextHigherIndex: MessageIndex
if let lastMessage = higherMessages.last {
if let lastMessage = higherThanAnchorMessages.last {
nextHigherIndex = lastMessage.index
} else {
nextHigherIndex = anchorIndex
}
higherMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextHigherIndex, includeFrom: false, to: upperBound, limit: self.limit - lowerMessages.count - higherMessages.count).map(mapEntry))
higherThanAnchorMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextHigherIndex, includeFrom: false, to: upperBound, limit: self.halfLimit - higherThanAnchorMessages.count).map(mapEntry))
}
if !lowerMessages.isEmpty && lowerMessages.count + higherMessages.count < self.limit {
let additionalLowerMessages = postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: lowerMessages[lowerMessages.count - 1].index, includeFrom: false, to: lowerBound, limit: self.limit - lowerMessages.count - higherMessages.count).map(mapEntry)
lowerMessages.append(contentsOf: additionalLowerMessages)
}
lowerOrAtAnchorMessages.reverse()
var messages: [MutableMessageHistoryEntry] = []
messages.append(contentsOf: lowerMessages.reversed())
messages.append(contentsOf: higherMessages)
assert(lowerOrAtAnchorMessages.count <= self.halfLimit)
assert(higherThanAnchorMessages.count <= self.halfLimit)
assert(messages.count <= self.limit)
let bounds = postbox.messageHistoryTable.fetchBoundaries(peerId: space.peerId, namespace: space.namespace, tag: self.tag)
if let tag = self.tag, self.statistics.contains(.combinedLocation) {
/*if let tag = self.tag, self.statistics.contains(.combinedLocation) {
if let first = messages.first {
let messageIndex = first.index
let previousCount = postbox.messageHistoryTagsTable.getMessageCountInRange(tag: tag, peerId: space.peerId, namespace: space.namespace, lowerBound: MessageIndex.lowerBound(peerId: space.peerId, namespace: space.namespace), upperBound: messageIndex)
@ -600,9 +810,9 @@ final class HistoryViewLoadedState {
nextLocation.1 = max(0, nextLocation.1 - 1)
}
}
}
}*/
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(entries: messages, bounds: bounds)
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(lowerOrAtAnchor: lowerOrAtAnchorMessages, higherThanAnchor: higherThanAnchorMessages)
}
func insertHole(space: PeerIdAndNamespace, range: ClosedRange<MessageId.Id>) -> Bool {
@ -618,15 +828,14 @@ final class HistoryViewLoadedState {
if self.orderedEntriesBySpace[space] == nil {
return false
}
guard let entryIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) else {
guard let entry = self.orderedEntriesBySpace[space]!.find(index: index) else {
return false
}
let entry = self.orderedEntriesBySpace[space]!.entries[entryIndex]
var updated = false
if self.remove(postbox: postbox, index: index) {
if self.remove(index: index) {
updated = true
}
if self.add(postbox: postbox, entry: entry.updatedTimestamp(timestamp)) {
if self.add(entry: entry.updatedTimestamp(timestamp)) {
updated = true
}
return updated
@ -646,43 +855,46 @@ final class HistoryViewLoadedState {
if self.orderedEntriesBySpace[space] == nil {
continue
}
for i in 0 ..< self.orderedEntriesBySpace[space]!.entries.count {
if let groupInfo = spaceMapping[self.orderedEntriesBySpace[space]!.entries[i].index.id.id] {
let spaceUpdated = self.orderedEntriesBySpace[space]!.mutableScan({ entry in
if let groupInfo = spaceMapping[entry.index.id.id] {
updated = true
switch self.orderedEntriesBySpace[space]!.entries[i] {
switch entry {
case let .IntermediateMessageEntry(message, location, monthLocation):
self.orderedEntriesBySpace[space]!.entries[i] = .IntermediateMessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation)
return .IntermediateMessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation)
case let .MessageEntry(messageEntry):
self.orderedEntriesBySpace[space]!.entries[i] = .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedGroupInfo(groupInfo), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedGroupInfo(groupInfo), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
}
}
return nil
})
if spaceUpdated {
updated = true
}
}
return updated
}
func updateEmbeddedMedia(postbox: Postbox, index: MessageIndex, buffer: ReadBuffer) -> Bool {
func updateEmbeddedMedia(index: MessageIndex, buffer: ReadBuffer) -> Bool {
let space = PeerIdAndNamespace(peerId: index.id.peerId, namespace: index.id.namespace)
if self.orderedEntriesBySpace[space] == nil {
return false
}
guard let itemIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) else {
return false
}
switch self.orderedEntriesBySpace[space]!.entries[itemIndex] {
case let .IntermediateMessageEntry(message, location, monthLocation):
self.orderedEntriesBySpace[space]!.entries[itemIndex] = .IntermediateMessageEntry(message.withUpdatedEmbeddedMedia(buffer), location, monthLocation)
case let .MessageEntry(messageEntry):
self.orderedEntriesBySpace[space]!.entries[itemIndex] = .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
}
return true
return self.orderedEntriesBySpace[space]!.update(index: index, { entry in
switch entry {
case let .IntermediateMessageEntry(message, location, monthLocation):
return .IntermediateMessageEntry(message.withUpdatedEmbeddedMedia(buffer), location, monthLocation)
case let .MessageEntry(messageEntry):
return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
}
})
}
func updateMedia(updatedMedia: [MediaId: Media?]) -> Bool {
var hasChanges = false
var updated = false
for space in self.orderedEntriesBySpace.keys {
for i in 0 ..< self.orderedEntriesBySpace[space]!.entries.count {
switch self.orderedEntriesBySpace[space]!.entries[i] {
let spaceUpdated = self.orderedEntriesBySpace[space]!.mutableScan({ entry in
switch entry {
case let .MessageEntry(value):
let message = value.message
@ -706,41 +918,25 @@ final class HistoryViewLoadedState {
}
}
let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds)
self.orderedEntriesBySpace[space]!.entries[i] = .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes))
hasChanges = true
}
case let .IntermediateMessageEntry(message, _, _):
var rebuild = false
for mediaId in message.referencedMedia {
if let media = updatedMedia[mediaId] , media?.id != mediaId {
rebuild = true
break
}
}
if rebuild {
var referencedMedia: [MediaId] = []
for mediaId in message.referencedMedia {
if let media = updatedMedia[mediaId] , media?.id != mediaId {
if let id = media?.id {
referencedMedia.append(id)
}
} else {
referencedMedia.append(mediaId)
}
}
hasChanges = true
return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes))
}
case .IntermediateMessageEntry:
break
}
return nil
})
if spaceUpdated {
updated = true
}
}
return hasChanges
return updated
}
func add(postbox: Postbox, entry: MutableMessageHistoryEntry) -> Bool {
func add(entry: MutableMessageHistoryEntry) -> Bool {
let space = PeerIdAndNamespace(peerId: entry.index.id.peerId, namespace: entry.index.id.namespace)
if self.orderedEntriesBySpace[space] == nil {
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(entries: [], bounds: nil)
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(lowerOrAtAnchor: [], higherThanAnchor: [])
}
var updated = false
@ -763,66 +959,48 @@ final class HistoryViewLoadedState {
}
}*/
let insertionIndex = binaryInsertionIndex(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: entry.index)
if insertionIndex < self.orderedEntriesBySpace[space]!.entries.count {
if self.orderedEntriesBySpace[space]!.entries[insertionIndex].index == entry.index {
assertionFailure("Inserting an existing index is not allowed")
self.orderedEntriesBySpace[space]!.entries[insertionIndex] = entry
return true
}
}
var shouldBeAdded = false
if insertionIndex == 0 {
if let bounds = self.orderedEntriesBySpace[space]!.bounds {
if entry.index <= bounds.lower {
shouldBeAdded = true
if self.anchor.isEqualOrGreater(than: entry.index) {
let insertionIndex = binaryInsertionIndex(self.orderedEntriesBySpace[space]!.lowerOrAtAnchor, extract: { $0.index }, searchItem: entry.index)
if insertionIndex < self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count {
if self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[insertionIndex].index == entry.index {
assertionFailure("Inserting an existing index is not allowed")
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[insertionIndex] = entry
return true
}
} else {
//assert(self.orderedEntriesBySpace[space]!.entries.isEmpty, "A non-empty entry list should have non-nil bounds")
shouldBeAdded = true
}
} else if insertionIndex == self.orderedEntriesBySpace[space]!.entries.count {
if let bounds = self.orderedEntriesBySpace[space]!.bounds {
if entry.index >= bounds.upper {
shouldBeAdded = true
}
} else {
//assert(self.orderedEntriesBySpace[space]!.entries.isEmpty, "A non-empty entry list should have non-nil bounds")
shouldBeAdded = true
}
} else {
shouldBeAdded = true
}
if shouldBeAdded {
self.orderedEntriesBySpace[space]!.entries.insert(entry, at: insertionIndex)
if let currentBounds = self.orderedEntriesBySpace[space]!.bounds {
if entry.index < currentBounds.lower {
self.orderedEntriesBySpace[space]!.bounds = (lower: entry.index, upper: currentBounds.upper)
} else if entry.index > currentBounds.upper {
self.orderedEntriesBySpace[space]!.bounds = (lower: currentBounds.lower, upper: entry.index)
}
} else {
self.orderedEntriesBySpace[space]!.bounds = (lower: entry.index, upper: entry.index)
}
if self.orderedEntriesBySpace[space]!.entries.count > self.limit {
let anchorIndex = binaryIndexOrLower(self.orderedEntriesBySpace[space]!.entries, self.anchor)
if anchorIndex > self.limit / 2 {
self.orderedEntriesBySpace[space]!.entries.removeFirst()
} else {
self.orderedEntriesBySpace[space]!.entries.removeLast()
if insertionIndex == 0 && self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count >= self.halfLimit {
return updated
}
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.insert(entry, at: insertionIndex)
if self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count > self.halfLimit {
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.removeFirst()
}
return true
} else {
let insertionIndex = binaryInsertionIndex(self.orderedEntriesBySpace[space]!.higherThanAnchor, extract: { $0.index }, searchItem: entry.index)
if insertionIndex < self.orderedEntriesBySpace[space]!.higherThanAnchor.count {
if self.orderedEntriesBySpace[space]!.higherThanAnchor[insertionIndex].index == entry.index {
assertionFailure("Inserting an existing index is not allowed")
self.orderedEntriesBySpace[space]!.higherThanAnchor[insertionIndex] = entry
return true
}
}
updated = true
if insertionIndex == self.orderedEntriesBySpace[space]!.higherThanAnchor.count && self.orderedEntriesBySpace[space]!.higherThanAnchor.count >= self.halfLimit {
return updated
}
self.orderedEntriesBySpace[space]!.higherThanAnchor.insert(entry, at: insertionIndex)
if self.orderedEntriesBySpace[space]!.higherThanAnchor.count > self.halfLimit {
self.orderedEntriesBySpace[space]!.higherThanAnchor.removeLast()
}
return true
}
return updated
}
func remove(postbox: Postbox, index: MessageIndex) -> Bool {
func remove(index: MessageIndex) -> Bool {
let space = PeerIdAndNamespace(peerId: index.id.peerId, namespace: index.id.namespace)
if self.orderedEntriesBySpace[space] == nil {
return false
@ -842,16 +1020,7 @@ final class HistoryViewLoadedState {
}
}*/
if let itemIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) {
if let currentBounds = self.orderedEntriesBySpace[space]!.bounds {
if currentBounds.lower == index || currentBounds.upper
== index {
self.orderedEntriesBySpace[space]!.bounds = postbox.messageHistoryTable.fetchBoundaries(peerId: space.peerId, namespace: space.namespace, tag: self.tag)
}
} else {
//assertionFailure("A non-empty entry list should have non-nil bounds")
}
self.orderedEntriesBySpace[space]!.entries.remove(at: itemIndex)
if self.orderedEntriesBySpace[space]!.remove(index: index) {
self.spacesWithRemovals.insert(space)
updated = true
}
@ -866,48 +1035,62 @@ final class HistoryViewLoadedState {
}
self.spacesWithRemovals.removeAll()
}
let combinedSpacesAndIndices = sampleEntries(orderedEntriesBySpace: self.orderedEntriesBySpace, anchor: self.anchor, limit: self.limit)
let (clipRanges, sampledHole) = sampleHoleRanges(orderedEntriesBySpace: self.orderedEntriesBySpace, holes: self.holes, anchor: self.anchor, tag: self.tag)
let combinedSpacesAndIndicesByDirection = sampleEntries(orderedEntriesBySpace: self.orderedEntriesBySpace, anchor: self.anchor, halfLimit: self.halfLimit)
let (clipRanges, sampledHole) = sampleHoleRanges(orderedEntriesBySpace: self.orderedEntriesBySpace, holes: self.holes, anchor: self.anchor, tag: self.tag, halfLimit: self.halfLimit)
var holesToLower = false
var holesToHigher = false
var result: [MessageHistoryMessageEntry] = []
if combinedSpacesAndIndices.isEmpty {
if combinedSpacesAndIndicesByDirection.lowerOrAtAnchor.isEmpty && combinedSpacesAndIndicesByDirection.higherThanAnchor.isEmpty {
if !clipRanges.isEmpty {
holesToLower = true
holesToHigher = true
}
} else {
outer: for i in 0 ..< combinedSpacesAndIndices.count {
let (space, index) = combinedSpacesAndIndices[i]
if !clipRanges.isEmpty {
let entryIndex = self.orderedEntriesBySpace[space]!.entries[index].index
for range in clipRanges {
if range.contains(entryIndex) {
if i == 0 {
holesToLower = true
let directions = [combinedSpacesAndIndicesByDirection.lowerOrAtAnchor, combinedSpacesAndIndicesByDirection.higherThanAnchor]
for directionIndex in 0 ..< directions.count {
outer: for i in 0 ..< directions[directionIndex].count {
let (space, index) = directions[directionIndex][i]
let entry: MutableMessageHistoryEntry
if directionIndex == 0 {
entry = self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[index]
} else {
entry = self.orderedEntriesBySpace[space]!.higherThanAnchor[index]
}
if !clipRanges.isEmpty {
let entryIndex = entry.index
for range in clipRanges {
if range.contains(entryIndex) {
if directionIndex == 0 && i == 0 {
holesToLower = true
}
if directionIndex == 1 && i == directions[directionIndex].count - 1 {
holesToHigher = true
}
continue outer
}
if i == combinedSpacesAndIndices.count - 1 {
holesToHigher = true
}
continue outer
}
}
}
switch self.orderedEntriesBySpace[space]!.entries[index] {
case let .MessageEntry(value):
result.append(value)
case let .IntermediateMessageEntry(message, location, monthLocation):
let renderedMessage = postbox.messageHistoryTable.renderMessage(message, peerTable: postbox.peerTable)
var authorIsContact = false
if let author = renderedMessage.author {
authorIsContact = postbox.contactsTable.isContact(peerId: author.id)
}
let entry = MessageHistoryMessageEntry(message: renderedMessage, location: location, monthLocation: monthLocation, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: authorIsContact))
self.orderedEntriesBySpace[space]!.entries[index] = .MessageEntry(entry)
result.append(entry)
switch entry {
case let .MessageEntry(value):
result.append(value)
case let .IntermediateMessageEntry(message, location, monthLocation):
let renderedMessage = postbox.messageHistoryTable.renderMessage(message, peerTable: postbox.peerTable)
var authorIsContact = false
if let author = renderedMessage.author {
authorIsContact = postbox.contactsTable.isContact(peerId: author.id)
}
let entry = MessageHistoryMessageEntry(message: renderedMessage, location: location, monthLocation: monthLocation, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: authorIsContact))
if directionIndex == 0 {
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[index] = .MessageEntry(entry)
} else {
self.orderedEntriesBySpace[space]!.higherThanAnchor[index] = .MessageEntry(entry)
}
result.append(entry)
}
}
}
}
@ -948,13 +1131,13 @@ enum HistoryViewLoadingSample {
final class HistoryViewLoadingState {
var messageId: MessageId
let tag: MessageTags?
let limit: Int
let halfLimit: Int
var holes: HistoryViewHoles
init(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, messageId: MessageId, limit: Int) {
init(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, messageId: MessageId, halfLimit: Int) {
self.messageId = messageId
self.tag = tag
self.limit = limit
self.halfLimit = halfLimit
self.holes = HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))
}
@ -995,14 +1178,14 @@ enum HistoryViewState {
case loaded(HistoryViewLoadedState)
case loading(HistoryViewLoadingState)
init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, limit: Int, locations: MessageHistoryViewPeerIds) {
init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds) {
switch inputAnchor {
case let .index(index):
self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
case .lowerBound:
self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
case .upperBound:
self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
case .unread:
let anchorPeerId: PeerId
switch locations {
@ -1035,26 +1218,26 @@ enum HistoryViewState {
}
}
if let messageId = messageId {
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, limit: limit)
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, halfLimit: halfLimit)
let sampledState = loadingState.checkAndSample(postbox: postbox)
switch sampledState {
case let .ready(anchor, holes):
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: holes))
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes))
case .loadHole:
self = .loading(loadingState)
}
} else {
self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
}
} else {
preconditionFailure()
}
case let .message(messageId):
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, limit: limit)
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, halfLimit: halfLimit)
let sampledState = loadingState.checkAndSample(postbox: postbox)
switch sampledState {
case let .ready(anchor, holes):
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: holes))
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes))
case .loadHole:
self = .loading(loadingState)
}

View File

@ -935,7 +935,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
#if DEBUG
//debugSaveState(basePath: basePath, name: "previous1")
//debugRestoreState(basePath: basePath, name: "previous1")
debugRestoreState(basePath: basePath, name: "previous1")
#endif
let startTime = CFAbsoluteTimeGetCurrent()

View File

@ -159,6 +159,7 @@ public final class SqliteValueBox: ValueBox {
private let lock = NSRecursiveLock()
fileprivate let basePath: String
private let inMemory: Bool
private let encryptionParameters: ValueBoxEncryptionParameters?
private let databasePath: String
private var database: Database!
@ -196,8 +197,9 @@ public final class SqliteValueBox: ValueBox {
private let queue: Queue
public init(basePath: String, queue: Queue, encryptionParameters: ValueBoxEncryptionParameters?, upgradeProgress: (Float) -> Void) {
public init(basePath: String, queue: Queue, encryptionParameters: ValueBoxEncryptionParameters?, upgradeProgress: (Float) -> Void, inMemory: Bool = false) {
self.basePath = basePath
self.inMemory = inMemory
self.encryptionParameters = encryptionParameters
self.databasePath = basePath + "/db_sqlite"
self.queue = queue
@ -247,7 +249,7 @@ public final class SqliteValueBox: ValueBox {
#endif
var database: Database
if let result = Database(path) {
if let result = Database(self.inMemory ? ":memory:" : path) {
database = result
} else {
postboxLog("Couldn't open DB")

View File

@ -22,7 +22,7 @@ private extension MessageTags {
}
class MessageHistoryIndexTableTests: XCTestCase {
var valueBox: ValueBox?
var valueBox: SqliteValueBox?
var path: String?
var postbox: Postbox?
@ -45,7 +45,7 @@ class MessageHistoryIndexTableTests: XCTestCase {
arc4random_buf(bytes, 16)
})
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!))
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: true, key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!), upgradeProgress: { _ in })
let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [
peerId.namespace: [
@ -53,7 +53,7 @@ class MessageHistoryIndexTableTests: XCTestCase {
]
]
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [.media], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil)
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [.media], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil, messageNamespacesRequiringGroupStatsValidation: Set())
self.postbox = Postbox(queue: Queue.mainQueue(), basePath: path!, seedConfiguration: seedConfiguration, valueBox: self.valueBox!)
}

View File

@ -10,42 +10,33 @@ import SwiftSignalKit
private let peerId = PeerId(namespace: 1, id: 1)
private let namespace: Int32 = 1
private func extract(from array: [Int32], aroundIndex: Int, limit: Int) -> [Int32] {
private func extract(from array: [Int32], aroundIndex: Int, halfLimit: Int) -> [Int32] {
var lower: [Int32] = []
var higher: [Int32] = []
var i = aroundIndex
while i >= 0 && lower.count < limit / 2 + 1 {
while i >= 0 && lower.count < halfLimit {
lower.append(array[i])
i -= 1
}
var j = aroundIndex + 1
while j < array.count && higher.count < limit - lower.count {
while j < array.count && higher.count < halfLimit {
higher.append(array[j])
j += 1
}
if !lower.isEmpty && lower.count + higher.count < limit {
var additionalLower: [Int32] = []
while i >= 0 && additionalLower.count < limit - lower.count - higher.count {
additionalLower.append(array[i])
i -= 1
}
lower.append(contentsOf: additionalLower)
}
var result: [Int32] = []
result.append(contentsOf: lower.reversed())
result.append(contentsOf: higher)
assert(result.count <= limit)
assert(result.count <= halfLimit * 2)
return result
}
class MessageHistoryViewTests: XCTestCase {
var valueBox: ValueBox?
var valueBox: SqliteValueBox?
var path: String?
var postbox: Postbox?
@ -68,7 +59,7 @@ class MessageHistoryViewTests: XCTestCase {
arc4random_buf(bytes, 16)
})
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!))
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!), upgradeProgress: { _ in }, inMemory: true)
let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [
peerId.namespace: [:
@ -76,7 +67,7 @@ class MessageHistoryViewTests: XCTestCase {
]
]
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil)
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil, messageNamespacesRequiringGroupStatsValidation: Set())
self.postbox = Postbox(queue: Queue.mainQueue(), basePath: path!, seedConfiguration: seedConfiguration, valueBox: self.valueBox!)
}
@ -101,10 +92,14 @@ class MessageHistoryViewTests: XCTestCase {
}).start()
}
private func addMessage(_ id: Int32, _ timestamp: Int32, _ groupingKey: Int64? = nil) {
private func addMessage(_ id: Int32, _ timestamp: Int32, _ groupingKey: Int64? = nil) -> UInt32 {
var stableId: UInt32?
let _ = self.postbox!.transaction({ transaction -> Void in
let _ = transaction.addMessages([StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [])], location: .Random)
let messageId = MessageId(peerId: peerId, namespace: namespace, id: id)
let _ = transaction.addMessages([StoreMessage(id: messageId, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [])], location: .Random)
stableId = transaction.getMessage(messageId)!.stableId
}).start()
return stableId!
}
private func removeMessage(_ id: Int32) {
@ -120,7 +115,7 @@ class MessageHistoryViewTests: XCTestCase {
}
func testEmpty() {
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
switch state {
case let .loaded(loadedState):
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
@ -134,10 +129,10 @@ class MessageHistoryViewTests: XCTestCase {
var testIds: [MessageId.Id] = []
for i in 1 ..< 11 {
testIds.append(Int32(i * 10))
addMessage(Int32(i * 10), Int32(i * 10))
let _ = addMessage(Int32(i * 10), Int32(i * 10))
}
for i in 3 ... testIds.count + 10 {
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: i, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: i, locations: .single(peerId))
switch state {
case let .loaded(loadedState):
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
@ -154,7 +149,7 @@ class MessageHistoryViewTests: XCTestCase {
}
}
for i in 3 ... testIds.count + 10 {
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .lowerBound, tag: nil, limit: i, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .lowerBound, tag: nil, statistics: [], halfLimit: i, locations: .single(peerId))
switch state {
case let .loaded(loadedState):
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
@ -172,11 +167,11 @@ class MessageHistoryViewTests: XCTestCase {
}
for i in 3 ... testIds.count + 10 {
for j in testIds[0] - 10 ... testIds.last! + 10 {
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(j)), timestamp: Int32(j))), tag: nil, limit: i, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(j)), timestamp: Int32(j))), tag: nil, statistics: [], halfLimit: i, locations: .single(peerId))
let clippedTestIds: [Int32]
if let index = testIds.firstIndex(where: { $0 > Int32(j) }), index >= 0 {
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, limit: i)
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, halfLimit: i)
} else {
if i >= testIds.count {
clippedTestIds = testIds
@ -233,12 +228,12 @@ class MessageHistoryViewTests: XCTestCase {
for operationSetIndex in 0 ..< operationSets.count {
let operations = operationSets[operationSetIndex]
for limit in [3, 4, 5, 6, 7, 200] {
for halfLimit in [3, 4, 5, 6, 7, 200] {
for position in 10 ... 110 {
removeAllMessages()
var testIds: [MessageId.Id] = []
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, limit: limit, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, statistics: [], halfLimit: halfLimit, locations: .single(peerId))
switch state {
case let .loaded(loadedState):
for operationIndex in 0 ..< operations.count {
@ -252,20 +247,20 @@ class MessageHistoryViewTests: XCTestCase {
let attributesData = ReadBuffer(data: Data())
addMessage(Int32(insertId), Int32(insertId))
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: UInt32(insertId), stableVersion: 0, id: MessageId(peerId: peerId, namespace: namespace, id: insertId), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: insertId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
let stableId = addMessage(Int32(insertId), Int32(insertId))
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: stableId, stableVersion: 0, id: MessageId(peerId: peerId, namespace: namespace, id: insertId), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: insertId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
let ids = entries.map({ $0.message.id.id })
let clippedTestIds: [Int32]
if let index = testIds.firstIndex(where: { $0 > Int32(position) }), index >= 0 {
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, limit: limit)
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, halfLimit: halfLimit)
} else {
if limit >= testIds.count {
if halfLimit >= testIds.count {
clippedTestIds = testIds
} else {
clippedTestIds = Array(testIds.dropFirst(testIds.count - limit))
clippedTestIds = Array(testIds.dropFirst(testIds.count - halfLimit))
}
}
@ -310,12 +305,12 @@ class MessageHistoryViewTests: XCTestCase {
for operationSetIndex in 0 ..< operationSets.count {
let operations = operationSets[operationSetIndex]
for limit in [3, 4, 5, 6, 7, 200] {
for halfLimit in [3, 4, 5, 6, 7, 200] {
for position in 10 ... 110 {
removeAllMessages()
var testIds: [MessageId.Id] = []
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, limit: limit, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, statistics: [], halfLimit: halfLimit, locations: .single(peerId))
switch state {
case let .loaded(loadedState):
for operationIndex in 0 ..< operations.count {
@ -337,12 +332,12 @@ class MessageHistoryViewTests: XCTestCase {
let messageId = MessageId(peerId: peerId, namespace: namespace, id: itemId)
if isAdd {
addMessage(Int32(itemId), Int32(itemId))
let stableId = addMessage(Int32(itemId), Int32(itemId))
let attributesData = ReadBuffer(data: Data())
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: UInt32(messageId.id), stableVersion: 0, id: messageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: itemId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: stableId, stableVersion: 0, id: messageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: itemId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
} else {
removeMessage(itemId)
let _ = loadedState.remove(postbox: self.postbox!, index: MessageIndex(id: messageId, timestamp: itemId))
let _ = loadedState.remove(index: MessageIndex(id: messageId, timestamp: itemId))
}
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
@ -350,12 +345,12 @@ class MessageHistoryViewTests: XCTestCase {
let clippedTestIds: [Int32]
if let index = testIds.firstIndex(where: { $0 > Int32(position) }), index >= 0 {
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, limit: limit)
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, halfLimit: halfLimit)
} else {
if limit >= testIds.count {
if halfLimit >= testIds.count {
clippedTestIds = testIds
} else {
clippedTestIds = Array(testIds.dropFirst(testIds.count - limit))
clippedTestIds = Array(testIds.dropFirst(testIds.count - halfLimit))
}
}
@ -371,7 +366,7 @@ class MessageHistoryViewTests: XCTestCase {
func testLoadInitialHole() {
addHole(1 ... 1000, space: .everywhere)
var state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: Int32(100))), tag: nil, limit: 10, locations: .single(peerId))
var state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: Int32(100))), tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
switch state {
case .loaded:
XCTAssert(false)
@ -405,7 +400,7 @@ class MessageHistoryViewTests: XCTestCase {
default:
XCTAssert(false)
}
state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: nil, limit: 10, locations: .single(peerId), postbox: self.postbox!, holes: holes))
state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId), postbox: self.postbox!, holes: holes))
case .loadHole:
XCTAssert(false)
}
@ -421,13 +416,13 @@ class MessageHistoryViewTests: XCTestCase {
}
func testEdgeHoles1() {
addMessage(100, 100)
addMessage(200, 200)
addMessage(300, 300)
let _ = addMessage(100, 100)
let _ = addMessage(200, 200)
let _ = addMessage(300, 300)
addHole(1 ... 100, space: .everywhere)
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
guard case let .loaded(loadedState) = state else {
XCTAssert(false)
return
@ -441,13 +436,13 @@ class MessageHistoryViewTests: XCTestCase {
}
func testEdgeHoles2() {
addMessage(100, 100)
addMessage(200, 200)
addMessage(300, 300)
let _ = addMessage(100, 100)
let _ = addMessage(200, 200)
let _ = addMessage(300, 300)
addHole(1 ... 99, space: .everywhere)
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
guard case let .loaded(loadedState) = state else {
XCTAssert(false)
return
@ -461,13 +456,13 @@ class MessageHistoryViewTests: XCTestCase {
}
func testEdgeHoles3() {
addMessage(100, 100)
addMessage(200, 200)
addMessage(300, 300)
let _ = addMessage(100, 100)
let _ = addMessage(200, 200)
let _ = addMessage(300, 300)
addHole(300 ... 400, space: .everywhere)
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
guard case let .loaded(loadedState) = state else {
XCTAssert(false)
return
@ -475,19 +470,19 @@ class MessageHistoryViewTests: XCTestCase {
let sampledState = loadedState.completeAndSample(postbox: self.postbox!)
let ids = sampledState.entries.map({ $0.message.id.id })
XCTAssert(ids == [])
XCTAssert(sampledState.hole == SampledHistoryViewHole(peerId: peerId, namespace: namespace, tag: nil, indices: IndexSet(integersIn: 300 ... 400), startId: 300, endId: 1))
XCTAssert(sampledState.holesToHigher == true)
XCTAssert(sampledState.hole == SampledHistoryViewHole(peerId: peerId, namespace: namespace, tag: nil, indices: IndexSet(integersIn: 300 ... 400), startId: 400, endId: 1))
XCTAssert(sampledState.holesToHigher == false)
XCTAssert(sampledState.holesToLower == true)
}
func testEdgeHoles4() {
addMessage(100, 100)
addMessage(200, 200)
addMessage(300, 300)
let _ = addMessage(100, 100)
let _ = addMessage(200, 200)
let _ = addMessage(300, 300)
addHole(300 ... 400, space: .everywhere)
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: 200)), tag: nil, limit: 10, locations: .single(peerId))
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: 200)), tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
guard case let .loaded(loadedState) = state else {
XCTAssert(false)
return