/* 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 */ #include "media/streaming/media_streaming_reader.h" #include "media/streaming/media_streaming_loader.h" #include "storage/cache/storage_cache_database.h" #include "data/data_session.h" namespace Media { namespace Streaming { namespace { template // Range::value_type is Pair int FindNotLoadedStart(Range &&parts, int offset) { auto result = offset; for (const auto &part : parts) { const auto partStart = part.first; const auto partEnd = partStart + part.second.size(); if (partStart <= result && partEnd >= result) { result = partEnd; } else { break; } } return result; } template // Range::value_type is Pair void CopyLoaded(bytes::span buffer, Range &&parts, int offset, int till) { auto filled = offset; for (const auto &part : parts) { const auto bytes = bytes::make_span(part.second); const auto partStart = part.first; const auto partEnd = int(partStart + bytes.size()); const auto copyTill = std::min(partEnd, till); Assert(partStart <= filled && filled < copyTill); const auto from = filled - partStart; const auto copy = copyTill - filled; bytes::copy(buffer, bytes.subspan(from, copy)); buffer = buffer.subspan(copy); filled += copy; } } } // namespace Reader::Reader( not_null owner, std::unique_ptr loader) : _owner(owner) , _loader(std::move(loader)) { _loader->parts( ) | rpl::start_with_next([=](LoadedPart &&part) { QMutexLocker lock(&_loadedPartsMutex); _loadedParts.push_back(std::move(part)); lock.unlock(); if (const auto waiting = _waiting.load()) { _waiting = nullptr; waiting->release(); } }, _lifetime); } int Reader::size() const { return _loader->size(); } bool Reader::failed() const { return _failed; } bool Reader::fill( bytes::span buffer, int offset, crl::semaphore *notify) { Expects(offset + buffer.size() <= size()); const auto wait = [&](int offset) { _waiting = notify; loadFor(offset); return false; }; const auto done = [&] { _waiting = nullptr; return true; }; const auto failed = [&] { _waiting = nullptr; if (notify) { notify->release(); } return false; }; processLoadedParts(); if (_failed) { return failed(); } const auto after = ranges::upper_bound( _data, offset, ranges::less(), &base::flat_map::value_type::first); if (after == begin(_data)) { return wait(offset); } const auto till = int(offset + buffer.size()); const auto start = after - 1; const auto finish = ranges::lower_bound( start, end(_data), till, ranges::less(), &base::flat_map::value_type::first); const auto parts = ranges::make_iterator_range(start, finish); const auto haveTill = FindNotLoadedStart(parts, offset); if (haveTill < till) { return wait(haveTill); } CopyLoaded(buffer, parts, offset, till); return done(); } void Reader::processLoadedParts() { QMutexLocker lock(&_loadedPartsMutex); auto loaded = std::move(_loadedParts); lock.unlock(); if (_failed) { return; } for (auto &part : loaded) { if (part.offset == LoadedPart::kFailedOffset || (part.bytes.size() != Loader::kPartSize && part.offset + part.bytes.size() != size())) { _failed = true; return; } _data.emplace(part.offset, std::move(part.bytes)); } } void Reader::loadFor(int offset) { const auto part = offset / Loader::kPartSize; _loader->load(part * Loader::kPartSize); } Reader::~Reader() = default; } // namespace Streaming } // namespace Media