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
|
||||
|
||||
public init?(_ location: String) {
|
||||
if location != ":memory:" {
|
||||
let _ = open(location + "-guard", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)
|
||||
}
|
||||
let flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX
|
||||
let res = sqlite3_open_v2(location.description, &self.handle, flags, nil)
|
||||
let res = sqlite3_open_v2(location, &self.handle, flags, nil)
|
||||
if res != SQLITE_OK {
|
||||
postboxLog("sqlite3_open_v2: \(res)")
|
||||
return nil
|
||||
|
@ -253,12 +253,12 @@ final class MutableMessageHistoryView {
|
||||
self.topTaggedMessages = topTaggedMessages
|
||||
self.additionalDatas = additionalDatas
|
||||
|
||||
self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, statistics: self.orderStatistics, limit: count + 2, locations: peerIds)
|
||||
self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds)
|
||||
if case let .loading(loadingState) = self.state {
|
||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||
switch sampledState {
|
||||
case let .ready(anchor, holes):
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: self.orderStatistics, limit: count + 2, locations: peerIds, postbox: postbox, holes: holes))
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds, postbox: postbox, holes: holes))
|
||||
self.sampledState = self.state.sample(postbox: postbox)
|
||||
case .loadHole:
|
||||
break
|
||||
@ -270,12 +270,12 @@ final class MutableMessageHistoryView {
|
||||
}
|
||||
|
||||
private func reset(postbox: Postbox) {
|
||||
self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds)
|
||||
self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds)
|
||||
if case let .loading(loadingState) = self.state {
|
||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||
switch sampledState {
|
||||
case let .ready(anchor, holes):
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds, postbox: postbox, holes: holes))
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes))
|
||||
case .loadHole:
|
||||
break
|
||||
}
|
||||
@ -284,7 +284,7 @@ final class MutableMessageHistoryView {
|
||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||
switch sampledState {
|
||||
case let .ready(anchor, holes):
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds, postbox: postbox, holes: holes))
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes))
|
||||
case .loadHole:
|
||||
break
|
||||
}
|
||||
@ -381,18 +381,18 @@ final class MutableMessageHistoryView {
|
||||
switch operation {
|
||||
case let .InsertMessage(message):
|
||||
if unwrappedTag.isEmpty || message.tags.contains(unwrappedTag) {
|
||||
if loadedState.add(postbox: postbox, entry: .IntermediateMessageEntry(message, nil, nil)) {
|
||||
if loadedState.add(entry: .IntermediateMessageEntry(message, nil, nil)) {
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
case let .Remove(indicesAndTags):
|
||||
for (index, _) in indicesAndTags {
|
||||
if loadedState.remove(postbox: postbox, index: index) {
|
||||
if loadedState.remove(index: index) {
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
case let .UpdateEmbeddedMedia(index, buffer):
|
||||
if loadedState.updateEmbeddedMedia(postbox: postbox, index: index, buffer: buffer) {
|
||||
if loadedState.updateEmbeddedMedia(index: index, buffer: buffer) {
|
||||
hasChanges = true
|
||||
}
|
||||
case let .UpdateGroupInfos(groupInfos):
|
||||
@ -455,7 +455,7 @@ final class MutableMessageHistoryView {
|
||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||
switch sampledState {
|
||||
case let .ready(anchor, holes):
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, limit: self.fillCount + 2, locations: self.peerIds, postbox: postbox, holes: holes))
|
||||
self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, statistics: self.orderStatistics, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes))
|
||||
case .loadHole:
|
||||
break
|
||||
}
|
||||
@ -745,15 +745,17 @@ public final class MessageHistoryView {
|
||||
assert(Set(entries.map({ $0.message.stableId })).count == entries.count)
|
||||
if !entries.isEmpty {
|
||||
let anchorIndex = binaryIndexOrLower(entries, state.anchor)
|
||||
let lowerCount = mutableView.fillCount / 2 + 1
|
||||
let upperCount = mutableView.fillCount / 2
|
||||
if anchorIndex >= 0 && entries.count - anchorIndex >= upperCount {
|
||||
let lowerOrEqualThanAnchorCount = anchorIndex + 1
|
||||
let higherThanAnchorCount = entries.count - anchorIndex - 1
|
||||
|
||||
if higherThanAnchorCount > mutableView.fillCount {
|
||||
self.laterId = entries[entries.count - 1].index
|
||||
entries.removeLast()
|
||||
} else {
|
||||
self.laterId = nil
|
||||
}
|
||||
if anchorIndex >= lowerCount {
|
||||
|
||||
if lowerOrEqualThanAnchorCount > mutableView.fillCount {
|
||||
self.earlierId = entries[0].index
|
||||
entries.removeFirst()
|
||||
} else {
|
||||
|
@ -165,13 +165,12 @@ private func binaryIndexOrLower(_ inputArr: [MutableMessageHistoryEntry], _ sear
|
||||
return hi
|
||||
}
|
||||
|
||||
private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], anchor: HistoryViewAnchor, limit: Int) -> [(PeerIdAndNamespace, Int)] {
|
||||
private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], anchor: HistoryViewAnchor, halfLimit: Int) -> (lowerOrAtAnchor:[(PeerIdAndNamespace, Int)], higherThanAnchor: [(PeerIdAndNamespace, Int)]) {
|
||||
var previousAnchorIndices: [PeerIdAndNamespace: Int] = [:]
|
||||
var nextAnchorIndices: [PeerIdAndNamespace: Int] = [:]
|
||||
for (space, items) in orderedEntriesBySpace {
|
||||
let index = binaryIndexOrLower(items.entries, anchor)
|
||||
previousAnchorIndices[space] = index
|
||||
nextAnchorIndices[space] = index + 1
|
||||
previousAnchorIndices[space] = items.lowerOrAtAnchor.count - 1
|
||||
nextAnchorIndices[space] = 0
|
||||
}
|
||||
|
||||
var backwardsResult: [(PeerIdAndNamespace, Int)] = []
|
||||
@ -182,7 +181,7 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
|
||||
for (space, value) in previousAnchorIndices {
|
||||
if value != -1 {
|
||||
if let minSpaceValue = minSpace {
|
||||
if orderedEntriesBySpace[space]!.entries[value].index > orderedEntriesBySpace[minSpaceValue]!.entries[previousAnchorIndices[minSpaceValue]!].index {
|
||||
if orderedEntriesBySpace[space]!.lowerOrAtAnchor[value].index > orderedEntriesBySpace[minSpaceValue]!.lowerOrAtAnchor[previousAnchorIndices[minSpaceValue]!].index {
|
||||
minSpace = space
|
||||
}
|
||||
} else {
|
||||
@ -193,16 +192,22 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
|
||||
if let minSpace = minSpace {
|
||||
backwardsResult.append((minSpace, previousAnchorIndices[minSpace]!))
|
||||
previousAnchorIndices[minSpace]! -= 1
|
||||
if (result.count + backwardsResult.count) == limit {
|
||||
if backwardsResult.count == halfLimit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if minSpace == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
while true {
|
||||
var maxSpace: PeerIdAndNamespace?
|
||||
for (space, value) in nextAnchorIndices {
|
||||
if value != orderedEntriesBySpace[space]!.entries.count {
|
||||
if value != orderedEntriesBySpace[space]!.higherThanAnchor.count {
|
||||
if let maxSpaceValue = maxSpace {
|
||||
if orderedEntriesBySpace[space]!.entries[value].index < orderedEntriesBySpace[maxSpaceValue]!.entries[nextAnchorIndices[maxSpaceValue]!].index {
|
||||
if orderedEntriesBySpace[space]!.higherThanAnchor[value].index < orderedEntriesBySpace[maxSpaceValue]!.higherThanAnchor[nextAnchorIndices[maxSpaceValue]!].index {
|
||||
maxSpace = space
|
||||
}
|
||||
} else {
|
||||
@ -213,16 +218,16 @@ private func sampleEntries(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHi
|
||||
if let maxSpace = maxSpace {
|
||||
result.append((maxSpace, nextAnchorIndices[maxSpace]!))
|
||||
nextAnchorIndices[maxSpace]! += 1
|
||||
if (result.count + backwardsResult.count) == limit {
|
||||
if result.count == halfLimit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if minSpace == nil && maxSpace == nil {
|
||||
if maxSpace == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return backwardsResult.reversed() + result
|
||||
return (backwardsResult.reversed(), result)
|
||||
}
|
||||
|
||||
struct SampledHistoryViewHole: Equatable {
|
||||
@ -274,9 +279,9 @@ private func isIndex(index: MessageIndex, closerTo anchor: HistoryViewAnchor, th
|
||||
}
|
||||
}
|
||||
|
||||
private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], holes: HistoryViewHoles, anchor: HistoryViewAnchor, tag: MessageTags?) -> (clipRanges: [ClosedRange<MessageIndex>], sampledHole: SampledHistoryViewHole?) {
|
||||
private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries], holes: HistoryViewHoles, anchor: HistoryViewAnchor, tag: MessageTags?, halfLimit: Int) -> (clipRanges: [ClosedRange<MessageIndex>], sampledHole: SampledHistoryViewHole?) {
|
||||
var clipRanges: [ClosedRange<MessageIndex>] = []
|
||||
var sampledHole: (itemIndex: Int?, hole: SampledHistoryViewHole)?
|
||||
var sampledHole: (distanceFromAnchor: Int?, hole: SampledHistoryViewHole)?
|
||||
|
||||
for (space, indices) in holes.holesBySpace {
|
||||
if indices.isEmpty {
|
||||
@ -292,7 +297,7 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
|
||||
}
|
||||
}
|
||||
}
|
||||
guard let items = orderedEntriesBySpace[space], !items.entries.isEmpty else {
|
||||
guard let items = orderedEntriesBySpace[space], (!items.lowerOrAtAnchor.isEmpty || !items.higherThanAnchor.isEmpty) else {
|
||||
let holeBounds: (startId: MessageId.Id, endId: MessageId.Id)
|
||||
switch anchor {
|
||||
case .lowerBound:
|
||||
@ -307,11 +312,176 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
|
||||
continue
|
||||
}
|
||||
}
|
||||
guard let bounds = items.bounds else {
|
||||
assertionFailure("A non-empty entry list should have non-nil bounds")
|
||||
continue
|
||||
|
||||
for item in items.lowerOrAtAnchor {
|
||||
if item.index.id.id == 76891 {
|
||||
assert(true)
|
||||
}
|
||||
let anchorIndex = binaryIndexOrLower(items.entries, anchor)
|
||||
}
|
||||
for item in items.higherThanAnchor {
|
||||
if item.index.id.id == 76891 {
|
||||
assert(true)
|
||||
}
|
||||
}
|
||||
|
||||
var lowerOrAtAnchorHole: (distanceFromAnchor: Int, hole: SampledHistoryViewHole)?
|
||||
|
||||
for i in (-1 ..< items.lowerOrAtAnchor.count).reversed() {
|
||||
let startingMessageId: MessageId.Id
|
||||
if items.higherThanAnchor.isEmpty {
|
||||
startingMessageId = Int32.max - 1
|
||||
} else {
|
||||
startingMessageId = items.higherThanAnchor[0].index.id.id
|
||||
}
|
||||
let currentMessageId: MessageId.Id
|
||||
if i == -1 {
|
||||
if items.lowerOrAtAnchor.count >= halfLimit {
|
||||
break
|
||||
}
|
||||
currentMessageId = 1
|
||||
} else {
|
||||
currentMessageId = items.lowerOrAtAnchor[i].index.id.id
|
||||
}
|
||||
let range: ClosedRange<Int> = Int(currentMessageId) ... Int(startingMessageId)
|
||||
if indices.intersects(integersIn: range) {
|
||||
let holeStartIndex: Int
|
||||
if let value = indices.integerLessThanOrEqualTo(Int(startingMessageId)) {
|
||||
holeStartIndex = value
|
||||
} else {
|
||||
holeStartIndex = indices[indices.endIndex]
|
||||
}
|
||||
lowerOrAtAnchorHole = (items.lowerOrAtAnchor.count - i, SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, indices: indices, startId: Int32(holeStartIndex), endId: 1))
|
||||
|
||||
if i == -1 {
|
||||
if items.lowerOrAtAnchor.count == 0 {
|
||||
if items.higherThanAnchor.count == 0 {
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
|
||||
} else {
|
||||
let clipIndex = items.higherThanAnchor[0].index.predecessor()
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
|
||||
}
|
||||
} else {
|
||||
let clipIndex = items.lowerOrAtAnchor[0].index.predecessor()
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
|
||||
}
|
||||
} else {
|
||||
if i == items.lowerOrAtAnchor.count - 1 {
|
||||
if items.higherThanAnchor.count == 0 {
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
|
||||
} else {
|
||||
let clipIndex = items.higherThanAnchor[0].index.predecessor()
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
|
||||
}
|
||||
} else {
|
||||
let clipIndex: MessageIndex
|
||||
if indices.contains(Int(items.lowerOrAtAnchor[i + 1].index.id.id)) {
|
||||
clipIndex = items.lowerOrAtAnchor[i + 1].index
|
||||
} else {
|
||||
clipIndex = items.lowerOrAtAnchor[i + 1].index.predecessor()
|
||||
}
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... clipIndex)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var higherThanAnchorHole: (distanceFromAnchor: Int, hole: SampledHistoryViewHole)?
|
||||
|
||||
for i in (0 ..< items.higherThanAnchor.count + 1) {
|
||||
let startingMessageId: MessageId.Id
|
||||
if items.lowerOrAtAnchor.isEmpty {
|
||||
startingMessageId = 1
|
||||
} else {
|
||||
startingMessageId = items.lowerOrAtAnchor[items.lowerOrAtAnchor.count - 1].index.id.id
|
||||
}
|
||||
let currentMessageId: MessageId.Id
|
||||
if i == items.higherThanAnchor.count {
|
||||
if items.higherThanAnchor.count >= halfLimit {
|
||||
break
|
||||
}
|
||||
currentMessageId = Int32.max - 1
|
||||
} else {
|
||||
currentMessageId = items.higherThanAnchor[i].index.id.id
|
||||
}
|
||||
let range: ClosedRange<Int> = Int(startingMessageId) ... Int(currentMessageId)
|
||||
if indices.intersects(integersIn: range) {
|
||||
let holeStartIndex: Int
|
||||
if let value = indices.integerGreaterThanOrEqualTo(Int(startingMessageId)) {
|
||||
holeStartIndex = value
|
||||
} else {
|
||||
holeStartIndex = indices[indices.startIndex]
|
||||
}
|
||||
higherThanAnchorHole = (i, SampledHistoryViewHole(peerId: space.peerId, namespace: space.namespace, tag: tag, indices: indices, startId: Int32(holeStartIndex), endId: Int32.max - 1))
|
||||
|
||||
if i == items.higherThanAnchor.count {
|
||||
if items.higherThanAnchor.count == 0 {
|
||||
if items.lowerOrAtAnchor.count == 0 {
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
|
||||
} else {
|
||||
let clipIndex = items.lowerOrAtAnchor[items.lowerOrAtAnchor.count - 1].index.successor()
|
||||
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
|
||||
}
|
||||
} else {
|
||||
let clipIndex = items.higherThanAnchor[items.higherThanAnchor.count - 1].index.successor()
|
||||
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
|
||||
}
|
||||
} else {
|
||||
if i == 0 {
|
||||
if items.lowerOrAtAnchor.count == 0 {
|
||||
clipRanges.append(MessageIndex.absoluteLowerBound() ... MessageIndex.absoluteUpperBound())
|
||||
} else {
|
||||
let clipIndex = items.lowerOrAtAnchor[items.lowerOrAtAnchor.count - 1].index.successor()
|
||||
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
|
||||
}
|
||||
} else {
|
||||
let clipIndex: MessageIndex
|
||||
if indices.contains(Int(items.higherThanAnchor[i - 1].index.id.id)) {
|
||||
clipIndex = items.higherThanAnchor[i - 1].index
|
||||
} else {
|
||||
clipIndex = items.higherThanAnchor[i - 1].index.successor()
|
||||
}
|
||||
clipRanges.append(clipIndex ... MessageIndex.absoluteUpperBound())
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var chosenHole: (distanceFromAnchor: Int, hole: SampledHistoryViewHole)?
|
||||
if let lowerOrAtAnchorHole = lowerOrAtAnchorHole, let higherThanAnchorHole = higherThanAnchorHole {
|
||||
if items.lowerOrAtAnchor.isEmpty != items.higherThanAnchor.isEmpty {
|
||||
if !items.lowerOrAtAnchor.isEmpty {
|
||||
chosenHole = lowerOrAtAnchorHole
|
||||
} else {
|
||||
chosenHole = higherThanAnchorHole
|
||||
}
|
||||
} else {
|
||||
if lowerOrAtAnchorHole.distanceFromAnchor < higherThanAnchorHole.distanceFromAnchor {
|
||||
chosenHole = lowerOrAtAnchorHole
|
||||
} else {
|
||||
chosenHole = higherThanAnchorHole
|
||||
}
|
||||
}
|
||||
} else if let lowerOrAtAnchorHole = lowerOrAtAnchorHole {
|
||||
chosenHole = lowerOrAtAnchorHole
|
||||
} else if let higherThanAnchorHole = higherThanAnchorHole {
|
||||
chosenHole = higherThanAnchorHole
|
||||
}
|
||||
|
||||
if let chosenHole = chosenHole {
|
||||
if let current = sampledHole {
|
||||
if let distance = current.distanceFromAnchor {
|
||||
if chosenHole.distanceFromAnchor < distance {
|
||||
sampledHole = (chosenHole.distanceFromAnchor, chosenHole.hole)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sampledHole = (chosenHole.distanceFromAnchor, chosenHole.hole)
|
||||
}
|
||||
}
|
||||
|
||||
/*let anchorIndex = binaryIndexOrLower(items.entries, anchor)
|
||||
let anchorStartingMessageId: MessageId.Id
|
||||
if anchorIndex == -1 {
|
||||
anchorStartingMessageId = 1
|
||||
@ -406,7 +576,7 @@ private func sampleHoleRanges(orderedEntriesBySpace: [PeerIdAndNamespace: Ordere
|
||||
}
|
||||
}
|
||||
higherDirectionIndex += 1
|
||||
}
|
||||
}*/
|
||||
}
|
||||
return (clipRanges, sampledHole?.hole)
|
||||
}
|
||||
@ -443,8 +613,61 @@ struct HistoryViewHoles {
|
||||
}
|
||||
|
||||
struct OrderedHistoryViewEntries {
|
||||
var entries: [MutableMessageHistoryEntry]
|
||||
var bounds: (lower: MessageIndex, upper: MessageIndex)?
|
||||
var lowerOrAtAnchor: [MutableMessageHistoryEntry]
|
||||
var higherThanAnchor: [MutableMessageHistoryEntry]
|
||||
|
||||
func find(index: MessageIndex) -> MutableMessageHistoryEntry? {
|
||||
if let entryIndex = binarySearch(self.lowerOrAtAnchor, extract: { $0.index }, searchItem: index) {
|
||||
return self.lowerOrAtAnchor[entryIndex]
|
||||
} else if let entryIndex = binarySearch(self.higherThanAnchor, extract: { $0.index }, searchItem: index) {
|
||||
return self.higherThanAnchor[entryIndex]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
mutating func mutableScan(_ f: (MutableMessageHistoryEntry) -> MutableMessageHistoryEntry?) -> Bool {
|
||||
for i in 0 ..< self.lowerOrAtAnchor.count {
|
||||
if let updated = f(self.lowerOrAtAnchor[i]) {
|
||||
self.lowerOrAtAnchor[i] = updated
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i in 0 ..< self.higherThanAnchor.count {
|
||||
if let updated = f(self.higherThanAnchor[i]) {
|
||||
self.higherThanAnchor[i] = updated
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
mutating func update(index: MessageIndex, _ f: (MutableMessageHistoryEntry) -> MutableMessageHistoryEntry?) -> Bool {
|
||||
if let entryIndex = binarySearch(self.lowerOrAtAnchor, extract: { $0.index }, searchItem: index) {
|
||||
if let updated = f(self.lowerOrAtAnchor[entryIndex]) {
|
||||
self.lowerOrAtAnchor[entryIndex] = updated
|
||||
return true
|
||||
}
|
||||
} else if let entryIndex = binarySearch(self.higherThanAnchor, extract: { $0.index }, searchItem: index) {
|
||||
if let updated = f(self.higherThanAnchor[entryIndex]) {
|
||||
self.higherThanAnchor[entryIndex] = updated
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
mutating func remove(index: MessageIndex) -> Bool {
|
||||
if let entryIndex = binarySearch(self.lowerOrAtAnchor, extract: { $0.index }, searchItem: index) {
|
||||
self.lowerOrAtAnchor.remove(at: entryIndex)
|
||||
return true
|
||||
} else if let entryIndex = binarySearch(self.higherThanAnchor, extract: { $0.index }, searchItem: index) {
|
||||
self.higherThanAnchor.remove(at: entryIndex)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HistoryViewLoadedSample {
|
||||
@ -459,17 +682,17 @@ final class HistoryViewLoadedState {
|
||||
let anchor: HistoryViewAnchor
|
||||
let tag: MessageTags?
|
||||
let statistics: MessageHistoryViewOrderStatistics
|
||||
let limit: Int
|
||||
let halfLimit: Int
|
||||
var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries]
|
||||
var holes: HistoryViewHoles
|
||||
var spacesWithRemovals = Set<PeerIdAndNamespace>()
|
||||
|
||||
init(anchor: HistoryViewAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, limit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) {
|
||||
precondition(limit >= 3)
|
||||
init(anchor: HistoryViewAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) {
|
||||
precondition(halfLimit >= 3)
|
||||
self.anchor = anchor
|
||||
self.tag = tag
|
||||
self.statistics = statistics
|
||||
self.limit = limit
|
||||
self.halfLimit = halfLimit
|
||||
self.orderedEntriesBySpace = [:]
|
||||
self.holes = holes
|
||||
|
||||
@ -509,56 +732,43 @@ final class HistoryViewLoadedState {
|
||||
anchorIndex = upperBound
|
||||
}
|
||||
|
||||
var lowerMessages: [MutableMessageHistoryEntry] = []
|
||||
var higherMessages: [MutableMessageHistoryEntry] = []
|
||||
var lowerOrAtAnchorMessages: [MutableMessageHistoryEntry] = []
|
||||
var higherThanAnchorMessages: [MutableMessageHistoryEntry] = []
|
||||
|
||||
if let currentEntries = self.orderedEntriesBySpace[space], !currentEntries.entries.isEmpty {
|
||||
let index = binaryIndexOrLower(currentEntries.entries, self.anchor)
|
||||
if index >= 0 {
|
||||
lowerMessages = Array(currentEntries.entries[0 ... index].reversed())
|
||||
}
|
||||
if index < currentEntries.entries.count {
|
||||
higherMessages = Array(currentEntries.entries[index + 1 ..< currentEntries.entries.count])
|
||||
}
|
||||
if let currentEntries = self.orderedEntriesBySpace[space] {
|
||||
lowerOrAtAnchorMessages = currentEntries.lowerOrAtAnchor.reversed()
|
||||
higherThanAnchorMessages = currentEntries.higherThanAnchor
|
||||
}
|
||||
|
||||
func mapEntry(_ message: IntermediateMessage) -> MutableMessageHistoryEntry {
|
||||
return .IntermediateMessageEntry(message, nil, nil)
|
||||
}
|
||||
|
||||
if lowerMessages.count < self.limit / 2 + 1 {
|
||||
if lowerOrAtAnchorMessages.count < self.halfLimit {
|
||||
let nextLowerIndex: (index: MessageIndex, includeFrom: Bool)
|
||||
if let lastMessage = lowerMessages.last {
|
||||
if let lastMessage = lowerOrAtAnchorMessages.last {
|
||||
nextLowerIndex = (lastMessage.index, false)
|
||||
} else {
|
||||
nextLowerIndex = (anchorIndex, true)
|
||||
}
|
||||
lowerMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, limit: self.limit / 2 + 1 - lowerMessages.count).map(mapEntry))
|
||||
lowerOrAtAnchorMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, limit: self.halfLimit - lowerOrAtAnchorMessages.count).map(mapEntry))
|
||||
}
|
||||
if higherMessages.count < self.limit - lowerMessages.count {
|
||||
if higherThanAnchorMessages.count < self.halfLimit {
|
||||
let nextHigherIndex: MessageIndex
|
||||
if let lastMessage = higherMessages.last {
|
||||
if let lastMessage = higherThanAnchorMessages.last {
|
||||
nextHigherIndex = lastMessage.index
|
||||
} else {
|
||||
nextHigherIndex = anchorIndex
|
||||
}
|
||||
higherMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextHigherIndex, includeFrom: false, to: upperBound, limit: self.limit - lowerMessages.count - higherMessages.count).map(mapEntry))
|
||||
higherThanAnchorMessages.append(contentsOf: postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: nextHigherIndex, includeFrom: false, to: upperBound, limit: self.halfLimit - higherThanAnchorMessages.count).map(mapEntry))
|
||||
}
|
||||
|
||||
if !lowerMessages.isEmpty && lowerMessages.count + higherMessages.count < self.limit {
|
||||
let additionalLowerMessages = postbox.messageHistoryTable.fetch(peerId: space.peerId, namespace: space.namespace, tag: self.tag, from: lowerMessages[lowerMessages.count - 1].index, includeFrom: false, to: lowerBound, limit: self.limit - lowerMessages.count - higherMessages.count).map(mapEntry)
|
||||
lowerMessages.append(contentsOf: additionalLowerMessages)
|
||||
}
|
||||
lowerOrAtAnchorMessages.reverse()
|
||||
|
||||
var messages: [MutableMessageHistoryEntry] = []
|
||||
messages.append(contentsOf: lowerMessages.reversed())
|
||||
messages.append(contentsOf: higherMessages)
|
||||
assert(lowerOrAtAnchorMessages.count <= self.halfLimit)
|
||||
assert(higherThanAnchorMessages.count <= self.halfLimit)
|
||||
|
||||
assert(messages.count <= self.limit)
|
||||
|
||||
let bounds = postbox.messageHistoryTable.fetchBoundaries(peerId: space.peerId, namespace: space.namespace, tag: self.tag)
|
||||
|
||||
if let tag = self.tag, self.statistics.contains(.combinedLocation) {
|
||||
/*if let tag = self.tag, self.statistics.contains(.combinedLocation) {
|
||||
if let first = messages.first {
|
||||
let messageIndex = first.index
|
||||
let previousCount = postbox.messageHistoryTagsTable.getMessageCountInRange(tag: tag, peerId: space.peerId, namespace: space.namespace, lowerBound: MessageIndex.lowerBound(peerId: space.peerId, namespace: space.namespace), upperBound: messageIndex)
|
||||
@ -600,9 +810,9 @@ final class HistoryViewLoadedState {
|
||||
nextLocation.1 = max(0, nextLocation.1 - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(entries: messages, bounds: bounds)
|
||||
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(lowerOrAtAnchor: lowerOrAtAnchorMessages, higherThanAnchor: higherThanAnchorMessages)
|
||||
}
|
||||
|
||||
func insertHole(space: PeerIdAndNamespace, range: ClosedRange<MessageId.Id>) -> Bool {
|
||||
@ -618,15 +828,14 @@ final class HistoryViewLoadedState {
|
||||
if self.orderedEntriesBySpace[space] == nil {
|
||||
return false
|
||||
}
|
||||
guard let entryIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) else {
|
||||
guard let entry = self.orderedEntriesBySpace[space]!.find(index: index) else {
|
||||
return false
|
||||
}
|
||||
let entry = self.orderedEntriesBySpace[space]!.entries[entryIndex]
|
||||
var updated = false
|
||||
if self.remove(postbox: postbox, index: index) {
|
||||
if self.remove(index: index) {
|
||||
updated = true
|
||||
}
|
||||
if self.add(postbox: postbox, entry: entry.updatedTimestamp(timestamp)) {
|
||||
if self.add(entry: entry.updatedTimestamp(timestamp)) {
|
||||
updated = true
|
||||
}
|
||||
return updated
|
||||
@ -646,43 +855,46 @@ final class HistoryViewLoadedState {
|
||||
if self.orderedEntriesBySpace[space] == nil {
|
||||
continue
|
||||
}
|
||||
for i in 0 ..< self.orderedEntriesBySpace[space]!.entries.count {
|
||||
if let groupInfo = spaceMapping[self.orderedEntriesBySpace[space]!.entries[i].index.id.id] {
|
||||
let spaceUpdated = self.orderedEntriesBySpace[space]!.mutableScan({ entry in
|
||||
if let groupInfo = spaceMapping[entry.index.id.id] {
|
||||
updated = true
|
||||
switch self.orderedEntriesBySpace[space]!.entries[i] {
|
||||
switch entry {
|
||||
case let .IntermediateMessageEntry(message, location, monthLocation):
|
||||
self.orderedEntriesBySpace[space]!.entries[i] = .IntermediateMessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation)
|
||||
return .IntermediateMessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation)
|
||||
case let .MessageEntry(messageEntry):
|
||||
self.orderedEntriesBySpace[space]!.entries[i] = .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedGroupInfo(groupInfo), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
|
||||
return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedGroupInfo(groupInfo), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if spaceUpdated {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func updateEmbeddedMedia(postbox: Postbox, index: MessageIndex, buffer: ReadBuffer) -> Bool {
|
||||
func updateEmbeddedMedia(index: MessageIndex, buffer: ReadBuffer) -> Bool {
|
||||
let space = PeerIdAndNamespace(peerId: index.id.peerId, namespace: index.id.namespace)
|
||||
if self.orderedEntriesBySpace[space] == nil {
|
||||
return false
|
||||
}
|
||||
guard let itemIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) else {
|
||||
return false
|
||||
}
|
||||
switch self.orderedEntriesBySpace[space]!.entries[itemIndex] {
|
||||
|
||||
return self.orderedEntriesBySpace[space]!.update(index: index, { entry in
|
||||
switch entry {
|
||||
case let .IntermediateMessageEntry(message, location, monthLocation):
|
||||
self.orderedEntriesBySpace[space]!.entries[itemIndex] = .IntermediateMessageEntry(message.withUpdatedEmbeddedMedia(buffer), location, monthLocation)
|
||||
return .IntermediateMessageEntry(message.withUpdatedEmbeddedMedia(buffer), location, monthLocation)
|
||||
case let .MessageEntry(messageEntry):
|
||||
self.orderedEntriesBySpace[space]!.entries[itemIndex] = .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
|
||||
return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes))
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func updateMedia(updatedMedia: [MediaId: Media?]) -> Bool {
|
||||
var hasChanges = false
|
||||
var updated = false
|
||||
for space in self.orderedEntriesBySpace.keys {
|
||||
for i in 0 ..< self.orderedEntriesBySpace[space]!.entries.count {
|
||||
switch self.orderedEntriesBySpace[space]!.entries[i] {
|
||||
let spaceUpdated = self.orderedEntriesBySpace[space]!.mutableScan({ entry in
|
||||
switch entry {
|
||||
case let .MessageEntry(value):
|
||||
let message = value.message
|
||||
|
||||
@ -706,41 +918,25 @@ final class HistoryViewLoadedState {
|
||||
}
|
||||
}
|
||||
let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds)
|
||||
self.orderedEntriesBySpace[space]!.entries[i] = .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes))
|
||||
hasChanges = true
|
||||
return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes))
|
||||
}
|
||||
case let .IntermediateMessageEntry(message, _, _):
|
||||
var rebuild = false
|
||||
for mediaId in message.referencedMedia {
|
||||
if let media = updatedMedia[mediaId] , media?.id != mediaId {
|
||||
rebuild = true
|
||||
case .IntermediateMessageEntry:
|
||||
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)
|
||||
return nil
|
||||
})
|
||||
if spaceUpdated {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasChanges
|
||||
return updated
|
||||
}
|
||||
|
||||
func add(postbox: Postbox, entry: MutableMessageHistoryEntry) -> Bool {
|
||||
func add(entry: MutableMessageHistoryEntry) -> Bool {
|
||||
let space = PeerIdAndNamespace(peerId: entry.index.id.peerId, namespace: entry.index.id.namespace)
|
||||
|
||||
if self.orderedEntriesBySpace[space] == nil {
|
||||
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(entries: [], bounds: nil)
|
||||
self.orderedEntriesBySpace[space] = OrderedHistoryViewEntries(lowerOrAtAnchor: [], higherThanAnchor: [])
|
||||
}
|
||||
|
||||
var updated = false
|
||||
@ -763,66 +959,48 @@ final class HistoryViewLoadedState {
|
||||
}
|
||||
}*/
|
||||
|
||||
let insertionIndex = binaryInsertionIndex(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: entry.index)
|
||||
if 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 {
|
||||
if self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[insertionIndex].index == entry.index {
|
||||
assertionFailure("Inserting an existing index is not allowed")
|
||||
self.orderedEntriesBySpace[space]!.entries[insertionIndex] = entry
|
||||
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 {
|
||||
let anchorIndex = binaryIndexOrLower(self.orderedEntriesBySpace[space]!.entries, self.anchor)
|
||||
if anchorIndex > self.limit / 2 {
|
||||
self.orderedEntriesBySpace[space]!.entries.removeFirst()
|
||||
} else {
|
||||
self.orderedEntriesBySpace[space]!.entries.removeLast()
|
||||
}
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
|
||||
if insertionIndex == 0 && self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count >= self.halfLimit {
|
||||
return updated
|
||||
}
|
||||
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.insert(entry, at: insertionIndex)
|
||||
if self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.count > self.halfLimit {
|
||||
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor.removeFirst()
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
let insertionIndex = binaryInsertionIndex(self.orderedEntriesBySpace[space]!.higherThanAnchor, extract: { $0.index }, searchItem: entry.index)
|
||||
|
||||
func remove(postbox: Postbox, index: MessageIndex) -> Bool {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func remove(index: MessageIndex) -> Bool {
|
||||
let space = PeerIdAndNamespace(peerId: index.id.peerId, namespace: index.id.namespace)
|
||||
if self.orderedEntriesBySpace[space] == nil {
|
||||
return false
|
||||
@ -842,16 +1020,7 @@ final class HistoryViewLoadedState {
|
||||
}
|
||||
}*/
|
||||
|
||||
if let itemIndex = binarySearch(self.orderedEntriesBySpace[space]!.entries, extract: { $0.index }, searchItem: index) {
|
||||
if let currentBounds = self.orderedEntriesBySpace[space]!.bounds {
|
||||
if currentBounds.lower == index || currentBounds.upper
|
||||
== index {
|
||||
self.orderedEntriesBySpace[space]!.bounds = postbox.messageHistoryTable.fetchBoundaries(peerId: space.peerId, namespace: space.namespace, tag: self.tag)
|
||||
}
|
||||
} else {
|
||||
//assertionFailure("A non-empty entry list should have non-nil bounds")
|
||||
}
|
||||
self.orderedEntriesBySpace[space]!.entries.remove(at: itemIndex)
|
||||
if self.orderedEntriesBySpace[space]!.remove(index: index) {
|
||||
self.spacesWithRemovals.insert(space)
|
||||
updated = true
|
||||
}
|
||||
@ -866,29 +1035,38 @@ final class HistoryViewLoadedState {
|
||||
}
|
||||
self.spacesWithRemovals.removeAll()
|
||||
}
|
||||
let combinedSpacesAndIndices = sampleEntries(orderedEntriesBySpace: self.orderedEntriesBySpace, anchor: self.anchor, limit: self.limit)
|
||||
let (clipRanges, sampledHole) = sampleHoleRanges(orderedEntriesBySpace: self.orderedEntriesBySpace, holes: self.holes, anchor: self.anchor, tag: self.tag)
|
||||
let combinedSpacesAndIndicesByDirection = sampleEntries(orderedEntriesBySpace: self.orderedEntriesBySpace, anchor: self.anchor, halfLimit: self.halfLimit)
|
||||
let (clipRanges, sampledHole) = sampleHoleRanges(orderedEntriesBySpace: self.orderedEntriesBySpace, holes: self.holes, anchor: self.anchor, tag: self.tag, halfLimit: self.halfLimit)
|
||||
|
||||
var holesToLower = false
|
||||
var holesToHigher = false
|
||||
var result: [MessageHistoryMessageEntry] = []
|
||||
if combinedSpacesAndIndices.isEmpty {
|
||||
if combinedSpacesAndIndicesByDirection.lowerOrAtAnchor.isEmpty && combinedSpacesAndIndicesByDirection.higherThanAnchor.isEmpty {
|
||||
if !clipRanges.isEmpty {
|
||||
holesToLower = true
|
||||
holesToHigher = true
|
||||
}
|
||||
} else {
|
||||
outer: for i in 0 ..< combinedSpacesAndIndices.count {
|
||||
let (space, index) = combinedSpacesAndIndices[i]
|
||||
let directions = [combinedSpacesAndIndicesByDirection.lowerOrAtAnchor, combinedSpacesAndIndicesByDirection.higherThanAnchor]
|
||||
for directionIndex in 0 ..< directions.count {
|
||||
outer: for i in 0 ..< directions[directionIndex].count {
|
||||
let (space, index) = directions[directionIndex][i]
|
||||
|
||||
let entry: MutableMessageHistoryEntry
|
||||
if directionIndex == 0 {
|
||||
entry = self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[index]
|
||||
} else {
|
||||
entry = self.orderedEntriesBySpace[space]!.higherThanAnchor[index]
|
||||
}
|
||||
|
||||
if !clipRanges.isEmpty {
|
||||
let entryIndex = self.orderedEntriesBySpace[space]!.entries[index].index
|
||||
let entryIndex = entry.index
|
||||
for range in clipRanges {
|
||||
if range.contains(entryIndex) {
|
||||
if i == 0 {
|
||||
if directionIndex == 0 && i == 0 {
|
||||
holesToLower = true
|
||||
}
|
||||
if i == combinedSpacesAndIndices.count - 1 {
|
||||
if directionIndex == 1 && i == directions[directionIndex].count - 1 {
|
||||
holesToHigher = true
|
||||
}
|
||||
continue outer
|
||||
@ -896,7 +1074,7 @@ final class HistoryViewLoadedState {
|
||||
}
|
||||
}
|
||||
|
||||
switch self.orderedEntriesBySpace[space]!.entries[index] {
|
||||
switch entry {
|
||||
case let .MessageEntry(value):
|
||||
result.append(value)
|
||||
case let .IntermediateMessageEntry(message, location, monthLocation):
|
||||
@ -906,11 +1084,16 @@ final class HistoryViewLoadedState {
|
||||
authorIsContact = postbox.contactsTable.isContact(peerId: author.id)
|
||||
}
|
||||
let entry = MessageHistoryMessageEntry(message: renderedMessage, location: location, monthLocation: monthLocation, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: authorIsContact))
|
||||
self.orderedEntriesBySpace[space]!.entries[index] = .MessageEntry(entry)
|
||||
if directionIndex == 0 {
|
||||
self.orderedEntriesBySpace[space]!.lowerOrAtAnchor[index] = .MessageEntry(entry)
|
||||
} else {
|
||||
self.orderedEntriesBySpace[space]!.higherThanAnchor[index] = .MessageEntry(entry)
|
||||
}
|
||||
result.append(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(Set(result.map({ $0.message.stableId })).count == result.count)
|
||||
return HistoryViewLoadedSample(anchor: self.anchor, entries: result, holesToLower: holesToLower, holesToHigher: holesToHigher, hole: sampledHole)
|
||||
}
|
||||
@ -948,13 +1131,13 @@ enum HistoryViewLoadingSample {
|
||||
final class HistoryViewLoadingState {
|
||||
var messageId: MessageId
|
||||
let tag: MessageTags?
|
||||
let limit: Int
|
||||
let halfLimit: Int
|
||||
var holes: HistoryViewHoles
|
||||
|
||||
init(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, messageId: MessageId, limit: Int) {
|
||||
init(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, messageId: MessageId, halfLimit: Int) {
|
||||
self.messageId = messageId
|
||||
self.tag = tag
|
||||
self.limit = limit
|
||||
self.halfLimit = halfLimit
|
||||
self.holes = HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))
|
||||
}
|
||||
|
||||
@ -995,14 +1178,14 @@ enum HistoryViewState {
|
||||
case loaded(HistoryViewLoadedState)
|
||||
case loading(HistoryViewLoadingState)
|
||||
|
||||
init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, limit: Int, locations: MessageHistoryViewPeerIds) {
|
||||
init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds) {
|
||||
switch inputAnchor {
|
||||
case let .index(index):
|
||||
self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
case .lowerBound:
|
||||
self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
case .upperBound:
|
||||
self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
case .unread:
|
||||
let anchorPeerId: PeerId
|
||||
switch locations {
|
||||
@ -1035,26 +1218,26 @@ enum HistoryViewState {
|
||||
}
|
||||
}
|
||||
if let messageId = messageId {
|
||||
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, limit: limit)
|
||||
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, halfLimit: halfLimit)
|
||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||
switch sampledState {
|
||||
case let .ready(anchor, holes):
|
||||
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: holes))
|
||||
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes))
|
||||
case .loadHole:
|
||||
self = .loading(loadingState)
|
||||
}
|
||||
} else {
|
||||
self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag))))
|
||||
}
|
||||
} else {
|
||||
preconditionFailure()
|
||||
}
|
||||
case let .message(messageId):
|
||||
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, limit: limit)
|
||||
let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, messageId: messageId, halfLimit: halfLimit)
|
||||
let sampledState = loadingState.checkAndSample(postbox: postbox)
|
||||
switch sampledState {
|
||||
case let .ready(anchor, holes):
|
||||
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, limit: limit, locations: locations, postbox: postbox, holes: holes))
|
||||
self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes))
|
||||
case .loadHole:
|
||||
self = .loading(loadingState)
|
||||
}
|
||||
|
@ -935,7 +935,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
||||
|
||||
#if DEBUG
|
||||
//debugSaveState(basePath: basePath, name: "previous1")
|
||||
//debugRestoreState(basePath: basePath, name: "previous1")
|
||||
debugRestoreState(basePath: basePath, name: "previous1")
|
||||
#endif
|
||||
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
|
@ -159,6 +159,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
private let lock = NSRecursiveLock()
|
||||
|
||||
fileprivate let basePath: String
|
||||
private let inMemory: Bool
|
||||
private let encryptionParameters: ValueBoxEncryptionParameters?
|
||||
private let databasePath: String
|
||||
private var database: Database!
|
||||
@ -196,8 +197,9 @@ public final class SqliteValueBox: ValueBox {
|
||||
|
||||
private let queue: Queue
|
||||
|
||||
public init(basePath: String, queue: Queue, encryptionParameters: ValueBoxEncryptionParameters?, upgradeProgress: (Float) -> Void) {
|
||||
public init(basePath: String, queue: Queue, encryptionParameters: ValueBoxEncryptionParameters?, upgradeProgress: (Float) -> Void, inMemory: Bool = false) {
|
||||
self.basePath = basePath
|
||||
self.inMemory = inMemory
|
||||
self.encryptionParameters = encryptionParameters
|
||||
self.databasePath = basePath + "/db_sqlite"
|
||||
self.queue = queue
|
||||
@ -247,7 +249,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
#endif
|
||||
|
||||
var database: Database
|
||||
if let result = Database(path) {
|
||||
if let result = Database(self.inMemory ? ":memory:" : path) {
|
||||
database = result
|
||||
} else {
|
||||
postboxLog("Couldn't open DB")
|
||||
|
@ -22,7 +22,7 @@ private extension MessageTags {
|
||||
}
|
||||
|
||||
class MessageHistoryIndexTableTests: XCTestCase {
|
||||
var valueBox: ValueBox?
|
||||
var valueBox: SqliteValueBox?
|
||||
var path: String?
|
||||
|
||||
var postbox: Postbox?
|
||||
@ -45,7 +45,7 @@ class MessageHistoryIndexTableTests: XCTestCase {
|
||||
arc4random_buf(bytes, 16)
|
||||
})
|
||||
|
||||
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!))
|
||||
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: true, key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!), upgradeProgress: { _ in })
|
||||
|
||||
let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [
|
||||
peerId.namespace: [
|
||||
@ -53,7 +53,7 @@ class MessageHistoryIndexTableTests: XCTestCase {
|
||||
]
|
||||
]
|
||||
|
||||
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [.media], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil)
|
||||
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [.media], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil, messageNamespacesRequiringGroupStatsValidation: Set())
|
||||
|
||||
self.postbox = Postbox(queue: Queue.mainQueue(), basePath: path!, seedConfiguration: seedConfiguration, valueBox: self.valueBox!)
|
||||
}
|
||||
|
@ -10,42 +10,33 @@ import SwiftSignalKit
|
||||
private let peerId = PeerId(namespace: 1, id: 1)
|
||||
private let namespace: Int32 = 1
|
||||
|
||||
private func extract(from array: [Int32], aroundIndex: Int, limit: Int) -> [Int32] {
|
||||
private func extract(from array: [Int32], aroundIndex: Int, halfLimit: Int) -> [Int32] {
|
||||
var lower: [Int32] = []
|
||||
var higher: [Int32] = []
|
||||
|
||||
var i = aroundIndex
|
||||
while i >= 0 && lower.count < limit / 2 + 1 {
|
||||
while i >= 0 && lower.count < halfLimit {
|
||||
lower.append(array[i])
|
||||
i -= 1
|
||||
}
|
||||
|
||||
var j = aroundIndex + 1
|
||||
while j < array.count && higher.count < limit - lower.count {
|
||||
while j < array.count && higher.count < halfLimit {
|
||||
higher.append(array[j])
|
||||
j += 1
|
||||
}
|
||||
|
||||
if !lower.isEmpty && lower.count + higher.count < limit {
|
||||
var additionalLower: [Int32] = []
|
||||
while i >= 0 && additionalLower.count < limit - lower.count - higher.count {
|
||||
additionalLower.append(array[i])
|
||||
i -= 1
|
||||
}
|
||||
lower.append(contentsOf: additionalLower)
|
||||
}
|
||||
|
||||
var result: [Int32] = []
|
||||
result.append(contentsOf: lower.reversed())
|
||||
result.append(contentsOf: higher)
|
||||
|
||||
assert(result.count <= limit)
|
||||
assert(result.count <= halfLimit * 2)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
class MessageHistoryViewTests: XCTestCase {
|
||||
var valueBox: ValueBox?
|
||||
var valueBox: SqliteValueBox?
|
||||
var path: String?
|
||||
|
||||
var postbox: Postbox?
|
||||
@ -68,7 +59,7 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
arc4random_buf(bytes, 16)
|
||||
})
|
||||
|
||||
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!))
|
||||
self.valueBox = SqliteValueBox(basePath: path!, queue: Queue.mainQueue(), encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: randomKey)!, salt: ValueBoxEncryptionParameters.Salt(data: randomSalt)!), upgradeProgress: { _ in }, inMemory: true)
|
||||
|
||||
let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [
|
||||
peerId.namespace: [:
|
||||
@ -76,7 +67,7 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
]
|
||||
]
|
||||
|
||||
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil)
|
||||
let seedConfiguration = SeedConfiguration(globalMessageIdsPeerIdNamespaces: Set(), initializeChatListWithHole: (topLevel: nil, groups: nil), messageHoles: messageHoles, existingMessageTags: [], messageTagsWithSummary: [], existingGlobalMessageTags: [], peerNamespacesRequiringMessageTextIndex: [], peerSummaryCounterTags: { _ in PeerSummaryCounterTags(rawValue: 0) }, additionalChatListIndexNamespace: nil, messageNamespacesRequiringGroupStatsValidation: Set())
|
||||
|
||||
self.postbox = Postbox(queue: Queue.mainQueue(), basePath: path!, seedConfiguration: seedConfiguration, valueBox: self.valueBox!)
|
||||
}
|
||||
@ -101,10 +92,14 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
}).start()
|
||||
}
|
||||
|
||||
private func addMessage(_ id: Int32, _ timestamp: Int32, _ groupingKey: Int64? = nil) {
|
||||
private func addMessage(_ id: Int32, _ timestamp: Int32, _ groupingKey: Int64? = nil) -> UInt32 {
|
||||
var stableId: UInt32?
|
||||
let _ = self.postbox!.transaction({ transaction -> Void in
|
||||
let _ = transaction.addMessages([StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [])], location: .Random)
|
||||
let messageId = MessageId(peerId: peerId, namespace: namespace, id: id)
|
||||
let _ = transaction.addMessages([StoreMessage(id: messageId, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [])], location: .Random)
|
||||
stableId = transaction.getMessage(messageId)!.stableId
|
||||
}).start()
|
||||
return stableId!
|
||||
}
|
||||
|
||||
private func removeMessage(_ id: Int32) {
|
||||
@ -120,7 +115,7 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testEmpty() {
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
|
||||
switch state {
|
||||
case let .loaded(loadedState):
|
||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||
@ -134,10 +129,10 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
var testIds: [MessageId.Id] = []
|
||||
for i in 1 ..< 11 {
|
||||
testIds.append(Int32(i * 10))
|
||||
addMessage(Int32(i * 10), Int32(i * 10))
|
||||
let _ = addMessage(Int32(i * 10), Int32(i * 10))
|
||||
}
|
||||
for i in 3 ... testIds.count + 10 {
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: i, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: i, locations: .single(peerId))
|
||||
switch state {
|
||||
case let .loaded(loadedState):
|
||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||
@ -154,7 +149,7 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
for i in 3 ... testIds.count + 10 {
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .lowerBound, tag: nil, limit: i, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .lowerBound, tag: nil, statistics: [], halfLimit: i, locations: .single(peerId))
|
||||
switch state {
|
||||
case let .loaded(loadedState):
|
||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||
@ -172,11 +167,11 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
}
|
||||
for i in 3 ... testIds.count + 10 {
|
||||
for j in testIds[0] - 10 ... testIds.last! + 10 {
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(j)), timestamp: Int32(j))), tag: nil, limit: i, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(j)), timestamp: Int32(j))), tag: nil, statistics: [], halfLimit: i, locations: .single(peerId))
|
||||
|
||||
let clippedTestIds: [Int32]
|
||||
if let index = testIds.firstIndex(where: { $0 > Int32(j) }), index >= 0 {
|
||||
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, limit: i)
|
||||
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, halfLimit: i)
|
||||
} else {
|
||||
if i >= testIds.count {
|
||||
clippedTestIds = testIds
|
||||
@ -233,12 +228,12 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
|
||||
for operationSetIndex in 0 ..< operationSets.count {
|
||||
let operations = operationSets[operationSetIndex]
|
||||
for limit in [3, 4, 5, 6, 7, 200] {
|
||||
for halfLimit in [3, 4, 5, 6, 7, 200] {
|
||||
for position in 10 ... 110 {
|
||||
removeAllMessages()
|
||||
|
||||
var testIds: [MessageId.Id] = []
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, limit: limit, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, statistics: [], halfLimit: halfLimit, locations: .single(peerId))
|
||||
switch state {
|
||||
case let .loaded(loadedState):
|
||||
for operationIndex in 0 ..< operations.count {
|
||||
@ -252,20 +247,20 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
|
||||
let attributesData = ReadBuffer(data: Data())
|
||||
|
||||
addMessage(Int32(insertId), Int32(insertId))
|
||||
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: UInt32(insertId), stableVersion: 0, id: MessageId(peerId: peerId, namespace: namespace, id: insertId), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: insertId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
|
||||
let stableId = addMessage(Int32(insertId), Int32(insertId))
|
||||
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: stableId, stableVersion: 0, id: MessageId(peerId: peerId, namespace: namespace, id: insertId), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: insertId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
|
||||
|
||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||
let ids = entries.map({ $0.message.id.id })
|
||||
|
||||
let clippedTestIds: [Int32]
|
||||
if let index = testIds.firstIndex(where: { $0 > Int32(position) }), index >= 0 {
|
||||
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, limit: limit)
|
||||
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, halfLimit: halfLimit)
|
||||
} else {
|
||||
if limit >= testIds.count {
|
||||
if halfLimit >= testIds.count {
|
||||
clippedTestIds = testIds
|
||||
} else {
|
||||
clippedTestIds = Array(testIds.dropFirst(testIds.count - limit))
|
||||
clippedTestIds = Array(testIds.dropFirst(testIds.count - halfLimit))
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,12 +305,12 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
|
||||
for operationSetIndex in 0 ..< operationSets.count {
|
||||
let operations = operationSets[operationSetIndex]
|
||||
for limit in [3, 4, 5, 6, 7, 200] {
|
||||
for halfLimit in [3, 4, 5, 6, 7, 200] {
|
||||
for position in 10 ... 110 {
|
||||
removeAllMessages()
|
||||
|
||||
var testIds: [MessageId.Id] = []
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, limit: limit, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: namespace, id: Int32(position)), timestamp: Int32(position))), tag: nil, statistics: [], halfLimit: halfLimit, locations: .single(peerId))
|
||||
switch state {
|
||||
case let .loaded(loadedState):
|
||||
for operationIndex in 0 ..< operations.count {
|
||||
@ -337,12 +332,12 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
|
||||
let messageId = MessageId(peerId: peerId, namespace: namespace, id: itemId)
|
||||
if isAdd {
|
||||
addMessage(Int32(itemId), Int32(itemId))
|
||||
let stableId = addMessage(Int32(itemId), Int32(itemId))
|
||||
let attributesData = ReadBuffer(data: Data())
|
||||
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: UInt32(messageId.id), stableVersion: 0, id: messageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: itemId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
|
||||
let _ = loadedState.add(entry: .IntermediateMessageEntry(IntermediateMessage(stableId: stableId, stableVersion: 0, id: messageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: itemId, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributesData: attributesData, embeddedMediaData: attributesData, referencedMedia: []), nil, nil))
|
||||
} else {
|
||||
removeMessage(itemId)
|
||||
let _ = loadedState.remove(postbox: self.postbox!, index: MessageIndex(id: messageId, timestamp: itemId))
|
||||
let _ = loadedState.remove(index: MessageIndex(id: messageId, timestamp: itemId))
|
||||
}
|
||||
|
||||
let entries = loadedState.completeAndSample(postbox: self.postbox!).entries
|
||||
@ -350,12 +345,12 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
|
||||
let clippedTestIds: [Int32]
|
||||
if let index = testIds.firstIndex(where: { $0 > Int32(position) }), index >= 0 {
|
||||
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, limit: limit)
|
||||
clippedTestIds = extract(from: testIds, aroundIndex: index - 1, halfLimit: halfLimit)
|
||||
} else {
|
||||
if limit >= testIds.count {
|
||||
if halfLimit >= testIds.count {
|
||||
clippedTestIds = testIds
|
||||
} else {
|
||||
clippedTestIds = Array(testIds.dropFirst(testIds.count - limit))
|
||||
clippedTestIds = Array(testIds.dropFirst(testIds.count - halfLimit))
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,7 +366,7 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
|
||||
func testLoadInitialHole() {
|
||||
addHole(1 ... 1000, space: .everywhere)
|
||||
var state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: Int32(100))), tag: nil, limit: 10, locations: .single(peerId))
|
||||
var state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: Int32(100))), tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
|
||||
switch state {
|
||||
case .loaded:
|
||||
XCTAssert(false)
|
||||
@ -405,7 +400,7 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
default:
|
||||
XCTAssert(false)
|
||||
}
|
||||
state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: nil, limit: 10, locations: .single(peerId), postbox: self.postbox!, holes: holes))
|
||||
state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId), postbox: self.postbox!, holes: holes))
|
||||
case .loadHole:
|
||||
XCTAssert(false)
|
||||
}
|
||||
@ -421,13 +416,13 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testEdgeHoles1() {
|
||||
addMessage(100, 100)
|
||||
addMessage(200, 200)
|
||||
addMessage(300, 300)
|
||||
let _ = addMessage(100, 100)
|
||||
let _ = addMessage(200, 200)
|
||||
let _ = addMessage(300, 300)
|
||||
|
||||
addHole(1 ... 100, space: .everywhere)
|
||||
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
|
||||
guard case let .loaded(loadedState) = state else {
|
||||
XCTAssert(false)
|
||||
return
|
||||
@ -441,13 +436,13 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testEdgeHoles2() {
|
||||
addMessage(100, 100)
|
||||
addMessage(200, 200)
|
||||
addMessage(300, 300)
|
||||
let _ = addMessage(100, 100)
|
||||
let _ = addMessage(200, 200)
|
||||
let _ = addMessage(300, 300)
|
||||
|
||||
addHole(1 ... 99, space: .everywhere)
|
||||
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
|
||||
guard case let .loaded(loadedState) = state else {
|
||||
XCTAssert(false)
|
||||
return
|
||||
@ -461,13 +456,13 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testEdgeHoles3() {
|
||||
addMessage(100, 100)
|
||||
addMessage(200, 200)
|
||||
addMessage(300, 300)
|
||||
let _ = addMessage(100, 100)
|
||||
let _ = addMessage(200, 200)
|
||||
let _ = addMessage(300, 300)
|
||||
|
||||
addHole(300 ... 400, space: .everywhere)
|
||||
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, limit: 10, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .upperBound, tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
|
||||
guard case let .loaded(loadedState) = state else {
|
||||
XCTAssert(false)
|
||||
return
|
||||
@ -475,19 +470,19 @@ class MessageHistoryViewTests: XCTestCase {
|
||||
let sampledState = loadedState.completeAndSample(postbox: self.postbox!)
|
||||
let ids = sampledState.entries.map({ $0.message.id.id })
|
||||
XCTAssert(ids == [])
|
||||
XCTAssert(sampledState.hole == SampledHistoryViewHole(peerId: peerId, namespace: namespace, tag: nil, indices: IndexSet(integersIn: 300 ... 400), startId: 300, endId: 1))
|
||||
XCTAssert(sampledState.holesToHigher == true)
|
||||
XCTAssert(sampledState.hole == SampledHistoryViewHole(peerId: peerId, namespace: namespace, tag: nil, indices: IndexSet(integersIn: 300 ... 400), startId: 400, endId: 1))
|
||||
XCTAssert(sampledState.holesToHigher == false)
|
||||
XCTAssert(sampledState.holesToLower == true)
|
||||
}
|
||||
|
||||
func testEdgeHoles4() {
|
||||
addMessage(100, 100)
|
||||
addMessage(200, 200)
|
||||
addMessage(300, 300)
|
||||
let _ = addMessage(100, 100)
|
||||
let _ = addMessage(200, 200)
|
||||
let _ = addMessage(300, 300)
|
||||
|
||||
addHole(300 ... 400, space: .everywhere)
|
||||
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: 200)), tag: nil, limit: 10, locations: .single(peerId))
|
||||
let state = HistoryViewState(postbox: self.postbox!, inputAnchor: .message(MessageId(peerId: peerId, namespace: namespace, id: 200)), tag: nil, statistics: [], halfLimit: 10, locations: .single(peerId))
|
||||
guard case let .loaded(loadedState) = state else {
|
||||
XCTAssert(false)
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user