core: bit.hpp (waiting for C++20)
This commit is contained in:
@@ -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
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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
69
src/entt/core/bit.hpp
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
60
test/entt/core/bit.cpp
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user