mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-23 22:12:43 +00:00
Merge branch 'master' of https://gitlab.com/peter-iakovlev/postbox
This commit is contained in:
commit
3f2f5a82f3
99
Postbox.xcodeproj/xcshareddata/xcschemes/Postbox.xcscheme
Normal file
99
Postbox.xcodeproj/xcshareddata/xcschemes/Postbox.xcscheme
Normal 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>
|
@ -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>
|
@ -28,9 +28,11 @@ public final class Database {
|
|||||||
internal var handle: OpaquePointer? = nil
|
internal var handle: OpaquePointer? = nil
|
||||||
|
|
||||||
public init?(_ location: String) {
|
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 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 {
|
if res != SQLITE_OK {
|
||||||
postboxLog("sqlite3_open_v2: \(res)")
|
postboxLog("sqlite3_open_v2: \(res)")
|
||||||
return nil
|
return nil
|
||||||
|
@ -253,12 +253,12 @@ final class MutableMessageHistoryView {
|
|||||||
self.topTaggedMessages = topTaggedMessages
|
self.topTaggedMessages = topTaggedMessages
|
||||||
self.additionalDatas = additionalDatas
|
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 {
|
if case let .loading(loadingState) = self.state {
|
||||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||||
switch sampledState {
|
switch sampledState {
|
||||||
case let .ready(anchor, holes):
|
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)
|
self.sampledState = self.state.sample(postbox: postbox)
|
||||||
case .loadHole:
|
case .loadHole:
|
||||||
break
|
break
|
||||||
@ -270,12 +270,12 @@ final class MutableMessageHistoryView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func reset(postbox: Postbox) {
|
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 {
|
if case let .loading(loadingState) = self.state {
|
||||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||||
switch sampledState {
|
switch sampledState {
|
||||||
case let .ready(anchor, holes):
|
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:
|
case .loadHole:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -284,7 +284,7 @@ final class MutableMessageHistoryView {
|
|||||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||||
switch sampledState {
|
switch sampledState {
|
||||||
case let .ready(anchor, holes):
|
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:
|
case .loadHole:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -381,18 +381,18 @@ final class MutableMessageHistoryView {
|
|||||||
switch operation {
|
switch operation {
|
||||||
case let .InsertMessage(message):
|
case let .InsertMessage(message):
|
||||||
if unwrappedTag.isEmpty || message.tags.contains(unwrappedTag) {
|
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
|
hasChanges = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .Remove(indicesAndTags):
|
case let .Remove(indicesAndTags):
|
||||||
for (index, _) in indicesAndTags {
|
for (index, _) in indicesAndTags {
|
||||||
if loadedState.remove(postbox: postbox, index: index) {
|
if loadedState.remove(index: index) {
|
||||||
hasChanges = true
|
hasChanges = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .UpdateEmbeddedMedia(index, buffer):
|
case let .UpdateEmbeddedMedia(index, buffer):
|
||||||
if loadedState.updateEmbeddedMedia(postbox: postbox, index: index, buffer: buffer) {
|
if loadedState.updateEmbeddedMedia(index: index, buffer: buffer) {
|
||||||
hasChanges = true
|
hasChanges = true
|
||||||
}
|
}
|
||||||
case let .UpdateGroupInfos(groupInfos):
|
case let .UpdateGroupInfos(groupInfos):
|
||||||
@ -455,7 +455,7 @@ final class MutableMessageHistoryView {
|
|||||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||||
switch sampledState {
|
switch sampledState {
|
||||||
case let .ready(anchor, holes):
|
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:
|
case .loadHole:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -745,15 +745,17 @@ public final class MessageHistoryView {
|
|||||||
assert(Set(entries.map({ $0.message.stableId })).count == entries.count)
|
assert(Set(entries.map({ $0.message.stableId })).count == entries.count)
|
||||||
if !entries.isEmpty {
|
if !entries.isEmpty {
|
||||||
let anchorIndex = binaryIndexOrLower(entries, state.anchor)
|
let anchorIndex = binaryIndexOrLower(entries, state.anchor)
|
||||||
let lowerCount = mutableView.fillCount / 2 + 1
|
let lowerOrEqualThanAnchorCount = anchorIndex + 1
|
||||||
let upperCount = mutableView.fillCount / 2
|
let higherThanAnchorCount = entries.count - anchorIndex - 1
|
||||||
if anchorIndex >= 0 && entries.count - anchorIndex >= upperCount {
|
|
||||||
|
if higherThanAnchorCount > mutableView.fillCount {
|
||||||
self.laterId = entries[entries.count - 1].index
|
self.laterId = entries[entries.count - 1].index
|
||||||
entries.removeLast()
|
entries.removeLast()
|
||||||
} else {
|
} else {
|
||||||
self.laterId = nil
|
self.laterId = nil
|
||||||
}
|
}
|
||||||
if anchorIndex >= lowerCount {
|
|
||||||
|
if lowerOrEqualThanAnchorCount > mutableView.fillCount {
|
||||||
self.earlierId = entries[0].index
|
self.earlierId = entries[0].index
|
||||||
entries.removeFirst()
|
entries.removeFirst()
|
||||||
} else {
|
} else {
|
||||||
|
@ -165,13 +165,12 @@ private func binaryIndexOrLower(_ inputArr: [MutableMessageHistoryEntry], _ sear
|
|||||||
return hi
|
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 previousAnchorIndices: [PeerIdAndNamespace: Int] = [:]
|
||||||
var nextAnchorIndices: [PeerIdAndNamespace: Int] = [:]
|
var nextAnchorIndices: [PeerIdAndNamespace: Int] = [:]
|
||||||
for (space, items) in orderedEntriesBySpace {
|
for (space, items) in orderedEntriesBySpace {
|
||||||
let index = binaryIndexOrLower(items.entries, anchor)
|
previousAnchorIndices[space] = items.lowerOrAtAnchor.count - 1
|
||||||
previousAnchorIndices[space] = index
|
nextAnchorIndices[space] = 0
|
||||||
nextAnchorIndices[space] = index + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var backwardsResult: [(PeerIdAndNamespace, Int)] = []
|
var backwardsResult: [(PeerIdAndNamespace, Int)] = []
|
||||||
@ -182,7 +181,7 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
|
|||||||
for (space, value) in previousAnchorIndices {
|
for (space, value) in previousAnchorIndices {
|
||||||
if value != -1 {
|
if value != -1 {
|
||||||
if let minSpaceValue = minSpace {
|
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
|
minSpace = space
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -193,16 +192,22 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
|
|||||||
if let minSpace = minSpace {
|
if let minSpace = minSpace {
|
||||||
backwardsResult.append((minSpace, previousAnchorIndices[minSpace]!))
|
backwardsResult.append((minSpace, previousAnchorIndices[minSpace]!))
|
||||||
previousAnchorIndices[minSpace]! -= 1
|
previousAnchorIndices[minSpace]! -= 1
|
||||||
if (result.count + backwardsResult.count) == limit {
|
if backwardsResult.count == halfLimit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if minSpace == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while true {
|
||||||
var maxSpace: PeerIdAndNamespace?
|
var maxSpace: PeerIdAndNamespace?
|
||||||
for (space, value) in nextAnchorIndices {
|
for (space, value) in nextAnchorIndices {
|
||||||
if value != orderedEntriesBySpace[space]!.entries.count {
|
if value != orderedEntriesBySpace[space]!.higherThanAnchor.count {
|
||||||
if let maxSpaceValue = maxSpace {
|
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
|
maxSpace = space
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -213,16 +218,16 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
|
|||||||
if let maxSpace = maxSpace {
|
if let maxSpace = maxSpace {
|
||||||
result.append((maxSpace, nextAnchorIndices[maxSpace]!))
|
result.append((maxSpace, nextAnchorIndices[maxSpace]!))
|
||||||
nextAnchorIndices[maxSpace]! += 1
|
nextAnchorIndices[maxSpace]! += 1
|
||||||
if (result.count + backwardsResult.count) == limit {
|
if result.count == halfLimit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if minSpace == nil && maxSpace == nil {
|
if maxSpace == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return backwardsResult.reversed() + result
|
return (backwardsResult.reversed(), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SampledHistoryViewHole: Equatable {
|
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 clipRanges: [ClosedRange<MessageIndex>] = []
|
||||||
var sampledHole: (itemIndex: Int?, hole: SampledHistoryViewHole)?
|
var sampledHole: (distanceFromAnchor: Int?, hole: SampledHistoryViewHole)?
|
||||||
|
|
||||||
for (space, indices) in holes.holesBySpace {
|
for (space, indices) in holes.holesBySpace {
|
||||||
if indices.isEmpty {
|
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)
|
let holeBounds: (startId: MessageId.Id, endId: MessageId.Id)
|
||||||
switch anchor {
|
switch anchor {
|
||||||
case .lowerBound:
|
case .lowerBound:
|
||||||
@ -307,11 +312,176 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
guard let bounds = items.bounds else {
|
|
||||||
assertionFailure("A non-empty entry list should have non-nil bounds")
|
for item in items.lowerOrAtAnchor {
|
||||||
continue
|
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
|
let anchorStartingMessageId: MessageId.Id
|
||||||
if anchorIndex == -1 {
|
if anchorIndex == -1 {
|
||||||
anchorStartingMessageId = 1
|
anchorStartingMessageId = 1
|
||||||
@ -406,7 +576,7 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
higherDirectionIndex += 1
|
higherDirectionIndex += 1
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
return (clipRanges, sampledHole?.hole)
|
return (clipRanges, sampledHole?.hole)
|
||||||
}
|
}
|
||||||
@ -443,8 +613,61 @@ struct HistoryViewHoles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct OrderedHistoryViewEntries {
|
struct OrderedHistoryViewEntries {
|
||||||
var entries: [MutableMessageHistoryEntry]
|
var lowerOrAtAnchor: [MutableMessageHistoryEntry]
|
||||||
var bounds: (lower: MessageIndex, upper: MessageIndex)?
|
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 {
|
struct HistoryViewLoadedSample {
|
||||||
@ -459,17 +682,17 @@ final class HistoryViewLoadedState {
|
|||||||
let anchor: HistoryViewAnchor
|
let anchor: HistoryViewAnchor
|
||||||
let tag: MessageTags?
|
let tag: MessageTags?
|
||||||
let statistics: MessageHistoryViewOrderStatistics
|
let statistics: MessageHistoryViewOrderStatistics
|
||||||
let limit: Int
|
let halfLimit: Int
|
||||||
var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries]
|
var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries]
|
||||||
var holes: HistoryViewHoles
|
var holes: HistoryViewHoles
|
||||||
var spacesWithRemovals = Set<PeerIdAndNamespace>()
|
var spacesWithRemovals = Set<PeerIdAndNamespace>()
|
||||||
|
|
||||||
init(anchor: HistoryViewAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, limit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) {
|
init(anchor: HistoryViewAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) {
|
||||||
precondition(limit >= 3)
|
precondition(halfLimit >= 3)
|
||||||
self.anchor = anchor
|
self.anchor = anchor
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
self.statistics = statistics
|
self.statistics = statistics
|
||||||
self.limit = limit
|
self.halfLimit = halfLimit
|
||||||
self.orderedEntriesBySpace = [:]
|
self.orderedEntriesBySpace = [:]
|
||||||
self.holes = holes
|
self.holes = holes
|
||||||
|
|
||||||
@ -509,56 +732,43 @@ final class HistoryViewLoadedState {
|
|||||||
anchorIndex = upperBound
|
anchorIndex = upperBound
|
||||||
}
|
}
|
||||||
|
|
||||||
var lowerMessages: [MutableMessageHistoryEntry] = []
|
var lowerOrAtAnchorMessages: [MutableMessageHistoryEntry] = []
|
||||||
var higherMessages: [MutableMessageHistoryEntry] = []
|
var higherThanAnchorMessages: [MutableMessageHistoryEntry] = []
|
||||||
|
|
||||||
if let currentEntries = self.orderedEntriesBySpace[space], !currentEntries.entries.isEmpty {
|
if let currentEntries = self.orderedEntriesBySpace[space] {
|
||||||
let index = binaryIndexOrLower(currentEntries.entries, self.anchor)
|
lowerOrAtAnchorMessages = currentEntries.lowerOrAtAnchor.reversed()
|
||||||
if index >= 0 {
|
higherThanAnchorMessages = currentEntries.higherThanAnchor
|
||||||
lowerMessages = Array(currentEntries.entries[0 ... index].reversed())
|
|
||||||
}
|
|
||||||
if index < currentEntries.entries.count {
|
|
||||||
higherMessages = Array(currentEntries.entries[index + 1 ..< currentEntries.entries.count])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapEntry(_ message: IntermediateMessage) -> MutableMessageHistoryEntry {
|
func mapEntry(_ message: IntermediateMessage) -> MutableMessageHistoryEntry {
|
||||||
return .IntermediateMessageEntry(message, nil, nil)
|
return .IntermediateMessageEntry(message, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lowerMessages.count < self.limit / 2 + 1 {
|
if lowerOrAtAnchorMessages.count < self.halfLimit {
|
||||||
let nextLowerIndex: (index: MessageIndex, includeFrom: Bool)
|
let nextLowerIndex: (index: MessageIndex, includeFrom: Bool)
|
||||||
if let lastMessage = lowerMessages.last {
|
if let lastMessage = lowerOrAtAnchorMessages.last {
|
||||||
nextLowerIndex = (lastMessage.index, false)
|
nextLowerIndex = (lastMessage.index, false)
|
||||||
} else {
|
} else {
|
||||||
nextLowerIndex = (anchorIndex, true)
|
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
|
let nextHigherIndex: MessageIndex
|
||||||
if let lastMessage = higherMessages.last {
|
if let lastMessage = higherThanAnchorMessages.last {
|
||||||
nextHigherIndex = lastMessage.index
|
nextHigherIndex = lastMessage.index
|
||||||
} else {
|
} else {
|
||||||
nextHigherIndex = anchorIndex
|
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 {
|
lowerOrAtAnchorMessages.reverse()
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
var messages: [MutableMessageHistoryEntry] = []
|
assert(lowerOrAtAnchorMessages.count <= self.halfLimit)
|
||||||
messages.append(contentsOf: lowerMessages.reversed())
|
assert(higherThanAnchorMessages.count <= self.halfLimit)
|
||||||
messages.append(contentsOf: higherMessages)
|
|
||||||
|
|
||||||
assert(messages.count <= self.limit)
|
/*if let tag = self.tag, self.statistics.contains(.combinedLocation) {
|
||||||
|
|
||||||
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 first = messages.first {
|
if let first = messages.first {
|
||||||
let messageIndex = first.index
|
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)
|
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)
|
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 {
|
func insertHole(space: PeerIdAndNamespace, range: ClosedRange<MessageId.Id>) -> Bool {
|
||||||
@ -618,15 +828,14 @@ final class HistoryViewLoadedState {
|
|||||||
if self.orderedEntriesBySpace[space] == nil {
|
if self.orderedEntriesBySpace[space] == nil {
|
||||||
return false
|
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
|
return false
|
||||||
}
|
}
|
||||||
let entry = self.orderedEntriesBySpace[space]!.entries[entryIndex]
|
|
||||||
var updated = false
|
var updated = false
|
||||||
if self.remove(postbox: postbox, index: index) {
|
if self.remove(index: index) {
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
if self.add(postbox: postbox, entry: entry.updatedTimestamp(timestamp)) {
|
if self.add(entry: entry.updatedTimestamp(timestamp)) {
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
return updated
|
return updated
|
||||||
@ -646,43 +855,46 @@ final class HistoryViewLoadedState {
|
|||||||
if self.orderedEntriesBySpace[space] == nil {
|
if self.orderedEntriesBySpace[space] == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for i in 0 ..< self.orderedEntriesBySpace[space]!.entries.count {
|
let spaceUpdated = self.orderedEntriesBySpace[space]!.mutableScan({ entry in
|
||||||
if let groupInfo = spaceMapping[self.orderedEntriesBySpace[space]!.entries[i].index.id.id] {
|
if let groupInfo = spaceMapping[entry.index.id.id] {
|
||||||
updated = true
|
updated = true
|
||||||
switch self.orderedEntriesBySpace[space]!.entries[i] {
|
switch entry {
|
||||||
case let .IntermediateMessageEntry(message, location, monthLocation):
|
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):
|
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
|
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)
|
let space = PeerIdAndNamespace(peerId: index.id.peerId, namespace: index.id.namespace)
|
||||||
if self.orderedEntriesBySpace[space] == nil {
|
if self.orderedEntriesBySpace[space] == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
guard let itemIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) else {
|
|
||||||
return false
|
return self.orderedEntriesBySpace[space]!.update(index: index, { entry in
|
||||||
}
|
switch entry {
|
||||||
switch self.orderedEntriesBySpace[space]!.entries[itemIndex] {
|
case let .IntermediateMessageEntry(message, location, monthLocation):
|
||||||
case let .IntermediateMessageEntry(message, location, monthLocation):
|
return .IntermediateMessageEntry(message.withUpdatedEmbeddedMedia(buffer), location, monthLocation)
|
||||||
self.orderedEntriesBySpace[space]!.entries[itemIndex] = .IntermediateMessageEntry(message.withUpdatedEmbeddedMedia(buffer), location, monthLocation)
|
case let .MessageEntry(messageEntry):
|
||||||
case let .MessageEntry(messageEntry):
|
return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
|
||||||
self.orderedEntriesBySpace[space]!.entries[itemIndex] = .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
|
}
|
||||||
}
|
})
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMedia(updatedMedia: [MediaId: Media?]) -> Bool {
|
func updateMedia(updatedMedia: [MediaId: Media?]) -> Bool {
|
||||||
var hasChanges = false
|
var updated = false
|
||||||
for space in self.orderedEntriesBySpace.keys {
|
for space in self.orderedEntriesBySpace.keys {
|
||||||
for i in 0 ..< self.orderedEntriesBySpace[space]!.entries.count {
|
let spaceUpdated = self.orderedEntriesBySpace[space]!.mutableScan({ entry in
|
||||||
switch self.orderedEntriesBySpace[space]!.entries[i] {
|
switch entry {
|
||||||
case let .MessageEntry(value):
|
case let .MessageEntry(value):
|
||||||
let message = value.message
|
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)
|
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))
|
return .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
|
|
||||||
}
|
}
|
||||||
|
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)
|
let space = PeerIdAndNamespace(peerId: entry.index.id.peerId, namespace: entry.index.id.namespace)
|
||||||
|
|
||||||
if self.orderedEntriesBySpace[space] == nil {
|
if self.orderedEntriesBySpace[space] == nil {
|
||||||
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(entries: [], bounds: nil)
|
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(lowerOrAtAnchor: [], higherThanAnchor: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
var updated = false
|
var updated = false
|
||||||
@ -763,66 +959,48 @@ final class HistoryViewLoadedState {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
let insertionIndex = binaryInsertionIndex(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: entry.index)
|
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]!.entries.count {
|
|
||||||
if self.orderedEntriesBySpace[space]!.entries[insertionIndex].index == entry.index {
|
if insertionIndex < self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count {
|
||||||
assertionFailure("Inserting an existing index is not allowed")
|
if self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[insertionIndex].index == entry.index {
|
||||||
self.orderedEntriesBySpace[space]!.entries[insertionIndex] = entry
|
assertionFailure("Inserting an existing index is not allowed")
|
||||||
return true
|
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[insertionIndex] = entry
|
||||||
}
|
return true
|
||||||
}
|
|
||||||
|
|
||||||
var shouldBeAdded = false
|
|
||||||
if insertionIndex == 0 {
|
|
||||||
if let bounds = self.orderedEntriesBySpace[space]!.bounds {
|
|
||||||
if entry.index <= bounds.lower {
|
|
||||||
shouldBeAdded = 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 {
|
if insertionIndex == 0 && self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count >= self.halfLimit {
|
||||||
let anchorIndex = binaryIndexOrLower(self.orderedEntriesBySpace[space]!.entries, self.anchor)
|
return updated
|
||||||
if anchorIndex > self.limit / 2 {
|
}
|
||||||
self.orderedEntriesBySpace[space]!.entries.removeFirst()
|
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.insert(entry, at: insertionIndex)
|
||||||
} else {
|
if self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count > self.halfLimit {
|
||||||
self.orderedEntriesBySpace[space]!.entries.removeLast()
|
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)
|
let space = PeerIdAndNamespace(peerId: index.id.peerId, namespace: index.id.namespace)
|
||||||
if self.orderedEntriesBySpace[space] == nil {
|
if self.orderedEntriesBySpace[space] == nil {
|
||||||
return false
|
return false
|
||||||
@ -842,16 +1020,7 @@ final class HistoryViewLoadedState {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if let itemIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) {
|
if self.orderedEntriesBySpace[space]!.remove(index: 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)
|
|
||||||
self.spacesWithRemovals.insert(space)
|
self.spacesWithRemovals.insert(space)
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
@ -866,48 +1035,62 @@ final class HistoryViewLoadedState {
|
|||||||
}
|
}
|
||||||
self.spacesWithRemovals.removeAll()
|
self.spacesWithRemovals.removeAll()
|
||||||
}
|
}
|
||||||
let combinedSpacesAndIndices = sampleEntries(orderedEntriesBySpace: self.orderedEntriesBySpace, anchor: self.anchor, limit: self.limit)
|
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)
|
let (clipRanges, sampledHole) = sampleHoleRanges(orderedEntriesBySpace: self.orderedEntriesBySpace, holes: self.holes, anchor: self.anchor, tag: self.tag, halfLimit: self.halfLimit)
|
||||||
|
|
||||||
var holesToLower = false
|
var holesToLower = false
|
||||||
var holesToHigher = false
|
var holesToHigher = false
|
||||||
var result: [MessageHistoryMessageEntry] = []
|
var result: [MessageHistoryMessageEntry] = []
|
||||||
if combinedSpacesAndIndices.isEmpty {
|
if combinedSpacesAndIndicesByDirection.lowerOrAtAnchor.isEmpty && combinedSpacesAndIndicesByDirection.higherThanAnchor.isEmpty {
|
||||||
if !clipRanges.isEmpty {
|
if !clipRanges.isEmpty {
|
||||||
holesToLower = true
|
holesToLower = true
|
||||||
holesToHigher = true
|
holesToHigher = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outer: for i in 0 ..< combinedSpacesAndIndices.count {
|
let directions = [combinedSpacesAndIndicesByDirection.lowerOrAtAnchor, combinedSpacesAndIndicesByDirection.higherThanAnchor]
|
||||||
let (space, index) = combinedSpacesAndIndices[i]
|
for directionIndex in 0 ..< directions.count {
|
||||||
|
outer: for i in 0 ..< directions[directionIndex].count {
|
||||||
if !clipRanges.isEmpty {
|
let (space, index) = directions[directionIndex][i]
|
||||||
let entryIndex = self.orderedEntriesBySpace[space]!.entries[index].index
|
|
||||||
for range in clipRanges {
|
let entry: MutableMessageHistoryEntry
|
||||||
if range.contains(entryIndex) {
|
if directionIndex == 0 {
|
||||||
if i == 0 {
|
entry = self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[index]
|
||||||
holesToLower = true
|
} 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 entry {
|
||||||
switch self.orderedEntriesBySpace[space]!.entries[index] {
|
case let .MessageEntry(value):
|
||||||
case let .MessageEntry(value):
|
result.append(value)
|
||||||
result.append(value)
|
case let .IntermediateMessageEntry(message, location, monthLocation):
|
||||||
case let .IntermediateMessageEntry(message, location, monthLocation):
|
let renderedMessage = postbox.messageHistoryTable.renderMessage(message, peerTable: postbox.peerTable)
|
||||||
let renderedMessage = postbox.messageHistoryTable.renderMessage(message, peerTable: postbox.peerTable)
|
var authorIsContact = false
|
||||||
var authorIsContact = false
|
if let author = renderedMessage.author {
|
||||||
if let author = renderedMessage.author {
|
authorIsContact = postbox.contactsTable.isContact(peerId: author.id)
|
||||||
authorIsContact = postbox.contactsTable.isContact(peerId: author.id)
|
}
|
||||||
}
|
let entry = MessageHistoryMessageEntry(message: renderedMessage, location: location, monthLocation: monthLocation, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: authorIsContact))
|
||||||
let entry = MessageHistoryMessageEntry(message: renderedMessage, location: location, monthLocation: monthLocation, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: authorIsContact))
|
if directionIndex == 0 {
|
||||||
self.orderedEntriesBySpace[space]!.entries[index] = .MessageEntry(entry)
|
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[index] = .MessageEntry(entry)
|
||||||
result.append(entry)
|
} else {
|
||||||
|
self.orderedEntriesBySpace[space]!.higherThanAnchor[index] = .MessageEntry(entry)
|
||||||
|
}
|
||||||
|
result.append(entry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -948,13 +1131,13 @@ enum HistoryViewLoadingSample {
|
|||||||
final class HistoryViewLoadingState {
|
final class HistoryViewLoadingState {
|
||||||
var messageId: MessageId
|
var messageId: MessageId
|
||||||
let tag: MessageTags?
|
let tag: MessageTags?
|
||||||
let limit: Int
|
let halfLimit: Int
|
||||||
var holes: HistoryViewHoles
|
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.messageId = messageId
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
self.limit = limit
|
self.halfLimit = halfLimit
|
||||||
self.holes = HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))
|
self.holes = HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -995,14 +1178,14 @@ enum HistoryViewState {
|
|||||||
case loaded(HistoryViewLoadedState)
|
case loaded(HistoryViewLoadedState)
|
||||||
case loading(HistoryViewLoadingState)
|
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 {
|
switch inputAnchor {
|
||||||
case let .index(index):
|
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:
|
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:
|
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:
|
case .unread:
|
||||||
let anchorPeerId: PeerId
|
let anchorPeerId: PeerId
|
||||||
switch locations {
|
switch locations {
|
||||||
@ -1035,26 +1218,26 @@ enum HistoryViewState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let messageId = messageId {
|
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)
|
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||||
switch sampledState {
|
switch sampledState {
|
||||||
case let .ready(anchor, holes):
|
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:
|
case .loadHole:
|
||||||
self = .loading(loadingState)
|
self = .loading(loadingState)
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
} else {
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
}
|
}
|
||||||
case let .message(messageId):
|
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)
|
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||||
switch sampledState {
|
switch sampledState {
|
||||||
case let .ready(anchor, holes):
|
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:
|
case .loadHole:
|
||||||
self = .loading(loadingState)
|
self = .loading(loadingState)
|
||||||
}
|
}
|
||||||
|
@ -935,7 +935,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
|||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
//debugSaveState(basePath: basePath, name: "previous1")
|
//debugSaveState(basePath: basePath, name: "previous1")
|
||||||
//debugRestoreState(basePath: basePath, name: "previous1")
|
debugRestoreState(basePath: basePath, name: "previous1")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let startTime = CFAbsoluteTimeGetCurrent()
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
@ -159,6 +159,7 @@ public final class SqliteValueBox: ValueBox {
|
|||||||
private let lock = NSRecursiveLock()
|
private let lock = NSRecursiveLock()
|
||||||
|
|
||||||
fileprivate let basePath: String
|
fileprivate let basePath: String
|
||||||
|
private let inMemory: Bool
|
||||||
private let encryptionParameters: ValueBoxEncryptionParameters?
|
private let encryptionParameters: ValueBoxEncryptionParameters?
|
||||||
private let databasePath: String
|
private let databasePath: String
|
||||||
private var database: Database!
|
private var database: Database!
|
||||||
@ -196,8 +197,9 @@ public final class SqliteValueBox: ValueBox {
|
|||||||
|
|
||||||
private let queue: Queue
|
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.basePath = basePath
|
||||||
|
self.inMemory = inMemory
|
||||||
self.encryptionParameters = encryptionParameters
|
self.encryptionParameters = encryptionParameters
|
||||||
self.databasePath = basePath + "/db_sqlite"
|
self.databasePath = basePath + "/db_sqlite"
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
@ -247,7 +249,7 @@ public final class SqliteValueBox: ValueBox {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
var database: Database
|
var database: Database
|
||||||
if let result = Database(path) {
|
if let result = Database(self.inMemory ? ":memory:" : path) {
|
||||||
database = result
|
database = result
|
||||||
} else {
|
} else {
|
||||||
postboxLog("Couldn't open DB")
|
postboxLog("Couldn't open DB")
|
||||||
|
@ -22,7 +22,7 @@ private extension MessageTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MessageHistoryIndexTableTests: XCTestCase {
|
class MessageHistoryIndexTableTests: XCTestCase {
|
||||||
var valueBox: ValueBox?
|
var valueBox: SqliteValueBox?
|
||||||
var path: String?
|
var path: String?
|
||||||
|
|
||||||
var postbox: Postbox?
|
var postbox: Postbox?
|
||||||
@ -45,7 +45,7 @@ class MessageHistoryIndexTableTests: XCTestCase {
|
|||||||
arc4random_buf(bytes, 16)
|
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>]] = [
|
let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [
|
||||||
peerId.namespace: [
|
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!)
|
self.postbox = Postbox(queue: Queue.mainQueue(), basePath: path!, seedConfiguration: seedConfiguration, valueBox: self.valueBox!)
|
||||||
}
|
}
|
||||||
|
@ -10,42 +10,33 @@ import SwiftSignalKit
|
|||||||
private let peerId = PeerId(namespace: 1, id: 1)
|
private let peerId = PeerId(namespace: 1, id: 1)
|
||||||
private let namespace: Int32 = 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 lower: [Int32] = []
|
||||||
var higher: [Int32] = []
|
var higher: [Int32] = []
|
||||||
|
|
||||||
var i = aroundIndex
|
var i = aroundIndex
|
||||||
while i >= 0 && lower.count < limit / 2 + 1 {
|
while i >= 0 && lower.count < halfLimit {
|
||||||
lower.append(array[i])
|
lower.append(array[i])
|
||||||
i -= 1
|
i -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var j = aroundIndex + 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])
|
higher.append(array[j])
|
||||||
j += 1
|
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] = []
|
var result: [Int32] = []
|
||||||
result.append(contentsOf: lower.reversed())
|
result.append(contentsOf: lower.reversed())
|
||||||
result.append(contentsOf: higher)
|
result.append(contentsOf: higher)
|
||||||
|
|
||||||
assert(result.count <= limit)
|
assert(result.count <= halfLimit * 2)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageHistoryViewTests: XCTestCase {
|
class MessageHistoryViewTests: XCTestCase {
|
||||||
var valueBox: ValueBox?
|
var valueBox: SqliteValueBox?
|
||||||
var path: String?
|
var path: String?
|
||||||
|
|
||||||
var postbox: Postbox?
|
var postbox: Postbox?
|
||||||
@ -68,7 +59,7 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
arc4random_buf(bytes, 16)
|
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>]] = [
|
let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [
|
||||||
peerId.namespace: [:
|
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!)
|
self.postbox = Postbox(queue: Queue.mainQueue(), basePath: path!, seedConfiguration: seedConfiguration, valueBox: self.valueBox!)
|
||||||
}
|
}
|
||||||
@ -101,10 +92,14 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
}).start()
|
}).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 _ = 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()
|
}).start()
|
||||||
|
return stableId!
|
||||||
}
|
}
|
||||||
|
|
||||||
private func removeMessage(_ id: Int32) {
|
private func removeMessage(_ id: Int32) {
|
||||||
@ -120,7 +115,7 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testEmpty() {
|
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 {
|
switch state {
|
||||||
case let .loaded(loadedState):
|
case let .loaded(loadedState):
|
||||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||||
@ -134,10 +129,10 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
var testIds: [MessageId.Id] = []
|
var testIds: [MessageId.Id] = []
|
||||||
for i in 1 ..< 11 {
|
for i in 1 ..< 11 {
|
||||||
testIds.append(Int32(i * 10))
|
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 {
|
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 {
|
switch state {
|
||||||
case let .loaded(loadedState):
|
case let .loaded(loadedState):
|
||||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||||
@ -154,7 +149,7 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i in 3 ... testIds.count + 10 {
|
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 {
|
switch state {
|
||||||
case let .loaded(loadedState):
|
case let .loaded(loadedState):
|
||||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||||
@ -172,11 +167,11 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
for i in 3 ... testIds.count + 10 {
|
for i in 3 ... testIds.count + 10 {
|
||||||
for j in testIds[0] - 10 ... testIds.last! + 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]
|
let clippedTestIds: [Int32]
|
||||||
if let index = testIds.firstIndex(where: { $0 > Int32(j) }), index >= 0 {
|
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 {
|
} else {
|
||||||
if i >= testIds.count {
|
if i >= testIds.count {
|
||||||
clippedTestIds = testIds
|
clippedTestIds = testIds
|
||||||
@ -233,12 +228,12 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
|
|
||||||
for operationSetIndex in 0 ..< operationSets.count {
|
for operationSetIndex in 0 ..< operationSets.count {
|
||||||
let operations = operationSets[operationSetIndex]
|
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 {
|
for position in 10 ... 110 {
|
||||||
removeAllMessages()
|
removeAllMessages()
|
||||||
|
|
||||||
var testIds: [MessageId.Id] = []
|
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 {
|
switch state {
|
||||||
case let .loaded(loadedState):
|
case let .loaded(loadedState):
|
||||||
for operationIndex in 0 ..< operations.count {
|
for operationIndex in 0 ..< operations.count {
|
||||||
@ -252,20 +247,20 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
|
|
||||||
let attributesData = ReadBuffer(data: Data())
|
let attributesData = ReadBuffer(data: Data())
|
||||||
|
|
||||||
addMessage(Int32(insertId), Int32(insertId))
|
let stableId = 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 _ = 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 entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||||
let ids = entries.map({ $0.message.id.id })
|
let ids = entries.map({ $0.message.id.id })
|
||||||
|
|
||||||
let clippedTestIds: [Int32]
|
let clippedTestIds: [Int32]
|
||||||
if let index = testIds.firstIndex(where: { $0 > Int32(position) }), index >= 0 {
|
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 {
|
} else {
|
||||||
if limit >= testIds.count {
|
if halfLimit >= testIds.count {
|
||||||
clippedTestIds = testIds
|
clippedTestIds = testIds
|
||||||
} else {
|
} 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 {
|
for operationSetIndex in 0 ..< operationSets.count {
|
||||||
let operations = operationSets[operationSetIndex]
|
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 {
|
for position in 10 ... 110 {
|
||||||
removeAllMessages()
|
removeAllMessages()
|
||||||
|
|
||||||
var testIds: [MessageId.Id] = []
|
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 {
|
switch state {
|
||||||
case let .loaded(loadedState):
|
case let .loaded(loadedState):
|
||||||
for operationIndex in 0 ..< operations.count {
|
for operationIndex in 0 ..< operations.count {
|
||||||
@ -337,12 +332,12 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
|
|
||||||
let messageId = MessageId(peerId: peerId, namespace: namespace, id: itemId)
|
let messageId = MessageId(peerId: peerId, namespace: namespace, id: itemId)
|
||||||
if isAdd {
|
if isAdd {
|
||||||
addMessage(Int32(itemId), Int32(itemId))
|
let stableId = addMessage(Int32(itemId), Int32(itemId))
|
||||||
let attributesData = ReadBuffer(data: Data())
|
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 {
|
} else {
|
||||||
removeMessage(itemId)
|
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
|
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||||
@ -350,12 +345,12 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
|
|
||||||
let clippedTestIds: [Int32]
|
let clippedTestIds: [Int32]
|
||||||
if let index = testIds.firstIndex(where: { $0 > Int32(position) }), index >= 0 {
|
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 {
|
} else {
|
||||||
if limit >= testIds.count {
|
if halfLimit >= testIds.count {
|
||||||
clippedTestIds = testIds
|
clippedTestIds = testIds
|
||||||
} else {
|
} else {
|
||||||
clippedTestIds = Array(testIds.dropFirst(testIds.count - limit))
|
clippedTestIds = Array(testIds.dropFirst(testIds.count - halfLimit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,7 +366,7 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
|
|
||||||
func testLoadInitialHole() {
|
func testLoadInitialHole() {
|
||||||
addHole(1 ... 1000, space: .everywhere)
|
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 {
|
switch state {
|
||||||
case .loaded:
|
case .loaded:
|
||||||
XCTAssert(false)
|
XCTAssert(false)
|
||||||
@ -405,7 +400,7 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
default:
|
default:
|
||||||
XCTAssert(false)
|
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:
|
case .loadHole:
|
||||||
XCTAssert(false)
|
XCTAssert(false)
|
||||||
}
|
}
|
||||||
@ -421,13 +416,13 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testEdgeHoles1() {
|
func testEdgeHoles1() {
|
||||||
addMessage(100, 100)
|
let _ = addMessage(100, 100)
|
||||||
addMessage(200, 200)
|
let _ = addMessage(200, 200)
|
||||||
addMessage(300, 300)
|
let _ = addMessage(300, 300)
|
||||||
|
|
||||||
addHole(1 ... 100, space: .everywhere)
|
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 {
|
guard case let .loaded(loadedState) = state else {
|
||||||
XCTAssert(false)
|
XCTAssert(false)
|
||||||
return
|
return
|
||||||
@ -441,13 +436,13 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testEdgeHoles2() {
|
func testEdgeHoles2() {
|
||||||
addMessage(100, 100)
|
let _ = addMessage(100, 100)
|
||||||
addMessage(200, 200)
|
let _ = addMessage(200, 200)
|
||||||
addMessage(300, 300)
|
let _ = addMessage(300, 300)
|
||||||
|
|
||||||
addHole(1 ... 99, space: .everywhere)
|
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 {
|
guard case let .loaded(loadedState) = state else {
|
||||||
XCTAssert(false)
|
XCTAssert(false)
|
||||||
return
|
return
|
||||||
@ -461,13 +456,13 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testEdgeHoles3() {
|
func testEdgeHoles3() {
|
||||||
addMessage(100, 100)
|
let _ = addMessage(100, 100)
|
||||||
addMessage(200, 200)
|
let _ = addMessage(200, 200)
|
||||||
addMessage(300, 300)
|
let _ = addMessage(300, 300)
|
||||||
|
|
||||||
addHole(300 ... 400, space: .everywhere)
|
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 {
|
guard case let .loaded(loadedState) = state else {
|
||||||
XCTAssert(false)
|
XCTAssert(false)
|
||||||
return
|
return
|
||||||
@ -475,19 +470,19 @@ class MessageHistoryViewTests: XCTestCase {
|
|||||||
let sampledState = loadedState.completeAndSample(postbox: self.postbox!)
|
let sampledState = loadedState.completeAndSample(postbox: self.postbox!)
|
||||||
let ids = sampledState.entries.map({ $0.message.id.id })
|
let ids = sampledState.entries.map({ $0.message.id.id })
|
||||||
XCTAssert(ids == [])
|
XCTAssert(ids == [])
|
||||||
XCTAssert(sampledState.hole == SampledHistoryViewHole(peerId: peerId, namespace: namespace, tag: nil, indices: IndexSet(integersIn: 300 ... 400), startId: 300, endId: 1))
|
XCTAssert(sampledState.hole == SampledHistoryViewHole(peerId: peerId, namespace: namespace, tag: nil, indices: IndexSet(integersIn: 300 ... 400), startId: 400, endId: 1))
|
||||||
XCTAssert(sampledState.holesToHigher == true)
|
XCTAssert(sampledState.holesToHigher == false)
|
||||||
XCTAssert(sampledState.holesToLower == true)
|
XCTAssert(sampledState.holesToLower == true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEdgeHoles4() {
|
func testEdgeHoles4() {
|
||||||
addMessage(100, 100)
|
let _ = addMessage(100, 100)
|
||||||
addMessage(200, 200)
|
let _ = addMessage(200, 200)
|
||||||
addMessage(300, 300)
|
let _ = addMessage(300, 300)
|
||||||
|
|
||||||
addHole(300 ... 400, space: .everywhere)
|
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 {
|
guard case let .loaded(loadedState) = state else {
|
||||||
XCTAssert(false)
|
XCTAssert(false)
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user