/* 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 "media/streaming/media_streaming_loader.h" #include "base/bytes.h" namespace Storage { namespace Cache { struct Key; } // namespace Cache } // namespace Storage namespace Data { class Session; } // namespace Data namespace Media { namespace Streaming { class Loader; struct LoadedPart; enum class Error; class Reader final { public: Reader(not_null owner, std::unique_ptr loader); [[nodiscard]] int size() const; [[nodiscard]] bool fill( int offset, bytes::span buffer, not_null notify); [[nodiscard]] std::optional failed() const; void headerDone(); void stop(); [[nodiscard]] bool isRemoteLoader() const; ~Reader(); private: static constexpr auto kLoadFromRemoteMax = 8; struct CacheHelper; template class StackIntVector { public: bool add(int value); auto values() const; private: std::array _storage = { -1 }; }; struct SerializedSlice { int number = -1; QByteArray data; }; struct FillResult { static constexpr auto kReadFromCacheMax = 2; StackIntVector sliceNumbersFromCache; StackIntVector offsetsFromLoader; SerializedSlice toCache; bool filled = false; }; struct Slice { enum class Flag : uchar { LoadingFromCache = 0x01, LoadedFromCache = 0x02, ChangedSinceCache = 0x04, }; friend constexpr inline bool is_flag_type(Flag) { return true; } using Flags = base::flags; struct PrepareFillResult { StackIntVector offsetsFromLoader; base::flat_map::const_iterator start; base::flat_map::const_iterator finish; bool ready = true; }; bytes::const_span processCacheData( bytes::const_span data, int maxSize); bytes::const_span processComplexCacheData( bytes::const_span data, int maxSize); void addPart(int offset, QByteArray bytes); PrepareFillResult prepareFill(int from, int till); // Get up to kLoadFromRemoteMax not loaded parts in from-till range. StackIntVector offsetsFromLoader( int from, int till) const; base::flat_map parts; Flags flags; }; class Slices { public: Slices(int size, bool useCache); void headerDone(bool fromCache); [[nodiscard]] bool headerWontBeFilled() const; [[nodiscard]] bool headerModeUnknown() const; [[nodiscard]] bool isFullInHeader() const; [[nodiscard]] bool isGoodHeader() const; void processCacheResult(int sliceNumber, bytes::const_span result); void processPart(int offset, QByteArray &&bytes); [[nodiscard]] FillResult fill(int offset, bytes::span buffer); [[nodiscard]] SerializedSlice unloadToCache(); private: enum class HeaderMode { Unknown, Small, Good, Full, NoCache, }; void applyHeaderCacheData(); [[nodiscard]] int maxSliceSize(int sliceNumber) const; [[nodiscard]] SerializedSlice serializeAndUnloadSlice( int sliceNumber); [[nodiscard]] SerializedSlice serializeAndUnloadUnused(); [[nodiscard]] QByteArray serializeComplexSlice( const Slice &slice) const; [[nodiscard]] QByteArray serializeAndUnloadFirstSliceNoHeader(); void markSliceUsed(int sliceIndex); [[nodiscard]] bool computeIsGoodHeader() const; [[nodiscard]] FillResult fillFromHeader( int offset, bytes::span buffer); std::vector _data; Slice _header; std::deque _usedSlices; int _size = 0; HeaderMode _headerMode = HeaderMode::Unknown; }; // 0 is for headerData, slice index = sliceNumber - 1. void readFromCache(int sliceNumber); bool processCacheResults(); void putToCache(SerializedSlice &&data); void cancelLoadInRange(int from, int till); void loadAtOffset(int offset); void checkLoadWillBeFirst(int offset); bool processLoadedParts(); bool fillFromSlices(int offset, bytes::span buffer); void finalizeCache(); static std::shared_ptr InitCacheHelper( std::optional baseKey); const not_null _owner; const std::unique_ptr _loader; const std::shared_ptr _cacheHelper; QMutex _loadedPartsMutex; std::vector _loadedParts; std::atomic _waiting = nullptr; PriorityQueue _loadingOffsets; Slices _slices; std::optional _failed; rpl::lifetime _lifetime; }; } // namespace Streaming } // namespace Media