Sfoglia il codice sorgente

core: bit.hpp (waiting for C++20)

Michele Caini 1 anno fa
parent
commit
081c8f799e

+ 1 - 0
CMakeLists.txt

@@ -123,6 +123,7 @@ if(ENTT_INCLUDE_HEADERS)
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/attribute.h>
+            $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/bit.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/compressed_pair.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/enum.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/family.hpp>

+ 0 - 1
TODO

@@ -36,5 +36,4 @@ TODO:
 * checkout tools workflow
 * meta: avoid unnecessary copies of iterators with meta containers
 * review constrained noexcept-ness (ie sigh)
-* has_single_bit/popcount vs is_power_of_two (move them to bit.hpp)
 * offer 16b from meta_traits to users or change type

+ 1 - 0
src/entt/container/dense_map.hpp

@@ -15,6 +15,7 @@
 #include "../core/compressed_pair.hpp"
 #include "../core/iterator.hpp"
 #include "../core/memory.hpp"
+#include "../core/bit.hpp"
 #include "../core/type_traits.hpp"
 #include "fwd.hpp"
 

+ 1 - 1
src/entt/container/dense_set.hpp

@@ -12,8 +12,8 @@
 #include <utility>
 #include <vector>
 #include "../config/config.h"
+#include "../core/bit.hpp"
 #include "../core/compressed_pair.hpp"
-#include "../core/memory.hpp"
 #include "../core/type_traits.hpp"
 #include "fwd.hpp"
 

+ 69 - 0
src/entt/core/bit.hpp

