From 37aabc0da991164ee9aeac1113096a9fb6003967 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 16 Apr 2020 13:06:46 +0400 Subject: [PATCH] Add generic DownloadLocation and ImageLocation. --- .../SourceFiles/ui/image/image_location.cpp | 244 +++++++++++++++++- .../SourceFiles/ui/image/image_location.h | 142 ++++++++++ 2 files changed, 384 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/ui/image/image_location.cpp b/Telegram/SourceFiles/ui/image/image_location.cpp index 1dfe3cf40..725b5a439 100644 --- a/Telegram/SourceFiles/ui/image/image_location.cpp +++ b/Telegram/SourceFiles/ui/image/image_location.cpp @@ -22,8 +22,16 @@ namespace { constexpr auto kDocumentBaseCacheTag = 0x0000000000010000ULL; constexpr auto kDocumentBaseCacheMask = 0x000000000000FF00ULL; constexpr auto kSerializeTypeShift = quint8(0x08); +constexpr auto kNonStorageLocationToken = quint8(0x10); const auto kInMediaCacheLocation = QString("*media_cache*"); +enum class NonStorageLocationType : quint8 { + Web, + Geo, + Url, + Memory, +}; + MTPInputPeer GenerateInputPeer( uint64 id, uint64 accessHash, @@ -264,8 +272,8 @@ MTPInputFileLocation StorageFileLocation::tl(int32 self) const { QByteArray StorageFileLocation::serialize() const { auto result = QByteArray(); - result.reserve(serializeSize()); if (valid()) { + result.reserve(serializeSize()); auto buffer = QBuffer(&result); buffer.open(QIODevice::WriteOnly); auto stream = QDataStream(&buffer); @@ -311,7 +319,11 @@ std::optional StorageFileLocation::FromSerialized( stream.setVersion(QDataStream::Qt_5_1); stream >> dcId - >> type + >> type; + if (type == kNonStorageLocationToken) { + return std::nullopt; + } + stream >> sizeLetter >> localId >> id @@ -628,6 +640,234 @@ std::optional StorageImageLocation::FromSerialized( return std::nullopt; } +QByteArray DownloadLocation::serialize() const { + if (!valid() || data.is()) { + return data.get_unchecked().serialize(); + } + auto result = QByteArray(); + auto buffer = QBuffer(&result); + buffer.open(QIODevice::WriteOnly); + auto stream = QDataStream(&buffer); + stream.setVersion(QDataStream::Qt_5_1); + stream << quint16(0) << kNonStorageLocationToken; + + data.match([&](const StorageFileLocation &data) { + Unexpected("Variant in DownloadLocation::serialize."); + }, [&](const WebFileLocation &data) { + stream + << quint8(NonStorageLocationType::Web) + << data.url() + << quint64(data.accessHash()); + }, [&](const GeoPointLocation &data) { + stream + << quint8(NonStorageLocationType::Geo) + << qreal(data.lat) + << qreal(data.lon) + << quint64(data.access) + << qint32(data.width) + << qint32(data.height) + << qint32(data.zoom) + << qint32(data.scale); + }, [&](const PlainUrlLocation &data) { + stream << quint8(NonStorageLocationType::Url) << data.url.toUtf8(); + }, [&](const InMemoryLocation &data) { + stream << quint8(NonStorageLocationType::Memory) << data.bytes; + }); + buffer.close(); + return result; +} + +int DownloadLocation::serializeSize() const { + if (!valid() || data.is()) { + return data.get_unchecked().serializeSize(); + } + auto result = sizeof(quint16) + sizeof(quint8) + sizeof(quint8); + data.match([&](const StorageFileLocation &data) { + Unexpected("Variant in DownloadLocation::serializeSize."); + }, [&](const WebFileLocation &data) { + result += Serialize::bytearraySize(data.url()) + sizeof(quint64); + }, [&](const GeoPointLocation &data) { + result += 2 * sizeof(qreal) + sizeof(quint64) + 4 * sizeof(qint32); + }, [&](const PlainUrlLocation &data) { + result += Serialize::bytearraySize(data.url.toUtf8()); + }, [&](const InMemoryLocation &data) { + result += Serialize::bytearraySize(data.bytes); + }); + return result; +} + +std::optional DownloadLocation::FromSerialized( + const QByteArray &serialized) { + quint16 dcId = 0; + quint8 token = 0; + auto stream = QDataStream(serialized); + stream.setVersion(QDataStream::Qt_5_1); + stream >> dcId >> token; + if (dcId != 0 || token != kNonStorageLocationToken) { + const auto storage = StorageFileLocation::FromSerialized(serialized); + return storage + ? std::make_optional(DownloadLocation{ *storage }) + : std::nullopt; + } + quint8 type = 0; + stream >> type; + switch (NonStorageLocationType(type)) { + case NonStorageLocationType::Web: { + QByteArray url; + quint64 accessHash = 0; + stream >> url >> accessHash; + return (stream.status() == QDataStream::Ok) + ? std::make_optional( + DownloadLocation{ WebFileLocation(url, accessHash) }) + : std::nullopt; + } break; + + case NonStorageLocationType::Geo: { + qreal lat = 0.; + qreal lon = 0.; + quint64 access = 0; + qint32 width = 0; + qint32 height = 0; + qint32 zoom = 0; + qint32 scale = 0; + stream >> lat >> lon >> access >> width >> height >> zoom >> scale; + return (stream.status() == QDataStream::Ok) + ? std::make_optional( + DownloadLocation{ GeoPointLocation{ + .lat = lat, + .lon = lon, + .access = access, + .width = width, + .height = height, + .zoom = zoom, + .scale = scale } }) + : std::nullopt; + } break; + + case NonStorageLocationType::Url: { + QByteArray utf; + stream >> utf; + const auto url = base::FromUtf8Safe(utf); + return (stream.status() == QDataStream::Ok) + ? std::make_optional(DownloadLocation{ PlainUrlLocation{ url } }) + : std::nullopt; + } break; + + case NonStorageLocationType::Memory: { + QByteArray bytes; + stream >> bytes; + return (stream.status() == QDataStream::Ok) + ? std::make_optional( + DownloadLocation{ InMemoryLocation{ bytes } }) + : std::nullopt; + } break; + } + return std::nullopt; +} + +DownloadLocation DownloadLocation::convertToModern( + StorageFileLocation::Type type, + uint64 id, + uint64 accessHash) const { + if (!data.is()) { + return *this; + } + auto &file = this->data.get_unchecked(); + return DownloadLocation{ file.convertToModern(type, id, accessHash) }; +} + +bool DownloadLocation::valid() const { + return data.match([](const GeoPointLocation &data) { + return true; + }, [](const StorageFileLocation &data) { + return data.valid(); + }, [](const WebFileLocation &data) { + return !data.isNull(); + }, [](const PlainUrlLocation &data) { + return !data.url.isEmpty(); + }, [](const InMemoryLocation &data) { + return !data.bytes.isEmpty(); + }); +} + +QByteArray DownloadLocation::fileReference() const { + if (!data.is()) { + return QByteArray(); + } + return data.get_unchecked().fileReference(); +} + +bool DownloadLocation::refreshFileReference(const QByteArray &data) { + if (!this->data.is()) { + return false; + } + auto &file = this->data.get_unchecked(); + return file.refreshFileReference(data); +} + +bool DownloadLocation::refreshFileReference( + const Data::UpdatedFileReferences &updates) { + if (!data.is()) { + return false; + } + auto &file = data.get_unchecked(); + return file.refreshFileReference(updates); +} + +ImageLocation::ImageLocation( + const DownloadLocation &file, + int width, + int height) +: _file(file) +, _width(width) +, _height(height) { +} + +QByteArray ImageLocation::serialize() const { + auto result = _file.serialize(); + if (!result.isEmpty() || (_width > 0) || (_height > 0)) { + result.reserve(result.size() + 2 * sizeof(qint32)); + auto buffer = QBuffer(&result); + buffer.open(QIODevice::Append); + auto stream = QDataStream(&buffer); + stream.setVersion(QDataStream::Qt_5_1); + stream << qint32(_width) << qint32(_height); + } + return result; +} + +int ImageLocation::serializeSize() const { + const auto partial = _file.serializeSize(); + return (partial > 0 || _width > 0 || _height > 0) + ? (partial + 2 * sizeof(qint32)) + : 0; +} + +std::optional ImageLocation::FromSerialized( + const QByteArray &serialized) { + if (const auto file = DownloadLocation::FromSerialized(serialized)) { + const auto my = 2 * sizeof(qint32); + const auto full = serialized.size(); + if (!full) { + return ImageLocation(*file, 0, 0); + } else if (full >= my) { + qint32 width = 0; + qint32 height = 0; + + const auto dimensions = QByteArray::fromRawData( + serialized.data() + full - my, my); + auto stream = QDataStream(dimensions); + stream.setVersion(QDataStream::Qt_5_1); + stream >> width >> height; + + return (stream.status() == QDataStream::Ok) + ? ImageLocation(*file, width, height) + : std::optional(); + } + } + return std::nullopt; +} + ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark) : _bookmark(bookmark) , _failed(_bookmark ? !_bookmark->enable() : false) { diff --git a/Telegram/SourceFiles/ui/image/image_location.h b/Telegram/SourceFiles/ui/image/image_location.h index 5a904f95c..9022a9173 100644 --- a/Telegram/SourceFiles/ui/image/image_location.h +++ b/Telegram/SourceFiles/ui/image/image_location.h @@ -362,6 +362,148 @@ inline bool operator>=( return !(a < b); } +struct PlainUrlLocation { + QString url; + + friend inline bool operator==( + const PlainUrlLocation &a, + const PlainUrlLocation &b) { + return (a.url == b.url); + } + friend inline bool operator<( + const PlainUrlLocation &a, + const PlainUrlLocation &b) { + return (a.url < b.url); + } +}; + +struct InMemoryLocation { + QByteArray bytes; + + friend inline bool operator==( + const InMemoryLocation &a, + const InMemoryLocation &b) { + return (a.bytes == b.bytes); + } + friend inline bool operator<( + const InMemoryLocation &a, + const InMemoryLocation &b) { + return (a.bytes < b.bytes); + } +}; + +class DownloadLocation { +public: + base::variant< + StorageFileLocation, + WebFileLocation, + GeoPointLocation, + PlainUrlLocation, + InMemoryLocation> data; + + [[nodiscard]] QByteArray serialize() const; + [[nodiscard]] int serializeSize() const; + [[nodiscard]] static std::optional FromSerialized( + const QByteArray &serialized); + + [[nodiscard]] DownloadLocation convertToModern( + StorageFileLocation::Type type, + uint64 id, + uint64 accessHash) const; + + [[nodiscard]] bool valid() const; + [[nodiscard]] QByteArray fileReference() const; + bool refreshFileReference(const QByteArray &data); + bool refreshFileReference(const Data::UpdatedFileReferences &updates); + +private: + friend inline bool operator==( + const DownloadLocation &a, + const DownloadLocation &b) { + return (a.data == b.data); + } + friend inline bool operator<( + const DownloadLocation &a, + const DownloadLocation &b) { + return (a.data < b.data); + } + +}; + +class ImageLocation { +public: + ImageLocation() = default; + ImageLocation( + const DownloadLocation &file, + int width, + int height); + + [[nodiscard]] QByteArray serialize() const; + [[nodiscard]] int serializeSize() const; + [[nodiscard]] static std::optional FromSerialized( + const QByteArray &serialized); + + [[nodiscard]] ImageLocation convertToModern( + StorageFileLocation::Type type, + uint64 id, + uint64 accessHash) const { + return ImageLocation( + _file.convertToModern(type, id, accessHash), + _width, + _height); + } + + [[nodiscard]] const DownloadLocation &file() const { + return _file; + } + [[nodiscard]] int width() const { + return _width; + } + [[nodiscard]] int height() const { + return _height; + } + + void setSize(int width, int height) { + _width = width; + _height = height; + } + + [[nodiscard]] bool valid() const { + return _file.valid(); + } + [[nodiscard]] QByteArray fileReference() const { + return _file.fileReference(); + } + bool refreshFileReference(const QByteArray &data) { + return _file.refreshFileReference(data); + } + bool refreshFileReference(const Data::UpdatedFileReferences &updates) { + return _file.refreshFileReference(updates); + } + + [[nodiscard]] static const ImageLocation &Invalid() { + static auto result = ImageLocation(); + return result; + } + +private: + friend inline bool operator==( + const ImageLocation &a, + const ImageLocation &b) { + return (a._file == b._file); + } + friend inline bool operator<( + const ImageLocation &a, + const ImageLocation &b) { + return (a._file < b._file); + } + + DownloadLocation _file; + int _width = 0; + int _height = 0; + +}; + class Image; class ImagePtr { public: