/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "data/data_abstract_sparse_ids.h" #include "data/data_messages.h" namespace Storage { struct SparseIdsListResult; struct SparseIdsSliceUpdate; } // namespace Storage class SparseIdsSlice final : public AbstractSparseIds> { public: using Key = MsgId; using AbstractSparseIds>::AbstractSparseIds; }; using SparseUnsortedIdsSlice = AbstractSparseIds>; class SparseIdsMergedSlice { public: using UniversalMsgId = MsgId; static constexpr MsgId kScheduledTopicId = ScheduledMaxMsgId; struct Key { Key( PeerId peerId, MsgId topicRootId, PeerId migratedPeerId, UniversalMsgId universalId) : peerId(peerId) , topicRootId(topicRootId) , migratedPeerId(topicRootId ? 0 : migratedPeerId) , universalId(universalId) { } friend inline constexpr bool operator==( const Key &, const Key &) = default; PeerId peerId = 0; MsgId topicRootId = 0; PeerId migratedPeerId = 0; UniversalMsgId universalId = 0; }; SparseIdsMergedSlice(Key key); SparseIdsMergedSlice( Key key, SparseIdsSlice part, std::optional migrated); SparseIdsMergedSlice( Key key, SparseUnsortedIdsSlice unsorted); std::optional fullCount() const; std::optional skippedBefore() const; std::optional skippedAfter() const; std::optional indexOf(FullMsgId fullId) const; int size() const; FullMsgId operator[](int index) const; std::optional distance(const Key &a, const Key &b) const; std::optional nearest(UniversalMsgId id) const; using SimpleViewerFunction = rpl::producer( PeerId peerId, MsgId topicRootId, SparseIdsSlice::Key simpleKey, int limitBefore, int limitAfter); static rpl::producer CreateViewer( SparseIdsMergedSlice::Key key, int limitBefore, int limitAfter, Fn simpleViewer); private: static SparseIdsSlice::Key PartKey(const Key &key) { return (key.universalId < 0) ? 1 : key.universalId; } static SparseIdsSlice::Key MigratedKey(const Key &key) { return (key.universalId < 0) ? (ServerMaxMsgId + key.universalId) : (key.universalId > 0) ? (ServerMaxMsgId - 1) : 0; } static std::optional MigratedSlice(const Key &key) { return key.migratedPeerId ? base::make_optional(SparseIdsSlice()) : std::nullopt; } static bool IsFromSlice(PeerId peerId, FullMsgId fullId) { return (peerId == fullId.peer); } static FullMsgId ComputeId(PeerId peerId, MsgId msgId) { return FullMsgId(peerId, msgId); } static FullMsgId ComputeId(const Key &key) { return (key.universalId >= 0) ? ComputeId(key.peerId, key.universalId) : ComputeId(key.migratedPeerId, ServerMaxMsgId + key.universalId); } static std::optional Add( const std::optional &a, const std::optional &b) { return (a && b) ? base::make_optional(*a + *b) : std::nullopt; } bool isFromPart(FullMsgId fullId) const { return IsFromSlice(_key.peerId, fullId); } bool isFromMigrated(FullMsgId fullId) const { return _migrated ? IsFromSlice(_key.migratedPeerId, fullId) : false; } int migratedSize() const { return isolatedInPart() ? 0 : _migrated->size(); } bool isolatedInPart() const { return IsServerMsgId(_key.universalId) && (!_migrated || _part.skippedBefore() != 0); } bool isolatedInMigrated() const { return IsServerMsgId(ServerMaxMsgId + _key.universalId) && (_migrated->skippedAfter() != 0); } Key _key; SparseIdsSlice _part; std::optional _migrated; std::optional _unsorted; }; class SparseIdsSliceBuilder { public: using Key = SparseIdsSlice::Key; SparseIdsSliceBuilder(Key key, int limitBefore, int limitAfter); bool applyInitial(const Storage::SparseIdsListResult &result); bool applyUpdate(const Storage::SparseIdsSliceUpdate &update); bool removeOne(MsgId messageId); bool removeAll(); bool invalidateBottom(); void checkInsufficient(); struct AroundData { MsgId aroundId = 0; Data::LoadDirection direction = Data::LoadDirection::Around; inline bool operator<(const AroundData &other) const { return (aroundId < other.aroundId) || ((aroundId == other.aroundId) && (direction < other.direction)); } }; auto insufficientAround() const { return _insufficientAround.events(); } SparseIdsSlice snapshot() const; private: enum class RequestDirection { Before, After, }; void requestMessages(RequestDirection direction); void requestMessagesCount(); void fillSkippedAndSliceToLimits(); void sliceToLimits(); void mergeSliceData( std::optional count, const base::flat_set &messageIds, std::optional skippedBefore = std::nullopt, std::optional skippedAfter = std::nullopt); Key _key; base::flat_set _ids; std::optional _fullCount; std::optional _skippedBefore; std::optional _skippedAfter; int _limitBefore = 0; int _limitAfter = 0; rpl::event_stream _insufficientAround; };