Swiftgram/submodules/Postbox/Sources/PeerMergedOperationLogView.swift

139 lines
5.9 KiB
Swift

import Foundation
final class MutablePeerMergedOperationLogView {
let tag: PeerOperationLogTag
let filterByPeerId: PeerId?
var entries: [PeerMergedOperationLogEntry]
var tailIndex: Int32?
let limit: Int
init(postbox: PostboxImpl, tag: PeerOperationLogTag, filterByPeerId: PeerId?, limit: Int) {
self.tag = tag
self.filterByPeerId = filterByPeerId
if let filterByPeerId = self.filterByPeerId {
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, peerId: filterByPeerId, fromIndex: 0, limit: limit)
} else {
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, fromIndex: 0, limit: limit)
}
self.tailIndex = postbox.peerMergedOperationLogIndexTable.tailIndex(tag: tag)
self.limit = limit
}
func replay(postbox: PostboxImpl, operations: [PeerMergedOperationLogOperation]) -> Bool {
var updated = false
if let filterByPeerId = self.filterByPeerId {
if operations.contains(where: { operation in
switch operation {
case let .append(entry):
if entry.tag == self.tag && entry.peerId == filterByPeerId {
return true
}
case let .remove(tag, peerId, _):
if tag == self.tag && peerId == filterByPeerId {
return true
}
case let .updateContents(entry):
if entry.tag == self.tag && entry.peerId == filterByPeerId {
return true
}
}
return false
}) {
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, peerId: filterByPeerId, fromIndex: 0, limit: limit)
updated = true
}
} else {
var invalidatedTail = false
for operation in operations {
switch operation {
case let .append(entry):
if entry.tag == self.tag {
if let tailIndex = self.tailIndex {
assert(entry.mergedIndex > tailIndex)
self.tailIndex = entry.mergedIndex
if self.entries.count < self.limit {
self.entries.append(entry)
updated = true
}
} else {
updated = true
if !self.entries.isEmpty && !invalidatedTail {
assertionFailure("self.entries.isEmpty == false for tag \(self.tag)")
}
self.entries.append(entry)
self.tailIndex = entry.mergedIndex
}
}
case let .updateContents(entry):
if entry.tag == self.tag {
loop: for i in 0 ..< self.entries.count {
if self.entries[i].tagLocalIndex == entry.tagLocalIndex {
self.entries[i] = entry
updated = true
break loop
}
}
}
case let .remove(tag, _, mergedIndices):
if tag == self.tag {
updated = true
for i in (0 ..< self.entries.count).reversed() {
if mergedIndices.contains(self.entries[i].mergedIndex) {
self.entries.remove(at: i)
}
}
if let tailIndex = self.tailIndex, mergedIndices.contains(tailIndex) {
self.tailIndex = nil
invalidatedTail = true
}
}
}
}
if updated {
if invalidatedTail {
self.tailIndex = postbox.peerMergedOperationLogIndexTable.tailIndex(tag: self.tag)
}
if self.entries.count < self.limit {
if let tailIndex = self.tailIndex {
if self.entries.isEmpty || self.entries.last!.mergedIndex < tailIndex {
var fromIndex: Int32 = 0
if !self.entries.isEmpty {
fromIndex = self.entries.last!.mergedIndex + 1
}
for entry in postbox.peerOperationLogTable.getMergedEntries(tag: self.tag, fromIndex: fromIndex, limit: self.limit - self.entries.count) {
self.entries.append(entry)
}
for i in 0 ..< self.entries.count {
if i != 0 {
assert(self.entries[i].mergedIndex >= self.entries[i - 1].mergedIndex + 1)
}
}
if !self.entries.isEmpty {
assert(self.entries.last!.mergedIndex <= tailIndex)
}
}
}
} else {
assert(self.tailIndex != nil)
if let tailIndex = self.tailIndex {
assert(self.entries.last!.mergedIndex <= tailIndex)
}
}
}
}
return updated
}
}
public final class PeerMergedOperationLogView {
public let entries: [PeerMergedOperationLogEntry]
init(_ view: MutablePeerMergedOperationLogView) {
self.entries = view.entries
}
}