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

This commit is contained in:
Michele Caini
2024-06-11 09:34:19 +02:00
parent 786329c1d0
commit 081c8f799e
15 changed files with 140 additions and 115 deletions

View File

@@ -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>

1
TODO
View File

@@ -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

View File

@@ -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"

View File

@@ -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
src/entt/core/bit.hpp Normal file
View File

@@ -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

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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)

View File

@@ -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>

View File

@@ -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
test/entt/core/bit.cpp Normal file
View File

@@ -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);
}

View File

@@ -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);