1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #pragma once
8 
9 #include "td/telegram/files/FileBitmask.h"
10 #include "td/telegram/files/FileType.h"
11 #include "td/telegram/net/DcId.h"
12 #include "td/telegram/PhotoSizeSource.h"
13 #include "td/telegram/telegram_api.h"
14 
15 #include "td/utils/base64.h"
16 #include "td/utils/buffer.h"
17 #include "td/utils/common.h"
18 #include "td/utils/format.h"
19 #include "td/utils/logging.h"
20 #include "td/utils/Slice.h"
21 #include "td/utils/StringBuilder.h"
22 #include "td/utils/Variant.h"
23 
24 #include <tuple>
25 #include <utility>
26 
27 namespace td {
28 
29 class FileReferenceView {
30  public:
invalid_file_reference()31   static Slice invalid_file_reference() {
32     return Slice("#");
33   }
34 };
35 
36 struct EmptyRemoteFileLocation {
37   template <class StorerT>
storeEmptyRemoteFileLocation38   void store(StorerT &storer) const {
39   }
40   template <class ParserT>
parseEmptyRemoteFileLocation41   void parse(ParserT &parser) {
42   }
43 };
44 
45 inline bool operator==(const EmptyRemoteFileLocation &lhs, const EmptyRemoteFileLocation &rhs) {
46   return true;
47 }
48 
49 inline bool operator!=(const EmptyRemoteFileLocation &lhs, const EmptyRemoteFileLocation &rhs) {
50   return !(lhs == rhs);
51 }
52 
53 struct PartialRemoteFileLocation {
54   int64 file_id_;
55   int32 part_count_;
56   int32 part_size_;
57   int32 ready_part_count_;
58   int32 is_big_;
59 
60   template <class StorerT>
61   void store(StorerT &storer) const;
62   template <class ParserT>
63   void parse(ParserT &parser);
64 };
65 
66 inline bool operator==(const PartialRemoteFileLocation &lhs, const PartialRemoteFileLocation &rhs) {
67   return lhs.file_id_ == rhs.file_id_ && lhs.part_count_ == rhs.part_count_ && lhs.part_size_ == rhs.part_size_ &&
68          lhs.ready_part_count_ == rhs.ready_part_count_ && lhs.is_big_ == rhs.is_big_;
69 }
70 
71 inline bool operator!=(const PartialRemoteFileLocation &lhs, const PartialRemoteFileLocation &rhs) {
72   return !(lhs == rhs);
73 }
74 
75 inline StringBuilder &operator<<(StringBuilder &sb, const PartialRemoteFileLocation &location) {
76   return sb << '[' << (location.is_big_ ? "Big" : "Small") << " partial remote location with " << location.part_count_
77             << " parts of size " << location.part_size_ << " with " << location.ready_part_count_ << " ready parts]";
78 }
79 
80 struct PhotoRemoteFileLocation {
81   int64 id_;
82   int64 access_hash_;
83   PhotoSizeSource source_;
84 
85   template <class StorerT>
86   void store(StorerT &storer) const;
87   template <class ParserT>
88   void parse(ParserT &parser);
89 
90   struct AsKey {
91     const PhotoRemoteFileLocation &key;
92     bool is_unique;
93 
94     template <class StorerT>
95     void store(StorerT &storer) const;
96   };
as_keyPhotoRemoteFileLocation97   AsKey as_key(bool is_unique) const {
98     return AsKey{*this, is_unique};
99   }
100 
101   bool operator<(const PhotoRemoteFileLocation &other) const {
102     if (id_ != other.id_) {
103       return id_ < other.id_;
104     }
105     return source_.get_unique() < other.source_.get_unique();
106   }
107   bool operator==(const PhotoRemoteFileLocation &other) const {
108     return id_ == other.id_ && source_.get_unique() == other.source_.get_unique();
109   }
110 };
111 
112 inline StringBuilder &operator<<(StringBuilder &string_builder, const PhotoRemoteFileLocation &location) {
113   return string_builder << "[ID = " << location.id_ << ", access_hash = " << location.access_hash_ << ", "
114                         << location.source_ << "]";
115 }
116 
117 struct WebRemoteFileLocation {
118   string url_;
119   int64 access_hash_;
120 
121   template <class StorerT>
122   void store(StorerT &storer) const;
123   template <class ParserT>
124   void parse(ParserT &parser);
125 
126   struct AsKey {
127     const WebRemoteFileLocation &key;
128 
129     template <class StorerT>
130     void store(StorerT &storer) const;
131   };
as_keyWebRemoteFileLocation132   AsKey as_key(bool /*is_unique*/) const {
133     return AsKey{*this};
134   }
135 
136   bool operator<(const WebRemoteFileLocation &other) const {
137     return url_ < other.url_;
138   }
139   bool operator==(const WebRemoteFileLocation &other) const {
140     return url_ == other.url_;
141   }
142 };
143 
144 inline StringBuilder &operator<<(StringBuilder &string_builder, const WebRemoteFileLocation &location) {
145   return string_builder << "[url = " << location.url_ << ", access_hash = " << location.access_hash_ << "]";
146 }
147 
148 struct CommonRemoteFileLocation {
149   int64 id_;
150   int64 access_hash_;
151 
152   template <class StorerT>
153   void store(StorerT &storer) const;
154   template <class ParserT>
155   void parse(ParserT &parser);
156 
157   struct AsKey {
158     const CommonRemoteFileLocation &key;
159 
160     template <class StorerT>
161     void store(StorerT &storer) const;
162   };
as_keyCommonRemoteFileLocation163   AsKey as_key(bool /*is_unique*/) const {
164     return AsKey{*this};
165   }
166 
167   bool operator<(const CommonRemoteFileLocation &other) const {
168     return id_ < other.id_;
169   }
170   bool operator==(const CommonRemoteFileLocation &other) const {
171     return id_ == other.id_;
172   }
173 };
174 
175 inline StringBuilder &operator<<(StringBuilder &string_builder, const CommonRemoteFileLocation &location) {
176   return string_builder << "[ID = " << location.id_ << ", access_hash = " << location.access_hash_ << "]";
177 }
178 
179 class FullRemoteFileLocation {
180  public:
181   FileType file_type_{FileType::None};
182 
183  private:
184   static constexpr int32 WEB_LOCATION_FLAG = 1 << 24;
185   static constexpr int32 FILE_REFERENCE_FLAG = 1 << 25;
186   DcId dc_id_;
187   string file_reference_;
188   enum class LocationType : int32 { Web, Photo, Common, None };
189   Variant<WebRemoteFileLocation, PhotoRemoteFileLocation, CommonRemoteFileLocation> variant_;
190 
location_type()191   LocationType location_type() const {
192     if (is_web()) {
193       return LocationType::Web;
194     }
195     switch (file_type_) {
196       case FileType::Photo:
197       case FileType::ProfilePhoto:
198       case FileType::Thumbnail:
199       case FileType::EncryptedThumbnail:
200       case FileType::Wallpaper:
201         return LocationType::Photo;
202       case FileType::Video:
203       case FileType::VoiceNote:
204       case FileType::Document:
205       case FileType::Sticker:
206       case FileType::Audio:
207       case FileType::Animation:
208       case FileType::Encrypted:
209       case FileType::VideoNote:
210       case FileType::SecureRaw:
211       case FileType::Secure:
212       case FileType::Background:
213       case FileType::DocumentAsFile:
214         return LocationType::Common;
215       case FileType::None:
216       case FileType::Size:
217       default:
218         UNREACHABLE();
219       case FileType::Temp:
220         return LocationType::None;
221     }
222   }
223 
web()224   WebRemoteFileLocation &web() {
225     return variant_.get<WebRemoteFileLocation>();
226   }
photo()227   PhotoRemoteFileLocation &photo() {
228     return variant_.get<PhotoRemoteFileLocation>();
229   }
common()230   CommonRemoteFileLocation &common() {
231     return variant_.get<CommonRemoteFileLocation>();
232   }
web()233   const WebRemoteFileLocation &web() const {
234     return variant_.get<WebRemoteFileLocation>();
235   }
photo()236   const PhotoRemoteFileLocation &photo() const {
237     return variant_.get<PhotoRemoteFileLocation>();
238   }
common()239   const CommonRemoteFileLocation &common() const {
240     return variant_.get<CommonRemoteFileLocation>();
241   }
242 
243   friend StringBuilder &operator<<(StringBuilder &string_builder,
244                                    const FullRemoteFileLocation &full_remote_file_location);
245 
key_type()246   int32 key_type() const {
247     auto type = static_cast<int32>(file_type_);
248     if (is_web()) {
249       type |= WEB_LOCATION_FLAG;
250     }
251     return type;
252   }
253 
check_file_reference()254   void check_file_reference() {
255     if (file_reference_ == FileReferenceView::invalid_file_reference()) {
256       LOG(ERROR) << "Tried to register file with invalid file reference";
257       file_reference_.clear();
258     }
259   }
260 
261  public:
262   template <class StorerT>
263   void store(StorerT &storer) const;
264   template <class ParserT>
265   void parse(ParserT &parser);
266 
267   struct AsKey {
268     const FullRemoteFileLocation &key;
269 
270     template <class StorerT>
271     void store(StorerT &storer) const;
272   };
as_key()273   AsKey as_key() const {
274     return AsKey{*this};
275   }
276 
277   struct AsUnique {
278     const FullRemoteFileLocation &key;
279 
280     template <class StorerT>
281     void store(StorerT &storer) const;
282   };
as_unique()283   AsUnique as_unique() const {
284     return AsUnique{*this};
285   }
286 
get_dc_id()287   DcId get_dc_id() const {
288     CHECK(!is_web());
289     return dc_id_;
290   }
291 
get_access_hash()292   int64 get_access_hash() const {
293     switch (location_type()) {
294       case LocationType::Photo:
295         return photo().access_hash_;
296       case LocationType::Common:
297         return common().access_hash_;
298       case LocationType::Web:
299         return web().access_hash_;
300       case LocationType::None:
301       default:
302         UNREACHABLE();
303         return 0;
304     }
305   }
306 
get_id()307   int64 get_id() const {
308     switch (location_type()) {
309       case LocationType::Photo:
310         return photo().id_;
311       case LocationType::Common:
312         return common().id_;
313       case LocationType::Web:
314       case LocationType::None:
315       default:
316         UNREACHABLE();
317         return 0;
318     }
319   }
320 
get_source()321   PhotoSizeSource get_source() const {
322     switch (location_type()) {
323       case LocationType::Photo:
324         return photo().source_;
325       case LocationType::Common:
326       case LocationType::Web:
327         return PhotoSizeSource::full_legacy(0, 0, 0);
328       case LocationType::None:
329       default:
330         UNREACHABLE();
331         return PhotoSizeSource::full_legacy(0, 0, 0);
332     }
333   }
334 
set_source(PhotoSizeSource source)335   void set_source(PhotoSizeSource source) {
336     CHECK(is_photo());
337     file_type_ = source.get_file_type("set_source");
338     photo().source_ = std::move(source);
339   }
340 
delete_file_reference(Slice bad_file_reference)341   bool delete_file_reference(Slice bad_file_reference) {
342     if (file_reference_ != FileReferenceView::invalid_file_reference() && file_reference_ == bad_file_reference) {
343       file_reference_ = FileReferenceView::invalid_file_reference().str();
344       return true;
345     }
346     return false;
347   }
348 
has_file_reference()349   bool has_file_reference() const {
350     return file_reference_ != FileReferenceView::invalid_file_reference();
351   }
352 
get_file_reference()353   Slice get_file_reference() const {
354     return file_reference_;
355   }
356 
get_url()357   string get_url() const {
358     if (is_web()) {
359       return web().url_;
360     }
361 
362     return string();
363   }
364 
is_web()365   bool is_web() const {
366     return variant_.get_offset() == 0;
367   }
is_photo()368   bool is_photo() const {
369     return location_type() == LocationType::Photo;
370   }
is_common()371   bool is_common() const {
372     return location_type() == LocationType::Common;
373   }
is_encrypted_secret()374   bool is_encrypted_secret() const {
375     return file_type_ == FileType::Encrypted;
376   }
is_encrypted_secure()377   bool is_encrypted_secure() const {
378     return file_type_ == FileType::Secure;
379   }
is_encrypted_any()380   bool is_encrypted_any() const {
381     return is_encrypted_secret() || is_encrypted_secure();
382   }
is_secure()383   bool is_secure() const {
384     return file_type_ == FileType::SecureRaw || file_type_ == FileType::Secure;
385   }
is_document()386   bool is_document() const {
387     return is_common() && !is_secure() && !is_encrypted_secret();
388   }
389 
390 #define as_input_web_file_location() as_input_web_file_location_impl(__FILE__, __LINE__)
as_input_web_file_location_impl(const char * file,int line)391   tl_object_ptr<telegram_api::inputWebFileLocation> as_input_web_file_location_impl(const char *file, int line) const {
392     LOG_CHECK(is_web()) << file << ' ' << line;
393     return make_tl_object<telegram_api::inputWebFileLocation>(web().url_, web().access_hash_);
394   }
395 
as_input_file_location()396   tl_object_ptr<telegram_api::InputFileLocation> as_input_file_location() const {
397     switch (location_type()) {
398       case LocationType::Photo: {
399         const auto &id = photo().id_;
400         const auto &access_hash = photo().access_hash_;
401         const auto &source = photo().source_;
402         switch (source.get_type("as_input_file_location")) {
403           case PhotoSizeSource::Type::Legacy:
404             UNREACHABLE();
405             break;
406           case PhotoSizeSource::Type::Thumbnail: {
407             auto &thumbnail = source.thumbnail();
408             switch (thumbnail.file_type) {
409               case FileType::Photo:
410                 return make_tl_object<telegram_api::inputPhotoFileLocation>(
411                     id, access_hash, BufferSlice(file_reference_),
412                     std::string(1, static_cast<char>(static_cast<uint8>(thumbnail.thumbnail_type))));
413               case FileType::Thumbnail:
414                 return make_tl_object<telegram_api::inputDocumentFileLocation>(
415                     id, access_hash, BufferSlice(file_reference_),
416                     std::string(1, static_cast<char>(static_cast<uint8>(thumbnail.thumbnail_type))));
417               default:
418                 UNREACHABLE();
419                 break;
420             }
421             break;
422           }
423           case PhotoSizeSource::Type::DialogPhotoSmall:
424           case PhotoSizeSource::Type::DialogPhotoBig: {
425             auto &dialog_photo = source.dialog_photo();
426             bool is_big = source.get_type("as_input_file_location 2") == PhotoSizeSource::Type::DialogPhotoBig;
427             return make_tl_object<telegram_api::inputPeerPhotoFileLocation>(
428                 is_big * telegram_api::inputPeerPhotoFileLocation::BIG_MASK, false /*ignored*/,
429                 dialog_photo.get_input_peer(), id);
430           }
431           case PhotoSizeSource::Type::StickerSetThumbnail:
432             UNREACHABLE();
433             break;
434           case PhotoSizeSource::Type::FullLegacy: {
435             const auto &full_legacy = source.full_legacy();
436             return make_tl_object<telegram_api::inputPhotoLegacyFileLocation>(
437                 id, access_hash, BufferSlice(file_reference_), full_legacy.volume_id, full_legacy.local_id,
438                 full_legacy.secret);
439           }
440           case PhotoSizeSource::Type::DialogPhotoSmallLegacy:
441           case PhotoSizeSource::Type::DialogPhotoBigLegacy: {
442             auto &dialog_photo = source.dialog_photo_legacy();
443             bool is_big = source.get_type("as_input_file_location 3") == PhotoSizeSource::Type::DialogPhotoBigLegacy;
444             return make_tl_object<telegram_api::inputPeerPhotoFileLocationLegacy>(
445                 is_big * telegram_api::inputPeerPhotoFileLocationLegacy::BIG_MASK, false /*ignored*/,
446                 dialog_photo.get_input_peer(), dialog_photo.volume_id, dialog_photo.local_id);
447           }
448           case PhotoSizeSource::Type::StickerSetThumbnailLegacy: {
449             auto &sticker_set_thumbnail = source.sticker_set_thumbnail_legacy();
450             return make_tl_object<telegram_api::inputStickerSetThumbLegacy>(
451                 sticker_set_thumbnail.get_input_sticker_set(), sticker_set_thumbnail.volume_id,
452                 sticker_set_thumbnail.local_id);
453           }
454           case PhotoSizeSource::Type::StickerSetThumbnailVersion: {
455             auto &sticker_set_thumbnail = source.sticker_set_thumbnail_version();
456             return make_tl_object<telegram_api::inputStickerSetThumb>(sticker_set_thumbnail.get_input_sticker_set(),
457                                                                       sticker_set_thumbnail.version);
458           }
459           default:
460             break;
461         }
462         UNREACHABLE();
463         return nullptr;
464       }
465       case LocationType::Common:
466         if (is_encrypted_secret()) {
467           return make_tl_object<telegram_api::inputEncryptedFileLocation>(common().id_, common().access_hash_);
468         } else if (is_secure()) {
469           return make_tl_object<telegram_api::inputSecureFileLocation>(common().id_, common().access_hash_);
470         } else {
471           return make_tl_object<telegram_api::inputDocumentFileLocation>(common().id_, common().access_hash_,
472                                                                          BufferSlice(file_reference_), string());
473         }
474       case LocationType::Web:
475       case LocationType::None:
476       default:
477         UNREACHABLE();
478         return nullptr;
479     }
480   }
481 
482 #define as_input_document() as_input_document_impl(__FILE__, __LINE__)
as_input_document_impl(const char * file,int line)483   tl_object_ptr<telegram_api::inputDocument> as_input_document_impl(const char *file, int line) const {
484     LOG_CHECK(is_common()) << file << ' ' << line;
485     LOG_CHECK(is_document()) << file << ' ' << line;
486     return make_tl_object<telegram_api::inputDocument>(common().id_, common().access_hash_,
487                                                        BufferSlice(file_reference_));
488   }
489 
490 #define as_input_photo() as_input_photo_impl(__FILE__, __LINE__)
as_input_photo_impl(const char * file,int line)491   tl_object_ptr<telegram_api::inputPhoto> as_input_photo_impl(const char *file, int line) const {
492     LOG_CHECK(is_photo()) << file << ' ' << line;
493     return make_tl_object<telegram_api::inputPhoto>(photo().id_, photo().access_hash_, BufferSlice(file_reference_));
494   }
495 
as_input_encrypted_file()496   tl_object_ptr<telegram_api::inputEncryptedFile> as_input_encrypted_file() const {
497     CHECK(is_encrypted_secret());
498     return make_tl_object<telegram_api::inputEncryptedFile>(common().id_, common().access_hash_);
499   }
500 
501 #define as_input_secure_file() as_input_secure_file_impl(__FILE__, __LINE__)
as_input_secure_file_impl(const char * file,int line)502   tl_object_ptr<telegram_api::inputSecureFile> as_input_secure_file_impl(const char *file, int line) const {
503     LOG_CHECK(is_secure()) << file << ' ' << line;
504     return make_tl_object<telegram_api::inputSecureFile>(common().id_, common().access_hash_);
505   }
506 
507   // this constructor is just for immediate unserialize
508   FullRemoteFileLocation() = default;
509 
510   // photo
FullRemoteFileLocation(const PhotoSizeSource & source,int64 id,int64 access_hash,DcId dc_id,std::string file_reference)511   FullRemoteFileLocation(const PhotoSizeSource &source, int64 id, int64 access_hash, DcId dc_id,
512                          std::string file_reference)
513       : file_type_(source.get_file_type("FullRemoteFileLocation"))
514       , dc_id_(dc_id)
515       , file_reference_(std::move(file_reference))
516       , variant_(PhotoRemoteFileLocation{id, access_hash, source}) {
517     CHECK(is_photo());
518     check_file_reference();
519   }
520 
521   // document
FullRemoteFileLocation(FileType file_type,int64 id,int64 access_hash,DcId dc_id,std::string file_reference)522   FullRemoteFileLocation(FileType file_type, int64 id, int64 access_hash, DcId dc_id, std::string file_reference)
523       : file_type_(file_type)
524       , dc_id_(dc_id)
525       , file_reference_(std::move(file_reference))
526       , variant_(CommonRemoteFileLocation{id, access_hash}) {
527     CHECK(is_common());
528     check_file_reference();
529   }
530 
531   // web document
FullRemoteFileLocation(FileType file_type,string url,int64 access_hash)532   FullRemoteFileLocation(FileType file_type, string url, int64 access_hash)
533       : file_type_(file_type), dc_id_(), variant_(WebRemoteFileLocation{std::move(url), access_hash}) {
534     CHECK(is_web());
535     CHECK(!web().url_.empty());
536   }
537 
538   bool operator<(const FullRemoteFileLocation &other) const {
539     if (key_type() != other.key_type()) {
540       return key_type() < other.key_type();
541     }
542     if (dc_id_ != other.dc_id_) {
543       return dc_id_ < other.dc_id_;
544     }
545     switch (location_type()) {
546       case LocationType::Photo:
547         return photo() < other.photo();
548       case LocationType::Common:
549         return common() < other.common();
550       case LocationType::Web:
551         return web() < other.web();
552       case LocationType::None:
553       default:
554         UNREACHABLE();
555         return false;
556     }
557   }
558   bool operator==(const FullRemoteFileLocation &other) const {
559     if (key_type() != other.key_type()) {
560       return false;
561     }
562     if (dc_id_ != other.dc_id_) {
563       return false;
564     }
565     switch (location_type()) {
566       case LocationType::Photo:
567         return photo() == other.photo();
568       case LocationType::Common:
569         return common() == other.common();
570       case LocationType::Web:
571         return web() == other.web();
572       case LocationType::None:
573       default:
574         UNREACHABLE();
575         return false;
576     }
577   }
578 
579   static const int32 KEY_MAGIC = 0x64374632;
580 };
581 
582 inline StringBuilder &operator<<(StringBuilder &string_builder,
583                                  const FullRemoteFileLocation &full_remote_file_location) {
584   string_builder << "[" << full_remote_file_location.file_type_;
585   if (!full_remote_file_location.is_web()) {
586     string_builder << ", " << full_remote_file_location.get_dc_id();
587   }
588   if (!full_remote_file_location.file_reference_.empty()) {
589     string_builder << ", " << tag("file_reference", base64_encode(full_remote_file_location.file_reference_));
590   }
591 
592   string_builder << ", location = ";
593   if (full_remote_file_location.is_web()) {
594     string_builder << full_remote_file_location.web();
595   } else if (full_remote_file_location.is_photo()) {
596     string_builder << full_remote_file_location.photo();
597   } else if (full_remote_file_location.is_common()) {
598     string_builder << full_remote_file_location.common();
599   }
600 
601   return string_builder << "]";
602 }
603 
604 class RemoteFileLocation {
605  public:
606   enum class Type : int32 { Empty, Partial, Full };
607 
type()608   Type type() const {
609     return static_cast<Type>(variant_.get_offset());
610   }
611 
partial()612   PartialRemoteFileLocation &partial() {
613     return variant_.get<1>();
614   }
full()615   FullRemoteFileLocation &full() {
616     return variant_.get<2>();
617   }
partial()618   const PartialRemoteFileLocation &partial() const {
619     return variant_.get<1>();
620   }
full()621   const FullRemoteFileLocation &full() const {
622     return variant_.get<2>();
623   }
624 
625   template <class StorerT>
626   void store(StorerT &storer) const;
627   template <class ParserT>
628   void parse(ParserT &parser);
629 
RemoteFileLocation()630   RemoteFileLocation() : variant_{EmptyRemoteFileLocation{}} {
631   }
RemoteFileLocation(FullRemoteFileLocation full)632   explicit RemoteFileLocation(FullRemoteFileLocation full) : variant_(std::move(full)) {
633   }
RemoteFileLocation(PartialRemoteFileLocation partial)634   explicit RemoteFileLocation(PartialRemoteFileLocation partial) : variant_(std::move(partial)) {
635   }
636 
637  private:
638   Variant<EmptyRemoteFileLocation, PartialRemoteFileLocation, FullRemoteFileLocation> variant_;
639 
640   friend bool operator==(const RemoteFileLocation &lhs, const RemoteFileLocation &rhs);
641 
is_empty()642   bool is_empty() const {
643     switch (type()) {
644       case Type::Empty:
645         return true;
646       case Type::Partial:
647         return partial().ready_part_count_ == 0;
648       case Type::Full:
649         return false;
650       default:
651         UNREACHABLE();
652         return false;
653     }
654   }
655 };
656 
657 inline bool operator==(const RemoteFileLocation &lhs, const RemoteFileLocation &rhs) {
658   if (lhs.is_empty() && rhs.is_empty()) {
659     return true;
660   }
661   return lhs.variant_ == rhs.variant_;
662 }
663 
664 inline bool operator!=(const RemoteFileLocation &lhs, const RemoteFileLocation &rhs) {
665   return !(lhs == rhs);
666 }
667 
668 inline StringBuilder &operator<<(StringBuilder &sb, const RemoteFileLocation &location) {
669   switch (location.type()) {
670     case RemoteFileLocation::Type::Empty:
671       return sb << "[empty remote location]";
672     case RemoteFileLocation::Type::Partial:
673       return sb << location.partial();
674     case RemoteFileLocation::Type::Full:
675       return sb << location.full();
676     default:
677       UNREACHABLE();
678       return sb;
679   }
680 }
681 
682 struct EmptyLocalFileLocation {
683   template <class StorerT>
storeEmptyLocalFileLocation684   void store(StorerT &storer) const {
685   }
686   template <class ParserT>
parseEmptyLocalFileLocation687   void parse(ParserT &parser) {
688   }
689 };
690 
691 inline bool operator==(const EmptyLocalFileLocation &lhs, const EmptyLocalFileLocation &rhs) {
692   return true;
693 }
694 
695 inline bool operator!=(const EmptyLocalFileLocation &lhs, const EmptyLocalFileLocation &rhs) {
696   return !(lhs == rhs);
697 }
698 
699 struct PartialLocalFileLocation {
700   FileType file_type_;
701   int32 part_size_;
702   string path_;
703   string iv_;
704   string ready_bitmask_;
705 
706   template <class StorerT>
707   void store(StorerT &storer) const;
708   template <class ParserT>
709   void parse(ParserT &parser);
710 };
711 
712 inline bool operator==(const PartialLocalFileLocation &lhs, const PartialLocalFileLocation &rhs) {
713   return lhs.file_type_ == rhs.file_type_ && lhs.path_ == rhs.path_ && lhs.part_size_ == rhs.part_size_ &&
714          lhs.iv_ == rhs.iv_ && lhs.ready_bitmask_ == rhs.ready_bitmask_;
715 }
716 
717 inline bool operator!=(const PartialLocalFileLocation &lhs, const PartialLocalFileLocation &rhs) {
718   return !(lhs == rhs);
719 }
720 
721 inline StringBuilder &operator<<(StringBuilder &sb, const PartialLocalFileLocation &location) {
722   return sb << "[partial local location of " << location.file_type_ << " with part size " << location.part_size_
723             << " and ready parts " << Bitmask(Bitmask::Decode{}, location.ready_bitmask_) << "] at \"" << location.path_
724             << '"';
725 }
726 
727 struct FullLocalFileLocation {
728   FileType file_type_;
729   string path_;
730   uint64 mtime_nsec_;
731 
732   template <class StorerT>
733   void store(StorerT &storer) const;
734   template <class ParserT>
735   void parse(ParserT &parser);
736 
as_keyFullLocalFileLocation737   const FullLocalFileLocation &as_key() const {
738     return *this;
739   }
740 
741   // TODO: remove this constructor
FullLocalFileLocationFullLocalFileLocation742   FullLocalFileLocation() : file_type_(FileType::Photo) {
743   }
FullLocalFileLocationFullLocalFileLocation744   FullLocalFileLocation(FileType file_type, string path, uint64 mtime_nsec)
745       : file_type_(file_type), path_(std::move(path)), mtime_nsec_(mtime_nsec) {
746   }
747 
748   static const int32 KEY_MAGIC = 0x84373817;
749 };
750 
751 inline bool operator<(const FullLocalFileLocation &lhs, const FullLocalFileLocation &rhs) {
752   return std::tie(lhs.file_type_, lhs.mtime_nsec_, lhs.path_) < std::tie(rhs.file_type_, rhs.mtime_nsec_, rhs.path_);
753 }
754 
755 inline bool operator==(const FullLocalFileLocation &lhs, const FullLocalFileLocation &rhs) {
756   return std::tie(lhs.file_type_, lhs.mtime_nsec_, lhs.path_) == std::tie(rhs.file_type_, rhs.mtime_nsec_, rhs.path_);
757 }
758 
759 inline bool operator!=(const FullLocalFileLocation &lhs, const FullLocalFileLocation &rhs) {
760   return !(lhs == rhs);
761 }
762 
763 inline StringBuilder &operator<<(StringBuilder &sb, const FullLocalFileLocation &location) {
764   return sb << "[full local location of " << location.file_type_ << "] at \"" << location.path_ << '"';
765 }
766 
767 struct PartialLocalFileLocationPtr {
768   unique_ptr<PartialLocalFileLocation> location_;  // must never be equal to nullptr
769 
PartialLocalFileLocationPtrPartialLocalFileLocationPtr770   PartialLocalFileLocationPtr() : location_(make_unique<PartialLocalFileLocation>()) {
771   }
PartialLocalFileLocationPtrPartialLocalFileLocationPtr772   explicit PartialLocalFileLocationPtr(PartialLocalFileLocation location)
773       : location_(make_unique<PartialLocalFileLocation>(std::move(location))) {
774   }
PartialLocalFileLocationPtrPartialLocalFileLocationPtr775   PartialLocalFileLocationPtr(const PartialLocalFileLocationPtr &other)
776       : location_(make_unique<PartialLocalFileLocation>(*other.location_)) {
777   }
778   PartialLocalFileLocationPtr &operator=(const PartialLocalFileLocationPtr &other) {
779     if (this != &other) {
780       *location_ = *other.location_;
781     }
782     return *this;
783   }
PartialLocalFileLocationPtrPartialLocalFileLocationPtr784   PartialLocalFileLocationPtr(PartialLocalFileLocationPtr &&other) noexcept
785       : location_(make_unique<PartialLocalFileLocation>(std::move(*other.location_))) {
786   }
787   PartialLocalFileLocationPtr &operator=(PartialLocalFileLocationPtr &&other) noexcept {
788     *location_ = std::move(*other.location_);
789     return *this;
790   }
791   ~PartialLocalFileLocationPtr() = default;
792 
793   template <class StorerT>
794   void store(StorerT &storer) const;
795   template <class ParserT>
796   void parse(ParserT &parser);
797 };
798 
799 inline bool operator==(const PartialLocalFileLocationPtr &lhs, const PartialLocalFileLocationPtr &rhs) {
800   return *lhs.location_ == *rhs.location_;
801 }
802 
803 class LocalFileLocation {
804  public:
805   enum class Type : int32 { Empty, Partial, Full };
806 
type()807   Type type() const {
808     return static_cast<Type>(variant_.get_offset());
809   }
810 
partial()811   PartialLocalFileLocation &partial() {
812     return *variant_.get<1>().location_;
813   }
full()814   FullLocalFileLocation &full() {
815     return variant_.get<2>();
816   }
partial()817   const PartialLocalFileLocation &partial() const {
818     return *variant_.get<1>().location_;
819   }
full()820   const FullLocalFileLocation &full() const {
821     return variant_.get<2>();
822   }
823 
file_name()824   CSlice file_name() const {
825     switch (type()) {
826       case Type::Partial:
827         return partial().path_;
828       case Type::Full:
829         return full().path_;
830       case Type::Empty:
831       default:
832         return CSlice();
833     }
834   }
835 
836   template <class StorerT>
837   void store(StorerT &storer) const;
838   template <class ParserT>
839   void parse(ParserT &parser);
840 
LocalFileLocation()841   LocalFileLocation() : variant_{EmptyLocalFileLocation()} {
842   }
LocalFileLocation(PartialLocalFileLocation partial)843   explicit LocalFileLocation(PartialLocalFileLocation partial)
844       : variant_(PartialLocalFileLocationPtr(std::move(partial))) {
845   }
LocalFileLocation(FullLocalFileLocation full)846   explicit LocalFileLocation(FullLocalFileLocation full) : variant_(std::move(full)) {
847   }
LocalFileLocation(FileType file_type,string path,uint64 mtime_nsec)848   LocalFileLocation(FileType file_type, string path, uint64 mtime_nsec)
849       : variant_(FullLocalFileLocation{file_type, std::move(path), mtime_nsec}) {
850   }
851 
852  private:
853   Variant<EmptyLocalFileLocation, PartialLocalFileLocationPtr, FullLocalFileLocation> variant_;
854 
855   friend bool operator==(const LocalFileLocation &lhs, const LocalFileLocation &rhs);
856 };
857 
858 inline bool operator==(const LocalFileLocation &lhs, const LocalFileLocation &rhs) {
859   return lhs.variant_ == rhs.variant_;
860 }
861 
862 inline bool operator!=(const LocalFileLocation &lhs, const LocalFileLocation &rhs) {
863   return !(lhs == rhs);
864 }
865 
866 inline StringBuilder &operator<<(StringBuilder &sb, const LocalFileLocation &location) {
867   switch (location.type()) {
868     case LocalFileLocation::Type::Empty:
869       return sb << "[empty local location]";
870     case LocalFileLocation::Type::Partial:
871       return sb << location.partial();
872     case LocalFileLocation::Type::Full:
873       return sb << location.full();
874     default:
875       UNREACHABLE();
876       return sb;
877   }
878 }
879 
880 struct FullGenerateFileLocation {
881   FileType file_type_{FileType::None};
882   string original_path_;
883   string conversion_;
884   static const int32 KEY_MAGIC = 0x8b60a1c8;
885 
886   template <class StorerT>
887   void store(StorerT &storer) const;
888   template <class ParserT>
889   void parse(ParserT &parser);
890 
as_keyFullGenerateFileLocation891   const FullGenerateFileLocation &as_key() const {
892     return *this;
893   }
894   FullGenerateFileLocation() = default;
FullGenerateFileLocationFullGenerateFileLocation895   FullGenerateFileLocation(FileType file_type, string original_path, string conversion)
896       : file_type_(file_type), original_path_(std::move(original_path)), conversion_(std::move(conversion)) {
897   }
898 };
899 
900 inline bool operator<(const FullGenerateFileLocation &lhs, const FullGenerateFileLocation &rhs) {
901   return std::tie(lhs.file_type_, lhs.original_path_, lhs.conversion_) <
902          std::tie(rhs.file_type_, rhs.original_path_, rhs.conversion_);
903 }
904 
905 inline bool operator==(const FullGenerateFileLocation &lhs, const FullGenerateFileLocation &rhs) {
906   return std::tie(lhs.file_type_, lhs.original_path_, lhs.conversion_) ==
907          std::tie(rhs.file_type_, rhs.original_path_, rhs.conversion_);
908 }
909 
910 inline bool operator!=(const FullGenerateFileLocation &lhs, const FullGenerateFileLocation &rhs) {
911   return !(lhs == rhs);
912 }
913 
914 inline StringBuilder &operator<<(StringBuilder &string_builder,
915                                  const FullGenerateFileLocation &full_generated_file_location) {
916   return string_builder << '[' << tag("file_type", full_generated_file_location.file_type_)
917                         << tag("original_path", full_generated_file_location.original_path_)
918                         << tag("conversion", full_generated_file_location.conversion_) << ']';
919 }
920 
921 class GenerateFileLocation {
922  public:
923   enum class Type : int32 { Empty, Full };
924 
type()925   Type type() const {
926     return type_;
927   }
928 
full()929   FullGenerateFileLocation &full() {
930     CHECK(type_ == Type::Full);
931     return full_;
932   }
full()933   const FullGenerateFileLocation &full() const {
934     CHECK(type_ == Type::Full);
935     return full_;
936   }
937 
938   template <class StorerT>
939   void store(StorerT &storer) const;
940   template <class ParserT>
941   void parse(ParserT &parser);
942 
GenerateFileLocation()943   GenerateFileLocation() : type_(Type::Empty) {
944   }
945 
GenerateFileLocation(const FullGenerateFileLocation & full)946   explicit GenerateFileLocation(const FullGenerateFileLocation &full) : type_(Type::Full), full_(full) {
947   }
948 
GenerateFileLocation(FileType file_type,string original_path,string conversion)949   GenerateFileLocation(FileType file_type, string original_path, string conversion)
950       : type_(Type::Full), full_{file_type, std::move(original_path), std::move(conversion)} {
951   }
952 
953  private:
954   Type type_;
955   FullGenerateFileLocation full_;
956 };
957 
958 inline bool operator==(const GenerateFileLocation &lhs, const GenerateFileLocation &rhs) {
959   if (lhs.type() != rhs.type()) {
960     return false;
961   }
962   switch (lhs.type()) {
963     case GenerateFileLocation::Type::Empty:
964       return true;
965     case GenerateFileLocation::Type::Full:
966       return lhs.full() == rhs.full();
967   }
968   UNREACHABLE();
969   return false;
970 }
971 
972 inline bool operator!=(const GenerateFileLocation &lhs, const GenerateFileLocation &rhs) {
973   return !(lhs == rhs);
974 }
975 
976 }  // namespace td
977