mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
617 lines
25 KiB
Swift
617 lines
25 KiB
Swift
import Foundation
|
|
|
|
public struct MessageHistoryViewId: Equatable {
|
|
let peerId: PeerId
|
|
let id: Int
|
|
let version: Int
|
|
|
|
init(peerId: PeerId, id: Int, version: Int = 0) {
|
|
self.peerId = peerId
|
|
self.id = id
|
|
self.version = version
|
|
}
|
|
|
|
var nextVersion: MessageHistoryViewId {
|
|
return MessageHistoryViewId(peerId: self.peerId, id: self.id, version: self.version + 1)
|
|
}
|
|
}
|
|
|
|
public func ==(lhs: MessageHistoryViewId, rhs: MessageHistoryViewId) -> Bool {
|
|
return lhs.peerId == rhs.peerId && lhs.id == rhs.id && lhs.version == rhs.version
|
|
}
|
|
|
|
enum MutableMessageHistoryEntry {
|
|
case IntermediateMessageEntry(IntermediateMessage, MessageHistoryEntryLocation?)
|
|
case MessageEntry(Message, MessageHistoryEntryLocation?)
|
|
case HoleEntry(MessageHistoryHole, MessageHistoryEntryLocation?)
|
|
|
|
var index: MessageIndex {
|
|
switch self {
|
|
case let .IntermediateMessageEntry(message, _):
|
|
return MessageIndex(id: message.id, timestamp: message.timestamp)
|
|
case let .MessageEntry(message, _):
|
|
return MessageIndex(id: message.id, timestamp: message.timestamp)
|
|
case let .HoleEntry(hole, _):
|
|
return hole.maxIndex
|
|
}
|
|
}
|
|
|
|
func updatedLocation(_ location: MessageHistoryEntryLocation?) -> MutableMessageHistoryEntry {
|
|
switch self {
|
|
case let .IntermediateMessageEntry(message, _):
|
|
return .IntermediateMessageEntry(message, location)
|
|
case let .MessageEntry(message, _):
|
|
return .MessageEntry(message, location)
|
|
case let .HoleEntry(hole, _):
|
|
return .HoleEntry(hole, location)
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct MessageHistoryEntryLocation: Equatable {
|
|
public let index: Int
|
|
public let count: Int
|
|
|
|
var predecessor: MessageHistoryEntryLocation? {
|
|
if index == 0 {
|
|
return nil
|
|
} else {
|
|
return MessageHistoryEntryLocation(index: index - 1, count: count)
|
|
}
|
|
}
|
|
|
|
var successor: MessageHistoryEntryLocation? {
|
|
if index == count - 1 {
|
|
return nil
|
|
} else {
|
|
return MessageHistoryEntryLocation(index: index + 1, count: count)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func ==(lhs: MessageHistoryEntryLocation, rhs: MessageHistoryEntryLocation) -> Bool {
|
|
return lhs.index == rhs.index && lhs.count == rhs.count
|
|
}
|
|
|
|
public enum MessageHistoryEntry: Comparable {
|
|
case MessageEntry(Message, MessageHistoryEntryLocation?)
|
|
case HoleEntry(MessageHistoryHole, MessageHistoryEntryLocation?)
|
|
|
|
public var index: MessageIndex {
|
|
switch self {
|
|
case let .MessageEntry(message, _):
|
|
return MessageIndex(id: message.id, timestamp: message.timestamp)
|
|
case let .HoleEntry(hole, _):
|
|
return hole.maxIndex
|
|
}
|
|
}
|
|
}
|
|
|
|
public func ==(lhs: MessageHistoryEntry, rhs: MessageHistoryEntry) -> Bool {
|
|
switch lhs {
|
|
case let .MessageEntry(lhsMessage, lhsLocation):
|
|
switch rhs {
|
|
case .HoleEntry:
|
|
return false
|
|
case let .MessageEntry(rhsMessage, rhsLocation):
|
|
if MessageIndex(lhsMessage) == MessageIndex(rhsMessage) && lhsMessage.flags == rhsMessage.flags && lhsLocation == rhsLocation {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
case let .HoleEntry(lhsHole, lhsLocation):
|
|
switch rhs {
|
|
case let .HoleEntry(rhsHole, rhsLocation):
|
|
return lhsHole == rhsHole && lhsLocation == rhsLocation
|
|
case .MessageEntry:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
public func <(lhs: MessageHistoryEntry, rhs: MessageHistoryEntry) -> Bool {
|
|
return lhs.index < rhs.index
|
|
}
|
|
|
|
final class MutableMessageHistoryViewReplayContext {
|
|
var invalidEarlier: Bool = false
|
|
var invalidLater: Bool = false
|
|
var removedEntries: Bool = false
|
|
|
|
func empty() -> Bool {
|
|
return !self.removedEntries && !invalidEarlier && !invalidLater
|
|
}
|
|
}
|
|
|
|
final class MutableMessageHistoryView {
|
|
private(set) var id: MessageHistoryViewId
|
|
let tagMask: MessageTags?
|
|
fileprivate var anchorIndex: MessageHistoryAnchorIndex
|
|
fileprivate let combinedReadState: CombinedPeerReadState?
|
|
fileprivate var earlier: MutableMessageHistoryEntry?
|
|
fileprivate var later: MutableMessageHistoryEntry?
|
|
fileprivate var entries: [MutableMessageHistoryEntry]
|
|
fileprivate let fillCount: Int
|
|
|
|
init(id: MessageHistoryViewId, anchorIndex: MessageHistoryAnchorIndex, combinedReadState: CombinedPeerReadState?, earlier: MutableMessageHistoryEntry?, entries: [MutableMessageHistoryEntry], later: MutableMessageHistoryEntry?, tagMask: MessageTags?, count: Int) {
|
|
self.id = id
|
|
self.anchorIndex = anchorIndex
|
|
self.combinedReadState = combinedReadState
|
|
self.earlier = earlier
|
|
self.entries = entries
|
|
self.later = later
|
|
self.tagMask = tagMask
|
|
self.fillCount = count
|
|
}
|
|
|
|
func incrementVersion() {
|
|
self.id = self.id.nextVersion
|
|
}
|
|
|
|
func updateVisibleRange(earliestVisibleIndex: MessageIndex, latestVisibleIndex: MessageIndex, context: MutableMessageHistoryViewReplayContext) -> Bool {
|
|
var minIndex: Int?
|
|
var maxIndex: Int?
|
|
|
|
for i in 0 ..< self.entries.count {
|
|
if self.entries[i].index >= earliestVisibleIndex {
|
|
minIndex = i
|
|
break
|
|
}
|
|
}
|
|
|
|
for i in (0 ..< self.entries.count).reversed() {
|
|
if self.entries[i].index <= latestVisibleIndex {
|
|
maxIndex = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if let minIndex = minIndex, let maxIndex = maxIndex {
|
|
var minClipIndex = minIndex
|
|
var maxClipIndex = maxIndex
|
|
|
|
while maxClipIndex - minClipIndex <= self.fillCount {
|
|
if maxClipIndex != self.entries.count - 1 {
|
|
maxClipIndex += 1
|
|
}
|
|
|
|
if minClipIndex != 0 {
|
|
minClipIndex -= 1
|
|
} else if maxClipIndex == self.entries.count - 1 {
|
|
break
|
|
}
|
|
}
|
|
|
|
if minClipIndex != 0 || maxClipIndex != self.entries.count - 1 {
|
|
if minClipIndex != 0 {
|
|
self.earlier = self.entries[minClipIndex - 1]
|
|
}
|
|
|
|
if maxClipIndex != self.entries.count - 1 {
|
|
self.later = self.entries[maxClipIndex + 1]
|
|
}
|
|
|
|
for _ in 0 ..< self.entries.count - 1 - maxClipIndex {
|
|
/*if case let .MessageEntry(message) = self.entries.last! {
|
|
print("remove last \(message.text)")
|
|
}*/
|
|
self.entries.removeLast()
|
|
}
|
|
|
|
for _ in 0 ..< minClipIndex {
|
|
/*if case let .MessageEntry(message) = self.entries.first! {
|
|
print("remove first \(message.text)")
|
|
}*/
|
|
self.entries.removeFirst()
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func updateAnchorIndex(_ getIndex: (MessageId) -> MessageHistoryAnchorIndex?) -> Bool {
|
|
if !self.anchorIndex.exact {
|
|
if let index = getIndex(self.anchorIndex.index.id) {
|
|
self.anchorIndex = index
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func refreshDueToExternalTransaction(fetchAroundHistoryEntries: (_ index: MessageIndex, _ count: Int, _ tagMask: MessageTags?) -> (entries: [MutableMessageHistoryEntry], lower: MutableMessageHistoryEntry?, upper: MutableMessageHistoryEntry?)) -> Bool {
|
|
var index = MessageIndex.upperBound(peerId: self.anchorIndex.index.id.peerId)
|
|
if !self.entries.isEmpty {
|
|
if let _ = self.later {
|
|
index = self.entries[self.entries.count / 2].index
|
|
}
|
|
}
|
|
|
|
let (entries, earlier, later) = fetchAroundHistoryEntries(index, max(self.fillCount, self.entries.count), self.tagMask)
|
|
|
|
self.entries = entries
|
|
self.earlier = earlier
|
|
self.later = later
|
|
|
|
/*let (entries, earlier, later) = self.fetchAroundHistoryEntries(index, count: count, tagMask: tagMask)
|
|
|
|
let mutableView = MutableMessageHistoryView(id: MessageHistoryViewId(peerId: peerId, id: self.takeNextViewId()), anchorIndex: anchorIndex, combinedReadState: fixedCombinedReadState ?? self.readStateTable.getCombinedState(peerId), earlier: earlier, entries: entries, later: later, tagMask: tagMask, count: count)*/
|
|
return true
|
|
}
|
|
|
|
func replay(_ operations: [MessageHistoryOperation], holeFillDirections: [MessageIndex: HoleFillDirection], updatedMedia: [MediaId: Media?], context: MutableMessageHistoryViewReplayContext) -> Bool {
|
|
let tagMask = self.tagMask
|
|
let unwrappedTagMask: UInt32 = tagMask?.rawValue ?? 0
|
|
|
|
var hasChanges = false
|
|
for operation in operations {
|
|
switch operation {
|
|
case let .InsertHole(hole):
|
|
if tagMask == nil || (hole.tags & unwrappedTagMask) != 0 {
|
|
if self.add(.HoleEntry(hole, nil), holeFillDirections: holeFillDirections) {
|
|
hasChanges = true
|
|
}
|
|
}
|
|
case let .InsertMessage(intermediateMessage):
|
|
if tagMask == nil || (intermediateMessage.tags.rawValue & unwrappedTagMask) != 0 {
|
|
if self.add(.IntermediateMessageEntry(intermediateMessage, nil), holeFillDirections: holeFillDirections) {
|
|
hasChanges = true
|
|
}
|
|
}
|
|
case let .Remove(indices):
|
|
if self.remove(Set(indices), context: context) {
|
|
hasChanges = true
|
|
}
|
|
case let .UpdateReadState(combinedReadState):
|
|
hasChanges = true
|
|
//self.combinedReadState = combinedReadState
|
|
case let .UpdateEmbeddedMedia(index, embeddedMediaData):
|
|
for i in 0 ..< self.entries.count {
|
|
if case let .IntermediateMessageEntry(message, _) = self.entries[i] , MessageIndex(message) == index {
|
|
self.entries[i] = .IntermediateMessageEntry(IntermediateMessage(stableId: message.stableId, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: embeddedMediaData, referencedMedia: message.referencedMedia), nil)
|
|
hasChanges = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !updatedMedia.isEmpty {
|
|
for i in 0 ..< self.entries.count {
|
|
switch self.entries[i] {
|
|
case let .MessageEntry(message, _):
|
|
var rebuild = false
|
|
for media in message.media {
|
|
if let mediaId = media.id, let _ = updatedMedia[mediaId] {
|
|
rebuild = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if rebuild {
|
|
var messageMedia: [Media] = []
|
|
for media in message.media {
|
|
if let mediaId = media.id, let updated = updatedMedia[mediaId] {
|
|
if let updated = updated {
|
|
messageMedia.append(updated)
|
|
}
|
|
} else {
|
|
messageMedia.append(media)
|
|
}
|
|
}
|
|
let updatedMessage = Message(stableId: message.stableId, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages)
|
|
self.entries[i] = .MessageEntry(updatedMessage, nil)
|
|
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
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return hasChanges
|
|
}
|
|
|
|
private func add(_ entry: MutableMessageHistoryEntry, holeFillDirections: [MessageIndex: HoleFillDirection]) -> Bool {
|
|
if self.entries.count == 0 {
|
|
self.entries.append(entry)
|
|
return true
|
|
} else {
|
|
let latestIndex = self.entries[self.entries.count - 1].index
|
|
let earliestIndex = self.entries[0].index
|
|
|
|
let index = entry.index
|
|
|
|
if index < earliestIndex {
|
|
if self.earlier == nil || self.earlier!.index < index {
|
|
self.entries.insert(entry, at: 0)
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
} else if index > latestIndex {
|
|
if let later = self.later {
|
|
if index < later.index {
|
|
self.entries.append(entry)
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
} else {
|
|
self.entries.append(entry)
|
|
return true
|
|
}
|
|
} else if index != earliestIndex && index != latestIndex {
|
|
var i = self.entries.count
|
|
while i >= 1 {
|
|
if self.entries[i - 1].index < index {
|
|
break
|
|
}
|
|
i -= 1
|
|
}
|
|
self.entries.insert(entry, at: i)
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
private func remove(_ indices: Set<MessageIndex>, context: MutableMessageHistoryViewReplayContext) -> Bool {
|
|
var hasChanges = false
|
|
if let earlier = self.earlier , indices.contains(earlier.index) {
|
|
context.invalidEarlier = true
|
|
hasChanges = true
|
|
}
|
|
|
|
if let later = self.later , indices.contains(later.index) {
|
|
context.invalidLater = true
|
|
hasChanges = true
|
|
}
|
|
|
|
if self.entries.count != 0 {
|
|
var i = self.entries.count - 1
|
|
while i >= 0 {
|
|
if indices.contains(self.entries[i].index) {
|
|
self.entries.remove(at: i)
|
|
context.removedEntries = true
|
|
hasChanges = true
|
|
}
|
|
i -= 1
|
|
}
|
|
}
|
|
|
|
return hasChanges
|
|
}
|
|
|
|
func updatePeers(_ peers: [PeerId: Peer]) -> Bool {
|
|
return false
|
|
}
|
|
|
|
func complete(context: MutableMessageHistoryViewReplayContext, fetchEarlier: (MessageIndex?, Int) -> [MutableMessageHistoryEntry], fetchLater: (MessageIndex?, Int) -> [MutableMessageHistoryEntry]) {
|
|
if context.removedEntries && self.entries.count < self.fillCount {
|
|
if self.entries.count == 0 {
|
|
let anchorIndex = (self.later ?? self.earlier)?.index
|
|
|
|
let fetchedEntries = fetchEarlier(anchorIndex, self.fillCount + 2)
|
|
if fetchedEntries.count >= self.fillCount + 2 {
|
|
self.earlier = fetchedEntries.last
|
|
for i in (1 ..< fetchedEntries.count - 1).reversed() {
|
|
self.entries.append(fetchedEntries[i])
|
|
}
|
|
self.later = fetchedEntries.first
|
|
}
|
|
} else {
|
|
let fetchedEntries = fetchEarlier(self.entries[0].index, self.fillCount - self.entries.count)
|
|
for entry in fetchedEntries {
|
|
self.entries.insert(entry, at: 0)
|
|
}
|
|
|
|
if context.invalidEarlier {
|
|
var earlyId: MessageIndex?
|
|
let i = 0
|
|
if i < self.entries.count {
|
|
earlyId = self.entries[i].index
|
|
}
|
|
|
|
let earlierEntries = fetchEarlier(earlyId, 1)
|
|
self.earlier = earlierEntries.first
|
|
}
|
|
|
|
if context.invalidLater {
|
|
var laterId: MessageIndex?
|
|
let i = self.entries.count - 1
|
|
if i >= 0 {
|
|
laterId = self.entries[i].index
|
|
}
|
|
|
|
let laterEntries = fetchLater(laterId, 1)
|
|
self.later = laterEntries.first
|
|
}
|
|
}
|
|
} else {
|
|
if context.invalidEarlier {
|
|
var earlyId: MessageIndex?
|
|
let i = 0
|
|
if i < self.entries.count {
|
|
earlyId = self.entries[i].index
|
|
}
|
|
|
|
let earlierEntries = fetchEarlier(earlyId, 1)
|
|
self.earlier = earlierEntries.first
|
|
}
|
|
|
|
if context.invalidLater {
|
|
var laterId: MessageIndex?
|
|
let i = self.entries.count - 1
|
|
if i >= 0 {
|
|
laterId = self.entries[i].index
|
|
}
|
|
|
|
let laterEntries = fetchLater(laterId, 1)
|
|
self.later = laterEntries.first
|
|
}
|
|
}
|
|
}
|
|
|
|
func render(_ renderIntermediateMessage: (IntermediateMessage) -> Message) {
|
|
if let earlier = self.earlier, case let .IntermediateMessageEntry(intermediateMessage, location) = earlier {
|
|
self.earlier = .MessageEntry(renderIntermediateMessage(intermediateMessage), location)
|
|
}
|
|
if let later = self.later, case let .IntermediateMessageEntry(intermediateMessage, location) = later {
|
|
self.later = .MessageEntry(renderIntermediateMessage(intermediateMessage), location)
|
|
}
|
|
|
|
for i in 0 ..< self.entries.count {
|
|
if case let .IntermediateMessageEntry(intermediateMessage, location) = self.entries[i] {
|
|
self.entries[i] = .MessageEntry(renderIntermediateMessage(intermediateMessage), location)
|
|
}
|
|
}
|
|
}
|
|
|
|
func firstHole() -> (MessageHistoryHole, HoleFillDirection)? {
|
|
if self.entries.isEmpty {
|
|
return nil
|
|
}
|
|
|
|
var referenceIndex = self.entries.count - 1
|
|
for i in 0 ..< self.entries.count {
|
|
if self.entries[i].index >= self.anchorIndex.index {
|
|
referenceIndex = i
|
|
break
|
|
}
|
|
}
|
|
|
|
var i = referenceIndex
|
|
var j = referenceIndex + 1
|
|
|
|
while i >= 0 || j < self.entries.count {
|
|
if j < self.entries.count {
|
|
if case let .HoleEntry(hole, _) = self.entries[j] {
|
|
if self.anchorIndex.index.id.namespace == hole.id.namespace {
|
|
if self.anchorIndex.index.id.id >= hole.min && self.anchorIndex.index.id.id <= hole.maxIndex.id.id {
|
|
return (hole, .AroundIndex(self.anchorIndex.index))
|
|
}
|
|
}
|
|
|
|
return (hole, hole.maxIndex <= self.anchorIndex.index ? .UpperToLower : .LowerToUpper)
|
|
}
|
|
}
|
|
|
|
if i >= 0 {
|
|
if case let .HoleEntry(hole, _) = self.entries[i] {
|
|
if self.anchorIndex.index.id.namespace == hole.id.namespace {
|
|
if self.anchorIndex.index.id.id >= hole.min && self.anchorIndex.index.id.id <= hole.maxIndex.id.id {
|
|
return (hole, .AroundIndex(self.anchorIndex.index))
|
|
}
|
|
}
|
|
|
|
return (hole, hole.maxIndex <= self.anchorIndex.index ? .UpperToLower : .LowerToUpper)
|
|
}
|
|
}
|
|
|
|
i -= 1
|
|
j += 1
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public final class MessageHistoryView {
|
|
public let id: MessageHistoryViewId
|
|
public let anchorIndex: MessageIndex
|
|
public let earlierId: MessageIndex?
|
|
public let laterId: MessageIndex?
|
|
public let entries: [MessageHistoryEntry]
|
|
public let maxReadIndex: MessageIndex?
|
|
public let combinedReadState: CombinedPeerReadState?
|
|
|
|
init(_ mutableView: MutableMessageHistoryView) {
|
|
self.id = mutableView.id
|
|
self.anchorIndex = mutableView.anchorIndex.index
|
|
|
|
var entries: [MessageHistoryEntry] = []
|
|
for entry in mutableView.entries {
|
|
switch entry {
|
|
case let .HoleEntry(hole, location):
|
|
entries.append(.HoleEntry(hole, location))
|
|
case let .MessageEntry(message, location):
|
|
entries.append(.MessageEntry(message, location))
|
|
case .IntermediateMessageEntry:
|
|
assertionFailure("unexpected IntermediateMessageEntry in MessageHistoryView.init()")
|
|
}
|
|
}
|
|
self.entries = entries
|
|
|
|
self.earlierId = mutableView.earlier?.index
|
|
self.laterId = mutableView.later?.index
|
|
|
|
self.combinedReadState = mutableView.combinedReadState
|
|
|
|
if let combinedReadState = mutableView.combinedReadState , combinedReadState.count != 0 {
|
|
var maxIndex: MessageIndex?
|
|
for (namespace, state) in combinedReadState.states {
|
|
var maxNamespaceIndex: MessageIndex?
|
|
var index = entries.count - 1
|
|
for entry in entries.reversed() {
|
|
if entry.index.id.namespace == namespace && entry.index.id.id <= state.maxIncomingReadId {
|
|
maxNamespaceIndex = entry.index
|
|
break
|
|
}
|
|
index -= 1
|
|
}
|
|
if maxNamespaceIndex == nil && index == -1 && entries.count != 0 {
|
|
index = 0
|
|
for entry in entries {
|
|
if entry.index.id.namespace == namespace {
|
|
maxNamespaceIndex = entry.index
|
|
break
|
|
}
|
|
index += 1
|
|
}
|
|
}
|
|
if let _ = maxNamespaceIndex , index + 1 < entries.count {
|
|
for i in index + 1 ..< entries.count {
|
|
if case let .MessageEntry(message, _) = entries[i] , !message.flags.contains(.Incoming) {
|
|
maxNamespaceIndex = MessageIndex(message)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if let maxNamespaceIndex = maxNamespaceIndex , maxIndex == nil || maxIndex! < maxNamespaceIndex {
|
|
maxIndex = maxNamespaceIndex
|
|
}
|
|
}
|
|
self.maxReadIndex = maxIndex
|
|
} else {
|
|
self.maxReadIndex = nil
|
|
}
|
|
}
|
|
}
|