diff --git a/include/dpp/managed.h b/include/dpp/managed.h index 5e79953517..612fcd0a64 100644 --- a/include/dpp/managed.h +++ b/include/dpp/managed.h @@ -37,23 +37,53 @@ namespace dpp { * Only the timestamp is relevant to us as useful metadata. */ snowflake id; + /** * @brief Constructor, initialises ID * @param nid ID to set */ - managed(const snowflake nid = 0); + constexpr managed(const snowflake nid = 0) noexcept : id{nid} {} + + /** + * @brief Copy constructor + * @param rhs Object to copy + */ + constexpr managed(const managed &rhs) noexcept = default; + + /** + * @brief Move constructor + * + * Effectively equivalent to copy constructor + * @param rhs Object to move from + */ + constexpr managed(managed &&rhs) noexcept = default; + /** * @brief Destroy the managed object */ virtual ~managed() = default; + /** + * @brief Copy assignment operator + * @param rhs Object to copy + */ + constexpr managed &operator=(const managed& rhs) = default; + + /** + * @brief Move assignment operator + * @param rhs Object to copy + */ + constexpr managed &operator=(managed&& rhs) = default; + /** * @brief Get the creation time of this object according to Discord. * * @return double creation time inferred from the snowflake ID. * The minimum possible value is the first second of 2015. */ - double get_creation_time() const; + constexpr double get_creation_time() const noexcept { + return id.get_creation_time(); + }; /** * @brief Comparison operator for comparing two managed objects by id @@ -62,7 +92,9 @@ namespace dpp { * @return true objects are the same id * @return false objects are not the same id */ - bool operator==(const managed& other) const noexcept; + constexpr bool operator==(const managed& other) const noexcept { + return id == other.id; + } /** * @brief Comparison operator for comparing two managed objects by id @@ -71,7 +103,9 @@ namespace dpp { * @return true objects are not the same id * @return false objects are the same id */ - bool operator!=(const managed& other) const noexcept; + constexpr bool operator!=(const managed& other) const noexcept { + return id != other.id; + } }; } // namespace dpp diff --git a/include/dpp/snowflake.h b/include/dpp/snowflake.h index df223f3f2a..dd6cb8a4d9 100644 --- a/include/dpp/snowflake.h +++ b/include/dpp/snowflake.h @@ -52,105 +52,75 @@ class DPP_EXPORT snowflake final { /** * @brief The snowflake value */ - uint64_t value; + uint64_t value = 0; public: + /** + * @brief Construct a snowflake object + */ + constexpr snowflake() noexcept = default; + /** * @brief Construct a snowflake object * @param value A snowflake value */ - snowflake(const uint64_t& value); + constexpr snowflake(const uint64_t value_) noexcept : value{value_} {} /** * @brief Construct a snowflake object * @param string_value A snowflake value */ - snowflake(const std::string& string_value); - - /** - * @brief Construct a snowflake object - */ - snowflake(); - - /** - * @brief Destroy the snowflake object - */ - ~snowflake() = default; + snowflake(std::string_view string_value) noexcept; /** * @brief For acting like an integer * @return The snowflake value */ - operator uint64_t() const; + constexpr operator uint64_t() const noexcept { + return value; + } /** * @brief Returns true if the snowflake holds an empty value (is 0) - * + * * @return true if empty (zero) */ - inline bool empty() const - { + constexpr bool empty() const noexcept { return value == 0; } /** * @brief Returns the stringified version of the snowflake value - * + * * @return std::string string form of snowflake value */ - inline std::string str() const - { + inline std::string str() const { return std::to_string(value); } - /** - * @brief Operator less than, used for maps/unordered maps - * when the snowflake is a key value. - * - * @param lhs fist snowflake to compare - * @param rhs second snowflake to compare - * @return true if lhs is less than rhs - */ - friend inline bool operator< (const snowflake& lhs, const snowflake& rhs) - { - return lhs.value < rhs.value; - } - /** * @brief Assign from std::string - * + * * @param snowflake_val string to assign from. */ - snowflake& operator=(const std::string &snowflake_val); - - /** - * @brief Assign from std::string - * - * @param snowflake_val value to assign from. - */ - snowflake& operator=(const uint64_t &snowflake_val); - - /** - * @brief Check if one snowflake value is equal to another - * - * @param other other snowflake to compare - * @return True if the snowflake objects match - */ - bool operator==(const snowflake& other) const; + snowflake& operator=(std::string_view snowflake_val) noexcept; /** - * @brief Check if one snowflake value is equal to a uint64_t - * - * @param other other snowflake to compare - * @return True if the snowflake objects match + * @brief Comparison operator with a string + * + * @param snowflake_val snowflake value as a string */ - bool operator==(const uint64_t& other) const; + inline bool operator==(std::string_view snowflake_val) const noexcept { + return *this == dpp::snowflake{snowflake_val}; + } /** * @brief For acting like an integer * @return A reference to the snowflake value */ - operator uint64_t &(); + constexpr operator uint64_t &() noexcept { + return value; + } /** * @brief For building json @@ -160,33 +130,42 @@ class DPP_EXPORT snowflake final { /** * @brief Get the creation time of this snowflake according to Discord. - * + * * @return double creation time inferred from the snowflake ID. * The minimum possible value is the first second of 2015. */ - double get_creation_time() const; + constexpr double get_creation_time() const noexcept { + constexpr uint64_t first_january_2016 = 1420070400000ull; + return static_cast((value >> 22) + first_january_2016) / 1000.0; + } /** * @brief Get the worker id that produced this snowflake value - * + * * @return uint8_t worker id */ - uint8_t get_worker_id() const; + constexpr uint8_t get_worker_id() const noexcept { + return static_cast((value & 0x3E0000) >> 17); + } /** * @brief Get the process id that produced this snowflake value - * + * * @return uint8_t process id */ - uint8_t get_process_id() const; + constexpr uint8_t get_process_id() const noexcept { + return static_cast((value & 0x1F000) >> 12); + } /** * @brief Get the increment, which is incremented for every snowflake * created over the one millisecond resolution in the timestamp. - * + * * @return uint64_t millisecond increment */ - uint16_t get_increment() const; + constexpr uint16_t get_increment() const noexcept { + return static_cast(value & 0xFFF); + } }; } // namespace dpp @@ -195,13 +174,13 @@ template<> struct std::hash { /** - * @brief Hashing function for dpp::slowflake + * @brief Hashing function for dpp::snowflake * Used by std::unordered_map. This just calls std::hash. - * + * * @param s Snowflake value to hash * @return std::size_t hash value */ - std::size_t operator()(dpp::snowflake const& s) const noexcept { + std::size_t operator()(dpp::snowflake s) const noexcept { return std::hash{}(s.value); } }; diff --git a/src/dpp/managed.cpp b/src/dpp/managed.cpp deleted file mode 100644 index 2a9d8fb1cb..0000000000 --- a/src/dpp/managed.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/************************************************************************************ - * - * D++, A Lightweight C++ library for Discord - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright 2021 Craig Edwards and D++ contributors - * (https://github.com/brainboxdotcc/DPP/graphs/contributors) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ************************************************************************************/ -#include - -namespace dpp { - -managed::managed(const snowflake nid) : id(nid) { -} - -bool managed::operator==(const managed& other) const noexcept { - return id == other.id; -} - -bool managed::operator!=(const managed& other) const noexcept { - return id != other.id; -} - -double managed::get_creation_time() const { - return this->id.get_creation_time(); -} - -} // namespace dpp diff --git a/src/dpp/snowflake.cpp b/src/dpp/snowflake.cpp index 788ee02481..97d2f9194b 100644 --- a/src/dpp/snowflake.cpp +++ b/src/dpp/snowflake.cpp @@ -19,74 +19,26 @@ * ************************************************************************************/ #include -#include +#include namespace dpp { -snowflake::snowflake(const uint64_t &value) : value(value) {} - -snowflake::snowflake(const std::string &string_value) { - try { - value = std::stoull(string_value); - } - catch (const std::exception &) { +snowflake::snowflake(std::string_view string_value) noexcept { + auto [end, err] = std::from_chars(string_value.data(), string_value.data() + string_value.size(), value); + if (end != string_value.data() + string_value.size()) value = 0; - } -} - -snowflake::snowflake() : snowflake(0) {} - -snowflake::operator uint64_t() const { - return value; -} - -snowflake::operator uint64_t &() { - return value; } -snowflake& snowflake::operator=(const std::string &snowflake_val) { - try { - value = std::stoull(snowflake_val); - } - catch (const std::exception &) { +snowflake& snowflake::operator=(std::string_view string_value) { + auto [end, err] = std::from_chars(string_value.data(), string_value.data() + string_value.size(), value); + if (end != string_value.data() + string_value.size()) value = 0; - } return *this; } -snowflake& snowflake::operator=(const uint64_t &snowflake_val) { - value = snowflake_val; - return *this; -} - -bool snowflake::operator==(const snowflake& other) const { - return other.value == value; -} - -bool snowflake::operator==(const uint64_t& other) const { - return other == value; -} - snowflake::operator nlohmann::json() const { /* Discord transfers snowflakes as strings for compatibility with javascript */ return std::to_string(value); } -double snowflake::get_creation_time() const { - const uint64_t first_january_2016 = 1420070400000; - return (double)((value >> 22) + first_january_2016) / 1000.0; -} - -uint8_t snowflake::get_worker_id() const { - return (uint8_t)((value & 0x3E0000) >> 17); -} - -uint8_t snowflake::get_process_id() const { - return (uint8_t)((value & 0x1F000) >> 12); -} - -uint16_t snowflake::get_increment() const { - return (value & 0xFFF); -} - } // namespace dpp diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 8173c6842c..ef5caa68e7 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -19,6 +19,7 @@ * limitations under the License. * ************************************************************************************/ +#define DPP_STATIC_TEST #include "test.h" #include #include @@ -193,6 +194,24 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test(WEBHOOK, false); } + { // test dpp::snowflake + bool success = true; + dpp::snowflake s = 69420; + json j; + j["value"] = 69420; + success = dpp::snowflake_not_null(&j, "value") == 69420 && success; + DPP_CHECK_CONSTRUCT_ASSIGN(SNOWFLAKE, dpp::snowflake, success); + s = 42069; + success = success && (s == 42069 && s == dpp::snowflake{42069} && s == "42069"); + success = success && (dpp::snowflake{69} < dpp::snowflake{420} && (dpp::snowflake{69} < 420)); + s = "69420"; + success = success && s == 69420; + s = dpp::snowflake{"1337"}; + success = success && s == 1337; + set_test(SNOWFLAKE, success); + } + + { // test interaction_create_t::get_parameter // create a fake interaction dpp::cluster cluster(""); @@ -299,9 +318,6 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b success = p == 5120 && success; auto s = std::to_string(p); success = s == "5120" && success; - json j; - j["value"] = p; - success = dpp::snowflake_not_null(&j, "value") == 5120 && success; p.set(0).add(~uint64_t{0}).remove(dpp::p_speak).set(dpp::p_administrator); success = !p.has(dpp::p_administrator, dpp::p_ban_members) && success; // must return false because they're not both set success = !p.has(dpp::p_administrator | dpp::p_ban_members) && success; diff --git a/src/unittest/test.h b/src/unittest/test.h index c3b295ae45..3b2a09147c 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -80,6 +80,7 @@ struct test_t { #define DPP_TEST(name, desc, flags) inline test_t name = {#name, desc, flags} /* Current list of unit tests */ +DPP_TEST(SNOWFLAKE, "dpp::snowflake class", tf_offline); DPP_TEST(CLUSTER, "Instantiate DPP cluster", tf_offline); DPP_TEST(BOTSTART, "cluster::start method", tf_online); DPP_TEST(CONNECTION, "Connection to client websocket", tf_online);