// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/telegram/ScheduledServerMessageId.h" #include "td/telegram/ServerMessageId.h" #include "td/telegram/telegram_api.h" #include "td/utils/common.h" #include "td/utils/HashTableUtils.h" #include "td/utils/StringBuilder.h" #include <limits> #include <type_traits> namespace td { enum class MessageType : int32 { None, Server, YetUnsent, Local }; class MessageId { int64 id = 0; static constexpr int32 SERVER_ID_SHIFT = 20; static constexpr int32 SHORT_TYPE_MASK = (1 << 2) - 1; static constexpr int32 TYPE_MASK = (1 << 3) - 1; static constexpr int32 FULL_TYPE_MASK = (1 << SERVER_ID_SHIFT) - 1; static constexpr int32 SCHEDULED_MASK = 4; static constexpr int32 TYPE_YET_UNSENT = 1; static constexpr int32 TYPE_LOCAL = 2; friend StringBuilder &operator<<(StringBuilder &string_builder, MessageId message_id); // ordinary message ID layout // |-------31--------|---17---|1|--2-| // |server_message_id|local_id|0|type| // scheduled message ID layout // |-------30-------|----18---|1|--2-| // |send_date-2**30 |server_id|1|type| // sponsored message ID layout // |-------31--------|---17---|1|-2| // |11111111111111111|local_id|0|10| ServerMessageId get_server_message_id_force() const; ScheduledServerMessageId get_scheduled_server_message_id_force() const { CHECK(is_scheduled()); return ScheduledServerMessageId(static_cast<int32>((id >> 3) & ((1 << 18) - 1))); } public: MessageId() = default; explicit MessageId(ServerMessageId server_message_id) : id(static_cast<int64>(server_message_id.get()) << SERVER_ID_SHIFT) { } MessageId(ScheduledServerMessageId server_message_id, int32 send_date, bool force = false); explicit constexpr MessageId(int64 message_id) : id(message_id) { } template <class T, typename = std::enable_if_t<std::is_convertible<T, int64>::value>> MessageId(T message_id) = delete; static constexpr MessageId min() { return MessageId(static_cast<int64>(MessageId::TYPE_YET_UNSENT)); } static constexpr MessageId max() { return MessageId(static_cast<int64>(std::numeric_limits<int32>::max()) << SERVER_ID_SHIFT); } static MessageId get_message_id(const telegram_api::Message *message_ptr, bool is_scheduled); static MessageId get_message_id(const tl_object_ptr<telegram_api::Message> &message_ptr, bool is_scheduled); static MessageId get_max_message_id(const vector<telegram_api::object_ptr<telegram_api::Message>> &messages); static vector<MessageId> get_message_ids(const vector<int64> &input_message_ids); static vector<int32> get_server_message_ids(const vector<MessageId> &message_ids); static vector<int32> get_scheduled_server_message_ids(const vector<MessageId> &message_ids); bool is_valid() const; bool is_valid_scheduled() const; bool is_valid_sponsored() const; int64 get() const { return id; } MessageType get_type() const; bool is_scheduled() const { return (id & SCHEDULED_MASK) != 0; } bool is_yet_unsent() const { CHECK(is_valid() || is_scheduled()); return (id & SHORT_TYPE_MASK) == TYPE_YET_UNSENT; } bool is_local() const { CHECK(is_valid() || is_scheduled()); return (id & SHORT_TYPE_MASK) == TYPE_LOCAL; } bool is_server() const { CHECK(is_valid()); return (id & FULL_TYPE_MASK) == 0; } bool is_scheduled_server() const { CHECK(is_valid_scheduled()); return (id & SHORT_TYPE_MASK) == 0; } bool is_any_server() const { return is_scheduled() ? is_scheduled_server() : is_server(); } ServerMessageId get_server_message_id() const { CHECK(id == 0 || is_server()); return get_server_message_id_force(); } // returns greatest server message identifier not bigger than this message identifier MessageId get_prev_server_message_id() const { CHECK(!is_scheduled()); return MessageId(id & ~FULL_TYPE_MASK); } // returns smallest server message identifier not less than this message identifier MessageId get_next_server_message_id() const { CHECK(!is_scheduled()); return MessageId((id + FULL_TYPE_MASK) & ~FULL_TYPE_MASK); } MessageId get_next_message_id(MessageType type) const; ScheduledServerMessageId get_scheduled_server_message_id() const { CHECK(is_scheduled_server()); return get_scheduled_server_message_id_force(); } int32 get_scheduled_message_date() const { CHECK(is_valid_scheduled()); return static_cast<int32>(id >> 21) + (1 << 30); } bool operator==(const MessageId &other) const { return id == other.id; } bool operator!=(const MessageId &other) const { return id != other.id; } friend bool operator<(const MessageId &lhs, const MessageId &rhs) { CHECK(lhs.is_scheduled() == rhs.is_scheduled()); return lhs.id < rhs.id; } friend bool operator>(const MessageId &lhs, const MessageId &rhs) { CHECK(lhs.is_scheduled() == rhs.is_scheduled()); return lhs.id > rhs.id; } friend bool operator<=(const MessageId &lhs, const MessageId &rhs) { CHECK(lhs.is_scheduled() == rhs.is_scheduled()); return lhs.id <= rhs.id; } friend bool operator>=(const MessageId &lhs, const MessageId &rhs) { CHECK(lhs.is_scheduled() == rhs.is_scheduled()); return lhs.id >= rhs.id; } template <class StorerT> void store(StorerT &storer) const { storer.store_long(id); } template <class ParserT> void parse(ParserT &parser) { id = parser.fetch_long(); } }; struct MessageIdHash { uint32 operator()(MessageId message_id) const { return Hash<int64>()(message_id.get()); } }; } // namespace td