@@ -0,0 +1,69 @@
+#ifndef ENTT_CORE_BIT_HPP
+#define ENTT_CORE_BIT_HPP
+
+#include <cstddef>
+#include <limits>
+#include <type_traits>
+#include "../config/config.h"
+
+namespace entt {
+
+/**
+ * @brief Returns the number of set bits in a value (waiting for C++20 and
+ * `std::popcount`).
+ * @tparam Type Unsigned integer type.
+ * @param value A value of unsigned integer type.
+ * @return The number of set bits in the value.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept {
+    return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not (waiting for C++20 and
+ * `std::has_single_bit`).
+ * @tparam Type Unsigned integer type.
+ * @param value A value of unsigned integer type.
+ * @return True if the value is a power of two, false otherwise.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept {
+    return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value
+ * (waiting for C++20 and `std::bit_ceil`).
+ * @tparam Type Unsigned integer type.
+ * @param value A value of unsigned integer type.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> next_power_of_two(const Type value) noexcept {
+    ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits<Type>::digits - 1)), "Numeric limits exceeded");
+    Type curr = value - (value != 0u);
+
+    for(int next = 1; next < std::numeric_limits<Type>::digits; next = next * 2) {
+        curr |= (curr >> next);
+    }
+
+    return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @tparam Type Unsigned integer type.
+ * @param value A value of unsigned integer type.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> fast_mod(const Type value, const std::size_t mod) noexcept {
+    ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two");
+    return value & (mod - 1u);
+}
+
+} // namespace entt
+
+#endif

+ 0 - 53
src/entt/core/memory.hpp

@@ -2,7 +2,6 @@
 #define ENTT_CORE_MEMORY_HPP
 
 #include <cstddef>
-#include <limits>
 #include <memory>
 #include <tuple>
 #include <type_traits>
@@ -11,58 +10,6 @@
 
 namespace entt {
 
-/**
- * @brief Returns the number of set bits in a value (waiting for C++20 and
- * `std::popcount`).
- * @tparam Type Unsigned integer type.
- * @param value A value of unsigned integer type.
- * @return The number of set bits in the value.
- */
-template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept {
-    return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
-}
-
-/**
- * @brief Checks whether a value is a power of two or not (waiting for C++20 and
- * `std::has_single_bit`).
- * @tparam Type Unsigned integer type.
- * @param value A value of unsigned integer type that may be a power of two.
- * @return True if the value is a power of two, false otherwise.
- */
-template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept {
-    return value && ((value & (value - 1)) == 0);
-}
-
-/**
- * @brief Computes the smallest power of two greater than or equal to a value
- * (waiting for C++20 and `std::bit_ceil`).
- * @param value The value to use.
- * @return The smallest power of two greater than or equal to the given value.
- */
-[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
-    ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
-    std::size_t curr = value - (value != 0u);
-
-    for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
-        curr |= curr >> next;
-    }
-
-    return ++curr;
-}
-
-/**
- * @brief Fast module utility function (powers of two only).
- * @param value A value for which to calculate the modulus.
- * @param mod _Modulus_, it must be a power of two.
- * @return The common remainder.
- */
-[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
-    ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two");
-    return value & (mod - 1u);
-}
-
 /**
  * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
  * @tparam Type Pointer type.

+ 1 - 1
src/entt/entity/entity.hpp

@@ -5,7 +5,7 @@
 #include <cstdint>
 #include <type_traits>
 #include "../config/config.h"
-#include "../core/memory.hpp"
+#include "../core/bit.hpp"
 #include "fwd.hpp"
 
 namespace entt {

+ 1 - 1
src/entt/entity/sparse_set.hpp

@@ -11,7 +11,7 @@
 #include "../config/config.h"
 #include "../core/algorithm.hpp"
 #include "../core/any.hpp"
-#include "../core/memory.hpp"
+#include "../core/bit.hpp"
 #include "../core/type_info.hpp"
 #include "entity.hpp"
 #include "fwd.hpp"

+ 2 - 1
src/entt/entity/storage.hpp

@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 #include "../config/config.h"
+#include "../core/bit.hpp"
 #include "../core/iterator.hpp"
 #include "../core/memory.hpp"
 #include "../core/type_info.hpp"
@@ -90,7 +91,7 @@ public:
     [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
         const auto pos = index() - value;
         constexpr auto page_size = component_traits<typename Container::value_type>::page_size;
-        return (*payload)[pos / page_size][fast_mod(pos, page_size)];
+        return (*payload)[pos / page_size][fast_mod(static_cast<std::size_t>(pos), page_size)];
     }
 
     [[nodiscard]] constexpr pointer operator->() const noexcept {

+ 1 - 0
src/entt/entt.hpp

@@ -8,6 +8,7 @@
 #include "core/algorithm.hpp"
 #include "core/any.hpp"
 #include "core/attribute.h"
+#include "core/bit.hpp"
 #include "core/compressed_pair.hpp"
 #include "core/enum.hpp"
 #include "core/family.hpp"

+ 1 - 0
test/CMakeLists.txt

@@ -232,6 +232,7 @@ SETUP_BASIC_TEST(table entt/container/table.cpp)
 
 SETUP_BASIC_TEST(algorithm entt/core/algorithm.cpp)
 SETUP_BASIC_TEST(any entt/core/any.cpp)
+SETUP_BASIC_TEST(bit entt/core/bit.cpp)
 SETUP_BASIC_TEST(compressed_pair entt/core/compressed_pair.cpp)
 SETUP_BASIC_TEST(enum entt/core/enum.cpp)
 SETUP_BASIC_TEST(family entt/core/family.cpp)

+ 1 - 0
test/entt/container/dense_map.cpp

@@ -10,6 +10,7 @@
 #include <vector>
 #include <gtest/gtest.h>
 #include <entt/container/dense_map.hpp>
+#include <entt/core/bit.hpp>
 #include <entt/core/iterator.hpp>
 #include <entt/core/memory.hpp>
 #include <entt/core/utility.hpp>

+ 1 - 0
test/entt/container/dense_set.cpp

@@ -10,6 +10,7 @@
 #include <vector>
 #include <gtest/gtest.h>
 #include <entt/container/dense_set.hpp>
+#include <entt/core/bit.hpp>
 #include <entt/core/memory.hpp>
 #include <entt/core/utility.hpp>
 #include "../../common/linter.hpp"

+ 60 - 0
test/entt/core/bit.cpp

@@ -0,0 +1,60 @@
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include <gtest/gtest.h>
+#include <entt/core/bit.hpp>
+#include "../../common/config.h"
+
+TEST(PopCount, Functionalities) {
+    // constexpr-ness guaranteed
+    constexpr auto zero_popcount = entt::popcount(0u);
+
+    ASSERT_EQ(zero_popcount, 0u);
+    ASSERT_EQ(entt::popcount(1u), 1u);
+    ASSERT_EQ(entt::popcount(2u), 1u);
+    ASSERT_EQ(entt::popcount(3u), 2u);
+    ASSERT_EQ(entt::popcount(7u), 3u);
+    ASSERT_EQ(entt::popcount(128u), 1u);
+    ASSERT_EQ(entt::popcount(201u), 4u);
+}
+
+TEST(HasSingleBit, Functionalities) {
+    // constexpr-ness guaranteed
+    constexpr auto zero_is_power_of_two = entt::has_single_bit(0u);
+
+    ASSERT_FALSE(zero_is_power_of_two);
+    ASSERT_TRUE(entt::has_single_bit(1u));
+    ASSERT_TRUE(entt::has_single_bit(2u));
+    ASSERT_TRUE(entt::has_single_bit(4u));
+    ASSERT_FALSE(entt::has_single_bit(7u));
+    ASSERT_TRUE(entt::has_single_bit(128u));
+    ASSERT_FALSE(entt::has_single_bit(200u));
+}
+
+TEST(NextPowerOfTwo, Functionalities) {
+    // constexpr-ness guaranteed
+    constexpr auto next_power_of_two_of_zero = entt::next_power_of_two(0u);
+
+    ASSERT_EQ(next_power_of_two_of_zero, 1u);
+    ASSERT_EQ(entt::next_power_of_two(1u), 1u);
+    ASSERT_EQ(entt::next_power_of_two(2u), 2u);
+    ASSERT_EQ(entt::next_power_of_two(3u), 4u);
+    ASSERT_EQ(entt::next_power_of_two(17u), 32u);
+    ASSERT_EQ(entt::next_power_of_two(32u), 32u);
+    ASSERT_EQ(entt::next_power_of_two(33u), 64u);
+    ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16))), static_cast<std::size_t>(std::pow(2, 16)));
+    ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16) + 1u)), static_cast<std::size_t>(std::pow(2, 17)));
+}
+
+ENTT_DEBUG_TEST(NextPowerOfTwoDeathTest, Functionalities) {
+    ASSERT_DEATH(static_cast<void>(entt::next_power_of_two((std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)) + 1)), "");
+}
+
+TEST(FastMod, Functionalities) {
+    // constexpr-ness guaranteed
+    constexpr auto fast_mod_of_zero = entt::fast_mod(0u, 8u);
+
+    ASSERT_EQ(fast_mod_of_zero, 0u);
+    ASSERT_EQ(entt::fast_mod(7u, 8u), 7u);
+    ASSERT_EQ(entt::fast_mod(8u, 8u), 0u);
+}

+ 0 - 57
test/entt/core/memory.cpp

@@ -1,7 +1,4 @@
 #include <array>
-#include <cmath>
-#include <cstddef>
-#include <limits>
 #include <memory>
 #include <tuple>
 #include <type_traits>
@@ -15,60 +12,6 @@
 #include "../../common/throwing_type.hpp"
 #include "../../common/tracked_memory_resource.hpp"
 
-TEST(PopCount, Functionalities) {
-    // constexpr-ness guaranteed
-    constexpr auto zero_popcount = entt::popcount(0u);
-
-    ASSERT_EQ(zero_popcount, 0u);
-    ASSERT_EQ(entt::popcount(1u), 1u);
-    ASSERT_EQ(entt::popcount(2u), 1u);
-    ASSERT_EQ(entt::popcount(3u), 2u);
-    ASSERT_EQ(entt::popcount(7u), 3u);
-    ASSERT_EQ(entt::popcount(128u), 1u);
-    ASSERT_EQ(entt::popcount(201u), 4u);
-}
-
-TEST(HasSingleBit, Functionalities) {
-    // constexpr-ness guaranteed
-    constexpr auto zero_is_power_of_two = entt::has_single_bit(0u);
-
-    ASSERT_FALSE(zero_is_power_of_two);
-    ASSERT_TRUE(entt::has_single_bit(1u));
-    ASSERT_TRUE(entt::has_single_bit(2u));
-    ASSERT_TRUE(entt::has_single_bit(4u));
-    ASSERT_FALSE(entt::has_single_bit(7u));
-    ASSERT_TRUE(entt::has_single_bit(128u));
-    ASSERT_FALSE(entt::has_single_bit(200u));
-}
-
-TEST(NextPowerOfTwo, Functionalities) {
-    // constexpr-ness guaranteed
-    constexpr auto next_power_of_two_of_zero = entt::next_power_of_two(0u);
-
-    ASSERT_EQ(next_power_of_two_of_zero, 1u);
-    ASSERT_EQ(entt::next_power_of_two(1u), 1u);
-    ASSERT_EQ(entt::next_power_of_two(2u), 2u);
-    ASSERT_EQ(entt::next_power_of_two(3u), 4u);
-    ASSERT_EQ(entt::next_power_of_two(17u), 32u);
-    ASSERT_EQ(entt::next_power_of_two(32u), 32u);
-    ASSERT_EQ(entt::next_power_of_two(33u), 64u);
-    ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16))), static_cast<std::size_t>(std::pow(2, 16)));
-    ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16) + 1u)), static_cast<std::size_t>(std::pow(2, 17)));
-}
-
-ENTT_DEBUG_TEST(NextPowerOfTwoDeathTest, Functionalities) {
-    ASSERT_DEATH(static_cast<void>(entt::next_power_of_two((std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)) + 1)), "");
-}
-
-TEST(FastMod, Functionalities) {
-    // constexpr-ness guaranteed
-    constexpr auto fast_mod_of_zero = entt::fast_mod(0u, 8u);
-
-    ASSERT_EQ(fast_mod_of_zero, 0u);
-    ASSERT_EQ(entt::fast_mod(7u, 8u), 7u);
-    ASSERT_EQ(entt::fast_mod(8u, 8u), 0u);
-}
-
 TEST(ToAddress, Functionalities) {
     const std::shared_ptr<int> shared = std::make_shared<int>();
     auto *plain = std::addressof(*shared);