From 04e3b250e7434450bafc1a4fee257239131f8d91 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 31 May 2019 19:45:03 +0300 Subject: [PATCH] Keep track of fully cached media files. --- Telegram/SourceFiles/data/data_document.cpp | 50 +++++++- Telegram/SourceFiles/data/data_document.h | 3 + Telegram/SourceFiles/data/data_session.cpp | 11 +- .../history/media/history_media_video.cpp | 2 +- .../media/streaming/media_streaming_file.cpp | 17 +++ .../media/streaming/media_streaming_file.h | 2 + .../streaming/media_streaming_file_delegate.h | 1 + .../streaming/media_streaming_player.cpp | 22 +++- .../media/streaming/media_streaming_player.h | 4 + .../streaming/media_streaming_reader.cpp | 116 +++++++++++++++++- .../media/streaming/media_streaming_reader.h | 10 ++ .../media/view/media_view_overlay_widget.cpp | 5 + .../storage/cache/storage_cache_database.cpp | 13 ++ .../storage/cache/storage_cache_database.h | 5 + .../cache/storage_cache_database_object.cpp | 23 +++- .../cache/storage_cache_database_object.h | 5 + Telegram/SourceFiles/storage/localstorage.cpp | 80 +++++++----- Telegram/SourceFiles/storage/localstorage.h | 3 +- .../SourceFiles/ui/image/image_location.cpp | 15 ++- .../SourceFiles/ui/image/image_location.h | 9 +- 20 files changed, 342 insertions(+), 54 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 50232afac..e2df5f699 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -816,6 +816,35 @@ bool DocumentData::uploading() const { return (uploadingData != nullptr); } +bool DocumentData::loadedInMediaCache() const { + return (_flags & Flag::LoadedInMediaCache); +} + +void DocumentData::setLoadedInMediaCache(bool loaded) { + const auto flags = loaded + ? (_flags | Flag::LoadedInMediaCache) + : (_flags & ~Flag::LoadedInMediaCache); + if (_flags == flags) { + return; + } + _flags = flags; + if (!this->loaded()) { + if (loadedInMediaCache()) { + Local::writeFileLocation( + mediaKey(), + FileLocation::InMediaCacheLocation()); + } else { + Local::removeFileLocation(mediaKey()); + } + owner().requestDocumentViewRepaint(this); + } +} + +void DocumentData::setLoadedInMediaCacheLocation() { + _location = FileLocation(); + _flags |= Flag::LoadedInMediaCache; +} + void DocumentData::setWaitingForAlbum() { if (uploading()) { uploadingData->waitingForAlbum = true; @@ -1010,13 +1039,21 @@ QByteArray DocumentData::data() const { const FileLocation &DocumentData::location(bool check) const { if (check && !_location.check()) { - const_cast(this)->_location = Local::readFileLocation(mediaKey()); + const auto location = Local::readFileLocation(mediaKey()); + const auto that = const_cast(this); + if (location.inMediaCache()) { + that->setLoadedInMediaCacheLocation(); + } else { + that->_location = location; + } } return _location; } void DocumentData::setLocation(const FileLocation &loc) { - if (loc.check()) { + if (loc.inMediaCache()) { + setLoadedInMediaCacheLocation(); + } else if (loc.check()) { _location = loc; } } @@ -1492,6 +1529,13 @@ void DocumentData::setRemoteLocation( Local::writeFileLocation(mediaKey(), _location); } else { _location = Local::readFileLocation(mediaKey()); + if (_location.inMediaCache()) { + setLoadedInMediaCacheLocation(); + } else if (_location.isEmpty() && loadedInMediaCache()) { + Local::writeFileLocation( + mediaKey(), + FileLocation::InMediaCacheLocation()); + } } } } @@ -1520,7 +1564,7 @@ void DocumentData::collectLocalData(not_null local) { ActiveCache().up(this); } } - if (!local->_location.isEmpty()) { + if (!local->_location.inMediaCache() && !local->_location.isEmpty()) { _location = local->_location; Local::writeFileLocation(mediaKey(), _location); } diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 89add5cde..b0d23519e 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -115,6 +115,8 @@ public: [[nodiscard]] float64 progress() const; [[nodiscard]] int loadOffset() const; [[nodiscard]] bool uploading() const; + [[nodiscard]] bool loadedInMediaCache() const; + void setLoadedInMediaCache(bool loaded); void setWaitingForAlbum(); [[nodiscard]] bool waitingForAlbum() const; @@ -266,6 +268,7 @@ private: void validateLottieSticker(); void validateGoodThumbnail(); void setMaybeSupportsStreaming(bool supports); + void setLoadedInMediaCacheLocation(); void destroyLoader() const; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index debf2bdc4..8d9f5bca9 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2504,15 +2504,18 @@ void Session::documentApplyFields( if (!date) { return; } - if (dc != 0 && access != 0) { - document->setRemoteLocation(dc, access, fileReference); - } document->date = date; document->setMimeString(mime); document->updateThumbnails(thumbnailInline, thumbnail); document->size = size; - document->recountIsImage(); document->setattributes(attributes); + + // Uses 'type' that is computed from attributes. + document->recountIsImage(); + if (dc != 0 && access != 0) { + document->setRemoteLocation(dc, access, fileReference); + } + if (document->sticker() && !document->sticker()->loc.valid() && thumbLocation.valid()) { diff --git a/Telegram/SourceFiles/history/media/history_media_video.cpp b/Telegram/SourceFiles/history/media/history_media_video.cpp index 1e28c0b96..a77791c0a 100644 --- a/Telegram/SourceFiles/history/media/history_media_video.cpp +++ b/Telegram/SourceFiles/history/media/history_media_video.cpp @@ -274,7 +274,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, crl void HistoryVideo::drawCornerStatus(Painter &p, bool selected) const { const auto padding = st::msgDateImgPadding; const auto radial = _animation && _animation->radial.animating(); - const auto cornerDownload = downloadInCorner() && !_data->loaded(); + const auto cornerDownload = downloadInCorner() && !_data->loaded() && !_data->loadedInMediaCache(); const auto addWidth = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : 0; const auto downloadWidth = cornerDownload ? st::normalFont->width(_downloadSize) : 0; const auto statusW = std::max(downloadWidth, st::normalFont->width(_statusText)) + 2 * padding.x() + addWidth; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp index 01310b695..60fa317ad 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp @@ -60,6 +60,9 @@ int File::Context::read(bytes::span buffer) { return -1; } } + + sendFullInCache(); + _offset += amount; return amount; } @@ -244,6 +247,9 @@ void File::Context::start(crl::time position) { } _reader->headerDone(); + if (_reader->isRemoteLoader()) { + sendFullInCache(true); + } if (video.codec || audio.codec) { seekToPosition(format.get(), video.codec ? video : audio, position); } @@ -258,6 +264,17 @@ void File::Context::start(crl::time position) { _format = std::move(format); } +void File::Context::sendFullInCache(bool force) { + const auto started = _fullInCache.has_value(); + if (force || started) { + const auto nowFullInCache = _reader->fullInCache(); + if (!started || *_fullInCache != nowFullInCache) { + _fullInCache = nowFullInCache; + _delegate->fileFullInCache(nowFullInCache); + } + } +} + void File::Context::readNextPacket() { auto result = readPacket(); if (unroll()) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_file.h b/Telegram/SourceFiles/media/streaming/media_streaming_file.h index 1c253b17a..e2e551849 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_file.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_file.h @@ -81,6 +81,7 @@ private: [[nodiscard]] base::variant readPacket(); void handleEndOfFile(); + void sendFullInCache(bool force = false); const not_null _delegate; const not_null _reader; @@ -89,6 +90,7 @@ private: int _size = 0; bool _failed = false; bool _readTillEnd = false; + std::optional _fullInCache; crl::semaphore _semaphore; std::atomic _interrupted = false; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_file_delegate.h b/Telegram/SourceFiles/media/streaming/media_streaming_file_delegate.h index b24271b21..ed2d4cd94 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_file_delegate.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_file_delegate.h @@ -22,6 +22,7 @@ public: Stream &&audio) = 0; virtual void fileError(Error error) = 0; virtual void fileWaitingForData() = 0; + virtual void fileFullInCache(bool fullInCache) = 0; // Return true if reading and processing more packets is desired. // Return false if sleeping until 'wake()' is called is desired. diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 357da5d17..57e833101 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -174,7 +174,7 @@ void Player::trackSendReceivedTill( Expects(state.duration != kTimeUnknown); Expects(state.receivedTill != kTimeUnknown); - if (!_remoteLoader) { + if (!_remoteLoader || _fullInCacheSinceStart.value_or(false)) { return; } const auto receivedTill = std::max( @@ -302,6 +302,15 @@ void Player::fileError(Error error) { }); } +void Player::fileFullInCache(bool fullInCache) { + crl::on_main(&_sessionGuard, [=] { + if (!_fullInCacheSinceStart.has_value()) { + _fullInCacheSinceStart = fullInCache; + } + _fullInCache.fire_copy(fullInCache); + }); +} + void Player::fileWaitingForData() { if (_waitingForData) { return; @@ -736,6 +745,10 @@ bool Player::finished() const { && (!_video || _videoFinished); } +float64 Player::speed() const { + return _options.speed; +} + void Player::setSpeed(float64 speed) { Expects(active()); Expects(speed >= 0.5 && speed <= 2.); @@ -767,6 +780,10 @@ rpl::producer Player::updates() const { return _updates.events(); } +rpl::producer Player::fullInCache() const { + return _fullInCache.events(); +} + QSize Player::videoSize() const { return _information.video.size; } @@ -803,7 +820,8 @@ Media::Player::TrackState Player::prepareLegacyState() const { } else if (_options.loop && result.length > 0) { result.position %= result.length; } - result.receivedTill = _remoteLoader + result.receivedTill = (_remoteLoader + && !_fullInCacheSinceStart.value_or(false)) ? getCurrentReceivedTill(result.length) : 0; result.frequency = kMsFrequency; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.h b/Telegram/SourceFiles/media/streaming/media_streaming_player.h index 417472827..754ce9af3 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.h @@ -57,6 +57,7 @@ public: [[nodiscard]] bool finished() const; [[nodiscard]] rpl::producer updates() const; + [[nodiscard]] rpl::producer fullInCache() const; [[nodiscard]] QSize videoSize() const; [[nodiscard]] QImage frame(const FrameRequest &request) const; @@ -82,6 +83,7 @@ private: bool fileReady(int headerSize, Stream &&video, Stream &&audio) override; void fileError(Error error) override; void fileWaitingForData() override; + void fileFullInCache(bool fullInCache) override; bool fileProcessPacket(Packet &&packet) override; bool fileReadMore() override; @@ -176,6 +178,8 @@ private: crl::time _nextFrameTime = kTimeUnknown; base::Timer _renderFrameTimer; rpl::event_stream _updates; + rpl::event_stream _fullInCache; + std::optional _fullInCacheSinceStart; crl::time _totalDuration = kTimeUnknown; crl::time _loopingShift = 0; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp index ee881be47..3702dbfbd 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp @@ -225,6 +225,7 @@ struct Reader::CacheHelper { QMutex mutex; base::flat_map results; + std::vector sizes; std::atomic waiting = nullptr; }; @@ -385,6 +386,17 @@ int Reader::Slices::headerSize() const { return _header.parts.size() * kPartSize; } +bool Reader::Slices::fullInCache() const { + return _fullInCache; +} + +int Reader::Slices::requestSliceSizesCount() const { + if (!headerModeUnknown() || isFullInHeader()) { + return 0; + } + return _data.size(); +} + bool Reader::Slices::headerWontBeFilled() const { return headerModeUnknown() && (_header.parts.size() >= kMaxPartsInHeader); @@ -438,6 +450,7 @@ void Reader::Slices::processCacheResult(int sliceNumber, PartsMap &&result) { return; } slice.processCacheData(std::move(result)); + checkSliceFullLoaded(sliceNumber); if (!sliceNumber) { applyHeaderCacheData(); if (isGoodHeader()) { @@ -448,6 +461,66 @@ void Reader::Slices::processCacheResult(int sliceNumber, PartsMap &&result) { } } +void Reader::Slices::processCachedSizes(const std::vector &sizes) { + Expects(sizes.size() == _data.size()); + + using Flag = Slice::Flag; + const auto count = int(sizes.size()); + auto loadedCount = 0; + for (auto i = 0; i != count; ++i) { + const auto sliceNumber = (i + 1); + const auto sliceSize = (sliceNumber < _data.size()) + ? kInSlice + : (_size - (sliceNumber - 1) * kInSlice); + const auto loaded = (sizes[i] == sliceSize); + + if (_data[i].flags & Flag::FullInCache) { + ++loadedCount; + } else if (loaded) { + _data[i].flags |= Flag::FullInCache; + ++loadedCount; + } + } + _fullInCache = (loadedCount == count); +} + +void Reader::Slices::checkSliceFullLoaded(int sliceNumber) { + if (!sliceNumber && !isFullInHeader()) { + return; + } + const auto partsCount = [&] { + if (!sliceNumber) { + return (_size + kPartSize - 1) / kPartSize; + } + return (sliceNumber < _data.size()) + ? kPartsInSlice + : ((_size - (sliceNumber - 1) * kInSlice + kPartSize - 1) + / kPartSize); + }(); + auto &slice = (sliceNumber ? _data[sliceNumber - 1] : _header); + const auto loaded = (slice.parts.size() == partsCount); + + using Flag = Slice::Flag; + if ((slice.flags & Flag::FullInCache) && !loaded) { + slice.flags &= ~Flag::FullInCache; + _fullInCache = false; + } else if (!(slice.flags & Flag::FullInCache) && loaded) { + slice.flags |= Flag::FullInCache; + _fullInCache = checkFullInCache(); + } +} + +bool Reader::Slices::checkFullInCache() const { + using Flag = Slice::Flag; + if (isFullInHeader()) { + return (_header.flags & Flag::FullInCache); + } + const auto i = ranges::find_if(_data, [](const Slice &slice) { + return !(slice.flags & Flag::FullInCache); + }); + return (i == end(_data)); +} + void Reader::Slices::processPart( int offset, QByteArray &&bytes) { @@ -455,6 +528,7 @@ void Reader::Slices::processPart( if (isFullInHeader()) { _header.addPart(offset, bytes); + checkSliceFullLoaded(0); return; } else if (_headerMode == HeaderMode::Unknown) { if (_header.parts.contains(offset)) { @@ -465,6 +539,7 @@ void Reader::Slices::processPart( } const auto index = offset / kInSlice; _data[index].addPart(offset - index * kInSlice, std::move(bytes)); + checkSliceFullLoaded(index + 1); } auto Reader::Slices::fill(int offset, bytes::span buffer) -> FillResult { @@ -655,7 +730,7 @@ Reader::SerializedSlice Reader::Slices::serializeAndUnloadUnused() { return false; }(); if (noNeedToSaveToCache) { - _data[purgeSlice] = Slice(); + unloadSlice(_data[purgeSlice]); return {}; } return serializeAndUnloadSlice(purgeSlice + 1); @@ -707,13 +782,21 @@ Reader::SerializedSlice Reader::Slices::serializeAndUnloadSlice( // HeaderMode::Good and we unload first slice. We still require // header data to continue working, so don't really unload the header. if (sliceNumber) { - slice = Slice(); + unloadSlice(slice); } else { slice.flags &= ~Slice::Flag::ChangedSinceCache; } return result; } +void Reader::Slices::unloadSlice(Slice &slice) const { + const auto full = (slice.flags & Slice::Flag::FullInCache); + slice = Slice(); + if (full) { + slice.flags |= Slice::Flag::FullInCache; + } +} + QByteArray Reader::Slices::serializeComplexSlice(const Slice &slice) const { auto result = QByteArray(); const auto count = slice.parts.size(); @@ -742,7 +825,7 @@ QByteArray Reader::Slices::serializeAndUnloadFirstSliceNoHeader() { slice.parts.erase(offset); } auto result = serializeComplexSlice(slice); - slice = Slice(); + unloadSlice(slice); return result; } @@ -1022,8 +1105,14 @@ void Reader::readFromCache(int sliceNumber) { const auto key = _cacheHelper->key(sliceNumber); const auto cache = std::weak_ptr(_cacheHelper); const auto weak = base::make_weak(this); - _owner->cacheBigFile().get(key, [=](QByteArray &&result) { - crl::async([=, result = std::move(result)]{ + const auto ready = [=]( + QByteArray &&result, + std::vector &&sizes = {}) { + crl::async([ + =, + result = std::move(result), + sizes = std::move(sizes) + ]() mutable{ auto entry = ParseCacheEntry( bytes::make_span(result), sliceNumber, @@ -1034,6 +1123,7 @@ void Reader::readFromCache(int sliceNumber) { if (!sliceNumber && entry.included) { strong->results.emplace(1, std::move(*entry.included)); } + strong->sizes = std::move(sizes); if (const auto waiting = strong->waiting.load()) { strong->waiting.store(nullptr, std::memory_order_release); waiting->release(); @@ -1044,7 +1134,13 @@ void Reader::readFromCache(int sliceNumber) { } } }); - }); + }; + auto keys = std::vector(); + const auto count = _slices.requestSliceSizesCount(); + for (auto i = 0; i != count; ++i) { + keys.push_back(_cacheHelper->key(i + 1)); + } + _owner->cacheBigFile().getWithSizes(key, std::move(keys), ready); } bool Reader::readFromCacheForDownloader(int sliceNumber) { @@ -1083,6 +1179,10 @@ int Reader::headerSize() const { return _slices.headerSize(); } +bool Reader::fullInCache() const { + return _slices.fullInCache(); +} + bool Reader::fill( int offset, bytes::span buffer, @@ -1184,6 +1284,7 @@ bool Reader::processCacheResults() { QMutexLocker lock(&_cacheHelper->mutex); auto loaded = base::take(_cacheHelper->results); + auto sizes = base::take(_cacheHelper->sizes); lock.unlock(); for (auto &[sliceNumber, cachedParts] : _downloaderReadCache) { @@ -1201,6 +1302,9 @@ bool Reader::processCacheResults() { for (auto &[sliceNumber, result] : loaded) { _slices.processCacheResult(sliceNumber, std::move(result)); } + if (!sizes.empty()) { + _slices.processCachedSizes(sizes); + } if (!loaded.empty() && (loaded.front().first == 0) && _slices.isGoodHeader()) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_reader.h b/Telegram/SourceFiles/media/streaming/media_streaming_reader.h index a8eb7a852..4501b2242 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_reader.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_reader.h @@ -50,6 +50,7 @@ public: [[nodiscard]] std::optional streamingError() const; void headerDone(); [[nodiscard]] int headerSize() const; + [[nodiscard]] bool fullInCache() const; // Thread safe. void startSleep(not_null wake); @@ -104,6 +105,7 @@ private: LoadingFromCache = 0x01, LoadedFromCache = 0x02, ChangedSinceCache = 0x04, + FullInCache = 0x08, }; friend constexpr inline bool is_flag_type(Flag) { return true; } using Flags = base::flags; @@ -135,13 +137,17 @@ private: void headerDone(bool fromCache); [[nodiscard]] int headerSize() const; + [[nodiscard]] bool fullInCache() const; [[nodiscard]] bool headerWontBeFilled() const; [[nodiscard]] bool headerModeUnknown() const; [[nodiscard]] bool isFullInHeader() const; [[nodiscard]] bool isGoodHeader() const; [[nodiscard]] bool waitingForHeaderCache() const; + [[nodiscard]] int requestSliceSizesCount() const; + void processCacheResult(int sliceNumber, PartsMap &&result); + void processCachedSizes(const std::vector &sizes); void processPart(int offset, QByteArray &&bytes); [[nodiscard]] FillResult fill(int offset, bytes::span buffer); @@ -172,12 +178,16 @@ private: [[nodiscard]] FillResult fillFromHeader( int offset, bytes::span buffer); + void unloadSlice(Slice &slice) const; + void checkSliceFullLoaded(int sliceNumber); + [[nodiscard]] bool checkFullInCache() const; std::vector _data; Slice _header; std::deque _usedSlices; int _size = 0; HeaderMode _headerMode = HeaderMode::Unknown; + bool _fullInCache = false; }; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index b9cce6c9f..5a398f8a1 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1993,6 +1993,11 @@ void OverlayWidget::initStreaming() { handleStreamingError(std::move(error)); }, _streamed->player.lifetime()); + _streamed->player.fullInCache( + ) | rpl::start_with_next([=](bool fullInCache) { + _doc->setLoadedInMediaCache(fullInCache); + }, _streamed->player.lifetime()); + restartAtSeekPosition(0); } diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_database.cpp index d7486edce..dfe5d0454 100644 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database.cpp +++ b/Telegram/SourceFiles/storage/cache/storage_cache_database.cpp @@ -153,6 +153,19 @@ void Database::getWithTag( }); } +void Database::getWithSizes( + const Key &key, + std::vector &&keys, + FnMut&&)> &&done) { + _wrapped.with([ + key, + keys = std::move(keys), + done = std::move(done) + ](Implementation &unwrapped) mutable { + unwrapped.getWithSizes(key, std::move(keys), std::move(done)); + }); +} + auto Database::statsOnMain() const -> rpl::producer { return _wrapped.producer_on_main([](const Implementation &unwrapped) { return unwrapped.stats(); diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database.h b/Telegram/SourceFiles/storage/cache/storage_cache_database.h index d28f536a1..c7998fd2a 100644 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database.h +++ b/Telegram/SourceFiles/storage/cache/storage_cache_database.h @@ -64,6 +64,11 @@ public: FnMut &&done = nullptr); void getWithTag(const Key &key, FnMut &&done); + void getWithSizes( + const Key &key, + std::vector &&keys, + FnMut&&)> &&done); + using Stats = details::Stats; using TaggedSummary = details::TaggedSummary; rpl::producer statsOnMain() const; diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp index 96fa5bd58..ccdeda4ce 100644 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp +++ b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp @@ -955,7 +955,28 @@ void DatabaseObject::get( } } -QByteArray DatabaseObject::readValueData(PlaceId place, size_type size) const { +void DatabaseObject::getWithSizes( + const Key &key, + std::vector &&keys, + FnMut&&)> &&done) { + get(key, [&](TaggedValue &&value) { + if (value.bytes.isEmpty()) { + invokeCallback(done, QByteArray(), std::vector()); + return; + } + + auto sizes = keys | ranges::view::transform([&](const Key &sizeKey) { + const auto i = _map.find(sizeKey); + return (i != end(_map)) ? int(i->second.size) : 0; + }) | ranges::to_vector; + + invokeCallback(done, std::move(value.bytes), std::move(sizes)); + }); +} + +QByteArray DatabaseObject::readValueData( + PlaceId place, + size_type size) const { const auto path = placePath(place); File data; const auto result = data.open(path, File::Mode::Read, _key); diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h index 585304d33..f729b985f 100644 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h +++ b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h @@ -56,6 +56,11 @@ public: const Key &to, FnMut &&done); + void getWithSizes( + const Key &key, + std::vector &&keys, + FnMut&&)> &&done); + rpl::producer stats() const; void clear(FnMut &&done); diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 660bf6289..6ca56195c 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -810,7 +810,9 @@ void _readLocations() { MediaKey key(first, second); _fileLocations.insert(key, loc); - _fileLocationPairs.insert(loc.fname, FileLocationPair(key, loc)); + if (!loc.inMediaCache()) { + _fileLocationPairs.insert(loc.fname, FileLocationPair(key, loc)); + } } if (endMarkFound) { @@ -3133,52 +3135,68 @@ bool hasDraft(const PeerId &peer) { } void writeFileLocation(MediaKey location, const FileLocation &local) { - if (local.fname.isEmpty()) return; - - FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location); - if (aliasIt != _fileLocationAliases.cend()) { - location = aliasIt.value(); + if (local.fname.isEmpty()) { + return; } - - FileLocationPairs::iterator i = _fileLocationPairs.find(local.fname); - if (i != _fileLocationPairs.cend()) { - if (i.value().second == local) { - if (i.value().first != location) { - _fileLocationAliases.insert(location, i.value().first); - _writeLocations(WriteMapWhen::Fast); - } - return; + if (!local.inMediaCache()) { + FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location); + if (aliasIt != _fileLocationAliases.cend()) { + location = aliasIt.value(); } - if (i.value().first != location) { - for (FileLocations::iterator j = _fileLocations.find(i.value().first), e = _fileLocations.end(); (j != e) && (j.key() == i.value().first);) { - if (j.value() == i.value().second) { - _fileLocations.erase(j); - break; + + FileLocationPairs::iterator i = _fileLocationPairs.find(local.fname); + if (i != _fileLocationPairs.cend()) { + if (i.value().second == local) { + if (i.value().first != location) { + _fileLocationAliases.insert(location, i.value().first); + _writeLocations(WriteMapWhen::Fast); } + return; } - _fileLocationPairs.erase(i); + if (i.value().first != location) { + for (FileLocations::iterator j = _fileLocations.find(i.value().first), e = _fileLocations.end(); (j != e) && (j.key() == i.value().first);) { + if (j.value() == i.value().second) { + _fileLocations.erase(j); + break; + } + } + _fileLocationPairs.erase(i); + } + } + _fileLocationPairs.insert(local.fname, FileLocationPair(location, local)); + } else { + for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { + if (i.value().inMediaCache() || i.value().check()) { + return; + } + i = _fileLocations.erase(i); } } _fileLocations.insert(location, local); - _fileLocationPairs.insert(local.fname, FileLocationPair(location, local)); _writeLocations(WriteMapWhen::Fast); } -FileLocation readFileLocation(MediaKey location, bool check) { +void removeFileLocation(MediaKey location) { + FileLocations::iterator i = _fileLocations.find(location); + if (i == _fileLocations.end()) { + return; + } + while (i != _fileLocations.end() && (i.key() == location)) { + i = _fileLocations.erase(i); + } + _writeLocations(WriteMapWhen::Fast); +} + +FileLocation readFileLocation(MediaKey location) { FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location); if (aliasIt != _fileLocationAliases.cend()) { location = aliasIt.value(); } FileLocations::iterator i = _fileLocations.find(location); - for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { - if (check) { - if (!i.value().check()) { - _fileLocationPairs.remove(i.value().fname); - i = _fileLocations.erase(i); - _writeLocations(); - continue; - } + if (i != _fileLocations.end()) { + if (i.value().inMediaCache()) { + int a = 0; } return i.value(); } diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h index e4d403d24..5a54c5bea 100644 --- a/Telegram/SourceFiles/storage/localstorage.h +++ b/Telegram/SourceFiles/storage/localstorage.h @@ -110,7 +110,8 @@ bool hasDraftCursors(const PeerId &peer); bool hasDraft(const PeerId &peer); void writeFileLocation(MediaKey location, const FileLocation &local); -FileLocation readFileLocation(MediaKey location, bool check = true); +FileLocation readFileLocation(MediaKey location); +void removeFileLocation(MediaKey location); Storage::EncryptionKey cacheKey(); QString cachePath(); diff --git a/Telegram/SourceFiles/ui/image/image_location.cpp b/Telegram/SourceFiles/ui/image/image_location.cpp index 8bae472c7..123d60a37 100644 --- a/Telegram/SourceFiles/ui/image/image_location.cpp +++ b/Telegram/SourceFiles/ui/image/image_location.cpp @@ -20,6 +20,7 @@ namespace { constexpr auto kDocumentBaseCacheTag = 0x0000000000010000ULL; constexpr auto kDocumentBaseCacheMask = 0x000000000000FF00ULL; constexpr auto kSerializeTypeShift = quint8(0x08); +const auto kInMediaCacheLocation = QString("*media_cache*"); MTPInputPeer GenerateInputPeer( uint64 id, @@ -622,7 +623,7 @@ ReadAccessEnabler::~ReadAccessEnabler() { } FileLocation::FileLocation(const QString &name) : fname(name) { - if (fname.isEmpty()) { + if (fname.isEmpty() || fname == kInMediaCacheLocation) { size = 0; } else { setBookmark(psPathBookmark(name)); @@ -646,8 +647,14 @@ FileLocation::FileLocation(const QString &name) : fname(name) { } } +FileLocation FileLocation::InMediaCacheLocation() { + return FileLocation(kInMediaCacheLocation); +} + bool FileLocation::check() const { - if (fname.isEmpty()) return false; + if (fname.isEmpty() || fname == kInMediaCacheLocation) { + return false; + } ReadAccessEnabler enabler(_bookmark); if (enabler.failed()) { @@ -683,6 +690,10 @@ QByteArray FileLocation::bookmark() const { return _bookmark ? _bookmark->bookmark() : QByteArray(); } +bool FileLocation::inMediaCache() const { + return (fname == kInMediaCacheLocation); +} + void FileLocation::setBookmark(const QByteArray &bm) { _bookmark.reset(bm.isEmpty() ? nullptr : new PsFileBookmark(bm)); } diff --git a/Telegram/SourceFiles/ui/image/image_location.h b/Telegram/SourceFiles/ui/image/image_location.h index f43067b61..5a904f95c 100644 --- a/Telegram/SourceFiles/ui/image/image_location.h +++ b/Telegram/SourceFiles/ui/image/image_location.h @@ -434,13 +434,16 @@ public: FileLocation() = default; explicit FileLocation(const QString &name); - bool check() const; - const QString &name() const; + static FileLocation InMediaCacheLocation(); + + [[nodiscard]] bool check() const; + [[nodiscard]] const QString &name() const; void setBookmark(const QByteArray &bookmark); QByteArray bookmark() const; - bool isEmpty() const { + [[nodiscard]] bool isEmpty() const { return name().isEmpty(); } + [[nodiscard]] bool inMediaCache() const; bool accessEnable() const; void accessDisable() const;