From 31b7d815a81e3927baead22c24b7930886c24d09 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 25 Dec 2018 22:02:31 +0400 Subject: [PATCH] Fix hashing and offsets --- .../AccountStateManagementUtils.swift | 8 +- TelegramCore/AccountStateManager.swift | 2 +- TelegramCore/AccountStateReset.swift | 398 +++++------------- 3 files changed, 106 insertions(+), 302 deletions(-) diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index 076e12e630..b77116dfae 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -1241,7 +1241,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo langCode = langPackDifference.langCode } updatedState.updateLangPack(langCode: langCode, difference: difference) - case let .updateMessagePoll(flags, pollId, poll, results): + case let .updateMessagePoll(_, pollId, poll, results): updatedState.updateMessagePoll(MediaId(namespace: Namespaces.Media.CloudPoll, id: pollId), poll: poll, results: results) default: break @@ -1262,9 +1262,9 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } if !channelPeers.isEmpty { let resetSignal = resetChannels(network: network, peers: channelPeers, state: updatedState) - |> map { resultState -> (AccountMutableState, Bool, Int32?) in - return (resultState, true, nil) - } + |> map { resultState -> (AccountMutableState, Bool, Int32?) in + return (resultState, true, nil) + } pollChannelSignals = [resetSignal] } else { pollChannelSignals = [] diff --git a/TelegramCore/AccountStateManager.swift b/TelegramCore/AccountStateManager.swift index 228a466089..24debb5226 100644 --- a/TelegramCore/AccountStateManager.swift +++ b/TelegramCore/AccountStateManager.swift @@ -371,7 +371,7 @@ public final class AccountStateManager { if let authorizedState = state.state { var ptsTotalLimit: Int32 = 10000 #if DEBUG - ptsTotalLimit = 1000 + //ptsTotalLimit = 10 #endif let request = network.request(Api.functions.updates.getDifference(flags: 1 << 0, pts: authorizedState.pts, ptsTotalLimit: ptsTotalLimit, date: authorizedState.date, qts: authorizedState.qts)) |> retryRequest diff --git a/TelegramCore/AccountStateReset.swift b/TelegramCore/AccountStateReset.swift index c03c384d9a..eedaae0ec6 100644 --- a/TelegramCore/AccountStateReset.swift +++ b/TelegramCore/AccountStateReset.swift @@ -10,6 +10,7 @@ import Foundation #endif private struct LocalChatListEntryRange { + var entries: [ChatListNamespaceEntry] var upperBound: ChatListIndex? var lowerBound: ChatListIndex var count: Int32 @@ -25,6 +26,59 @@ private func combineHash(_ value: Int32, into hash: inout UInt32) { hash = (hash &* 20261) &+ low } +private func combineChatListNamespaceEntryHash(index: ChatListIndex, readState: PeerReadState?, topMessageAttributes: [MessageAttribute], tagSummary: MessageHistoryTagNamespaceSummary?, interfaceState: PeerChatInterfaceState?, into hash: inout UInt32) { + /* + dialog.pinned ? 1 : 0, + dialog.unread_mark ? 1 : 0, + dialog.peer.channel_id || dialog.peer.chat_id || dialog.peer.user_id, + dialog.top_message.id, + top_message.edit_date || top_message.date, + dialog.read_inbox_max_id, + dialog.read_outbox_max_id, + dialog.unread_count, + dialog.unread_mentions_count, + draft.draft.date || 0 + + */ + + combineHash(index.pinningIndex != nil ? 1 : 0, into: &hash) + if let readState = readState, readState.markedUnread { + combineHash(1, into: &hash) + } else { + combineHash(0, into: &hash) + } + combineHash(index.messageIndex.id.peerId.id, into: &hash) + combineHash(index.messageIndex.id.id, into: &hash) + var timestamp = index.messageIndex.timestamp + for attribute in topMessageAttributes { + if let attribute = attribute as? EditedMessageAttribute { + timestamp = max(timestamp, attribute.date) + } + } + combineHash(timestamp, into: &hash) + if let readState = readState, case let .idBased(maxIncomingReadId, maxOutgoingReadId, _, count, _) = readState { + combineHash(maxIncomingReadId, into: &hash) + combineHash(maxOutgoingReadId, into: &hash) + combineHash(count, into: &hash) + } else { + combineHash(0, into: &hash) + combineHash(0, into: &hash) + combineHash(0, into: &hash) + } + + if let tagSummary = tagSummary { + combineHash(tagSummary.count, into: &hash) + } else { + combineHash(0, into: &hash) + } + + if let embeddedState = interfaceState?.chatListEmbeddedState { + combineHash(embeddedState.timestamp, into: &hash) + } else { + combineHash(0, into: &hash) + } +} + private func localChatListEntryRanges(_ entries: [ChatListNamespaceEntry], limit: Int) -> [LocalChatListEntryRange] { var result: [LocalChatListEntryRange] = [] var currentRange: LocalChatListEntryRange? @@ -35,61 +89,13 @@ private func localChatListEntryRanges(_ entries: [ChatListNamespaceEntry], limit if let current = currentRange { updatedRange = current } else { - updatedRange = LocalChatListEntryRange(upperBound: result.last?.lowerBound, lowerBound: index, count: 0, hash: 0) + updatedRange = LocalChatListEntryRange(entries: [], upperBound: result.last?.lowerBound, lowerBound: index, count: 0, hash: 0) } + updatedRange.entries.append(entries[i]) updatedRange.lowerBound = index updatedRange.count += 1 - /* - dialog.pinned ? 1 : 0, - dialog.unread_mark ? 1 : 0, - dialog.peer.channel_id || dialog.peer.chat_id || dialog.peer.user_id, - dialog.top_message.id, - top_message.edit_date || top_message.date, - dialog.read_inbox_max_id, - dialog.read_outbox_max_id, - dialog.unread_count, - dialog.unread_mentions_count, - draft.draft.date || 0 - - */ - - combineHash(index.pinningIndex != nil ? 1 : 0, into: &updatedRange.hash) - if let readState = readState, readState.markedUnread { - combineHash(1, into: &updatedRange.hash) - } else { - combineHash(0, into: &updatedRange.hash) - } - combineHash(index.messageIndex.id.peerId.id, into: &updatedRange.hash) - combineHash(index.messageIndex.id.id, into: &updatedRange.hash) - var timestamp = index.messageIndex.timestamp - for attribute in topMessageAttributes { - if let attribute = attribute as? EditedMessageAttribute { - timestamp = max(timestamp, attribute.date) - } - } - combineHash(timestamp, into: &updatedRange.hash) - if let readState = readState, case let .idBased(maxIncomingReadId, maxOutgoingReadId, _, count, _) = readState { - combineHash(maxIncomingReadId, into: &updatedRange.hash) - combineHash(maxOutgoingReadId, into: &updatedRange.hash) - combineHash(count, into: &updatedRange.hash) - } else { - combineHash(0, into: &updatedRange.hash) - combineHash(0, into: &updatedRange.hash) - combineHash(0, into: &updatedRange.hash) - } - - if let tagSummary = tagSummary { - combineHash(tagSummary.count, into: &updatedRange.hash) - } else { - combineHash(0, into: &updatedRange.hash) - } - - if let embeddedState = interfaceState?.chatListEmbeddedState { - combineHash(embeddedState.timestamp, into: &updatedRange.hash) - } else { - combineHash(0, into: &updatedRange.hash) - } + combineChatListNamespaceEntryHash(index: index, readState: readState, topMessageAttributes: topMessageAttributes, tagSummary: tagSummary, interfaceState: interfaceState, into: &updatedRange.hash) if Int(updatedRange.count) >= limit { result.append(updatedRange) @@ -127,14 +133,14 @@ func accountStateReset(postbox: Postbox, network: Network, accountPeerId: PeerId return transaction.getChatListNamespaceEntries(groupId: nil, namespace: Namespaces.Message.Cloud, summaryTag: MessageTags.unseenPersonalMessage) } |> mapToSignal { localChatListEntries -> Signal in - let localRanges = localChatListEntryRanges(localChatListEntries, limit: 10) + let localRanges = localChatListEntryRanges(localChatListEntries, limit: 100) var signal: Signal = .complete() for i in 0 ..< localRanges.count { let upperBound: MessageIndex let head = i == 0 let localRange = localRanges[i] if let rangeUpperBound = localRange.upperBound { - upperBound = rangeUpperBound.messageIndex + upperBound = rangeUpperBound.messageIndex.predecessor() } else { upperBound = MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 0), timestamp: 0) } @@ -170,6 +176,48 @@ func accountStateReset(postbox: Postbox, network: Network, accountPeerId: PeerId return postbox.transaction { transaction -> Void in for range in collectedRanges { let previousPeerIds = transaction.resetChatList(keepPeerNamespaces: [Namespaces.Peer.SecretChat], upperBound: range.local.upperBound ?? ChatListIndex.absoluteUpperBound, lowerBound: range.local.lowerBound) + #if DEBUG + for peerId in previousPeerIds { + print("pre \(peerId) [\(transaction.getPeer(peerId)?.displayTitle ?? "nil")]") + } + print("pre hash \(range.local.hash)") + print("") + + var preRecalculatedHash: UInt32 = 0 + for entry in range.local.entries { + switch entry { + case let .peer(index, readState, topMessageAttributes, tagSummary, interfaceState): + print("val \(index.messageIndex.id.peerId) [\(transaction.getPeer(index.messageIndex.id.peerId)?.displayTitle ?? "nil")]") + combineChatListNamespaceEntryHash(index: index, readState: readState, topMessageAttributes: topMessageAttributes, tagSummary: nil, interfaceState: nil, into: &preRecalculatedHash) + default: + break + } + } + print("pre recalculated hash \(preRecalculatedHash)") + print("") + + var hash: UInt32 = 0 + range.remote.storeMessages.compactMap({ message -> MessageIndex? in + if case let .Id(id) = message.id { + if range.remote.topMessageIds[id.peerId] == id { + return message.index + } + } + return nil + }).sorted(by: { lhs, rhs in + return lhs > rhs + }).forEach({ index in + var topMessageAttributes: [MessageAttribute] = [] + for message in range.remote.storeMessages { + if case let .Id(id) = message.id, id == index.id { + topMessageAttributes = message.attributes + } + } + combineChatListNamespaceEntryHash(index: ChatListIndex(pinningIndex: nil, messageIndex: index), readState: range.remote.readStates[index.id.peerId]?[Namespaces.Message.Cloud], topMessageAttributes: topMessageAttributes, tagSummary: nil, interfaceState: nil, into: &hash) + print("upd \(index.id.peerId) [\(transaction.getPeer(index.id.peerId)?.displayTitle ?? "nil")]") + }) + print("upd hash \(hash)") + #endif updatePeers(transaction: transaction, peers: range.remote.peers, update: { _, updated -> Peer in return updated @@ -243,249 +291,5 @@ func accountStateReset(postbox: Postbox, network: Network, accountPeerId: PeerId } } } - - return combineLatest(network.request(Api.functions.messages.getDialogs(flags: 0, offsetDate: 0, offsetId: 0, offsetPeer: .inputPeerEmpty, limit: 100, hash: 0)) - |> retryRequest, pinnedChats, state) - |> mapToSignal { result, pinnedChats, state -> Signal in - var dialogsDialogs: [Api.Dialog] = [] - var dialogsMessages: [Api.Message] = [] - var dialogsChats: [Api.Chat] = [] - var dialogsUsers: [Api.User] = [] - - var holeExists = false - - switch result { - case let .dialogs(dialogs, messages, chats, users): - dialogsDialogs = dialogs - dialogsMessages = messages - dialogsChats = chats - dialogsUsers = users - case let .dialogsSlice(_, dialogs, messages, chats, users): - dialogsDialogs = dialogs - dialogsMessages = messages - dialogsChats = chats - dialogsUsers = users - holeExists = true - case .dialogsNotModified: - dialogsDialogs = [] - dialogsMessages = [] - dialogsChats = [] - dialogsUsers = [] - } - - let replacePinnedItemIds: [PinnedItemId] - switch pinnedChats { - case let .peerDialogs(apiDialogs, apiMessages, apiChats, apiUsers, _): - dialogsDialogs.append(contentsOf: apiDialogs) - dialogsMessages.append(contentsOf: apiMessages) - dialogsChats.append(contentsOf: apiChats) - dialogsUsers.append(contentsOf: apiUsers) - - var itemIds: [PinnedItemId] = [] - - loop: for dialog in apiDialogs { - switch dialog { - case let .dialog(_, peer, _, _, _, _, _, _, _, _): - itemIds.append(.peer(peer.peerId)) - /*feed*/ - /*case let .dialogFeed(_, _, _, feedId, _, _, _, _): - itemIds.append(.group(PeerGroupId(rawValue: feedId))) - continue loop*/ - } - } - - replacePinnedItemIds = itemIds - } - - var replacementHole: ChatListHole? - var storeMessages: [StoreMessage] = [] - var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] - var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] - var chatStates: [PeerId: PeerChatState] = [:] - var notificationSettings: [PeerId: PeerNotificationSettings] = [:] - - var topMesageIds: [PeerId: MessageId] = [:] - - loop: for dialog in dialogsDialogs { - let apiPeer: Api.Peer - let apiReadInboxMaxId: Int32 - let apiReadOutboxMaxId: Int32 - let apiTopMessage: Int32 - let apiUnreadCount: Int32 - let apiMarkedUnread: Bool - let apiUnreadMentionsCount: Int32 - var apiChannelPts: Int32? - let apiNotificationSettings: Api.PeerNotifySettings - switch dialog { - case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _): - apiPeer = peer - apiTopMessage = topMessage - apiReadInboxMaxId = readInboxMaxId - apiReadOutboxMaxId = readOutboxMaxId - apiUnreadCount = unreadCount - apiMarkedUnread = (flags & (1 << 3)) != 0 - apiUnreadMentionsCount = unreadMentionsCount - apiNotificationSettings = peerNotificationSettings - apiChannelPts = pts - /*feed*/ - /*case .dialogFeed: - //assertionFailure() - continue loop*/ - } - - let peerId: PeerId - switch apiPeer { - case let .peerUser(userId): - peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } - - if readStates[peerId] == nil { - readStates[peerId] = [:] - } - readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount, markedUnread: apiMarkedUnread) - - if apiTopMessage != 0 { - mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)) - topMesageIds[peerId] = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: apiTopMessage) - } - - if let apiChannelPts = apiChannelPts { - chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts) - } else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudUser { - switch state { - case let .state(pts, _, _, _, _): - chatStates[peerId] = RegularChatState(invalidatedPts: pts) - } - } - - notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) - } - - for message in dialogsMessages { - if let storeMessage = StoreMessage(apiMessage: message) { - storeMessages.append(storeMessage) - } - } - - if holeExists { - for dialog in dialogsDialogs { - switch dialog { - case let .dialog(flags, peer, topMessage, _, _, _, _, _, _, _): - let isPinned = (flags & (1 << 2)) != 0 - - if !isPinned { - var timestamp: Int32? - for message in storeMessages { - if case let .Id(id) = message.id, id.id == topMessage { - timestamp = message.timestamp - } - } - - if let timestamp = timestamp { - let index = MessageIndex(id: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: topMessage - 1), timestamp: timestamp) - if (replacementHole == nil || replacementHole!.index > index) { - replacementHole = ChatListHole(index: index) - } - } - } - /*feed*/ - /*case .dialogFeed: - //assertionFailure() - break*/ - } - } - } - - var peers: [Peer] = [] - var peerPresences: [PeerId: PeerPresence] = [:] - for chat in dialogsChats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in dialogsUsers { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if let presence = TelegramUserPresence(apiUser: user) { - peerPresences[telegramUser.id] = presence - } - } - - return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages in - let previousPeerIds = transaction.resetChatList(keepPeerNamespaces: Set([Namespaces.Peer.SecretChat]), replacementHole: replacementHole) - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) - - transaction.updateCurrentPeerNotificationSettings(notificationSettings) - - var allPeersWithMessages = Set() - for message in storeMessages { - allPeersWithMessages.insert(message.id.peerId) - } - - for (_, messageId) in topMesageIds { - if messageId.id > 1 { - var skipHole = false - if let localTopId = transaction.getTopPeerMessageIndex(peerId: messageId.peerId, namespace: messageId.namespace)?.id { - if localTopId >= messageId { - skipHole = true - } - } - if !skipHole { - transaction.addHole(MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: messageId.id - 1)) - } - } - } - - let _ = transaction.addMessages(storeMessages, location: .UpperHistoryBlock) - - transaction.resetIncomingReadStates(readStates) - - for (peerId, chatState) in chatStates { - if let chatState = chatState as? ChannelState { - if let current = transaction.getPeerChatState(peerId) as? ChannelState { - transaction.setPeerChatState(peerId, state: current.withUpdatedPts(chatState.pts)) - } else { - transaction.setPeerChatState(peerId, state: chatState) - } - } else { - transaction.setPeerChatState(peerId, state: chatState) - } - } - - transaction.setPinnedItemIds(replacePinnedItemIds) - - for (peerId, summary) in mentionTagSummaries { - transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) - } - - let namespacesWithHoles: [PeerId.Namespace: [MessageId.Namespace]] = [ - Namespaces.Peer.CloudUser: [Namespaces.Message.Cloud], - Namespaces.Peer.CloudGroup: [Namespaces.Message.Cloud], - Namespaces.Peer.CloudChannel: [Namespaces.Message.Cloud] - ] - for peerId in previousPeerIds { - if !allPeersWithMessages.contains(peerId), let namespaces = namespacesWithHoles[peerId.namespace] { - for namespace in namespaces { - transaction.addHole(MessageId(peerId: peerId, namespace: namespace, id: Int32.max - 1)) - } - } - } - - if let currentState = transaction.getState() as? AuthorizedAccountState, let embeddedState = currentState.state { - switch state { - case let .state(pts, _, _, seq, _): - transaction.setState(currentState.changedState(AuthorizedAccountState.State(pts: pts, qts: embeddedState.qts, date: embeddedState.date, seq: seq))) - } - } - }) - } } }