├── .gitignore ├── test ├── test_bit.cpp ├── main.cpp ├── test.cpp ├── test.hpp ├── test_intdiv.cpp ├── assert.cpp ├── test_intlog.cpp ├── test_bitileave.cpp └── assert.hpp ├── include └── bitmanip │ ├── all.hpp │ ├── bitrot.hpp │ ├── bitcount.hpp │ ├── build.hpp │ ├── bitrev.hpp │ ├── bit.hpp │ ├── wbits.hpp │ ├── intdiv.hpp │ ├── wileave.hpp │ ├── bitileave.hpp │ ├── builtin.hpp │ └── intlog.hpp ├── format.sh ├── README.md ├── CMakeLists.txt ├── LICENSE ├── .clang-format └── bitmanip.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | **/.directory 2 | build*/ 3 | CMakeLists.txt.user 4 | -------------------------------------------------------------------------------- /test/test_bit.cpp: -------------------------------------------------------------------------------- 1 | #include "test.hpp" 2 | 3 | #include "bitmanip/bit.hpp" 4 | 5 | namespace bitmanip { 6 | namespace { 7 | 8 | } 9 | } // namespace bitmanip 10 | -------------------------------------------------------------------------------- /include/bitmanip/all.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_ALL_HPP 2 | #define BITMANIP_ALL_HPP 3 | 4 | #include "build.hpp" 5 | #include "builtin.hpp" 6 | 7 | #include "bitcount.hpp" 8 | #include "bitileave.hpp" 9 | #include "bitrev.hpp" 10 | #include "bitrot.hpp" 11 | 12 | #include "intdiv.hpp" 13 | #include "intlog.hpp" 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="$(dirname $0)" 4 | 5 | function format { 6 | clang-format-9 -style=file -i "$1" 7 | } 8 | 9 | for d in include include/bitmanip test; do 10 | FULL_DIR="$SCRIPT_DIR/$d" 11 | for f in "$FULL_DIR"/*.hpp "$FULL_DIR"/*.h "$FULL_DIR"/*.cpp; do 12 | if [[ -f "$f" && $(basename "$f") != 3rd* ]]; then 13 | (printf 'Formatting %s\n' "$f"; format "$f") & 14 | fi 15 | done 16 | done 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bit Manipulation 2 | 3 | This is a single-header C++17 library for manipulating bits. 4 | 5 | It implements many of Sean Eron Anderson's [Bit Twiddling Hacks](https://graphics.stanford.edu/~seander/bithacks.html) 6 | and adds some more unique ones on top of that. 7 | 8 | Most of the implementations are templated and designed for any integer type. 9 | All of the implementations are `constexpr`. 10 | 11 | ## Configuration 12 | 13 | You will find a `USER CONFIGURATION` section at the top of the header file. 14 | Uncomment `#define` lines in this section to customize this library to your own needs. 15 | 16 | ## License 17 | 18 | See the `LICENSE` file. 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(bitmanip LANGUAGES CXX) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | set(HEADER_DIR include/bitmanip) 9 | set(TEST_DIR test) 10 | 11 | add_executable(bitmanip_test 12 | ${TEST_DIR}/main.cpp 13 | ${TEST_DIR}/test_bit.cpp 14 | ${TEST_DIR}/test_bitileave.cpp 15 | ${TEST_DIR}/test_intdiv.cpp 16 | ${TEST_DIR}/test_intlog.cpp 17 | ${TEST_DIR}/test.cpp 18 | ${TEST_DIR}/test.hpp 19 | ${TEST_DIR}/assert.cpp 20 | ${TEST_DIR}/assert.hpp 21 | 22 | ${HEADER_DIR}/all.hpp 23 | ${HEADER_DIR}/build.hpp 24 | ${HEADER_DIR}/builtin.hpp 25 | 26 | ${HEADER_DIR}/bit.hpp 27 | ${HEADER_DIR}/bitcount.hpp 28 | ${HEADER_DIR}/bitileave.hpp 29 | ${HEADER_DIR}/bitrev.hpp 30 | ${HEADER_DIR}/bitrot.hpp 31 | 32 | ${HEADER_DIR}/intdiv.hpp 33 | ${HEADER_DIR}/intlog.hpp) 34 | 35 | target_include_directories(bitmanip_test PUBLIC include/) 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jan Schultke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | //#define BITMANIP_DISABLE_STATIC_TESTS 2 | 3 | #include "test.hpp" 4 | 5 | namespace bitmanip { 6 | namespace { 7 | 8 | // TEST EXECUTION ====================================================================================================== 9 | 10 | int testFailureCount = 0; 11 | 12 | constexpr const char *TEST_ORDER[]{"traits", "bit", "bitcount", "bitileave", "bitrev", "bitrot", "intdiv", "intlog"}; 13 | 14 | void runTest(const Test &test) noexcept 15 | { 16 | static thread_local const char *previousCategory = ""; 17 | 18 | if (std::strcmp(previousCategory, test.category) != 0) { 19 | previousCategory = test.category; 20 | BITMANIP_LOG(IMPORTANT, "Category: \"" + std::string{test.category} + "\" tests"); 21 | } 22 | BITMANIP_LOG(INFO, "Running \"" + std::string{test.name} + "\" ..."); 23 | testFailureCount += not test.operator()(); 24 | } 25 | 26 | int runTests() noexcept 27 | { 28 | BITMANIP_LOG(INFO, "Running " + stringify(getTestCount()) + " tests ..."); 29 | 30 | setTestOrder(TEST_ORDER, std::size(TEST_ORDER)); 31 | forEachTest(&runTest); 32 | 33 | if (testFailureCount == 0) { 34 | BITMANIP_LOG(IMPORTANT, "All " + stringify(getTestCount()) + " tests passed"); 35 | } 36 | else { 37 | BITMANIP_LOG(ERROR, 38 | "Some tests failed (" + stringify(testFailureCount) + "/" + stringify(getTestCount()) + ") total"); 39 | } 40 | return testFailureCount; 41 | } 42 | 43 | } // namespace 44 | } // namespace bitmanip 45 | 46 | int main() 47 | { 48 | bitmanip::setLogLevel(bitmanip::LogLevel::DEBUG); 49 | bitmanip::enableLoggingSourceLocation(false); 50 | bitmanip::enableLoggingTimestamp(false); 51 | 52 | return bitmanip::runTests(); 53 | } 54 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "test.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace bitmanip { 9 | 10 | Test::~Test() = default; 11 | 12 | // The reason why we have such strange code design with a pointer to a local static is that registerTests(Test*) gets 13 | // invoked during static initialization. 14 | // To make sure that testsPtr is the first thing to be initialized, we have initialize it locally during the first 15 | // invocation of registerTest(). 16 | static std::vector> *testsPtr; 17 | static bool anyTestsRegistered = false; 18 | 19 | static std::unordered_map testPriorityMap; 20 | 21 | unsigned getPriority(const std::string &prefix) 22 | { 23 | auto location = testPriorityMap.find(prefix); 24 | BITMANIP_ASSERT_MSG(location != testPriorityMap.end(), "Priority for category " + prefix + " is unspecified"); 25 | return location->second; 26 | } 27 | 28 | void detail::registerTest(Test *test) 29 | { 30 | static std::vector> tests{}; 31 | [[maybe_unused]] static const int reg_ = (testsPtr = &tests, anyTestsRegistered = true, 0); 32 | 33 | tests.push_back(std::unique_ptr{test}); 34 | } 35 | 36 | void forEachTest(TestConsumer *action) 37 | { 38 | if (not anyTestsRegistered) { 39 | return; 40 | } 41 | 42 | std::sort(testsPtr->begin(), testsPtr->end(), [](const auto &l, const auto &r) -> bool { 43 | unsigned lp = getPriority(l->category); 44 | unsigned rp = getPriority(r->category); 45 | return lp < rp; 46 | }); 47 | 48 | for (const auto &testPtr : *testsPtr) { 49 | action(*testPtr); 50 | } 51 | } 52 | 53 | void setTestOrder(const char *const prefixes[], std::size_t count) 54 | { 55 | BITMANIP_ASSERT_MSG(testPriorityMap.empty(), "Test order can only be specified once"); 56 | for (unsigned i = 0; i < count; ++i) { 57 | testPriorityMap.emplace(prefixes[i], i); 58 | } 59 | } 60 | 61 | std::size_t getTestCount() 62 | { 63 | return anyTestsRegistered ? testsPtr->size() : 0; 64 | } 65 | 66 | } // namespace bitmanip 67 | -------------------------------------------------------------------------------- /include/bitmanip/bitrot.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_BITROT_HPP 2 | #define BITMANIP_BITROT_HPP 3 | 4 | #include "bit.hpp" 5 | 6 | namespace bitmanip { 7 | 8 | // BIT ROTATION ======================================================================================================== 9 | 10 | namespace detail { 11 | 12 | template 13 | [[nodiscard]] constexpr Uint rotateLeft_shift(Uint n, unsigned rot) noexcept 14 | { 15 | constexpr unsigned char mask = 8 * sizeof(Uint) - 1; 16 | 17 | rot &= mask; 18 | Uint hi = n << rot; 19 | Uint lo = n >> (-rot & mask); 20 | return hi | lo; 21 | } 22 | 23 | template 24 | [[nodiscard]] constexpr Uint rotateRight_shift(Uint n, unsigned rot) noexcept 25 | { 26 | constexpr unsigned char mask = 8 * sizeof(Uint) - 1; 27 | 28 | rot &= mask; 29 | Uint lo = n >> rot; 30 | Uint hi = n << (-rot & mask); 31 | return hi | lo; 32 | } 33 | 34 | } // namespace detail 35 | 36 | /** 37 | * @brief Rotates an integer to the left by a given amount of bits. 38 | * This is similar to a leftshift, but the bits shifted out of the integer are inserted on the low side of the 39 | * integer. 40 | * @param n the number to shift 41 | * @param rot bit count 42 | */ 43 | template 44 | [[nodiscard]] constexpr Uint rotateLeft(Uint n, unsigned rot = 1) noexcept 45 | { 46 | #ifdef BITMANIP_HAS_BUILTIN_ROTL 47 | return builtin::isconsteval() ? detail::rotateLeft_shift(n, rot) : builtin::rotl(n, rot); 48 | #else 49 | return detail::rotateLeft_shift(n, rot); 50 | #endif 51 | } 52 | 53 | /** 54 | * @brief Rotates an integer to the right by a given amount of bits. 55 | * This is similar to a leftshift, but the bits shifted out of the integer are inserted on the high side of the 56 | * integer. 57 | * @param n the number to shift 58 | * @param rot bit count 59 | */ 60 | template 61 | [[nodiscard]] constexpr Uint rotateRight(Uint n, unsigned rot = 1) noexcept 62 | { 63 | #ifdef BITMANIP_HAS_BUILTIN_ROTL 64 | return builtin::isconsteval() ? detail::rotateRight_shift(n, rot) : builtin::rotr(n, rot); 65 | #else 66 | return detail::rotateRight_shift(n, rot); 67 | #endif 68 | } 69 | 70 | } // namespace bitmanip 71 | 72 | #endif // BITROT_HPP 73 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | 3 | AccessModifierOffset: -4 4 | 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Left 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: false 16 | AllowShortCaseLabelsOnASingleLine: true 17 | AllowShortFunctionsOnASingleLine: Empty 18 | AllowShortIfStatementsOnASingleLine: true 19 | AllowShortLambdasOnASingleLine: Empty 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: true 24 | 25 | BinPackArguments: false 26 | BinPackParameters: false 27 | 28 | BreakBeforeBinaryOperators: false 29 | BreakBeforeBraces: Stroustrup 30 | BreakBeforeTernaryOperators: true 31 | BreakConstructorInitializers: BeforeComma 32 | BreakInheritanceList: BeforeComma 33 | BreakStringLiterals: false 34 | 35 | ColumnLimit: 120 36 | 37 | CommentPragmas: '' 38 | 39 | CompactNamespaces: false 40 | 41 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 42 | ConstructorInitializerIndentWidth: 4 43 | 44 | ContinuationIndentWidth: 4 45 | 46 | Cpp11BracedListStyle: true 47 | 48 | DerivePointerAlignment: false 49 | 50 | DisableFormat: false 51 | 52 | FixNamespaceComments: true 53 | 54 | ForEachMacros: [] 55 | TypenameMacros: [] 56 | 57 | IncludeBlocks: Preserve 58 | 59 | IndentCaseLabels: false 60 | IndentPPDirectives: None 61 | IndentWidth: 4 62 | IndentWrappedFunctionNames: false 63 | IndentFunctionDeclarationAfterType: false 64 | 65 | Language: Cpp 66 | 67 | MaxEmptyLinesToKeep: 1 68 | KeepEmptyLinesAtTheStartOfBlocks: false 69 | 70 | NamespaceIndentation: None 71 | 72 | PenaltyBreakBeforeFirstCallParameter: 1 73 | PenaltyBreakComment: 300 74 | PenaltyBreakString: 1000 75 | PenaltyBreakFirstLessLess: 120 76 | PenaltyExcessCharacter: 1000 77 | PenaltyReturnTypeOnItsOwnLine: 1000 78 | 79 | PointerAlignment: Right 80 | 81 | ReflowComments: true 82 | 83 | SortUsingDeclarations: true 84 | 85 | SpaceAfterCStyleCast: true 86 | SpaceAfterLogicalNot: false 87 | SpaceAfterTemplateKeyword: true 88 | SpaceBeforeAssignmentOperators: true 89 | SpaceBeforeCpp11BracedList: false 90 | SpaceBeforeCtorInitializerColon: true 91 | SpaceBeforeInheritanceColon: true 92 | SpaceBeforeParens: ControlStatements 93 | SpaceBeforeRangeBasedForLoopColon: true 94 | SpaceInEmptyParentheses: false 95 | SpacesBeforeTrailingComments: 2 96 | SpacesInAngles: false 97 | SpacesInCStyleCastParentheses: false 98 | SpacesInContainerLiterals: true 99 | SpacesInParentheses: false 100 | 101 | Standard: Cpp11 102 | 103 | TabWidth: 4 104 | UseTab: Never 105 | -------------------------------------------------------------------------------- /test/test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_TEST_HPP 2 | #define BITMANIP_TEST_HPP 3 | 4 | #include "assert.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace bitmanip { 10 | 11 | struct Test { 12 | public: 13 | const char *category; 14 | const char *name; 15 | 16 | explicit Test(const char *category, const char *name) : category{category}, name{name} {} 17 | virtual ~Test(); 18 | 19 | bool operator()() const noexcept 20 | { 21 | try { 22 | run(); 23 | return true; 24 | } 25 | catch (...) { 26 | return false; 27 | } 28 | } 29 | 30 | protected: 31 | virtual void run() const = 0; 32 | }; 33 | 34 | using TestConsumer = void(const Test &test); 35 | 36 | namespace detail { 37 | 38 | void registerTest(Test *test); 39 | 40 | } // namespace detail 41 | 42 | void setTestOrder(const char *const prefixes[], std::size_t count); 43 | 44 | void forEachTest(TestConsumer *action); 45 | 46 | std::size_t getTestCount(); 47 | 48 | #define BITMANIP_TEST_CLASS_NAME(category, name) Test_##category##_##name##_ 49 | 50 | #define BITMANIP_TEST(category, name) \ 51 | struct BITMANIP_TEST_CLASS_NAME(category, name) : ::bitmanip::Test { \ 52 | [[maybe_unused]] BITMANIP_TEST_CLASS_NAME(category, name)() : ::bitmanip::Test{#category, #name} {} \ 53 | void run() const final; \ 54 | }; \ 55 | static const int category##_##name##_ = \ 56 | (::bitmanip::detail::registerTest(new BITMANIP_TEST_CLASS_NAME(category, name)), 0); \ 57 | \ 58 | void BITMANIP_TEST_CLASS_NAME(category, name)::run() const 59 | 60 | #ifndef BITMANIP_DISABLE_STATIC_TESTS 61 | #define BITMANIP_STATIC_ASSERT_EQ(x, y) \ 62 | static_assert(x == y); \ 63 | BITMANIP_ASSERT_EQ(x, y) 64 | #define BITMANIP_STATIC_ASSERT(...) \ 65 | static_assert(__VA_ARGS__); \ 66 | BITMANIP_ASSERT(__VA_ARGS__) 67 | #else 68 | #define BITMANIP_STATIC_ASSERT_EQ(x, y) BITMANIP_ASSERT_EQ(x, y) 69 | #define BITMANIP_STATIC_ASSERT(...) BITMANIP_ASSERT(__VA_ARGS__) 70 | #endif 71 | 72 | // UTILITY ============================================================================================================= 73 | 74 | using fast_rng32 = std::linear_congruential_engine; 75 | using fast_rng64 = std::linear_congruential_engine; 76 | using default_rng = std::mt19937; 77 | 78 | constexpr std::uint32_t DEFAULT_SEED = 12345; 79 | 80 | inline std::uint32_t hardwareSeed() 81 | { 82 | return std::random_device{}(); 83 | } 84 | 85 | } // namespace bitmanip 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /test/test_intdiv.cpp: -------------------------------------------------------------------------------- 1 | #include "test.hpp" 2 | 3 | #include "bitmanip/intdiv.hpp" 4 | 5 | namespace bitmanip { 6 | namespace { 7 | 8 | template 9 | struct test_intdiv_tester { 10 | static constexpr T (*divFunc)(T, T) = div; 11 | 12 | template 13 | static constexpr void run() 14 | { 15 | if constexpr (den != 0) { 16 | // INT_MIN leads to unavoidable underflow for some divisions: 17 | // INT_MIN / -1 => signed integer overflow, UB 18 | constexpr bool isMinEdgeCase = std::min(T(num), T(den)) == std::numeric_limits::min(); 19 | 20 | BITMANIP_STATIC_ASSERT_EQ(divFunc(T(num), T(den)), T(quot)); 21 | 22 | if constexpr (std::is_signed_v && not isMinEdgeCase) { 23 | BITMANIP_STATIC_ASSERT_EQ(divFunc(T(num), T(-den)), T(-quot)); 24 | BITMANIP_STATIC_ASSERT_EQ(divFunc(T(-num), T(den)), T(-quot)); 25 | BITMANIP_STATIC_ASSERT_EQ(divFunc(T(-num), T(-den)), T(quot)); 26 | } 27 | } 28 | } 29 | }; 30 | 31 | template 32 | void test_intdiv_manual_norem_impl() 33 | { 34 | using tester = test_intdiv_tester; 35 | 36 | // trivial cases 37 | tester::template run<0, 1, 0>(); 38 | tester::template run<0, 1, 0>(); 39 | tester::template run<1, 1, 1>(); 40 | 41 | // division by one 42 | tester::template run<0, 1, 0>(); 43 | tester::template run<1, 1, 1>(); 44 | tester::template run<2, 1, 2>(); 45 | tester::template run<3, 1, 3>(); 46 | 47 | // division by 2 48 | tester::template run<0, 2, 0>(); 49 | tester::template run<2, 2, 1>(); 50 | tester::template run<4, 2, 2>(); 51 | tester::template run<8, 2, 4>(); 52 | tester::template run<16, 2, 8>(); 53 | tester::template run<32, 2, 16>(); 54 | tester::template run<64, 2, 32>(); 55 | 56 | // division by seven 57 | tester::template run<0, 7, 0>(); 58 | tester::template run<7, 7, 1>(); 59 | tester::template run<14, 7, 2>(); 60 | tester::template run<21, 7, 3>(); 61 | tester::template run<28, 7, 4>(); 62 | tester::template run<35, 7, 5>(); 63 | 64 | constexpr T min = std::numeric_limits::min(); 65 | constexpr T max = std::numeric_limits::max(); 66 | 67 | tester::template run<0, max, 0>(); 68 | tester::template run<0, min, 0>(); 69 | 70 | tester::template run(); 71 | tester::template run(); 72 | 73 | tester::template run(); 74 | tester::template run(); 75 | } 76 | 77 | template 78 | void test_intdiv_manual_norem() 79 | { 80 | test_intdiv_manual_norem_impl(); 81 | test_intdiv_manual_norem_impl(); 82 | test_intdiv_manual_norem_impl(); 83 | test_intdiv_manual_norem_impl(); 84 | test_intdiv_manual_norem_impl(); 85 | test_intdiv_manual_norem_impl(); 86 | } 87 | 88 | BITMANIP_TEST(traits, commonSignedType) 89 | { 90 | BITMANIP_STATIC_ASSERT(std::is_same_v>); 91 | BITMANIP_STATIC_ASSERT(std::is_same_v>); 92 | BITMANIP_STATIC_ASSERT(std::is_same_v>); 93 | BITMANIP_STATIC_ASSERT(std::is_same_v>); 94 | } 95 | 96 | BITMANIP_TEST(intdiv, manual_norem_trunc) 97 | { 98 | test_intdiv_manual_norem(); 99 | } 100 | 101 | BITMANIP_TEST(intdiv, manual_norem_floor) 102 | { 103 | test_intdiv_manual_norem(); 104 | } 105 | 106 | BITMANIP_TEST(intdiv, manual_norem_ceil) 107 | { 108 | test_intdiv_manual_norem(); 109 | } 110 | 111 | BITMANIP_TEST(intdiv, manual_norem_magnify) 112 | { 113 | test_intdiv_manual_norem(); 114 | } 115 | 116 | BITMANIP_TEST(intdiv, manual_norem_round_trunc) 117 | { 118 | test_intdiv_manual_norem(); 119 | } 120 | 121 | BITMANIP_TEST(intdiv, manual_norem_round_magnify) 122 | { 123 | test_intdiv_manual_norem(); 124 | } 125 | 126 | } // namespace 127 | } // namespace bitmanip 128 | -------------------------------------------------------------------------------- /include/bitmanip/bitcount.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_BITCOUNT_HPP 2 | #define BITMANIP_BITCOUNT_HPP 3 | 4 | #include "bit.hpp" 5 | #include "builtin.hpp" 6 | 7 | #include 8 | 9 | #ifndef BITMANIP_HAS_BUILTIN_POPCOUNT 10 | #include 11 | #endif 12 | 13 | namespace bitmanip { 14 | 15 | // CLZ AND CTZ ========================================================================================================= 16 | 17 | namespace detail { 18 | 19 | template 20 | [[nodiscard]] constexpr unsigned char countLeadingZeros_naive(Uint input) noexcept 21 | { 22 | constexpr Uint highestBit = Uint{1} << (bits_v - 1); 23 | 24 | if (input == 0) { 25 | return bits_v; 26 | } 27 | unsigned char result = 0; 28 | for (; (input & highestBit) == 0; input <<= 1) { 29 | ++result; 30 | } 31 | return result; 32 | } 33 | 34 | template 35 | [[nodiscard]] constexpr unsigned char countTrailingZeros_naive(Uint input) noexcept 36 | { 37 | if (input == 0) { 38 | return bits_v; 39 | } 40 | unsigned char result = 0; 41 | for (; (input & 1) == 0; input >>= 1) { 42 | ++result; 43 | } 44 | return result; 45 | } 46 | 47 | template 48 | [[nodiscard]] constexpr unsigned char findFirstSet_parallel(Uint input) noexcept 49 | { 50 | constexpr std::size_t iterations = log2bits_v; 51 | constexpr std::size_t resultMask = bits_v - 1; 52 | 53 | std::size_t result = bits_v; 54 | for (std::size_t i = 0, add = 1; i < iterations; ++i, add <<= 1) { 55 | bool hasBits = input & ALTERNATING_MASKS[i]; 56 | result -= hasBits * add; 57 | } 58 | 59 | return static_cast(result & resultMask); 60 | } 61 | 62 | template 63 | [[nodiscard]] constexpr unsigned char countTrailingZeros_ffs(Uint input) noexcept 64 | { 65 | Uint ffs = findFirstSet_parallel(input); 66 | return ffs == 0 ? bits_v : ffs - 1; 67 | } 68 | 69 | } // namespace detail 70 | 71 | /** 72 | * @brief Counts the number of leading zeros in a number. 73 | * The number of leading zeros in 0 is equal to the number of bits of the input type. 74 | * Example: countLeadingZeros(u8{7}) = 5 75 | */ 76 | template 77 | [[nodiscard]] constexpr unsigned char countLeadingZeros(Uint input) noexcept 78 | { 79 | #ifdef BITMANIP_HAS_BUILTIN_CLZ 80 | if (builtin::isconsteval()) { 81 | return detail::countLeadingZeros_naive(input); 82 | } 83 | return input == 0 ? bits_v : static_cast(builtin::clz(input)); 84 | #else 85 | return detail::countLeadingZeros_naive(input); 86 | #endif 87 | } 88 | 89 | /** 90 | * @brief Counts the number of trailing zeros in a number. 91 | * The number of trailing zeros in 0 is equal to the number of bits of the input type. 92 | * Example: countTrailingZeros(u8{8}) = 3 93 | */ 94 | template 95 | [[nodiscard]] constexpr unsigned char countTrailingZeros(Uint input) noexcept 96 | { 97 | #ifdef BITMANIP_HAS_BUILTIN_CTZ 98 | if (builtin::isconsteval()) { 99 | return detail::countTrailingZeros_naive(input); 100 | } 101 | return input == 0 ? bits_v : static_cast(builtin::ctz(input)); 102 | #else 103 | return detail::countTrailingZeros_naive(input); 104 | #endif 105 | } 106 | 107 | // BIT COUNTING ======================================================================================================== 108 | 109 | namespace detail { 110 | 111 | // bitset operations are not constexpr so we need a naive implementations for constexpr contexts 112 | 113 | template 114 | [[nodiscard]] constexpr unsigned char popCount_naive(Int input) noexcept 115 | { 116 | unsigned char result = 0; 117 | for (; input != 0; input >>= 1) { 118 | result += input & 1; 119 | } 120 | return result; 121 | } 122 | 123 | // This implementation is taken from https://stackoverflow.com/a/21618038 . 124 | // The core idea is to XOR the high half bits with the low half bits recursively. 125 | // In the end, the lowest bit will have been XORed with every other bit. 126 | template 127 | [[nodiscard]] constexpr bool parity_xor(Int input) noexcept 128 | { 129 | constexpr unsigned iterations = log2bits_v - 1; 130 | 131 | for (unsigned shift = 1 << iterations; shift != 0; shift >>= 1) { 132 | input ^= input >> shift; 133 | } 134 | return input & 1; 135 | } 136 | 137 | } // namespace detail 138 | 139 | template 140 | [[nodiscard]] constexpr unsigned char popCount(Uint input) noexcept 141 | { 142 | #ifdef BITMANIP_HAS_BUILTIN_POPCOUNT 143 | return builtin::isconsteval() ? detail::popCount_naive(input) 144 | : static_cast(builtin::popcount(input)); 145 | #else 146 | return detail::popCount_stdBitset(input); 147 | #endif 148 | } 149 | 150 | template 151 | [[nodiscard]] constexpr bool parity(Uint input) noexcept 152 | { 153 | #ifdef BITMANIP_HAS_BUILTIN_PARITY 154 | return builtin::isconsteval() ? detail::parity_xor(input) : builtin::parity(input); 155 | #else 156 | return static_cast(std::bitset>{input}.count()); 157 | #endif 158 | } 159 | 160 | } // namespace bitmanip 161 | 162 | #endif 163 | -------------------------------------------------------------------------------- /include/bitmanip/build.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_BUILD_HPP 2 | #define BITMANIP_BUILD_HPP 3 | /* 4 | * build.hpp 5 | * ----------- 6 | * Captures information about the build, such as the C++ standard, compiler, debug/release build, etc. 7 | * 8 | * This header does not and should never have additional includes. 9 | * See https://godbolt.org/z/xza5qY for testing. 10 | */ 11 | 12 | // C++ STANDARD DETECTION ============================================================================================== 13 | 14 | #if __cplusplus >= 199711L 15 | #define BITMANIP_CPP98_LEAST 16 | #endif 17 | #if __cplusplus >= 201103L 18 | #define BITMANIP_CPP11_LEAST 19 | #endif 20 | #if __cplusplus >= 201402L 21 | #define BITMANIP_CPP14_LEAST 22 | #endif 23 | #if __cplusplus >= 201703L 24 | #define BITMANIP_CPP17_LEAST 25 | #endif 26 | #if __cplusplus >= 202002L 27 | #define BITMANIP_CPP20_LEAST 28 | #endif 29 | 30 | #if __cplusplus == 199711L 31 | #define BITMANIP_CPP98 32 | #elif __cplusplus == 201103L 33 | #define BITMANIP_CPP11 34 | #elif __cplusplus == 201402L 35 | #define BITMANIP_CPP14 36 | #elif __cplusplus == 201703L 37 | #define BITMANIP_CPP17 38 | #elif __cplusplus == 202002L 39 | #define BITMANIP_CPP20 40 | #endif 41 | 42 | // COMPILER DETECTION ================================================================================================== 43 | 44 | #ifdef _MSC_VER 45 | #define BITMANIP_MSVC _MSC_VER 46 | #endif 47 | 48 | #ifdef __GNUC__ 49 | #define BITMANIP_GNU_OR_CLANG 50 | #endif 51 | 52 | #ifdef __clang__ 53 | #define BITMANIP_CLANG __clang_major__ 54 | #endif 55 | 56 | #if defined(__GNUC__) && !defined(__clang__) 57 | #define BITMANIP_GNU __GNUC__ 58 | #endif 59 | 60 | // Ensure that alternative operator keywords like not, and, etc. exist for MSVC (they don't by default). 61 | // In C++20, the header was removed from the standard. 62 | #if defined(BITMANIP_MSVC) && !defined(BITMANIP_CPP20_LEAST) 63 | #include 64 | #endif 65 | 66 | #ifdef BITMANIP_GNU_OR_CLANG 67 | #define BITMANIP_FWDHEADER(header) 68 | #else 69 | #define BITMANIP_FWDHEADER(header)
70 | #endif 71 | 72 | // ARCH DETECTION ====================================================================================================== 73 | 74 | #ifdef __i386__ 75 | #define BITMANIP_X86 76 | #endif 77 | 78 | #ifdef __x86_64__ 79 | #define BITMANIP_X64 80 | #endif 81 | 82 | #ifdef _M_IX86 83 | #define BITMANIP_X86 84 | #endif 85 | 86 | #ifdef _M_AMD64 87 | #define BITMANIP_X64 88 | #endif 89 | 90 | #ifdef _M_ARM64 91 | #define BITMANIP_ARM64 92 | #endif 93 | 94 | #if defined(BITMANIP_X86) || defined(BITMANIP_X64) 95 | #define BITMANIP_X86_OR_X64 96 | #endif 97 | 98 | #if defined(BITMANIP_X64) || defined(BITMANIP_ARM64) 99 | #define BITMANIP_64_BIT 100 | #endif 101 | 102 | // OS DETECTION ======================================================================================================== 103 | 104 | #ifdef __unix__ 105 | #define BITMANIP_UNIX 106 | #endif 107 | 108 | // ENDIANNES DETECTION ================================================================================================= 109 | /* 110 | * In this section, the bool constant NATIVE_ENDIAN_LITTLE is defined. 111 | * This happens either using __BYTE_ORDER__ macros or C++20's std::endian. 112 | */ 113 | 114 | #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ 115 | // C++17 ENDIANNESS DETECTION USING __BYTE__ORDER MACRO ---------------------------------------------------------------- 116 | 117 | namespace bitmanip::build { 118 | 119 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 120 | constexpr bool NATIVE_ENDIAN_LITTLE = true; 121 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 122 | constexpr bool NATIVE_ENDIAN_LITTLE = false; 123 | #elif __BYTE_ORDER == __ORDER_PDP_ENDIAN__ 124 | #error "voxelio can't compile on platforms with PDP endianness" 125 | #else 126 | #error "__BYTE_ORDER__ has unrecognized value" 127 | #endif 128 | 129 | } // namespace bitmanip::build 130 | 131 | #elif BITMANIP_CPP20_LEAST 132 | // C++20 ENDIANNESS DETECTION ------------------------------------------------------------------------------------------ 133 | #include 134 | 135 | namespace voxelio::build { 136 | 137 | constexpr bool NATIVE_ENDIAN_LITTLE = std::endian::native == std::endian::little; 138 | 139 | } // namespace voxelio::build 140 | 141 | #elif defined(BITMANIP_X86_OR_X64) 142 | // ARCHITECTURE BASED ENDIANNESS DETECTION ----------------------------------------------------------------------------- 143 | 144 | namespace voxelio::build { 145 | 146 | constexpr bool NATIVE_ENDIAN_LITTLE = true; 147 | 148 | } // namespace voxelio::build 149 | 150 | #else 151 | #error "Failed to detect platform endianness" 152 | #endif 153 | 154 | // ENDIAN ENUM ========================================================================================================= 155 | 156 | namespace bitmanip::build { 157 | 158 | /** 159 | * @brief Represents a byte order. Can be either Big Endian or Little Endian. 160 | */ 161 | enum class Endian : unsigned { 162 | /// Least significant byte first. 163 | LITTLE = 0, 164 | /// Most significant byte first. 165 | BIG = 1, 166 | NATIVE = NATIVE_ENDIAN_LITTLE ? LITTLE : BIG 167 | }; 168 | 169 | } // namespace bitmanip::build 170 | 171 | // SOURCE LOCATION ===================================================================================================== 172 | 173 | namespace bitmanip { 174 | 175 | struct SourceLocation { 176 | const char *file; 177 | const char *function; 178 | unsigned long line; 179 | }; 180 | 181 | } // namespace bitmanip 182 | 183 | #endif // BITMANIP_BUILD_HPP 184 | -------------------------------------------------------------------------------- /test/assert.cpp: -------------------------------------------------------------------------------- 1 | #include "assert.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace bitmanip { 10 | 11 | namespace { 12 | namespace ansi { 13 | 14 | #define ASCII_ESC "\x1b" 15 | 16 | constexpr const char *RESET = ASCII_ESC "[0m"; 17 | 18 | // constexpr const char *FG_16C_BLK = ASCII_ESC "[38;5;0m"; 19 | // constexpr const char *FG_16C_RED = ASCII_ESC "[38;5;1m"; 20 | constexpr const char *FG_16C_GRN = ASCII_ESC "[38;5;2m"; 21 | constexpr const char *FG_16C_ORG = ASCII_ESC "[38;5;3m"; 22 | // constexpr const char *FG_16C_BLU = ASCII_ESC "[38;5;4m"; 23 | // constexpr const char *FG_16C_MAG = ASCII_ESC "[38;5;5m"; 24 | // constexpr const char *FG_16C_CYA = ASCII_ESC "[38;5;6m"; 25 | constexpr const char *FG_16C_BRI_GRA = ASCII_ESC "[38;5;7m"; 26 | // constexpr const char *FG_16C_GRA = ASCII_ESC "[38;5;8m"; 27 | constexpr const char *FG_16C_BRI_RED = ASCII_ESC "[38;5;9m"; 28 | // constexpr const char *FG_16C_BRI_GRN = ASCII_ESC "[38;5;10m"; 29 | constexpr const char *FG_16C_YLW = ASCII_ESC "[38;5;11m"; 30 | constexpr const char *FG_16C_BRI_BLU = ASCII_ESC "[38;5;12m"; 31 | constexpr const char *FG_16C_BRI_MAG = ASCII_ESC "[38;5;13m"; 32 | // constexpr const char *FG_16C_BRI_CYA = ASCII_ESC "[38;5;14m"; 33 | // constexpr const char *FG_16C_WHT = ASCII_ESC "[38;5;15m"; 34 | 35 | } // namespace ansi 36 | 37 | // constexpr const char* ISO8601_DATETIME = "%Y-%m-%d %H:%M:%S"; 38 | constexpr const char *ISO8601_TIME = "%H:%M:%S"; 39 | 40 | std::time_t time() noexcept 41 | { 42 | std::time_t rawTime; 43 | BITMANIP_ASSERT_MSG(std::time(&rawTime) != -1, "Failed to get system time"); 44 | return rawTime; 45 | } 46 | 47 | std::tm *localtime(std::time_t time) noexcept 48 | { 49 | std::tm *timeInfo = std::localtime(&time); 50 | BITMANIP_ASSERT_NOTNULL(timeInfo); 51 | return timeInfo; 52 | } 53 | 54 | std::string currentIso8601Time() noexcept 55 | { 56 | constexpr size_t resultLength = 8; 57 | 58 | std::tm *timeInfo = localtime(time()); 59 | char buffer[resultLength + 1]; 60 | std::strftime(buffer, sizeof(buffer), ISO8601_TIME, timeInfo); 61 | 62 | return {buffer, resultLength}; 63 | } 64 | 65 | constexpr const char *prefixOf(LogLevel level) noexcept 66 | { 67 | switch (level) { 68 | case LogLevel::NONE: return ansi::FG_16C_ORG; 69 | case LogLevel::FAILURE: return ansi::FG_16C_BRI_RED; 70 | case LogLevel::ERROR: return ansi::FG_16C_BRI_RED; 71 | case LogLevel::WARNING: return ansi::FG_16C_YLW; 72 | case LogLevel::IMPORTANT: return ansi::FG_16C_GRN; 73 | case LogLevel::INFO: return ansi::FG_16C_BRI_BLU; 74 | case LogLevel::DEBUG: return ansi::FG_16C_BRI_MAG; 75 | } 76 | BITMANIP_ASSERT_UNREACHABLE(); 77 | } 78 | 79 | void logToCout(std::string_view msg) noexcept 80 | { 81 | std::cout << msg; 82 | } 83 | 84 | void flushCout() noexcept 85 | { 86 | std::cout.flush(); 87 | } 88 | 89 | void doNothing() {} 90 | 91 | [[maybe_unused]] std::string substrBeforeFirst(const std::string &str, char delimiter) 92 | { 93 | const auto pos = str.find_first_of(delimiter); 94 | return pos == std::string::npos ? str : str.substr(0, pos); 95 | } 96 | 97 | [[maybe_unused]] std::string substrBeforeLast(const std::string &str, char delimiter) 98 | { 99 | const auto pos = str.find_last_of(delimiter); 100 | return pos == std::string::npos ? str : str.substr(0, pos); 101 | } 102 | 103 | [[maybe_unused]] std::string substrAfterFirst(const std::string &str, char delimiter) 104 | { 105 | const auto pos = str.find_first_of(delimiter); 106 | return pos == std::string::npos ? str : str.substr(pos + 1, str.size()); 107 | } 108 | 109 | [[maybe_unused]] std::string substrAfterLast(const std::string &str, char delimiter) 110 | { 111 | const auto pos = str.find_last_of(delimiter); 112 | return pos == std::string::npos ? str : str.substr(pos + 1, str.size()); 113 | } 114 | 115 | } // namespace 116 | 117 | LogLevel detail::logLevel = LogLevel::INFO; 118 | LogCallback detail::logBackend = &logToCout; 119 | LogFormatter detail::logFormatter = &defaultFormat; 120 | LogFlusher detail::logFlusher = &flushCout; 121 | 122 | bool detail::isTimestampLogging = true; 123 | bool detail::isLevelLogging = true; 124 | bool detail::isSourceLogging = true; 125 | 126 | void defaultFormat(LogLevel level, SourceLocation location, const std::string_view msg[], std::size_t parts) noexcept 127 | { 128 | #ifdef BITMANIP_UNIX 129 | #define BITMANIP_IF_UNIX(code) code 130 | #define BITMANIP_IF_WINDOWS(code) 131 | #else 132 | #define BITMANIP_IF_UNIX(code) 133 | #define BITMANIP_IF_WINDOWS(code) code 134 | #endif 135 | 136 | std::string prefix; 137 | 138 | if (detail::isTimestampLogging) { 139 | prefix += "["; 140 | prefix += currentIso8601Time(); 141 | prefix += "] ["; 142 | } 143 | else { 144 | prefix += '['; 145 | } 146 | BITMANIP_IF_UNIX(prefix += prefixOf(level)); 147 | prefix += fixedWidthNameOf(level); 148 | BITMANIP_IF_UNIX(prefix += ansi::RESET); 149 | prefix += "] "; 150 | 151 | if (detail::isSourceLogging) { 152 | BITMANIP_IF_UNIX(prefix += ansi::FG_16C_BRI_GRA); 153 | prefix += substrAfterLast(location.file, BITMANIP_IF_WINDOWS('\\') BITMANIP_IF_UNIX('/')); 154 | prefix += '@'; 155 | prefix += bitmanip::stringify(location.line); 156 | prefix += ": "; 157 | BITMANIP_IF_UNIX(prefix += ansi::RESET); 158 | } 159 | 160 | logRaw(prefix); 161 | for (std::size_t i = 0; i < parts; ++i) { 162 | logRaw(msg[i]); 163 | } 164 | logRaw("\n"); 165 | } 166 | 167 | void setLogBackend(LogCallback callback) noexcept 168 | { 169 | if (callback == nullptr) { 170 | callback = &logToCout; 171 | } 172 | detail::logBackend = callback; 173 | detail::logFlusher = &doNothing; 174 | } 175 | 176 | void setLogFormatter(LogFormatter callback) noexcept 177 | { 178 | detail::logFormatter = callback == nullptr ? &defaultFormat : callback; 179 | } 180 | 181 | void setLogFlusher(LogFlusher flusher) noexcept 182 | { 183 | detail::logFlusher = flusher; 184 | } 185 | 186 | } // namespace bitmanip 187 | -------------------------------------------------------------------------------- /include/bitmanip/bitrev.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_ENDIAN_HPP 2 | #define BITMANIP_ENDIAN_HPP 3 | /* 4 | * endian.hpp 5 | * ----------- 6 | * Provides portable conversions between integers and byte arrays. 7 | * The encode() and decode() methods are the most notable functions in this header. 8 | * There are also convenience functions encodeBig(), decodeLittle(), ... 9 | * 10 | * Whenever possible, the conversion doesn't take place using bitwise operations but by using std::memcpy and reversing 11 | * the byte order using builtin::byteSwap. 12 | */ 13 | 14 | #include "bit.hpp" 15 | #include "build.hpp" 16 | #include "builtin.hpp" 17 | 18 | #include 19 | 20 | namespace bitmanip { 21 | 22 | // PORTABLE BIT/BYTE REVERSAL ========================================================================================== 23 | 24 | namespace detail { 25 | 26 | template 27 | [[nodiscard]] constexpr Int reverseBytes_naive(Int integer) noexcept 28 | { 29 | std::uint8_t octets[sizeof(Int)]{}; 30 | for (std::size_t i = 0; i < sizeof(Int); ++i) { 31 | octets[i] = static_cast(integer >> (i * 8)); 32 | } 33 | Int result = 0; 34 | for (std::size_t i = 0; i < sizeof(Int); ++i) { 35 | std::size_t shift = i * 8; 36 | result |= Int{octets[sizeof(Int) - i - 1]} << shift; 37 | } 38 | return result; 39 | } 40 | 41 | template 42 | [[nodiscard]] constexpr Int reverseBits_shift(Int integer, const unsigned bitLimit = 0) noexcept 43 | { 44 | constexpr unsigned start = log2bits_v; 45 | 46 | for (unsigned i = start; --i != (bitLimit - 1);) { 47 | Int lo = integer & ALTERNATING_MASKS[i]; 48 | Int hi = integer & ~ALTERNATING_MASKS[i]; 49 | 50 | lo <<= 1 << i; 51 | hi >>= 1 << i; 52 | 53 | integer = lo | hi; 54 | } 55 | 56 | return integer; 57 | } 58 | 59 | template 60 | [[nodiscard]] constexpr Int reverseBytes_shift(Int integer) noexcept 61 | { 62 | return reverseBits_shift(integer, 3); 63 | } 64 | 65 | } // namespace detail 66 | 67 | /** 68 | * @brief Reverses the bytes of any integer. 69 | * @param integer the integer 70 | */ 71 | template 72 | [[nodiscard]] constexpr Uint reverseBytes(Uint integer) noexcept 73 | { 74 | if constexpr (sizeof(Uint) == 1) { 75 | return integer; 76 | } 77 | else { 78 | #ifdef BITMANIP_HAS_BUILTIN_BSWAP 79 | return builtin::isconsteval() ? detail::reverseBytes_shift(integer) : builtin::bswap(integer); 80 | #else 81 | return detail::reverseBytes_shift(integer); 82 | #endif 83 | } 84 | } 85 | 86 | /** 87 | * @brief REverses teh bits of any integer. 88 | * @param integer the integer 89 | */ 90 | template 91 | [[nodiscard]] constexpr Uint reverseBits(Uint integer) noexcept 92 | { 93 | #ifdef BITMANIP_HAS_BUILTIN_BITREV 94 | return builtin::isconsteval() ? detail::reverseBits_shift(integer) : builtin::bitrev(integer); 95 | #endif 96 | return detail::reverseBits_shift(integer); 97 | } 98 | 99 | using build::Endian; 100 | 101 | // ENDIAN-CORRECT ENCODE/DECODE ======================================================================================== 102 | 103 | namespace detail { 104 | 105 | template 106 | [[nodiscard]] constexpr std::enable_if_t, Int> toNativeEndian(Int integer) 107 | { 108 | if constexpr (ENDIAN != Endian::NATIVE) { 109 | using Uint = std::make_unsigned_t; 110 | 111 | return static_cast(reverseBytes(static_cast(integer))); 112 | } 113 | else { 114 | return integer; 115 | } 116 | } 117 | 118 | } // namespace detail 119 | 120 | template 121 | [[nodiscard]] inline Int decode(const std::uint8_t buffer[sizeof(Int)]) 122 | { 123 | if constexpr (sizeof(Int) == 1) { 124 | return static_cast(buffer[0]); 125 | } 126 | else { 127 | Int result = 0; 128 | std::memcpy(&result, buffer, sizeof(Int)); 129 | return detail::toNativeEndian(result); 130 | } 131 | } 132 | 133 | template 134 | inline void encode(Int integer, std::uint8_t out[sizeof(Int)]) 135 | { 136 | if constexpr (sizeof(Int) == 1) { 137 | out[0] = static_cast(integer); 138 | } 139 | else { 140 | integer = detail::toNativeEndian(integer); 141 | std::memcpy(out, &integer, sizeof(Int)); 142 | } 143 | } 144 | 145 | // CONVENIENCE FUNCTIONS =============================================================================================== 146 | 147 | /** 148 | * @brief Convenience function that forwards to decode. 149 | */ 150 | template 151 | [[nodiscard]] constexpr Int decodeLittle(const std::uint8_t buffer[sizeof(Int)]) 152 | { 153 | return decode(buffer); 154 | } 155 | 156 | /** 157 | * @brief Convenience function that forwards to decode. 158 | */ 159 | template 160 | [[nodiscard]] constexpr Int decodeBig(const std::uint8_t buffer[sizeof(Int)]) 161 | { 162 | return decode(buffer); 163 | } 164 | 165 | /** 166 | * @brief Convenience function that forwards to decode. 167 | */ 168 | template 169 | [[nodiscard]] constexpr Int decodeNative(const std::uint8_t buffer[sizeof(Int)]) 170 | { 171 | return decode(buffer); 172 | } 173 | 174 | /** 175 | * @brief Convenience function that forwards to encode. 176 | */ 177 | template 178 | constexpr void encodeLittle(Int integer, std::uint8_t out[]) 179 | { 180 | encode(integer, out); 181 | } 182 | 183 | /** 184 | * @brief Convenience function that forwards to encode. 185 | */ 186 | template 187 | constexpr void encodeBig(Int integer, std::uint8_t out[]) 188 | { 189 | encode(integer, out); 190 | } 191 | 192 | /** 193 | * @brief Convenience function that forwards to encode. 194 | */ 195 | template 196 | constexpr void encodeNative(Int integer, std::uint8_t out[]) 197 | { 198 | encode(integer, out); 199 | } 200 | 201 | } // namespace bitmanip 202 | 203 | #endif // ENDIAN_HPP 204 | -------------------------------------------------------------------------------- /include/bitmanip/bit.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_BIT_HPP 2 | #define BITMANIP_BIT_HPP 3 | 4 | #include 5 | #include 6 | 7 | #define BITMANIP_INTEGRAL_TYPENAME(T) typename T, ::std::enable_if_t<::std::is_integral_v, int> = 0 8 | #define BITMANIP_UNSIGNED_TYPENAME(T) typename T, ::std::enable_if_t<::std::is_unsigned_v, int> = 0 9 | 10 | namespace bitmanip { 11 | 12 | // TYPE BIT COUNTS ===================================================================================================== 13 | 14 | /** 15 | * @brief templated variable which contains the number of bits for any given integer type. 16 | * Unlike std::numeric_limits::digits, this simply considers the bit count, which is different from the digit count 17 | * for signed types. 18 | * Example: bits_v = 32 19 | */ 20 | template 21 | constexpr unsigned bits_v = sizeof(Int) * 8; 22 | 23 | /** 24 | * @brief templated variable which contains the log2 of the number of bits of a type. 25 | * Example: log2bits_v = 5 26 | */ 27 | template 28 | constexpr unsigned log2bits_v = "0112222333333334"[sizeof(Int) - 1] + 3 - '0'; 29 | 30 | // TRAITS ============================================================================================================== 31 | 32 | namespace detail { 33 | 34 | template 35 | auto nextLargerUint_impl() 36 | { 37 | constexpr auto bits = bits_v; 38 | 39 | if constexpr (bits >= 64) { 40 | return std::uintmax_t{0}; 41 | } 42 | else if constexpr (bits >= 32) { 43 | return std::uint_least64_t{0}; 44 | } 45 | else if constexpr (bits >= 16) { 46 | return std::uint_least32_t{0}; 47 | } 48 | else if constexpr (bits >= 8) { 49 | return std::uint_least16_t{0}; 50 | } 51 | } 52 | 53 | } // namespace detail 54 | 55 | template 56 | using nextLargerUintType = decltype(detail::nextLargerUint_impl()); 57 | 58 | template 59 | constexpr bool areUnsigned = (std::is_unsigned_v && ...); 60 | 61 | // ALTERNATING BIT SEQUENCE ============================================================================================ 62 | 63 | /** 64 | * @brief Creates an alternating sequence of 1s and 0s, starting with 1. 65 | * 66 | * Examples: alternate(1, 2) = 0b...0101010101 67 | * alternate(1, 3) = 0b...1001001001 68 | * alternate(2, 2) = 0b...1100110011 69 | * alternate(2, 4) = 0b...1100000011 70 | * 71 | * @param period describes how many 1 and 0 bits per period should be stored, must not be zero 72 | * @param mod the total number of bits per period, before scaling by the period parameter. Only the first bit of each 73 | * unscaled period is a 1-bit. If mod == 1, then all bits are 1-bits. Must not be zero. 74 | */ 75 | template 76 | [[nodiscard]] constexpr Uint alternate(unsigned period = 1, unsigned mod = 2) noexcept 77 | { 78 | Uint result = 0; 79 | for (std::size_t i = 0; i < bits_v; ++i) { 80 | Uint bit = ((i / period) % mod) == 0; 81 | result |= bit << i; 82 | } 83 | return result; 84 | } 85 | 86 | namespace detail { 87 | 88 | template 89 | inline constexpr Uint ALTERNATING_MASKS[7] = {alternate(1, MOD), 90 | alternate(2, MOD), 91 | alternate(4, MOD), 92 | alternate(8, MOD), 93 | alternate(16, MOD), 94 | alternate(32, MOD), 95 | alternate(64, MOD)}; 96 | 97 | } // namespace detail 98 | 99 | // BIT GETTING / SETTING =============================================================================================== 100 | 101 | template 102 | [[nodiscard]] constexpr bool getBit(Uint input, unsigned index) noexcept 103 | { 104 | return (input >> index) & Uint{1}; 105 | } 106 | 107 | template 108 | [[nodiscard]] constexpr Uint clearBit(Uint input, unsigned index) noexcept 109 | { 110 | return input & ~(Uint{1} << index); 111 | } 112 | 113 | template 114 | [[nodiscard]] constexpr Uint flipBit(Uint input, unsigned index) noexcept 115 | { 116 | return input ^ (Uint{1} << index); 117 | } 118 | 119 | template 120 | [[nodiscard]] constexpr Uint setBit(Uint input, unsigned index) noexcept 121 | { 122 | return input | (Uint{1} << index); 123 | } 124 | 125 | template 126 | [[nodiscard]] constexpr Uint setBit(Uint input, unsigned index, bool value) noexcept 127 | { 128 | return clearBit(input, index) | (Uint{value} << index); 129 | } 130 | 131 | // ADVANCES SINGLE-BIT OPERATIONS ====================================================================================== 132 | 133 | template 134 | [[nodiscard]] constexpr Uint makeMask(Uint length) noexcept 135 | { 136 | return (Uint{1} << length) - Uint{1}; 137 | } 138 | 139 | template 140 | [[nodiscard]] constexpr Uint makeHighest(bool value) noexcept 141 | { 142 | return Uint{value} << (bits_v - 1); 143 | } 144 | 145 | template 146 | [[nodiscard]] constexpr Int signFill(Int input) noexcept 147 | { 148 | if constexpr (std::is_signed_v) { 149 | return input >> (bits_v - Int{1}); 150 | } 151 | else { 152 | return static_cast(signFill(static_cast>(input))); 153 | } 154 | } 155 | 156 | template 157 | [[nodiscard]] constexpr Uint isolateLsb(Uint input) noexcept 158 | { 159 | return input & -input; 160 | } 161 | 162 | template 163 | [[nodiscard]] constexpr Uint resetLsb(Uint input) noexcept 164 | { 165 | return input & (input - 1); 166 | } 167 | 168 | template 169 | [[nodiscard]] constexpr Uint hiMaskUntilLsb(Uint input) noexcept 170 | { 171 | return input ^ -input; 172 | } 173 | 174 | template 175 | [[nodiscard]] constexpr Uint loMaskUntilLsb(Uint input) noexcept 176 | { 177 | return ~(input ^ -input); 178 | } 179 | 180 | } // namespace bitmanip 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /test/test_intlog.cpp: -------------------------------------------------------------------------------- 1 | #include "bitmanip/intlog.hpp" 2 | 3 | #include "test.hpp" 4 | 5 | namespace bitmanip { 6 | namespace { 7 | 8 | template 9 | void test_log2_floor_forPow2() 10 | { 11 | for (size_t i = 1; i < 32; ++i) { 12 | BITMANIP_ASSERT_EQ(log2_floor_32(std::uint32_t{1} << i), i); 13 | } 14 | if constexpr (log2_floor_64 == nullptr) { 15 | return; 16 | } 17 | for (size_t i = 1; i < 64; ++i) { 18 | BITMANIP_ASSERT_EQ(log2_floor_64(std::uint64_t{1} << i), i); 19 | } 20 | } 21 | 22 | template 23 | void test_log2_floor_general() 24 | { 25 | for (size_t i = 4; i < 32; ++i) { 26 | std::uint32_t input = std::uint32_t{1} << i; 27 | BITMANIP_ASSERT_EQ(log2_floor_32(input), i); 28 | BITMANIP_ASSERT_EQ(log2_floor_32(input + 1), i); 29 | BITMANIP_ASSERT_EQ(log2_floor_32(input + 2), i); 30 | BITMANIP_ASSERT_EQ(log2_floor_32(input + 3), i); 31 | } 32 | if constexpr (log2_floor_64 == nullptr) { 33 | return; 34 | } 35 | for (size_t i = 4; i < 64; ++i) { 36 | std::uint64_t input = std::uint64_t{1} << i; 37 | BITMANIP_ASSERT_EQ(log2_floor_64(input), i); 38 | BITMANIP_ASSERT_EQ(log2_floor_64(input + 1), i); 39 | BITMANIP_ASSERT_EQ(log2_floor_64(input + 2), i); 40 | BITMANIP_ASSERT_EQ(log2_floor_64(input + 3), i); 41 | } 42 | } 43 | 44 | BITMANIP_TEST(intlog, log10floor_approximate_table) 45 | { 46 | auto table = detail::makeGuessTable(); 47 | BITMANIP_ASSERT_NE(detail::approximateGuessTable(table), detail::NO_APPROXIMATION); 48 | } 49 | 50 | BITMANIP_TEST(intlog, log2floor_naive_forPow2) 51 | { 52 | test_log2_floor_forPow2, log2floor_naive>(); 53 | } 54 | 55 | BITMANIP_TEST(intlog, log2floor_fast_forPow2) 56 | { 57 | test_log2_floor_forPow2, log2floor_fast>(); 58 | } 59 | 60 | BITMANIP_TEST(intlog, log2floor_debruijn_forPow2) 61 | { 62 | test_log2_floor_forPow2(); 63 | } 64 | 65 | BITMANIP_TEST(intlog, log2floor_forPow2) 66 | { 67 | test_log2_floor_forPow2, log2floor>(); 68 | } 69 | 70 | BITMANIP_TEST(intlog, log2floor_naive_general) 71 | { 72 | test_log2_floor_general, log2floor_naive>(); 73 | } 74 | 75 | BITMANIP_TEST(intlog, log2floor_fast_general) 76 | { 77 | test_log2_floor_general, log2floor_fast>(); 78 | } 79 | 80 | BITMANIP_TEST(intlog, log2floor_debruijn_general) 81 | { 82 | test_log2_floor_general(); 83 | } 84 | 85 | BITMANIP_TEST(intlog, log2floor_general) 86 | { 87 | test_log2_floor_general(); 88 | } 89 | 90 | BITMANIP_TEST(intlog, log2floor_manual) 91 | { 92 | BITMANIP_STATIC_ASSERT_EQ(log2floor(0u), 0u); 93 | BITMANIP_STATIC_ASSERT_EQ(log2floor_debruijn(0u), 0u); 94 | BITMANIP_STATIC_ASSERT_EQ(log2floor_fast(0u), 0u); 95 | BITMANIP_STATIC_ASSERT_EQ(log2floor_naive(0u), 0u); 96 | } 97 | 98 | BITMANIP_TEST(intlog, log10floor_manual) 99 | { 100 | BITMANIP_STATIC_ASSERT_EQ(log10floor(0u), 0u); 101 | BITMANIP_STATIC_ASSERT_EQ(log10floor(9u), 0u); 102 | BITMANIP_STATIC_ASSERT_EQ(log10floor(10u), 1u); 103 | BITMANIP_STATIC_ASSERT_EQ(log10floor(99u), 1u); 104 | BITMANIP_STATIC_ASSERT_EQ(log10floor(100u), 2u); 105 | BITMANIP_STATIC_ASSERT_EQ(log10floor(255u), 2u); 106 | 107 | BITMANIP_STATIC_ASSERT_EQ(log10floor(0u), 0u); 108 | BITMANIP_STATIC_ASSERT_EQ(log10floor(1u), 0u); 109 | BITMANIP_STATIC_ASSERT_EQ(log10floor(2u), 0u); 110 | BITMANIP_STATIC_ASSERT_EQ(log10floor(3u), 0u); 111 | BITMANIP_STATIC_ASSERT_EQ(log10floor(4u), 0u); 112 | BITMANIP_STATIC_ASSERT_EQ(log10floor(5u), 0u); 113 | BITMANIP_STATIC_ASSERT_EQ(log10floor(6u), 0u); 114 | BITMANIP_STATIC_ASSERT_EQ(log10floor(7u), 0u); 115 | BITMANIP_STATIC_ASSERT_EQ(log10floor(8u), 0u); 116 | BITMANIP_STATIC_ASSERT_EQ(log10floor(9u), 0u); 117 | BITMANIP_STATIC_ASSERT_EQ(log10floor(10u), 1u); 118 | BITMANIP_STATIC_ASSERT_EQ(log10floor(11u), 1u); 119 | BITMANIP_STATIC_ASSERT_EQ(log10floor(12u), 1u); 120 | BITMANIP_STATIC_ASSERT_EQ(log10floor(13u), 1u); 121 | BITMANIP_STATIC_ASSERT_EQ(log10floor(14u), 1u); 122 | BITMANIP_STATIC_ASSERT_EQ(log10floor(15u), 1u); 123 | BITMANIP_STATIC_ASSERT_EQ(log10floor(16u), 1u); 124 | BITMANIP_STATIC_ASSERT_EQ(log10floor(99u), 1u); 125 | BITMANIP_STATIC_ASSERT_EQ(log10floor(100u), 2u); 126 | BITMANIP_STATIC_ASSERT_EQ(log10floor(999u), 2u); 127 | BITMANIP_STATIC_ASSERT_EQ(log10floor(1'000u), 3u); 128 | BITMANIP_STATIC_ASSERT_EQ(log10floor(9'999u), 3u); 129 | BITMANIP_STATIC_ASSERT_EQ(log10floor(10000u), 4u); 130 | BITMANIP_STATIC_ASSERT_EQ(log10floor(99'999u), 4u); 131 | BITMANIP_STATIC_ASSERT_EQ(log10floor(100'000u), 5u); 132 | BITMANIP_STATIC_ASSERT_EQ(log10floor(999'999u), 5u); 133 | BITMANIP_STATIC_ASSERT_EQ(log10floor(1'000'000u), 6u); 134 | BITMANIP_STATIC_ASSERT_EQ(log10floor(9'999'999u), 6u); 135 | BITMANIP_STATIC_ASSERT_EQ(log10floor(10'000'000u), 7u); 136 | BITMANIP_STATIC_ASSERT_EQ(log10floor(99'999'999u), 7u); 137 | BITMANIP_STATIC_ASSERT_EQ(log10floor(100'000'000u), 8u); 138 | BITMANIP_STATIC_ASSERT_EQ(log10floor(999'999'999u), 8u); 139 | BITMANIP_STATIC_ASSERT_EQ(log10floor(1'000'000'000u), 9u); 140 | BITMANIP_STATIC_ASSERT_EQ(log10floor(2'000'000'000u), 9u); 141 | BITMANIP_STATIC_ASSERT_EQ(log10floor(4'000'000'000u), 9u); 142 | BITMANIP_STATIC_ASSERT_EQ(log10floor(std::uint32_t(~0)), 9u); 143 | 144 | BITMANIP_STATIC_ASSERT_EQ(log10floor(std::uint64_t{1} << 63), 18u); 145 | BITMANIP_STATIC_ASSERT_EQ(log10floor(9'999'999'999'999'999'999ull), 18u); 146 | BITMANIP_STATIC_ASSERT_EQ(log10floor(10'000'000'000'000'000'000ull), 19u); 147 | BITMANIP_STATIC_ASSERT_EQ(log10floor(~std::uint64_t{0}), 19u); 148 | } 149 | 150 | } // namespace 151 | } // namespace bitmanip 152 | -------------------------------------------------------------------------------- /include/bitmanip/wbits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WBITS_HPP 2 | #define WBITS_HPP 3 | 4 | #include "bits.hpp" 5 | 6 | namespace voxelio::wide { 7 | 8 | template , int> = 0> 9 | constexpr usize popCount(const Int input[], usize count) 10 | { 11 | usize result = 0; 12 | for (usize i = 0; i < count; ++i) { 13 | result += voxelio::popCount(input[i]); 14 | } 15 | return result; 16 | } 17 | 18 | template , int> = 0> 19 | constexpr void bitClear(Int dest[], usize count) 20 | { 21 | for (usize i = 0; i < count; ++i) { 22 | dest[i] = 0; 23 | } 24 | } 25 | 26 | template , int> = 0> 27 | constexpr void bitNot(Int out[], usize count) 28 | { 29 | for (usize i = 0; i < count; ++i) { 30 | out[i] = ~out[i]; 31 | } 32 | } 33 | 34 | template , int> = 0> 35 | constexpr void bitAnd(Int dest[], const Int r[], usize count) 36 | { 37 | for (usize i = 0; i < count; ++i) { 38 | dest[i] &= r[i]; 39 | } 40 | } 41 | 42 | template , int> = 0> 43 | constexpr void bitOr(Int dest[], const Int r[], usize count) 44 | { 45 | for (usize i = 0; i < count; ++i) { 46 | dest[i] |= r[i]; 47 | } 48 | } 49 | 50 | template , int> = 0> 51 | constexpr void bitXor(Int dest[], const Int r[], usize count) 52 | { 53 | for (usize i = 0; i < count; ++i) { 54 | dest[i] ^= r[i]; 55 | } 56 | } 57 | 58 | template , int> = 0> 59 | constexpr void leftShift(Int dest[], Int shift, usize count) 60 | { 61 | constexpr usize typeBits = sizeof(Int) * 8; 62 | 63 | VXIO_DEBUG_ASSERT_NE(count, 0u); 64 | 65 | for (; shift >= typeBits; shift -= typeBits) { 66 | for (usize i = count; i-- > 1;) { 67 | dest[i] = dest[i - 1]; 68 | } 69 | dest[0] = 0; 70 | } 71 | 72 | if (shift == 0) { 73 | return; 74 | } 75 | 76 | Int carryMask = ~(~Int{0} << shift); 77 | 78 | dest[count - 1] <<= shift; 79 | 80 | for (usize i = count - 1; i-- > 0;) { 81 | Int shifted = voxelio::leftRot(dest[i], static_cast(shift)); 82 | dest[i + 0] = shifted & ~carryMask; 83 | dest[i + 1] = shifted & carryMask; 84 | } 85 | } 86 | 87 | template , int> = 0> 88 | constexpr void rightShift(Int dest[], Int shift, usize count) 89 | { 90 | constexpr usize typeBits = sizeof(Int) * 8; 91 | 92 | VXIO_DEBUG_ASSERT_NE(count, 0u); 93 | 94 | for (; shift >= typeBits; shift -= typeBits) { 95 | for (usize i = 0; i < count - 1; ++i) { 96 | dest[i] = dest[i + 1]; 97 | } 98 | dest[count - 1] = 0; 99 | } 100 | 101 | if (shift == 0) { 102 | return; 103 | } 104 | 105 | Int carryMask = ~(~Int{0} >> shift); 106 | 107 | dest[0] >>= shift; 108 | 109 | for (usize i = 1; i < count; ++i) { 110 | Int shifted = voxelio::rightRot(dest[i], static_cast(shift)); 111 | dest[i - 0] = shifted & ~carryMask; 112 | dest[i - 1] = shifted & carryMask; 113 | } 114 | } 115 | 116 | template = 0> 117 | class Bits { 118 | public: 119 | using valueType = uintmax_t; 120 | 121 | private: 122 | /** the size in value types of this BitVec */ 123 | static constexpr usize size_ = divCeil(BITS, sizeof(valueType) * 8); 124 | /** the number of bits that spill into the topmost element (0 if none) */ 125 | static constexpr usize bitSpill = BITS % (sizeof(valueType) * size_); 126 | /** the bit mask of the spillage into the topmost element or 0b111...111 if there is no spillage */ 127 | static constexpr usize spillMask = ~valueType{0} << bitSpill; 128 | 129 | constexpr void fixBack() 130 | { 131 | data_[size() - 1] &= spillMask; 132 | } 133 | 134 | valueType data_[size_]; 135 | 136 | public: 137 | constexpr Bits() = default; 138 | constexpr Bits(const Bits &) = default; 139 | constexpr Bits(Bits &&) = default; 140 | 141 | constexpr Bits(valueType value) : data_{} 142 | { 143 | data_[0] = value; 144 | fixBack(); 145 | } 146 | 147 | constexpr valueType *data() 148 | { 149 | return data_; 150 | } 151 | 152 | constexpr const valueType *data() const 153 | { 154 | return data_; 155 | } 156 | 157 | constexpr usize size() const 158 | { 159 | return size_; 160 | } 161 | 162 | void clear() 163 | { 164 | for (usize i = 0; i < size(); ++i) { 165 | data_[i] = 0; 166 | } 167 | } 168 | 169 | // UNARY OPERATORS ================================================================================================= 170 | 171 | constexpr operator bool() 172 | { 173 | for (usize i = 0; i < size(); ++i) { 174 | if (data_[i]) return true; 175 | } 176 | return false; 177 | } 178 | 179 | constexpr Bits &operator~() 180 | { 181 | wide::bitNot(data(), size()); 182 | fixBack(); 183 | return *this; 184 | } 185 | 186 | // ASSIGNMENT OPERATORS ============================================================================================ 187 | 188 | constexpr Bits &operator=(const Bits &) = default; 189 | constexpr Bits &operator=(Bits &&) = default; 190 | 191 | constexpr Bits &operator=(valueType value) 192 | { 193 | clear(); 194 | data_[0] = value; 195 | return *this; 196 | } 197 | 198 | constexpr Bits &operator&=(Bits other) 199 | { 200 | wide::bitAnd(data(), other.data(), size()); 201 | return *this; 202 | } 203 | 204 | constexpr Bits &operator|=(Bits other) 205 | { 206 | wide::bitOr(data(), other.data(), size()); 207 | return *this; 208 | } 209 | 210 | constexpr Bits &operator^=(Bits other) 211 | { 212 | wide::bitXor(data(), other.data(), size()); 213 | return *this; 214 | } 215 | 216 | constexpr Bits &operator<<=(valueType shift) 217 | { 218 | wide::leftShift(data(), shift, size()); 219 | fixBack(); 220 | return *this; 221 | } 222 | 223 | constexpr Bits &operator>>=(valueType shift) 224 | { 225 | wide::rightShift(data(), shift, size()); 226 | return *this; 227 | } 228 | 229 | // BINARY OPERATORS ================================================================================================ 230 | 231 | constexpr Bits operator&(Bits other) 232 | { 233 | wide::bitAnd(other.data(), data(), size()); 234 | return other; 235 | } 236 | 237 | constexpr Bits operator|(Bits other) 238 | { 239 | wide::bitOr(other.data(), data(), size()); 240 | return other; 241 | } 242 | 243 | constexpr Bits operator^(Bits other) 244 | { 245 | wide::bitXor(other.data(), data(), size()); 246 | return other; 247 | } 248 | 249 | constexpr Bits operator<<(valueType shift) 250 | { 251 | Bits copy = *this; 252 | wide::leftShift(copy.data(), shift, size()); 253 | copy.fixBack(); 254 | return copy; 255 | } 256 | 257 | constexpr Bits operator>>(valueType shift) 258 | { 259 | Bits copy = *this; 260 | wide::rightShift(copy.data(), shift, size()); 261 | return copy; 262 | } 263 | }; 264 | 265 | template class Bits<128>; 266 | 267 | } // namespace voxelio::wide 268 | 269 | #endif // WBITS_HPP 270 | -------------------------------------------------------------------------------- /include/bitmanip/intdiv.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_INTDIV_HPP 2 | #define BITMANIP_INTDIV_HPP 3 | /* 4 | * intdiv.hpp 5 | * ----------- 6 | * Implements ceil, floor, outwards rounding modes for integer division. 7 | * - cloor is often necessary to get a consistent space downscaling. 8 | * - ceil is often necessary to get the size of a containing array of data which is not aligned to the container size 9 | */ 10 | 11 | #include "bit.hpp" 12 | 13 | #include 14 | 15 | namespace bitmanip { 16 | 17 | namespace detail { 18 | 19 | template 20 | [[nodiscard]] constexpr signed char divSgn(T n) 21 | { 22 | if constexpr (std::is_unsigned_v) { 23 | return 1; 24 | } 25 | else { 26 | return (n > T{0}) - (n < T{0}); 27 | } 28 | }; 29 | 30 | } // namespace detail 31 | 32 | enum class Rounding { 33 | /// Towards zero 34 | TRUNC, 35 | /// Away from zero 36 | MAGNIFY, 37 | /// Towards positive infinity 38 | CEIL, 39 | /// Towards negative infinity 40 | FLOOR, 41 | /// Towards the closest integer 42 | ROUND 43 | }; 44 | 45 | /** 46 | * @brief Similar to std::common_type_t, but if A or B are signed, the result will also be signed. 47 | * 48 | * This differs from the regular type promotion rules, where signed types are promoted to unsigned types. 49 | */ 50 | template && std::is_integral_v, int> = 0> 51 | using commonSignedType = std::conditional_t && std::is_unsigned_v, 52 | std::common_type_t, 53 | std::common_type_t, std::make_signed_t>>; 54 | 55 | /** 56 | * Performs a division with the usual truncating behavior of C/C++. 57 | * However, the result is the common signed type of the dividend and divisor, contrary to the usual unsigned conversion. 58 | * 59 | * @param x the dividend 60 | * @param y the divisor 61 | * @return trunc(x / y) 62 | */ 63 | template 64 | [[nodiscard]] constexpr commonSignedType divTrunc(Dividend x, Divisor y) noexcept 65 | { 66 | auto cx = static_cast>(x); 67 | auto cy = static_cast>(y); 68 | 69 | return cx / cy; 70 | } 71 | 72 | /** 73 | * Performs a division but rounds towards positive infinity. 74 | * 75 | * @param x the dividend 76 | * @param y the divisor 77 | * @return ceil(x / y) 78 | */ 79 | template 80 | [[nodiscard]] constexpr commonSignedType divCeil(Dividend x, Divisor y) noexcept 81 | { 82 | const bool quotientPositive = (x >= 0) == (y >= 0); 83 | 84 | auto cx = static_cast>(x); 85 | auto cy = static_cast>(y); 86 | 87 | return cx / cy + (cx % cy != 0 && quotientPositive); 88 | } 89 | 90 | /** 91 | * Performs a division but rounds towards negative infinity. 92 | * For positive numbers, this is equivalent to regular division. 93 | * For all numbers, this is equivalent to a floating point division and then a floor(). 94 | * Negative numbers will be decremented before division, leading to this type of rounding. 95 | * 96 | * Examples: 97 | * floor(-1/2) = floor(-0.5) = -1 98 | * floor(-2/2) = floor(-1) = -1 99 | * 100 | * This function imitates such behavior but without the use of any floating point arithmetic. 101 | * 102 | * @param x the dividend 103 | * @param y the divisor 104 | * @return floor(x / y) 105 | */ 106 | template 107 | [[nodiscard]] constexpr commonSignedType divFloor(Dividend x, Divisor y) noexcept 108 | { 109 | const bool quotientNegative = (x >= 0) != (y >= 0); 110 | 111 | auto cx = static_cast>(x); 112 | auto cy = static_cast>(y); 113 | 114 | return cx / cy - (cx % cy != 0 && quotientNegative); 115 | } 116 | 117 | /** 118 | * Performs a division but rounds away from zero. 119 | * For positive numbers, this is equivalent to ceiling division and for negative numbers, it is equivalent to flooring 120 | * division. 121 | * 122 | * Examples: 123 | * divMagnify(1,2) = 1 124 | * divMagnify(-5,10) = -1 125 | * 126 | * @param x the dividend 127 | * @param y the divisor 128 | * @param up(x / y) 129 | */ 130 | template 131 | [[nodiscard]] constexpr commonSignedType divMagnify(Dividend x, Divisor y) noexcept 132 | { 133 | signed char quotientSgn = detail::divSgn(x) * detail::divSgn(y); 134 | 135 | auto cx = static_cast>(x); 136 | auto cy = static_cast>(y); 137 | 138 | return cx / cy + (cx % cy != 0) * quotientSgn; 139 | } 140 | 141 | constexpr Rounding DEFAULT_ROUND_TIE_BREAK = Rounding::MAGNIFY; 142 | 143 | /** 144 | * Performs a division but rounds to the closest number. Ties (halfway cases) are broken with a rounding mode of choice. 145 | * UP and TRUNC are supported, where UP is the default, which matches the semantics of std::round. 146 | * 147 | * This means that by default, haflway cases like 0.5, -0.5 would be rounded to 1, -1 respectively. 148 | * 149 | * Examples: 150 | * divRound(1,2) = 1 151 | * divRound(-5,10) = -1 152 | * 153 | * @tparam TIE_BREAK the rounding mode for ties (halfway cases); limited to TRUNC and UP 154 | * @param x the dividend 155 | * @param y the divisor 156 | * @param round(x / y) 157 | */ 158 | template 159 | [[nodiscard]] constexpr commonSignedType divRound(Dividend x, Divisor y) noexcept 160 | { 161 | signed char sgnX = x < 0 ? -1 : 1; 162 | signed char sgnY = y < 0 ? -1 : 1; 163 | signed char sgnQ = sgnX * sgnY; 164 | 165 | auto cx = static_cast>(x); 166 | auto cy = static_cast>(y); 167 | 168 | auto absRemainder = cx % cy * sgnX; 169 | auto absHalfDvsor = (cy >> 1) * sgnY; 170 | 171 | bool increment = false; 172 | if constexpr (TIE_BREAK == Rounding::TRUNC) { 173 | increment = absRemainder > absHalfDvsor; 174 | } 175 | else if (TIE_BREAK == Rounding::MAGNIFY) { 176 | increment = absRemainder >= absHalfDvsor + (cy & 1); 177 | } 178 | static_assert(TIE_BREAK == Rounding::TRUNC || TIE_BREAK == Rounding::MAGNIFY, 179 | "Only TRUNC and UP rounding modes are allowed as tie breakers"); 180 | 181 | return cx / cy + increment * sgnQ; 182 | } 183 | 184 | /** 185 | * Performs a division with a rounding mode of choice. 186 | * 187 | * @tparam ROUND the rounding mode 188 | * @tparam TIE_BREAK the tie break for ROUND rounding, see divRound for limitations 189 | * @param x the dividend 190 | * @param y the divisor 191 | * @return (x / y), rounded with the chosen mode and tie break 192 | */ 193 | template 194 | [[nodiscard]] constexpr commonSignedType div(Dividend x, Divisor y) noexcept 195 | { 196 | if constexpr (ROUND == Rounding::TRUNC) { 197 | return divTrunc(x, y); 198 | } 199 | else if constexpr (ROUND == Rounding::CEIL) { 200 | return divCeil(x, y); 201 | } 202 | else if constexpr (ROUND == Rounding::FLOOR) { 203 | return divFloor(x, y); 204 | } 205 | else if constexpr (ROUND == Rounding::MAGNIFY) { 206 | return divMagnify(x, y); 207 | } 208 | else if constexpr (ROUND == Rounding::ROUND) { 209 | return divRound(x, y); 210 | } 211 | } 212 | 213 | // UNSIGNED REMAINDER (MODULUS) ======================================================================================== 214 | 215 | template && std::is_unsigned_v), int> = 0> 216 | [[nodiscard]] constexpr Uint umod(Int n, Uint mod) noexcept 217 | { 218 | if constexpr (std::is_unsigned_v) { 219 | return n % mod; 220 | } 221 | else { 222 | Uint rem = n % static_cast(mod); 223 | return static_cast(rem) + (mod & signFill(rem)); 224 | } 225 | } 226 | 227 | // MIDPOINT ============================================================================================================ 228 | 229 | template 230 | [[nodiscard]] constexpr Uint midpointFloor(Uint x, Uint y) 231 | { 232 | Uint sum = x + y; 233 | return sum >> Uint{1} | makeHighest(sum < x); 234 | } 235 | 236 | template 237 | [[nodiscard]] constexpr Uint midpointCeil(Uint x, Uint y) 238 | { 239 | return midpointFloor(x, y) + ((x + y) & Uint{1}); 240 | } 241 | 242 | template 243 | [[nodiscard]] constexpr Uint midpointToLeft(Uint x, Uint y) 244 | { 245 | return midpointFloor(x, y) + ((x + y) & Uint{1} & Uint{x > y}); 246 | } 247 | 248 | } // namespace bitmanip 249 | 250 | #endif // INTDIV_HPP 251 | -------------------------------------------------------------------------------- /include/bitmanip/wileave.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VXIO_WILEAVE_HPP 2 | #define VXIO_WILEAVE_HPP 3 | /* 4 | * wileave.hpp 5 | * ----------- 6 | * This header provides wide bit interleaving and de-interleaving functions. 7 | */ 8 | 9 | #include "ileave.hpp" 10 | 11 | namespace voxelio::wide { 12 | 13 | // WIDE INTERLEAVING =================================================================================================== 14 | 15 | template , int> = 0> 16 | constexpr void ileave_const([[maybe_unused]] const Uint inputs[], [[maybe_unused]] uint64_t outputs[]) 17 | { 18 | constexpr usize inputBytes = COUNT * sizeof(Uint); 19 | constexpr usize outputSize = voxelio::divCeil(inputBytes, sizeof(uint64_t)); 20 | // constexpr usize outputBytes = outputSize * sizeof (uint64_t); 21 | 22 | if constexpr (COUNT == 0) { 23 | return; 24 | } 25 | else if constexpr (COUNT == 1) { 26 | outputs[0] = inputs[0]; 27 | } 28 | else { 29 | static_assert(outputSize != 0); 30 | 31 | for (usize o = 0; o < outputSize; ++o) { 32 | outputs[o] = 0; 33 | } 34 | 35 | for (usize i = 0; i < COUNT; ++i) { 36 | Uint input = inputs[i]; 37 | if constexpr (outputSize == 1) { 38 | uint64_t result = ileaveZeros_const(input) << i; 39 | outputs[0] |= result; 40 | } 41 | else if constexpr (voxelio::isPow2or0(COUNT)) { 42 | constexpr usize rshift = sizeof(Uint) * 8 / outputSize; 43 | 44 | for (usize j = 0; j < outputSize; ++j) { 45 | uint64_t result = ileaveZeros_const(static_cast(input)) << i; 46 | outputs[j] |= result; 47 | input >>= rshift; 48 | } 49 | } 50 | else { 51 | // writeIndex is the index of current byte to write 52 | for (usize writeIndex = 0, nextIndex = 0; writeIndex < inputBytes; writeIndex = nextIndex) { 53 | // clang-format off 54 | nextIndex += COUNT; 55 | uint8_t nextByte = input & 0xff; // mask single byte 56 | uint64_t result = ileaveZeros_const(nextByte) << i; // perform zero-interleaving 57 | 58 | usize outputIndex = writeIndex / sizeof(uint64_t); // get index in output 59 | usize outputShift = writeIndex % sizeof(uint64_t) * 8; 60 | 61 | outputs[outputIndex] |= result << outputShift; // write result to output 62 | 63 | usize nextOutputIndex = nextIndex / sizeof(uint64_t); // get next index in output 64 | if (nextOutputIndex != outputIndex) { // detect spill 65 | usize spillIndex = nextIndex % sizeof(uint64_t); 66 | usize spillShift = (COUNT - spillIndex) * 8; 67 | outputs[nextOutputIndex] |= result >> spillShift; 68 | } 69 | 70 | input >>= 8; // rightshift next byte into place 71 | // clang-format on 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | namespace detail { 79 | 80 | template 81 | constexpr void ileave_naive(const Uint inputs[], uint64_t outputs[], usize count) 82 | { 83 | constexpr Uint singleInputBits = sizeof(Uint) * 8; 84 | constexpr Uint singleOutputBits = sizeof(uint64_t) * 8; 85 | 86 | VXIO_DEBUG_ASSERT_LE(count, 8); 87 | VXIO_ASSUME(count <= 8); 88 | 89 | if (count == 0) { 90 | return; 91 | } 92 | else if (count == 1) { 93 | outputs[0] = inputs[0]; 94 | return; 95 | } 96 | const usize outputSize = voxelio::divCeil(count * sizeof(Uint), sizeof(uint64_t)); 97 | for (usize o = 0; o < outputSize; ++o) { 98 | outputs[o] = 0; 99 | } 100 | 101 | const usize bits = count * singleInputBits; 102 | 103 | usize inputShift = 0; 104 | usize outputIndex = 0; 105 | 106 | for (usize b = 0, i = 0, o = 0; b < bits; ++b, ++i, ++o) { 107 | if (i == count) { 108 | i = 0; 109 | ++inputShift; 110 | } 111 | if (o == singleOutputBits) { 112 | o = 0; 113 | ++outputIndex; 114 | } 115 | bool bit = (inputs[i] >> inputShift) & 1; 116 | outputs[outputIndex] |= uint64_t{bit} << o; 117 | } 118 | } 119 | 120 | // alternative implementation adapting ileave_bytes_const to work with a runtime parameter 121 | template 122 | constexpr void ileave_jmp(const Uint inputs[], uint64_t outputs[], usize count) 123 | { 124 | VXIO_DEBUG_ASSERT_LE(count, 8); 125 | VXIO_ASSUME(count <= 8); 126 | 127 | switch (count) { 128 | case 0: wide::ileave_const<0>(inputs, outputs); return; 129 | case 1: wide::ileave_const<1>(inputs, outputs); return; 130 | case 2: wide::ileave_const<2>(inputs, outputs); return; 131 | case 3: wide::ileave_const<3>(inputs, outputs); return; 132 | case 4: wide::ileave_const<4>(inputs, outputs); return; 133 | case 5: wide::ileave_const<5>(inputs, outputs); return; 134 | case 6: wide::ileave_const<6>(inputs, outputs); return; 135 | case 7: wide::ileave_const<7>(inputs, outputs); return; 136 | case 8: wide::ileave_const<8>(inputs, outputs); return; 137 | } 138 | VXIO_DEBUG_ASSERT_UNREACHABLE(); 139 | } 140 | 141 | } // namespace detail 142 | 143 | template , int> = 0> 144 | constexpr void ileave(const Uint inputs[], uint64_t outputs[], usize count) 145 | { 146 | wide::detail::ileave_jmp(inputs, outputs, count); 147 | } 148 | 149 | // WIDE DEINTERLEAVING ================================================================================================= 150 | 151 | namespace detail { 152 | template 153 | constexpr void dileave_naive(const uint64_t inputs[], Uint outputs[], usize count); 154 | } 155 | 156 | template , int> = 0> 157 | constexpr void dileave_const([[maybe_unused]] const uint64_t inputs[], [[maybe_unused]] Uint outputs[]) 158 | { 159 | constexpr usize outputBytes = COUNT * sizeof(Uint); 160 | constexpr usize inputSize = voxelio::divCeil(outputBytes, sizeof(uint64_t)); 161 | // constexpr usize outputBytes = outputSize * sizeof (uint64_t); 162 | 163 | if constexpr (COUNT == 0) { 164 | return; 165 | } 166 | else if constexpr (COUNT == 1) { 167 | outputs[0] = static_cast(inputs[0]); 168 | } 169 | else { 170 | static_assert(inputSize != 0); 171 | 172 | if constexpr (inputSize == 1) { 173 | for (usize o = 0; o < COUNT; ++o) { 174 | uint64_t dileaved = remIleavedBits_const(inputs[0] >> o); 175 | outputs[o] = static_cast(dileaved); 176 | } 177 | } 178 | else if constexpr (voxelio::isPow2or0(COUNT)) { 179 | for (usize o = 0; o < COUNT; ++o) { 180 | Uint result = 0; 181 | 182 | constexpr usize lshift = sizeof(Uint) * 8 / inputSize; 183 | 184 | for (usize j = inputSize; j != 0; --j) { 185 | result <<= lshift; 186 | result |= remIleavedBits_const(inputs[j - 1] >> o); 187 | } 188 | 189 | outputs[o] = result; 190 | } 191 | } 192 | else { 193 | return wide::detail::dileave_naive(inputs, outputs, COUNT); 194 | } 195 | } 196 | } 197 | 198 | namespace detail { 199 | 200 | template 201 | constexpr void dileave_naive(const uint64_t inputs[], Uint outputs[], usize count) 202 | { 203 | constexpr Uint singleInputBits = sizeof(uint64_t) * 8; 204 | constexpr Uint singleOutputBits = sizeof(Uint) * 8; 205 | 206 | VXIO_DEBUG_ASSERT_LE(count, 8); 207 | VXIO_ASSUME(count <= 8); 208 | 209 | if (count == 0) { 210 | return; 211 | } 212 | else if (count == 1) { 213 | outputs[0] = static_cast(inputs[0]); 214 | return; 215 | } 216 | for (usize o = 0; o < count; ++o) { 217 | outputs[o] = 0; 218 | } 219 | 220 | const usize bits = count * singleOutputBits; 221 | 222 | usize inputIndex = 0; 223 | usize outputShift = 0; 224 | 225 | for (usize b = 0, i = 0, o = 0; b < bits; ++b, ++i, ++o) { 226 | if (i == singleInputBits) { 227 | i = 0; 228 | ++inputIndex; 229 | } 230 | if (o == count) { 231 | o = 0; 232 | ++outputShift; 233 | } 234 | bool bit = (inputs[inputIndex] >> i) & 1; 235 | outputs[o] |= Uint{bit} << outputShift; 236 | } 237 | } 238 | 239 | template 240 | constexpr void dileave_jmp(const uint64_t inputs[], Uint outputs[], usize count) 241 | { 242 | VXIO_DEBUG_ASSERT_LE(count, 8); 243 | VXIO_ASSUME(count <= 8); 244 | 245 | switch (count) { 246 | case 0: wide::dileave_const<0>(inputs, outputs); return; 247 | case 1: wide::dileave_const<1>(inputs, outputs); return; 248 | case 2: wide::dileave_const<2>(inputs, outputs); return; 249 | case 3: wide::dileave_const<3>(inputs, outputs); return; 250 | case 4: wide::dileave_const<4>(inputs, outputs); return; 251 | case 5: wide::dileave_const<5>(inputs, outputs); return; 252 | case 6: wide::dileave_const<6>(inputs, outputs); return; 253 | case 7: wide::dileave_const<7>(inputs, outputs); return; 254 | case 8: wide::dileave_const<8>(inputs, outputs); return; 255 | } 256 | VXIO_DEBUG_ASSERT_UNREACHABLE(); 257 | } 258 | 259 | } // namespace detail 260 | 261 | template , int> = 0> 262 | constexpr void dileave(const uint64_t inputs[], Uint outputs[], usize count) 263 | { 264 | wide::detail::dileave_jmp(inputs, outputs, count); 265 | } 266 | 267 | } // namespace voxelio::wide 268 | 269 | #endif // WILEAVE_HPP 270 | -------------------------------------------------------------------------------- /test/test_bitileave.cpp: -------------------------------------------------------------------------------- 1 | #include "bitmanip/bitileave.hpp" 2 | 3 | #include "test.hpp" 4 | 5 | namespace bitmanip { 6 | namespace { 7 | 8 | BITMANIP_TEST(bitileave, ileaveZeros_naive_manual) 9 | { 10 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xff, 0), 0xffu); 11 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xff, 1), 0b0101'0101'0101'0101u); 12 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xff, 2), 0b001001001001001001001001u); 13 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xffff'ffff, 1), 0x5555'5555'5555'5555u); 14 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xffff'ffff, 2), 0x9249'2492'4924'9249u); 15 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xffff'ffff, 3), 0x1111'1111'1111'1111u); 16 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xffff'ffff, 7), 0x0101'0101'0101'0101u); 17 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xffff'ffff, 15), 0x0001'0001'0001'0001u); 18 | BITMANIP_STATIC_ASSERT_EQ(detail::ileaveZeros_naive(0xffff'ffff, 31), 0x0000'0001'0000'0001u); 19 | 20 | BITMANIP_STATIC_ASSERT_EQ(ileaveZeros_const<4>(12345678), detail::ileaveZeros_naive(12345678, 4)); 21 | } 22 | 23 | BITMANIP_TEST(bitileave, duplBits_naive_manual) 24 | { 25 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(0xf, 0), 0u); 26 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(0xf, 2), 0xffu); 27 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(0x55, 2), 0x3333u); 28 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(0xff, 2), 0xffffu); 29 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(1, 1), 1u); 30 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(1, 2), 3u); 31 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(1, 4), 0xfu); 32 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(1, 8), 0xffu); 33 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(1, 16), 0xffffu); 34 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(1, 32), 0xffffffffu); 35 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(1, 64), 0xffffffffffffffffu); 36 | } 37 | 38 | BITMANIP_TEST(bitileave, ileaveBits_and_duplBits_manual) 39 | { 40 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 1), 1), 41 | 0x5555'5555'5555'5555u); 42 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 1), 2), 43 | 0x3333'3333'3333'3333u); 44 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 1), 4), 45 | 0x0f0f'0f0f'0f0f'0f0fu); 46 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 1), 8), 47 | 0x00ff'00ff'00ff'00ffu); 48 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 1), 16), 49 | 0x0000'ffff'0000'ffffu); 50 | 51 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 2), 1), 52 | 0x9249'2492'4924'9249u); 53 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 2), 2), 54 | 0x30C3'0C30'C30C'30C3u); 55 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 2), 4), 56 | 0xF00F'00F0'0F00'F00Fu); 57 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 2), 8), 58 | 0x00FF'0000'FF00'00FFu); 59 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 2), 16), 60 | 0xFFFF'0000'0000'FFFFu); 61 | 62 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 3), 1), 63 | 0x1111'1111'1111'1111u); 64 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 3), 2), 65 | 0x0303'0303'0303'0303u); 66 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 3), 4), 67 | 0x000f'000f'000f'000fu); 68 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 3), 8), 69 | 0x0000'00ff'0000'00ffu); 70 | BITMANIP_STATIC_ASSERT_EQ(detail::duplBits_naive(detail::ileaveZeros_naive(std::uint32_t(-1), 3), 16), 0xffffu); 71 | } 72 | 73 | BITMANIP_TEST(bitileave, remIleavedBits_naive_manual) 74 | { 75 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0xff, 0), 0xffu); 76 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0xff, 1), 0xfu); 77 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0b01010101, 1), 0b1111u); 78 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0x5555'5555'5555'5555, 1), 0xffff'ffffu); 79 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0x1111'1111'1111'1111, 3), 0b1111'1111'1111'1111u); 80 | 81 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0x5555'5555'5555'5555, 0), 0x5555'5555'5555'5555u); 82 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0x5, 1), 3u); 83 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0x5555'5555'5555'5555, 1), 0xffff'ffffu); 84 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0xffff'ffff'ffff'ffff, 1), 0xffff'ffffu); 85 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0xffff'ffff'ffff'ffff, 1), 0xffff'ffffu); 86 | BITMANIP_STATIC_ASSERT_EQ(detail::remIleavedBits_naive(0x9249'2492'4924'9249, 2), 0x3fffffu); 87 | } 88 | 89 | BITMANIP_TEST(bitileave, remIleavedBits_const_manual) 90 | { 91 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<0>(0xff), 0xffu); 92 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<1>(0xff), 0xfu); 93 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<1>(0b01010101), 0b1111u); 94 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<1>(0x5555'5555'5555'5555), 0xffff'ffffu); 95 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<3>(0x1111'1111'1111'1111), 0b1111'1111'1111'1111u); 96 | 97 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<4>(12345678), detail::remIleavedBits_naive(12345678, 4)); 98 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<8>(12345678), detail::remIleavedBits_naive(12345678, 8)); 99 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<16>(12345678), detail::remIleavedBits_naive(12345678, 16)); 100 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<32>(12345678), detail::remIleavedBits_naive(12345678, 32)); 101 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<63>(12345678), detail::remIleavedBits_naive(12345678, 63)); 102 | BITMANIP_STATIC_ASSERT_EQ(remIleavedBits_const<64>(12345678), detail::remIleavedBits_naive(12345678, 64)); 103 | } 104 | 105 | BITMANIP_TEST(bitileave, remIleavedBits_naive_matches_templated) 106 | { 107 | constexpr size_t iterations = 1024 * 8; 108 | 109 | std::mt19937 rng{12345}; 110 | std::uniform_int_distribution distr{0, std::numeric_limits::max()}; 111 | 112 | for (size_t i = 0; i < iterations; ++i) { 113 | const auto input = distr(rng); 114 | BITMANIP_ASSERT_EQ(remIleavedBits_const<1>(input), detail::remIleavedBits_naive(input, 1)); 115 | BITMANIP_ASSERT_EQ(remIleavedBits_const<2>(input), detail::remIleavedBits_naive(input, 2)); 116 | BITMANIP_ASSERT_EQ(remIleavedBits_const<3>(input), detail::remIleavedBits_naive(input, 3)); 117 | BITMANIP_ASSERT_EQ(remIleavedBits_const<4>(input), detail::remIleavedBits_naive(input, 4)); 118 | BITMANIP_ASSERT_EQ(remIleavedBits_const<5>(input), detail::remIleavedBits_naive(input, 5)); 119 | BITMANIP_ASSERT_EQ(remIleavedBits_const<6>(input), detail::remIleavedBits_naive(input, 6)); 120 | BITMANIP_ASSERT_EQ(remIleavedBits_const<7>(input), detail::remIleavedBits_naive(input, 7)); 121 | BITMANIP_ASSERT_EQ(remIleavedBits_const<8>(input), detail::remIleavedBits_naive(input, 8)); 122 | BITMANIP_ASSERT_EQ(remIleavedBits_const<9>(input), detail::remIleavedBits_naive(input, 9)); 123 | BITMANIP_ASSERT_EQ(remIleavedBits_const<10>(input), detail::remIleavedBits_naive(input, 10)); 124 | BITMANIP_ASSERT_EQ(remIleavedBits_const<11>(input), detail::remIleavedBits_naive(input, 11)); 125 | BITMANIP_ASSERT_EQ(remIleavedBits_const<12>(input), detail::remIleavedBits_naive(input, 12)); 126 | BITMANIP_ASSERT_EQ(remIleavedBits_const<13>(input), detail::remIleavedBits_naive(input, 13)); 127 | BITMANIP_ASSERT_EQ(remIleavedBits_const<14>(input), detail::remIleavedBits_naive(input, 14)); 128 | BITMANIP_ASSERT_EQ(remIleavedBits_const<15>(input), detail::remIleavedBits_naive(input, 15)); 129 | BITMANIP_ASSERT_EQ(remIleavedBits_const<16>(input), detail::remIleavedBits_naive(input, 16)); 130 | BITMANIP_ASSERT_EQ(remIleavedBits_const<32>(input), detail::remIleavedBits_naive(input, 32)); 131 | BITMANIP_ASSERT_EQ(remIleavedBits_const<63>(input), detail::remIleavedBits_naive(input, 63)); 132 | } 133 | } 134 | 135 | BITMANIP_TEST(bitileave, ileaveZeros_naive_matches_template) 136 | { 137 | constexpr size_t iterations = 1024 * 8; 138 | 139 | std::mt19937 rng{12345}; 140 | std::uniform_int_distribution distr{0, std::numeric_limits::max()}; 141 | 142 | for (size_t i = 0; i < iterations; ++i) { 143 | const auto input = distr(rng); 144 | BITMANIP_ASSERT_EQ(ileaveZeros_const<1>(input), detail::ileaveZeros_naive(input, 1)); 145 | BITMANIP_ASSERT_EQ(ileaveZeros_const<2>(input), detail::ileaveZeros_naive(input, 2)); 146 | BITMANIP_ASSERT_EQ(ileaveZeros_const<3>(input), detail::ileaveZeros_naive(input, 3)); 147 | BITMANIP_ASSERT_EQ(ileaveZeros_const<4>(input), detail::ileaveZeros_naive(input, 4)); 148 | BITMANIP_ASSERT_EQ(ileaveZeros_const<5>(input), detail::ileaveZeros_naive(input, 5)); 149 | BITMANIP_ASSERT_EQ(ileaveZeros_const<6>(input), detail::ileaveZeros_naive(input, 6)); 150 | BITMANIP_ASSERT_EQ(ileaveZeros_const<7>(input), detail::ileaveZeros_naive(input, 7)); 151 | BITMANIP_ASSERT_EQ(ileaveZeros_const<8>(input), detail::ileaveZeros_naive(input, 8)); 152 | BITMANIP_ASSERT_EQ(ileaveZeros_const<9>(input), detail::ileaveZeros_naive(input, 9)); 153 | BITMANIP_ASSERT_EQ(ileaveZeros_const<10>(input), detail::ileaveZeros_naive(input, 10)); 154 | BITMANIP_ASSERT_EQ(ileaveZeros_const<11>(input), detail::ileaveZeros_naive(input, 11)); 155 | BITMANIP_ASSERT_EQ(ileaveZeros_const<12>(input), detail::ileaveZeros_naive(input, 12)); 156 | BITMANIP_ASSERT_EQ(ileaveZeros_const<13>(input), detail::ileaveZeros_naive(input, 13)); 157 | BITMANIP_ASSERT_EQ(ileaveZeros_const<14>(input), detail::ileaveZeros_naive(input, 14)); 158 | BITMANIP_ASSERT_EQ(ileaveZeros_const<15>(input), detail::ileaveZeros_naive(input, 15)); 159 | BITMANIP_ASSERT_EQ(ileaveZeros_const<16>(input), detail::ileaveZeros_naive(input, 16)); 160 | BITMANIP_ASSERT_EQ(ileaveZeros_const<16>(input), detail::ileaveZeros_naive(input, 16)); 161 | BITMANIP_ASSERT_EQ(ileaveZeros_const<32>(input), detail::ileaveZeros_naive(input, 32)); 162 | BITMANIP_ASSERT_EQ(ileaveZeros_const<63>(input), detail::ileaveZeros_naive(input, 63)); 163 | } 164 | } 165 | 166 | BITMANIP_TEST(bitileave, ileave_manual) 167 | { 168 | BITMANIP_STATIC_ASSERT_EQ(ileave(0b1111'1111u, 0u), 0b1010'1010'1010'1010u); 169 | BITMANIP_STATIC_ASSERT_EQ(ileave(0u, 0b1'1111'1111u), 0b01'0101'0101'0101'0101u); 170 | BITMANIP_STATIC_ASSERT_EQ(ileave(0u, 0xffff'ffffu), 0x5555'5555'5555'5555u); 171 | BITMANIP_STATIC_ASSERT_EQ(ileave(0u, static_cast(ileave(0u, 0b11u))), 0b10001u); 172 | 173 | BITMANIP_STATIC_ASSERT_EQ(ileave(0u, 0u, 0b1111u), 0b001001001001u); 174 | BITMANIP_STATIC_ASSERT_EQ(ileave(0b1111u, 0u, 0u), 0b100100100100u); 175 | } 176 | 177 | BITMANIP_TEST(bitileave, ileaveBytes_manual) 178 | { 179 | for (size_t i = 0; i <= 8; ++i) { 180 | BITMANIP_ASSERT_EQ(ileaveBytes(0, i), 0u); 181 | } 182 | 183 | BITMANIP_ASSERT_EQ(ileaveBytes(0xcc, 1), 0xccu); 184 | 185 | BITMANIP_ASSERT_EQ(ileaveBytes(0xff, 2), 0x5555u); 186 | BITMANIP_ASSERT_EQ(ileaveBytes(0xff00, 2), 0xaaaau); 187 | 188 | BITMANIP_ASSERT_EQ(ileaveBytes(0x0000ff, 3), 0b001'001'001'001'001'001'001'001u << 0); 189 | BITMANIP_ASSERT_EQ(ileaveBytes(0x00ff00, 3), 0b001'001'001'001'001'001'001'001u << 1); 190 | BITMANIP_ASSERT_EQ(ileaveBytes(0xff0000, 3), 0b001'001'001'001'001'001'001'001u << 2); 191 | 192 | BITMANIP_ASSERT_EQ(ileaveBytes(0x000000ff, 8), 0x0101'0101'0101'0101u); 193 | BITMANIP_ASSERT_EQ(ileaveBytes(0x0000ff00, 8), 0x0202'0202'0202'0202u); 194 | BITMANIP_ASSERT_EQ(ileaveBytes(0x00ff0000, 8), 0x0404'0404'0404'0404u); 195 | BITMANIP_ASSERT_EQ(ileaveBytes(0xff000000, 8), 0x0808'0808'0808'0808u); 196 | 197 | BITMANIP_ASSERT_EQ(ileaveBytes(0xff000000ff, 8), 0x1111'1111'1111'1111u); 198 | } 199 | 200 | BITMANIP_TEST(bitileave, ileaveBytes_naive_matches_jmp) 201 | { 202 | constexpr size_t iterations = 1024 * 16; 203 | 204 | fast_rng64 rng{12345}; 205 | std::uniform_int_distribution distr; 206 | 207 | for (size_t i = 0; i < iterations; ++i) { 208 | std::uint64_t bytesAsInt = distr(rng); 209 | 210 | std::uint64_t jmp = detail::ileaveBytes_jmp(bytesAsInt, i % 9); 211 | std::uint64_t naive = detail::ileaveBytes_naive(bytesAsInt, i % 9); 212 | BITMANIP_ASSERT_EQ(jmp, naive); 213 | } 214 | } 215 | 216 | BITMANIP_TEST(bitileave, ileaveBytes_bitCountPreserved) 217 | { 218 | constexpr size_t iterations = 1024 * 16; 219 | 220 | fast_rng64 rng{12345}; 221 | std::uniform_int_distribution distr; 222 | 223 | for (size_t i = 0; i < iterations; ++i) { 224 | std::uint8_t count = i % 9; 225 | std::uint64_t next = distr(rng); 226 | std::uint64_t bytesAsInt = count == 0 ? 0 : next >> ((8 - count) * 8); 227 | 228 | std::uint64_t ileaved = ileaveBytes(bytesAsInt, count); 229 | BITMANIP_ASSERT_EQ(popCount(ileaved), popCount(bytesAsInt)); 230 | } 231 | } 232 | 233 | BITMANIP_TEST(bitileave, ileaveBytes_dileaveBytes_random) 234 | { 235 | constexpr size_t iterations = 1024 * 16; 236 | 237 | fast_rng64 rng{12345}; 238 | std::uniform_int_distribution distr; 239 | 240 | for (size_t i = 0; i < iterations; ++i) { 241 | std::uint8_t count = i % 9; 242 | std::uint64_t next = distr(rng); 243 | std::uint64_t bytesAsInt = count == 0 ? 0 : next >> ((8 - count) * 8); 244 | 245 | std::uint64_t ileaved = ileaveBytes(bytesAsInt, count); 246 | std::uint64_t dileaved = dileave_bytes(ileaved, count); 247 | 248 | BITMANIP_ASSERT_EQ(dileaved, bytesAsInt); 249 | } 250 | } 251 | 252 | BITMANIP_TEST(bitileave, ileave3_naive_matches_regular) 253 | { 254 | std::mt19937 rng{12345}; 255 | std::uniform_int_distribution distr{0, std::numeric_limits::max()}; 256 | 257 | for (size_t i = 0; i < 1024 * 1; ++i) { 258 | std::uint32_t v[] = {distr(rng), distr(rng), distr(rng)}; 259 | BITMANIP_ASSERT_EQ(ileave(v), detail::ileave_naive(v[0], v[1], v[2])); 260 | } 261 | } 262 | 263 | BITMANIP_TEST(bitileave, dileave3_reverses_ileave3) 264 | { 265 | std::mt19937 rng{12345}; 266 | std::uniform_int_distribution distr{0, 1u << 21}; 267 | 268 | for (size_t i = 0; i < 1024 * 1; ++i) { 269 | const auto x = distr(rng), y = distr(rng), z = distr(rng); 270 | std::uint32_t expected[] = {x, y, z}; 271 | std::uint32_t actual[3]; 272 | detail::dileave_naive(ileave(x, y, z), actual[0], actual[1], actual[2]); 273 | BITMANIP_ASSERT_EQ(actual, expected); 274 | } 275 | } 276 | 277 | } // namespace 278 | } // namespace bitmanip 279 | -------------------------------------------------------------------------------- /bitmanip.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef BITMANIP_HPP 3 | #define BITMANIP_HPP 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | // ===== USER CONFIGURATION ============================================================================================ 10 | 11 | // Uncomment the following #define if you prefer std::includes over the custom solutions provided in this section. 12 | // There are few functions that use and (of which only std::min is used), so you can save on 13 | // compilation time by using the definitions in this section. 14 | // #define BITMANIP_PREFER_STD_INCLUDES 15 | 16 | // Uncomment the following #define if you want a bitmanip:: namespace. 17 | // #define BITMANIP_PREFER_NAMESPACE 18 | 19 | // ====== CONDITIONALLY CONFIGURED CODE ================================================================================ 20 | #ifdef PREFER_STD_INCLUDES 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef BITMANIP_PREFER_NAMESPACE 26 | namespace bitmanip { 27 | #endif 28 | 29 | template 30 | using arr = std::array; 31 | 32 | using std::min; 33 | 34 | /** 35 | * @brief templated variable which contains the number of bits for any given integer type. 36 | * Example: bits_v = 32 37 | */ 38 | template , int> = 0> 39 | constexpr size_t bits_v = std::numeric_limits::digits; 40 | #else 41 | 42 | #ifdef BITMANIP_PREFER_NAMESPACE 43 | namespace bitmanip { 44 | #endif 45 | /** 46 | * @brief Simple implementation of min to avoid including the entirety of 47 | * @param x the first value 48 | * @param y the second value 49 | * @param the minimum of the values 50 | */ 51 | template 52 | constexpr T min(T x, T y) 53 | { 54 | return x < y ? x : y; 55 | } 56 | 57 | template 58 | struct arr { 59 | T data[N]; 60 | }; 61 | 62 | /** 63 | * @brief templated variable which contains the number of bits for any given integer type. 64 | * Example: bits_v = 32 65 | */ 66 | template , int> = 0> 67 | constexpr size_t bits_v = sizeof(Int) * 8; 68 | #endif 69 | // ==== UTILITY ======================================================================================================== 70 | 71 | /** 72 | * @brief Integer division but with ceiling (rounding up to the next integer) instead of rounding down as usual. 73 | * @param the dividend 74 | * @param the divisor 75 | * @param the quotient, rounded up to the nearest integer 76 | */ 77 | template ), int> = 0> 78 | constexpr Int div_ceil(Int x, Int y) 79 | { 80 | return 1 + ((x - 1) / y); 81 | } 82 | 83 | // ===== BIT MANIPULATION ============================================================================================== 84 | 85 | /** 86 | * @brief Returns whether an unsigned integer is a power of 2 or zero. 87 | * Note that this test is faster than having to test if val is a power of 2. 88 | * @param val the parameter to test 89 | * @return true if val is a power of 2 or if val is zero 90 | */ 91 | template , int> = 0> 92 | constexpr bool is_pow2_or_zero(Uint val) 93 | { 94 | return (val & (val - 1)) == 0; 95 | } 96 | 97 | /** 98 | * @brief Returns whether an unsigned integer is a power of 2. 99 | * @param val the parameter to test 100 | * @return true if val is a power of 2 101 | * @see is_pow2_or_zero 102 | */ 103 | template , int> = 0> 104 | constexpr bool is_pow2(Uint val) 105 | { 106 | return val != 0 && is_pow2_or_zero(val); 107 | } 108 | 109 | /** 110 | * @brief Naive implementation of log2 using repeated single-bit rightshifting. 111 | */ 112 | template , int> = 0> 113 | constexpr Uint log2_floor_naive(Uint val) noexcept 114 | { 115 | Uint result = 0; 116 | while (val >>= 1) { 117 | ++result; 118 | } 119 | return result; 120 | } 121 | 122 | /** 123 | * @brief Fast implementation of log2. 124 | * See https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog. 125 | * 126 | * Unrolled 32-bit version: 127 | * unsigned shift = (v > 0xFFFF) << 4; 128 | v >>= shift; 129 | r |= shift; 130 | 131 | shift = (v > 0xFF ) << 3; 132 | v >>= shift; 133 | r |= shift; 134 | 135 | shift = (v > 0xF ) << 2; 136 | v >>= shift; 137 | r |= shift; 138 | 139 | shift = (v > 0x3 ) << 1; 140 | v >>= shift; 141 | r |= shift; 142 | 143 | shift = (v > 1) << 0; 144 | r >>= shift; 145 | r |= shift; 146 | */ 147 | template , int> = 0> 148 | constexpr Uint log2_floor_fast(Uint v) noexcept 149 | { 150 | constexpr size_t iterations = log2_floor_naive(bits_v); 151 | unsigned result = 0; 152 | 153 | for (size_t i = iterations; i != 0; --i) { 154 | unsigned compBits = 1 << (i - 1); 155 | Uint compShift = Uint{1} << compBits; 156 | unsigned shift = unsigned{v >= compShift} << (i - 1); 157 | v >>= shift; 158 | result |= shift; 159 | } 160 | 161 | return result; 162 | } 163 | 164 | /** 165 | * @brief log2_floor implementation using De Bruijn multiplication. 166 | * See https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn. 167 | * @param val the value 168 | */ 169 | constexpr uint32_t log2_floor_debruijn(uint32_t val) noexcept 170 | { 171 | constexpr uint32_t MultiplyDeBruijnBitPosition[32] = {0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 172 | 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; 173 | 174 | // first round up to one less than a power of 2 175 | // this step is not necessary if val is a power of 2 176 | // Note: the link in @see says that this is rounding down, but it is actually rounding up 177 | val |= val >> 1; 178 | val |= val >> 2; 179 | val |= val >> 4; 180 | val |= val >> 8; 181 | val |= val >> 16; 182 | 183 | return MultiplyDeBruijnBitPosition[(val * uint32_t{0x07C4ACDD}) >> 27]; 184 | } 185 | 186 | /** 187 | * @brief Computes the floored binary logarithm of a given integer. 188 | * Example: log2_floor(100) = 6 189 | * 190 | * This templated function will choose the best available method depending on the type of the integer. 191 | * It may fail if a negative signed integer is given. 192 | * 193 | * @param v the value 194 | * @return the floored binary logarithm 195 | */ 196 | template , int> = 0> 197 | constexpr Int log2_floor(Int v) noexcept 198 | { 199 | if constexpr (std::is_signed_v) { 200 | using Uint = std::make_unsigned_t; 201 | DEBUG_ASSERT_GE(v, 0); 202 | return log2_floor_fast(static_cast(v)); 203 | } 204 | else { 205 | return log2_floor_fast(v); 206 | } 207 | } 208 | 209 | template <> 210 | constexpr uint32_t log2_floor(uint32_t v) noexcept 211 | { 212 | return log2_floor_debruijn(v); 213 | } 214 | 215 | /** 216 | * @brief Computes the ceiled binary logarithm of a given integer. 217 | * Example: log2_ceil(100) = 7 218 | * 219 | * This templated function will choose the best available method depending on the type of the integer. 220 | * It may fail if a negative signed integer is given. 221 | * 222 | * @param v the value 223 | * @return the floored binary logarithm 224 | */ 225 | template , int> = 0> 226 | constexpr Int log2_ceil(Int val) noexcept 227 | { 228 | const Int result = log2_floor(val); 229 | const bool inputIsntPow2 = val != (static_cast(1) << result); 230 | return result + inputIsntPow2; 231 | } 232 | 233 | /** 234 | * @brief Rounds up an unsigned integer to the next power of 2. 235 | * Powers of two are not affected. 236 | * Examples: 100 -> 128, 1 -> 1, 3 -> 4, 3000 -> 4096 237 | * @param v the value to round up 238 | */ 239 | template , int> = 0> 240 | constexpr Uint ceil_pow2(Uint v) 241 | { 242 | DEBUG_ASSERT_NE(v, 0); 243 | constexpr size_t iterations = log2_floor_naive(bits_v); 244 | // decrement is necessary so that powers of 2 don't get increased 245 | v--; 246 | for (size_t i = 0; i < iterations; ++i) { 247 | // after all iterations, all bits right to the msb will be filled with 1 248 | v |= v >> (1 << i); 249 | } 250 | // this step turns the number back to a power of two 251 | return v + 1; 252 | } 253 | 254 | /** 255 | * @brief Rounds down an unsigned integer to the next power of 2. 256 | * Powers of 2 are not affected. 257 | * Examples: 100 -> 64, 1 -> 1, 3 -> 2, 3000 -> 2048 258 | * @param v the value to round down 259 | */ 260 | template , int> = 0> 261 | constexpr Uint floor_pow2(Uint v) 262 | { 263 | DEBUG_ASSERT_NE(v, 0); 264 | constexpr size_t iterations = log2_floor_naive(bits_v); 265 | // leftshift is necessary to floor instead of ceil 266 | v >>= 1; 267 | for (size_t i = 0; i < iterations; ++i) { 268 | // after all iterations, all bits right to the msb will be filled with 1 269 | v |= v >> (1 << i); 270 | } 271 | // this step turns the number back to a power of two 272 | return v + 1; 273 | } 274 | 275 | /** 276 | * @brief Interleaves an input number with zero-bits per input bit. Example: 0b11 -> 0b0101 277 | * @param input the input number 278 | * @param bits the amount of zero bits per input bit to interleave 279 | * @return the input number interleaved with input-bits 280 | */ 281 | constexpr uint64_t ileave_zeros_naive(uint32_t input, size_t bits) 282 | { 283 | const auto lim = min(32, div_ceil(64, bits + 1)); 284 | 285 | uint64_t result = 0; 286 | for (size_t i = 0, b_out = 0; i < lim; ++i) { 287 | result |= static_cast(input & 1) << b_out; 288 | input >>= 1; 289 | b_out += bits + 1; 290 | } 291 | 292 | return result; 293 | } 294 | 295 | /** 296 | * @brief Removes each interleaved bits. Example: 0b010101 --rem 1--> 0b111 297 | * @param input the input number 298 | * @param bits input bits per output bit 299 | * @return the the output with removed bits 300 | */ 301 | constexpr uint64_t rem_ileaved_bits_naive(uint64_t input, size_t bits) noexcept 302 | { 303 | // increment once to avoid modulo divisions by 0 304 | // this way our function is noexcept and safe for all inputs 305 | ++bits; 306 | uint64_t result = 0; 307 | for (size_t i = 0, b_out = 0; i < 64; ++i) { 308 | if (i % bits == 0) { 309 | result |= (input & 1) << b_out++; 310 | } 311 | input >>= 1; 312 | } 313 | 314 | return result; 315 | } 316 | 317 | /** 318 | * @brief Duplicates each input bit times. Example: 0b101 -> 0b110011 319 | * @param input the input number 320 | * @param out_bits_per_in_bits the output bits per input bits 321 | * @return the output number or 0 if the bits parameter was zero 322 | */ 323 | constexpr uint64_t dupl_bits_naive(uint64_t input, size_t out_bits_per_in_bits) 324 | { 325 | if (out_bits_per_in_bits == 0) { 326 | return 0; 327 | } 328 | const auto lim = div_ceil(64, out_bits_per_in_bits); 329 | 330 | uint64_t result = 0; 331 | for (size_t i = 0, b_out = 0; i < lim; ++i) { 332 | for (size_t j = 0; j < out_bits_per_in_bits; ++j, ++b_out) { 333 | result |= static_cast((input >> i) & 1) << b_out; 334 | } 335 | } 336 | 337 | return result; 338 | } 339 | 340 | /** 341 | * @brief Interleaves zero-bits inbetween each input bit. 342 | * @param input the input number 343 | * @tparam BITS the number of bits to be interleaved, must be > 0 344 | * @return the input interleaved with bits 345 | */ 346 | template 347 | constexpr uint64_t ileave_zeros(uint32_t input) 348 | { 349 | if constexpr (BITS == 0) { 350 | return input; 351 | } 352 | else { 353 | constexpr uint64_t MASKS[] = { 354 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 1), 355 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 2), 356 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 4), 357 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 8), 358 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 16), 359 | }; 360 | // log2_floor(0) == 0 so this is always safe, even for 1 bit 361 | constexpr int start = 4 - (log2_floor(BITS >> 1)); 362 | 363 | uint64_t n = input; 364 | for (int i = start; i != -1; --i) { 365 | size_t shift = BITS * (1 << i); 366 | n |= n << shift; 367 | n &= MASKS[i]; 368 | } 369 | 370 | return n; 371 | } 372 | } 373 | 374 | /** 375 | * @brief Removes each interleaved bits. Example: 0b010101 --rem 1--> 0b111 376 | * If BITS is zero, no bits are removed and the input is returned. 377 | * @param input the input number 378 | * @param bits input bits per output bit 379 | * @return the the output with removed bits 380 | */ 381 | template 382 | constexpr uint64_t rem_ileaved_bits(uint64_t input) noexcept 383 | { 384 | if constexpr (BITS == 0) { 385 | return input; 386 | } 387 | else { 388 | constexpr uint64_t MASKS[] = { 389 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 1), 390 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 2), 391 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 4), 392 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 8), 393 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 16), 394 | dupl_bits_naive(ileave_zeros_naive(~uint32_t(0), BITS), 32), 395 | }; 396 | // log2_floor(0) == 0 so this is always safe, even for 1 bit 397 | constexpr size_t iterations = 5 - (log2_floor(BITS >> 1)); 398 | 399 | input &= MASKS[0]; 400 | 401 | for (size_t i = 0; i < iterations; ++i) { 402 | size_t rshift = (1 << i) * BITS; 403 | input |= input >> rshift; 404 | input &= MASKS[i + 1]; 405 | } 406 | 407 | return input; 408 | } 409 | } 410 | 411 | /** 412 | * @brief Interleaves 2 integers, where hi comprises the upper bits of each bit pair and lo the lower bits. 413 | * Examle: ileave2(0b111, 0b000) = 0b101010 414 | * 415 | * This is also referred to as a Morton Code in scientific literature. 416 | * 417 | * @param hi the high bits 418 | * @param lo the low bits 419 | * @return the interleaved bits 420 | */ 421 | constexpr uint64_t ileave2(uint32_t hi, uint32_t lo) 422 | { // TODO implement like ileav3 423 | constexpr uint64_t MASKS[] = {0x5555'5555'5555'5555, 424 | 0x3333'3333'3333'3333, 425 | 0x0F0F'0F0F'0F0F'0F0F, 426 | 0x00FF'00FF'00FF'00FF, 427 | 0x0000'FFFF'0000'FFFF}; 428 | 429 | uint64_t result = 0; 430 | uint32_t *nums[] = {&hi, &lo}; 431 | 432 | for (size_t i = 0; i < 2; ++i) { 433 | uint64_t n = *nums[i]; 434 | for (int i = 4; i != -1; --i) { 435 | n |= n << (1 << i); 436 | n &= MASKS[i]; 437 | } 438 | result |= n << (1 - i); 439 | } 440 | 441 | return result; 442 | } 443 | 444 | /** 445 | * @brief Interleaves 3 integers, where x comprises the uppermost bits of each bit triple and z the lowermost bits. 446 | * 447 | * This is also referred to as a Morton Code in scientific literature. 448 | * 449 | * @param x the highest bits 450 | * @param y the middle bits 451 | * @param z the lowest bits 452 | * @return the interleaved bits 453 | */ 454 | constexpr uint64_t ileave3(uint32_t x, uint32_t y, uint32_t z) 455 | { 456 | return (ileave_zeros<2>(x) << 2) | (ileave_zeros<2>(y) << 1) | ileave_zeros<2>(z); 457 | } 458 | 459 | /** 460 | * @brief Deinterleaves 3 integers which are interleaved in a single number. 461 | * Visualization: abcdefghi -> (adg, beh, cfi) 462 | * 463 | * This is also referred to as a Morton Code in scientific literature. 464 | * 465 | * @param n the number 466 | * @return the interleaved bits 467 | */ 468 | constexpr arr dileave3(uint64_t n) 469 | { 470 | uint32_t x = static_cast(rem_ileaved_bits<2>(n >> 2)); 471 | uint32_t y = static_cast(rem_ileaved_bits<2>(n >> 1)); 472 | uint32_t z = static_cast(rem_ileaved_bits<2>(n >> 0)); 473 | return {x, y, z}; 474 | } 475 | 476 | #ifdef BITMANIP_PREFER_NAMESPACE 477 | } 478 | #endif 479 | 480 | #endif // BITMANIP_HPP 481 | -------------------------------------------------------------------------------- /test/assert.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_ASSERT_HPP 2 | #define BITMANIP_ASSERT_HPP 3 | 4 | #include "bitmanip/build.hpp" 5 | #include "bitmanip/builtin.hpp" 6 | 7 | #include 8 | 9 | namespace bitmanip { 10 | 11 | namespace detail { 12 | 13 | template 14 | auto isStaticCastable_impl(int) -> decltype(static_cast(std::declval())); 15 | 16 | template 17 | auto isStaticCastable_impl(...) -> void; 18 | 19 | template 20 | constexpr bool isStaticCastable = not std::is_same_v(0)), void>; 21 | 22 | template 23 | auto stringify_impl(const T &t) 24 | { 25 | if constexpr (std::is_same_v) { 26 | return std::string{t ? "true" : "false"}; 27 | } 28 | else if constexpr (std::is_same_v) { 29 | return std::string{"nullptr"}; 30 | } 31 | else if constexpr (std::is_integral_v) { 32 | return std::to_string(t); 33 | } 34 | else if constexpr (std::is_floating_point_v) { 35 | return std::to_string(t); 36 | } 37 | else if constexpr (std::is_enum_v) { 38 | return std::to_string(static_cast>(t)); 39 | } 40 | else if constexpr (std::is_same_v) { 41 | return t; 42 | } 43 | else if constexpr (std::is_same_v) { 44 | return std::string{t}; 45 | } 46 | else if constexpr (std::is_same_v) { 47 | return std::string{t}; 48 | } 49 | else if constexpr (std::is_pointer_v) { 50 | return std::to_string(reinterpret_cast(t)); 51 | } 52 | else if constexpr (detail::isStaticCastable) { 53 | return static_cast(t); 54 | } 55 | } 56 | 57 | template 58 | auto stringify_impl(const T (&arr)[N]) 59 | { 60 | std::string result = "{" + stringify_impl(arr[0]); 61 | result.reserve((result.length() + 1) * N + 1); 62 | 63 | for (std::size_t i = 1; i < N; ++i) { 64 | result += ", "; 65 | result += stringify_impl(arr[i]); 66 | } 67 | 68 | return result + '}'; 69 | } 70 | 71 | } // namespace detail 72 | 73 | template 74 | constexpr bool isStringifieable = not std::is_same_v()))>; 75 | 76 | /** 77 | * @brief Stringifies any type. If the type is not stringifieable, substitution fails. 78 | * - bools are stringified as "true" or "false" 79 | * - std::nullptr_t is stringified as "nullptr" 80 | * - integers are stringified in decimal 81 | * - floats are stringified in decimal 82 | * - enums are stringified like their underlying type 83 | * - const char*, std::string and std::string_view are returned directly 84 | * - anything explicitly/implicitly convertible to std::string is converted 85 | * - pointer types are stringified in hexadecimal 86 | * - everything else is stringified using stream operator 87 | * @tparam the type 88 | * @param the value to stringify 89 | */ 90 | template 91 | auto stringify(const T &t) noexcept -> std::enable_if_t, std::string> 92 | { 93 | return detail::stringify_impl(t); 94 | } 95 | 96 | enum class LogLevel : unsigned { 97 | /** No messages get logged at this level. Use this as a log level to completely disable logging. */ 98 | NONE, 99 | /** Signals an imminent program failure. (failed assertion, thread crash, hardware errors) */ 100 | FAILURE, 101 | /** Signals that an error has occured but the program can continue (failed HTTP connection, I/O eror, etc.) */ 102 | ERROR, 103 | /** Warns the user about unusual behavior. (could not clear some data, had to retry some operation, etc.) */ 104 | WARNING, 105 | /** Important messages, more relevant than regular info messages. */ 106 | IMPORTANT, 107 | /** Default level on release builds. Used for general messages. */ 108 | INFO, 109 | /** Default level on debug builds. Used for messages that are only relevant to the developer. */ 110 | DEBUG 111 | }; 112 | 113 | constexpr const char *nameOf(LogLevel level) noexcept 114 | { 115 | switch (level) { 116 | case LogLevel::NONE: return "NONE"; 117 | case LogLevel::FAILURE: return "FAILURE"; 118 | case LogLevel::ERROR: return "ERROR"; 119 | case LogLevel::WARNING: return "WARNING"; 120 | case LogLevel::IMPORTANT: return "IMPORTANT"; 121 | case LogLevel::INFO: return "INFO"; 122 | case LogLevel::DEBUG: return "DEBUG"; 123 | } 124 | BITMANIP_UNREACHABLE(); 125 | } 126 | 127 | constexpr const char *fixedWidthNameOf(LogLevel level) noexcept 128 | { 129 | switch (level) { 130 | case LogLevel::NONE: return "NONE"; 131 | case LogLevel::FAILURE: return "FAIL"; 132 | case LogLevel::ERROR: return "EROR"; 133 | case LogLevel::WARNING: return "WARN"; 134 | case LogLevel::IMPORTANT: return "IMPO"; 135 | case LogLevel::INFO: return "INFO"; 136 | case LogLevel::DEBUG: return "DBUG"; 137 | } 138 | BITMANIP_UNREACHABLE(); 139 | } 140 | 141 | constexpr bool operator<=(LogLevel x, LogLevel y) noexcept 142 | { 143 | return static_cast(x) <= static_cast(y); 144 | } 145 | 146 | constexpr bool operator<(LogLevel x, LogLevel y) noexcept 147 | { 148 | return static_cast(x) < static_cast(y); 149 | } 150 | 151 | constexpr bool operator>=(LogLevel x, LogLevel y) noexcept 152 | { 153 | return static_cast(x) >= static_cast(y); 154 | } 155 | 156 | constexpr bool operator>(LogLevel x, LogLevel y) noexcept 157 | { 158 | return static_cast(x) > static_cast(y); 159 | } 160 | 161 | // CONFIGURATION ======================================================================================================= 162 | 163 | /// The function pointer type of the logging callback. 164 | using LogCallback = void (*)(std::string_view); 165 | /// The function pointer type of the logging formatter. 166 | using LogFormatter = void (*)(LogLevel, SourceLocation, const std::string_view[], std::size_t); 167 | /// The function pointer type of the logging flusher. 168 | using LogFlusher = void (*)(); 169 | 170 | namespace detail { 171 | 172 | extern LogLevel logLevel; 173 | extern LogCallback logBackend; 174 | extern LogFormatter logFormatter; 175 | extern LogFlusher logFlusher; 176 | 177 | extern bool isTimestampLogging; 178 | extern bool isLevelLogging; 179 | extern bool isSourceLogging; 180 | 181 | } // namespace detail 182 | 183 | /** 184 | * @brief Sets the logging backend callback for voxelio. 185 | * By default, voxelio logs to std::cout. 186 | * This behavior can be changed using this function. 187 | * 188 | * If the async flag is set, voxelio will automatically wrap the callback in a mutex-guarded logging function. 189 | * This way the callback will be thread-safe, even if it doesn't contain any locking code itself. 190 | * 191 | * @param callback the callback or nullptr if the behavior should be reset to default 192 | * @param async true if voxelio should automatically ensure thread-safety using a mutex 193 | */ 194 | void setLogBackend(LogCallback callback) noexcept; 195 | 196 | /** 197 | * @brief Sets the logging formatter to the given function or resets it to default if the pointer is nullptr. 198 | * The formatter should format messages and metadata and then pass it on to the backend using logRaw(). 199 | * @param formatter the formatter 200 | */ 201 | void setLogFormatter(LogFormatter formatter) noexcept; 202 | 203 | void setLogFlusher(LogFlusher flusher) noexcept; 204 | 205 | inline LogLevel getLogLevel() noexcept 206 | { 207 | return detail::logLevel; 208 | } 209 | 210 | inline void setLogLevel(LogLevel level) noexcept 211 | { 212 | detail::logLevel = level; 213 | } 214 | 215 | inline bool isLoggable(LogLevel level) noexcept 216 | { 217 | return level <= detail::logLevel; 218 | } 219 | 220 | inline void enableLoggingTimestamp(bool enable) noexcept 221 | { 222 | detail::isTimestampLogging = enable; 223 | } 224 | 225 | inline void enableLoggingLevel(bool enable) noexcept 226 | { 227 | detail::isLevelLogging = enable; 228 | } 229 | 230 | inline void enableLoggingSourceLocation(bool enable) noexcept 231 | { 232 | detail::isSourceLogging = enable; 233 | } 234 | 235 | // LOGGING FUNCTIONS =================================================================================================== 236 | 237 | /// Directly invokes the logging callback. 238 | inline void logRaw(std::string_view str) noexcept 239 | { 240 | detail::logBackend(str); 241 | } 242 | 243 | inline void log(LogLevel level, SourceLocation location, const std::string_view msg[], std::size_t parts) noexcept 244 | { 245 | detail::logFormatter(level, location, msg, parts); 246 | } 247 | 248 | inline void flushLog() noexcept 249 | { 250 | detail::logFlusher(); 251 | } 252 | 253 | /// The default format function. Invoking this bypasses the logFormatter and goes straight to the backend. 254 | void defaultFormat(LogLevel level, SourceLocation location, const std::string_view msg[], std::size_t parts) noexcept; 255 | 256 | namespace detail { 257 | 258 | template 259 | auto stringifyOrView(const T &arg) 260 | { 261 | if constexpr (isStaticCastable) { 262 | return static_cast(arg); 263 | } 264 | else { 265 | return stringify(arg); 266 | } 267 | } 268 | 269 | template && ...), int> = 0> 270 | void vlog(LogLevel level, SourceLocation loc, const Args &... args) 271 | { 272 | constexpr std::size_t argCount = sizeof...(Args); 273 | 274 | if constexpr (not(isStaticCastable && ...)) { 275 | vlog(level, loc, stringifyOrView(args)...); 276 | } 277 | else { 278 | std::string_view msg[argCount]{static_cast(args)...}; 279 | log(level, loc, msg, argCount); 280 | } 281 | } 282 | 283 | } // namespace detail 284 | 285 | #ifdef BITMANIP_DEBUG 286 | 287 | // clang-format off 288 | #define BITMANIP_LOG_SPAM_GUARD_BEGIN(level) do { \ 289 | if constexpr (::bitmanip::LogLevel::level < ::bitmanip::LogLevel::SPAM) { 290 | 291 | #define BITMANIP_LOG_SPAM_GUARD_END ; } } while (false) 292 | // clang-format on 293 | 294 | #else 295 | #define BITMANIP_LOG_SPAM_GUARD_BEGIN(level) ( 296 | #define BITMANIP_LOG_SPAM_GUARD_END ) 297 | #endif 298 | 299 | #define BITMANIP_LOG_IMPL(level, file, function, line, ...) \ 300 | BITMANIP_LOG_SPAM_GUARD_BEGIN(level) \ 301 | ::bitmanip::isLoggable(::bitmanip::LogLevel::level) \ 302 | ? ::bitmanip::detail::vlog(::bitmanip::LogLevel::level, {(file), (function), (line)}, __VA_ARGS__) \ 303 | : void(0) BITMANIP_LOG_SPAM_GUARD_END 304 | 305 | #define BITMANIP_LOG(level, ...) BITMANIP_LOG_IMPL(level, __FILE__, __func__, __LINE__, __VA_ARGS__) 306 | 307 | // ASSERTIONS ========================================================================================================== 308 | 309 | [[noreturn]] inline void assertFail(const std::string_view msg, SourceLocation loc) noexcept(false) 310 | { 311 | BITMANIP_LOG_IMPL(FAILURE, loc.file, loc.function, loc.line, "assertion error in ", loc.function, "(): ", msg); 312 | flushLog(); 313 | throw 0; 314 | } 315 | 316 | namespace detail { 317 | 318 | enum class Cmp { EQ, NE, GT, LT, GE, LE, _ }; 319 | 320 | constexpr bool eq(Cmp a, Cmp b) 321 | { 322 | return static_cast(a) == static_cast(b); 323 | } 324 | constexpr Cmp operator==(Cmp, Cmp) 325 | { 326 | return Cmp::EQ; 327 | } 328 | constexpr Cmp operator!=(Cmp, Cmp) 329 | { 330 | return Cmp::NE; 331 | } 332 | constexpr Cmp operator>(Cmp, Cmp) 333 | { 334 | return Cmp::GT; 335 | } 336 | constexpr Cmp operator<(Cmp, Cmp) 337 | { 338 | return Cmp::LT; 339 | } 340 | constexpr Cmp operator>=(Cmp, Cmp) 341 | { 342 | return Cmp::GE; 343 | } 344 | constexpr Cmp operator<=(Cmp, Cmp) 345 | { 346 | return Cmp::LE; 347 | } 348 | 349 | template 350 | constexpr bool cmp(const L &l, const R &r) noexcept 351 | { 352 | if constexpr (eq(Cmp, Cmp::EQ)) 353 | return l == r; 354 | else if constexpr (eq(Cmp, Cmp::NE)) 355 | return l != r; 356 | else if constexpr (eq(Cmp, Cmp::GT)) 357 | return l > r; 358 | else if constexpr (eq(Cmp, Cmp::LT)) 359 | return l < r; 360 | else if constexpr (eq(Cmp, Cmp::GE)) 361 | return l >= r; 362 | else if constexpr (eq(Cmp, Cmp::LE)) 363 | return l <= r; 364 | } 365 | 366 | template 367 | constexpr bool cmp(const L (&l)[N], const R (&r)[N]) noexcept 368 | { 369 | for (std::size_t i = 0; i < N; ++i) { 370 | if (not cmp(l[i], r[i])) { 371 | return false; 372 | } 373 | } 374 | return true; 375 | } 376 | 377 | } // namespace detail 378 | 379 | // The following definitions using ternary operators and the do ... while(false) pattern circumvent possible syntax 380 | // breaks in if-else chains. 381 | // 382 | // For example, 383 | // if (...) ASSERT(...); else ...; 384 | // 385 | // ... would break if ASSERT was using an if-statement or regular code-block, because the following semicolon is then an 386 | // additional statement, disconnecting the else-statement. 387 | #define BITMANIP_ASSERT_IMPL(expr, msg) \ 388 | (static_cast(expr) ? void(0) : ::bitmanip::assertFail(msg, {__FILE__, __func__, __LINE__})) 389 | 390 | #define BITMANIP_ASSERT_CMP(l, r, op) \ 391 | do { \ 392 | auto &&tl_ = (l); \ 393 | auto &&tr_ = (r); \ 394 | constexpr auto cmp_ = ::bitmanip::detail::Cmp::_ op ::bitmanip::detail::Cmp::_; \ 395 | BITMANIP_ASSERT_IMPL(::bitmanip::detail::cmp(tl_, tr_), \ 396 | "Comparison failed: " #l " " #op " " #r " (with \"" #l "\"=" + \ 397 | ::bitmanip::stringify(tl_) + ", \"" #r "\"=" + ::bitmanip::stringify(tr_) + ")"); \ 398 | } while (false) 399 | 400 | #define BITMANIP_ASSERT_CONSEQUENCE(l, r) \ 401 | do { \ 402 | auto &&tl_ = (l); \ 403 | auto &&tr_ = (r); \ 404 | BITMANIP_ASSERT_IMPL(!tl_ || tr_, \ 405 | "Consequence failed: " #l " => " #r " (with \"" #l "\"=" + ::bitmanip::stringify(tl_) + \ 406 | ", \"" #r "\"=" + ::bitmanip::stringify(tr_) + ")"); \ 407 | } while (false) 408 | 409 | #define BITMANIP_ASSERT_DIVISIBLE(l, r) \ 410 | do { \ 411 | auto &&tl_ = (l); \ 412 | auto &&tr_ = (r); \ 413 | BITMANIP_ASSERT_IMPL(tl_ % tr_ == 0, \ 414 | "Divisibility failed: " #l " / " #r " (with \"" #l "\"=" + ::bitmanip::stringify(tl_) + \ 415 | ", \"" #r "\"=" + ::bitmanip::stringify(tr_) + ")"); \ 416 | } while (false) 417 | 418 | // COMMON ASSERTS (ALWAYS DEFINED THE SAME) ============================================================================ 419 | 420 | #define BITMANIP_ASSERT(...) BITMANIP_ASSERT_IMPL((__VA_ARGS__), "\"" #__VA_ARGS__ "\" evaluated to false") 421 | 422 | #define BITMANIP_ASSERT_MSG(expr, msg) BITMANIP_ASSERT_IMPL(expr, '"' + ::std::string{msg} + '"') 423 | #define BITMANIP_ASSERT_FAIL(...) BITMANIP_ASSERT_MSG(false, __VA_ARGS__) 424 | #define BITMANIP_ASSERT_UNREACHABLE() BITMANIP_ASSERT_FAIL("This execution path must be unreachable") 425 | 426 | #define BITMANIP_ASSERT_NOTNULL(...) BITMANIP_ASSERT_IMPL((__VA_ARGS__) != nullptr, #__VA_ARGS__ " must never be null") 427 | #define BITMANIP_ASSERT_NULL(...) BITMANIP_ASSERT_IMPL((__VA_ARGS__) == nullptr, #__VA_ARGS__ " must always be null") 428 | #define BITMANIP_ASSERT_EQ(l, r) BITMANIP_ASSERT_CMP(l, r, ==) 429 | #define BITMANIP_ASSERT_NE(l, r) BITMANIP_ASSERT_CMP(l, r, !=) 430 | #define BITMANIP_ASSERT_LT(l, r) BITMANIP_ASSERT_CMP(l, r, <) 431 | #define BITMANIP_ASSERT_LE(l, r) BITMANIP_ASSERT_CMP(l, r, <=) 432 | #define BITMANIP_ASSERT_GT(l, r) BITMANIP_ASSERT_CMP(l, r, >) 433 | #define BITMANIP_ASSERT_GE(l, r) BITMANIP_ASSERT_CMP(l, r, >=) 434 | 435 | // ===================================================================================================================== 436 | 437 | } // namespace bitmanip 438 | 439 | #endif // ASSERT_HPP 440 | -------------------------------------------------------------------------------- /include/bitmanip/bitileave.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ILEAVE_HPP 2 | #define ILEAVE_HPP 3 | /* 4 | * ileave.hpp 5 | * ----------- 6 | * Provides bit-interleaving and bit-de-interleaving functionality. 7 | * Each function typically has multiple implementations in detail:: namespaces, which are then chosen in a single 8 | * function in bitmanip:: 9 | */ 10 | 11 | #include "bit.hpp" 12 | #include "intdiv.hpp" 13 | #include "intlog.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace bitmanip { 20 | namespace detail { 21 | 22 | // BIT DUPLICATION ===================================================================================================== 23 | 24 | /** 25 | * @brief Duplicates each input bit times. Example: 0b101 -> 0b110011 26 | * @param input the input number 27 | * @param out_bits_per_in_bits the output bits per input bits 28 | * @return the output number or 0 if the bits parameter was zero 29 | */ 30 | [[nodiscard]] constexpr std::uint64_t duplBits_naive(std::uint64_t input, std::size_t out_bits_per_in_bits) noexcept 31 | { 32 | if (out_bits_per_in_bits == 0) { 33 | return 0; 34 | } 35 | const auto lim = bitmanip::divCeil(64, out_bits_per_in_bits); 36 | 37 | std::uint64_t result = 0; 38 | for (std::size_t i = 0, b_out = 0; i < lim; ++i) { 39 | for (std::size_t j = 0; j < out_bits_per_in_bits; ++j, ++b_out) { 40 | result |= static_cast((input >> i) & 1) << b_out; 41 | } 42 | } 43 | 44 | return result; 45 | } 46 | 47 | // ZERO-BIT INTERLEAVING =============================================================================================== 48 | 49 | /** 50 | * @brief Interleaves an input number with zero-bits per input bit. Example: 0b11 -> 0b0101 51 | * @param input the input number 52 | * @param bits the amount of zero bits per input bit to interleave 53 | * @return the input number interleaved with input-bits 54 | */ 55 | [[nodiscard]] constexpr std::uint64_t ileaveZeros_naive(std::uint32_t input, std::size_t bits) noexcept 56 | { 57 | const auto lim = std::min(32, bitmanip::divCeil(64, bits + 1)); 58 | 59 | std::uint64_t result = 0; 60 | for (std::size_t i = 0, b_out = 0; i < lim; ++i) { 61 | result |= static_cast(input & 1) << b_out; 62 | input >>= 1; 63 | b_out += bits + 1; 64 | } 65 | 66 | return result; 67 | } 68 | 69 | template 70 | [[nodiscard]] constexpr std::uint64_t ileaveZeros_shift(std::uint32_t input) noexcept 71 | { 72 | if constexpr (BITS == 0) { 73 | return input; 74 | } 75 | else { 76 | constexpr std::uint64_t MASKS[] = { 77 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 1), 78 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 2), 79 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 4), 80 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 8), 81 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 16), 82 | }; 83 | // log2_floor(0) == 0 so this is always safe, even for 1 bit 84 | constexpr int start = 4 - static_cast(bitmanip::log2floor(BITS >> 1)); 85 | 86 | std::uint64_t n = input; 87 | for (int i = start; i != -1; --i) { 88 | unsigned shift = BITS * (1u << i); 89 | n |= n << shift; 90 | n &= MASKS[i]; 91 | } 92 | 93 | return n; 94 | } 95 | } 96 | 97 | #ifdef BITMANIP_HAS_BUILTIN_PDEP 98 | #define BITMANIP_HAS_BUILTIN_ILEAVE_ZEROS 99 | template 100 | [[nodiscard]] inline std::uint64_t ileaveZeros_builtin(std::uint32_t input) noexcept 101 | { 102 | constexpr std::uint64_t mask = detail::ileaveZeros_naive(~std::uint32_t(0), BITS) << SHIFT; 103 | return builtin::depositBits(std::uint64_t{input}, mask); 104 | } 105 | #endif 106 | 107 | } // namespace detail 108 | 109 | /** 110 | * @brief Interleaves BITS zero-bits inbetween each input bit and optionally leftshifts the result by SHIFT bits. 111 | * 112 | * SHIFT is an additional parameter because left-shifting is a no-op when the builtin implementation is available. 113 | * @param input the input number 114 | * @tparam BITS the number of bits to be interleaved or zero for an identity mapping 115 | * @tparam SHIFT the number of bits to left-shift the input by after interleaving zeros 116 | * @return the input interleaved with bits 117 | */ 118 | template 119 | [[nodiscard]] constexpr std::uint64_t ileaveZeros_const(std::uint32_t input) noexcept 120 | { 121 | #ifdef BITMANIP_HAS_BUILTIN_ILEAVE_ZEROS 122 | if (not isConstantEvaluated()) { 123 | return detail::ileaveZeros_builtin(input); 124 | } 125 | #endif 126 | return detail::ileaveZeros_shift(input) << SHIFT; 127 | } 128 | 129 | // BITWISE DE-INTERLEAVING ============================================================================================= 130 | 131 | namespace detail { 132 | 133 | /** 134 | * @brief Removes each interleaved bits. Example: 0b010101 --rem 1--> 0b111 135 | * @param input the input number 136 | * @param bits input bits per output bit 137 | * @return the the output with removed bits 138 | */ 139 | [[nodiscard]] constexpr std::uint64_t remIleavedBits_naive(std::uint64_t input, std::size_t bits) noexcept 140 | { 141 | // increment once to avoid modulo divisions by 0 142 | // this way our function is noexcept and safe for all inputs 143 | ++bits; 144 | std::uint64_t result = 0; 145 | for (std::size_t i = 0, b_out = 0; i < 64; ++i) { 146 | if (i % bits == 0) { 147 | result |= (input & 1) << b_out++; 148 | } 149 | input >>= 1; 150 | } 151 | 152 | return result; 153 | } 154 | 155 | template 156 | [[nodiscard]] constexpr std::uint64_t remIleavedBits_shift(std::uint64_t input) noexcept 157 | { 158 | if constexpr (BITS == 0) { 159 | return input; 160 | } 161 | else { 162 | constexpr std::uint64_t MASKS[] = { 163 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 1), 164 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 2), 165 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 4), 166 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 8), 167 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 16), 168 | detail::duplBits_naive(detail::ileaveZeros_naive(~std::uint32_t(0), BITS), 32), 169 | }; 170 | // log2_floor(0) == 0 so this is always safe, even for 1 bit 171 | constexpr std::size_t iterations = 5 - bitmanip::log2floor(BITS >> 1); 172 | 173 | input &= MASKS[0]; 174 | 175 | for (std::size_t i = 0; i < iterations; ++i) { 176 | unsigned rshift = (1u << i) * BITS; 177 | input |= input >> rshift; 178 | input &= MASKS[i + 1]; 179 | } 180 | 181 | return input; 182 | } 183 | } 184 | 185 | #ifdef BITMANIP_HAS_BUILTIN_PEXT 186 | #define BITMANIP_HAS_BUILTIN_REM_ILEAVED_BITS 187 | template 188 | inline std::uint64_t remIleavedBits_builtin(std::uint64_t input) noexcept 189 | { 190 | constexpr std::uint64_t mask = detail::ileaveZeros_naive(~std::uint32_t(0), BITS) << SHIFT; 191 | return builtin::extractBits(std::uint64_t{input}, mask); 192 | } 193 | #endif 194 | 195 | } // namespace detail 196 | 197 | /** 198 | * @brief Removes each interleaved bits. Example: 0b010101 --rem 1--> 0b111 199 | * If BITS is zero, no bits are removed and the input is returned. 200 | * @param input the input number 201 | * @param bits input bits per output bit 202 | * @return the the output with removed bits 203 | */ 204 | template 205 | [[nodiscard]] constexpr std::uint64_t remIleavedBits_const(std::uint64_t input) noexcept 206 | { 207 | #ifdef BITMANIP_HAS_BUILTIN_REM_ILEAVED_BITS 208 | if (not isConstantEvaluated()) { 209 | return detail::remIleavedBits_builtin(input); 210 | } 211 | #endif 212 | return detail::remIleavedBits_shift(input >> SHIFT); 213 | } 214 | 215 | // NUMBER INTERLEAVING ================================================================================================= 216 | 217 | namespace detail { 218 | 219 | using ull_type = unsigned long long; 220 | 221 | template 222 | [[nodiscard]] constexpr auto ileave_naive_impl(std::index_sequence, Uint... args) noexcept -> ull_type 223 | { 224 | constexpr std::size_t max = sizeof...(I) - 1; 225 | return ((ileaveZeros_naive(args, max) << max - I) | ...); 226 | } 227 | 228 | template 229 | [[nodiscard]] constexpr auto ileave_naive(Uint... args) noexcept -> std::enable_if_t, ull_type> 230 | { 231 | return ileave_naive_impl(std::make_index_sequence{}, args...); 232 | } 233 | 234 | template 235 | [[nodiscard]] constexpr auto ileave_impl(std::index_sequence, Uint... args) noexcept -> ull_type 236 | { 237 | constexpr std::size_t max = sizeof...(I) - 1; 238 | return (ileaveZeros_const(args) | ...); 239 | } 240 | 241 | template 242 | [[nodiscard]] constexpr auto ileave_arr_impl(std::index_sequence, Uint args[]) noexcept -> ull_type 243 | { 244 | constexpr std::size_t max = sizeof...(I) - 1; 245 | return (ileaveZeros_const(args[I]) | ...); 246 | } 247 | 248 | } // namespace detail 249 | 250 | /** 251 | * @brief Interleaves integers, where x comprises the uppermost bits of each bit triple and z the lowermost bits. 252 | * 253 | * This is also referred to as a Morton Code in scientific literature. 254 | * 255 | * @param x the highest bits 256 | * @param y the middle bits 257 | * @param z the lowest bits 258 | * @return the interleaved bits 259 | */ 260 | template 261 | [[nodiscard]] constexpr auto ileave(Uint... args) noexcept -> std::enable_if_t, unsigned long long> 262 | { 263 | return detail::ileave_impl(std::make_index_sequence{}, args...); 264 | } 265 | 266 | template 267 | [[nodiscard]] constexpr auto ileave(Uint out[N]) noexcept -> std::enable_if_t, unsigned long long> 268 | { 269 | return detail::ileave_arr_impl(std::make_index_sequence{}, out); 270 | } 271 | 272 | template 273 | [[nodiscard]] constexpr auto ileave(Uint (&out)[N]) noexcept -> std::enable_if_t, unsigned long long> 274 | { 275 | return detail::ileave_arr_impl(std::make_index_sequence{}, out); 276 | } 277 | 278 | // NUMBER DE-INTERLEAVING ============================================================================================== 279 | 280 | namespace detail { 281 | 282 | template 283 | constexpr void dileave_naive_impl(std::index_sequence, ull_type n, Uint &... out) noexcept 284 | { 285 | constexpr std::size_t max = sizeof...(I) - 1; 286 | ((out = remIleavedBits_naive(n >> (max - I), max)), ...); 287 | } 288 | 289 | template 290 | constexpr void dileave_naive(ull_type n, Uint &... out) noexcept 291 | { 292 | detail::dileave_naive_impl(std::make_index_sequence{}, n, out...); 293 | } 294 | 295 | template 296 | constexpr void dileave_impl(std::index_sequence, ull_type n, Uint &... out) noexcept 297 | { 298 | constexpr std::size_t max = sizeof...(I) - 1; 299 | ((out = static_cast(remIleavedBits_const(n))), ...); 300 | } 301 | 302 | template 303 | constexpr void dileave_arr_impl(std::index_sequence, ull_type n, Uint out[]) noexcept 304 | { 305 | constexpr std::size_t max = sizeof...(I) - 1; 306 | ((out[I] = static_cast(remIleavedBits_const(n))), ...); 307 | } 308 | 309 | } // namespace detail 310 | 311 | /** 312 | * @brief Deinterleaves integers which are interleaved in a single number. 313 | * Visualization: abcdefghi -> (adg, beh, cfi) 314 | * 315 | * This is also referred to as a Morton Code in scientific literature. 316 | * 317 | * @param n the number 318 | * @return the interleaved bits 319 | */ 320 | template 321 | constexpr auto dileave(unsigned long long n, Uint &... out) noexcept -> std::enable_if_t, void> 322 | { 323 | detail::dileave_impl(std::make_index_sequence{}, n, out...); 324 | } 325 | 326 | template 327 | constexpr auto dileave(unsigned long long n, Uint out[N]) noexcept -> std::enable_if_t, void> 328 | { 329 | detail::dileave_arr_impl(std::make_index_sequence{}, n, out); 330 | } 331 | 332 | template 333 | constexpr auto dileave(unsigned long long n, Uint (&out)[N]) noexcept -> std::enable_if_t, void> 334 | { 335 | detail::dileave_arr_impl(std::make_index_sequence{}, n, out); 336 | } 337 | 338 | // BYTE INTERLEAVING =================================================================================================== 339 | 340 | /** 341 | * @brief Interleaves up to 8 bytes into a 64-bit integer. 342 | * @param bytes the bytes in little-endian order 343 | * @tparam COUNT the number of bytes to interleave 344 | * @return the interleaved bytes stored in a 64-bit integer 345 | */ 346 | template = 0> 347 | [[nodiscard]] constexpr std::uint64_t ileaveBytes_const(std::uint64_t bytes) noexcept 348 | { 349 | std::uint64_t result = 0; 350 | // use if-constexpr to avoid instantiation of ileave_zeros 351 | if constexpr (COUNT != 0) { 352 | for (std::size_t i = 0; i < COUNT; ++i) { 353 | result |= ileaveZeros_const(bytes & 0xff) << i; 354 | bytes >>= 8; 355 | } 356 | } 357 | return result; 358 | } 359 | 360 | namespace detail { 361 | 362 | // alternative implementation using a naive algorithm 363 | [[nodiscard]] constexpr std::uint64_t ileaveBytes_naive(std::uint64_t bytes, std::size_t count) noexcept 364 | { 365 | BITMANIP_ASSUME(count <= 8); 366 | 367 | std::uint64_t result = 0; 368 | for (std::size_t i = 0; i < count; ++i) { 369 | result |= ileaveZeros_naive(bytes & 0xff, count - 1) << i; 370 | bytes >>= 8; 371 | } 372 | return result; 373 | } 374 | 375 | // alternative implementation adapting ileave_bytes_const to work with a runtime parameter 376 | [[nodiscard]] constexpr std::uint64_t ileaveBytes_jmp(std::uint64_t bytes, std::size_t count) noexcept 377 | { 378 | BITMANIP_ASSUME(count <= 8); 379 | 380 | switch (count) { 381 | case 0: return ileaveBytes_const<0>(bytes); 382 | case 1: return ileaveBytes_const<1>(bytes); 383 | case 2: return ileaveBytes_const<2>(bytes); 384 | case 3: return ileaveBytes_const<3>(bytes); 385 | case 4: return ileaveBytes_const<4>(bytes); 386 | case 5: return ileaveBytes_const<5>(bytes); 387 | case 6: return ileaveBytes_const<6>(bytes); 388 | case 7: return ileaveBytes_const<7>(bytes); 389 | case 8: return ileaveBytes_const<8>(bytes); 390 | } 391 | BITMANIP_UNREACHABLE(); 392 | } 393 | 394 | } // namespace detail 395 | 396 | /** 397 | * @brief Interleaves up to 8 bytes into a 64-bit integer. 398 | * @param bytes the bytes in little-endian order 399 | * @param count number of bytes to interleave 400 | * @return the interleaved bytes stored in a 64-bit integer 401 | */ 402 | [[nodiscard]] constexpr std::uint64_t ileaveBytes(std::uint64_t bytes, std::size_t count) noexcept 403 | { 404 | return detail::ileaveBytes_jmp(bytes, count); 405 | } 406 | 407 | // BYTE DE-INTERLEAVING ================================================================================================ 408 | 409 | template = 0> 410 | [[nodiscard]] constexpr std::uint64_t dileaveBytes_const(std::uint64_t ileaved) noexcept 411 | { 412 | std::uint64_t result = 0; 413 | // use if-constexpr to avoid instantiation of rem_ileaved_bits 414 | if constexpr (COUNT != 0) { 415 | for (std::size_t i = COUNT; i != 0; --i) { 416 | // if we also masked the result with 0xff, then this would be safe for a hi-polluted ileaved number 417 | result <<= 8; 418 | result |= remIleavedBits_const(ileaved >> (i - 1)); 419 | } 420 | } 421 | return result; 422 | } 423 | 424 | namespace detail { 425 | 426 | // alternative implementation adapting ileave_bytes_const to work with a runtime parameter 427 | [[nodiscard]] constexpr std::uint64_t dileaveBytes_jmp(std::uint64_t bytes, std::size_t count) noexcept 428 | { 429 | BITMANIP_ASSUME(count <= 8u); 430 | 431 | switch (count) { 432 | case 0: return dileaveBytes_const<0>(bytes); 433 | case 1: return dileaveBytes_const<1>(bytes); 434 | case 2: return dileaveBytes_const<2>(bytes); 435 | case 3: return dileaveBytes_const<3>(bytes); 436 | case 4: return dileaveBytes_const<4>(bytes); 437 | case 5: return dileaveBytes_const<5>(bytes); 438 | case 6: return dileaveBytes_const<6>(bytes); 439 | case 7: return dileaveBytes_const<7>(bytes); 440 | case 8: return dileaveBytes_const<8>(bytes); 441 | } 442 | BITMANIP_UNREACHABLE(); 443 | } 444 | 445 | } // namespace detail 446 | 447 | [[nodiscard]] constexpr std::uint64_t dileave_bytes(std::uint64_t bytes, std::size_t count) noexcept 448 | { 449 | return detail::dileaveBytes_jmp(bytes, count); 450 | } 451 | 452 | } // namespace bitmanip 453 | 454 | #endif // ILEAVE_HPP 455 | -------------------------------------------------------------------------------- /include/bitmanip/builtin.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_BUILTIN_HPP 2 | #define BITMANIP_BUILTIN_HPP 3 | /* 4 | * builtin.hpp 5 | * ----------- 6 | * Forwards to builtin functions in a compiler-independent way. 7 | */ 8 | 9 | #include "build.hpp" 10 | 11 | #ifdef BITMANIP_MSVC 12 | #include 13 | #endif 14 | 15 | #if defined(BITMANIP_X86_OR_X64) && defined(BITMANIP_GNU_OR_CLANG) 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | 22 | // BITMANIP_HAS_BUILTIN(builtin): 23 | // Checks whether a builtin exists. 24 | // This only works on recent versions of clang, as this macro does not exist in gcc. 25 | // For gcc, the result is always 1 (true), optimistically assuming that the builtin exists. 26 | #ifdef __has_builtin 27 | #define BITMANIP_HAS_BUILTIN_HAS_BUILTIN 28 | #define BITMANIP_HAS_BUILTIN(builtin) __has_builtin(builtin) 29 | #else 30 | #define BITMANIP_HAS_BUILTIN(builtin) 1 31 | #endif 32 | 33 | // METAPROGRAMMING ===================================================================================================== 34 | 35 | // BITMANIP_UNREACHABLE(): 36 | // Signals to the compiler that a given code point is unreachable. 37 | // This macro is always defined, but does nothing if no builtin is available. 38 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_unreachable) 39 | #define BITMANIP_HAS_BUILTIN_UNREACHABLE 40 | #define BITMANIP_UNREACHABLE() __builtin_unreachable() 41 | 42 | #elif defined(BITMANIP_MSVC) 43 | #define BITMANIP_HAS_BUILTIN_UNREACHABLE 44 | #define BITMANIP_UNREACHABLE() __assume(false) 45 | 46 | #else 47 | #define BITMANIP_UNREACHABLE() 48 | #endif 49 | 50 | // BITMANIP_ASSUME(): 51 | // Signals to the compiler that a given expression always evaluates to true. 52 | // This macro is always defined, but does nothing if no builtin is available. 53 | // Note that the expression inside of BITMANIP_ASSUME() is evaluated, although the result is unused. 54 | // This makes it unsafe to use when the expression has side effects. 55 | #if defined(BITMANIP_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_assume) 56 | #define BITMANIP_HAS_BUILTIN_ASSUME 57 | #define BITMANIP_ASSUME(condition) __builtin_assume(condition) 58 | 59 | #elif defined(BITMANIP_GNU) 60 | #define BITMANIP_HAS_BUILTIN_ASSUME 61 | #define BITMANIP_ASSUME(condition) (static_cast(condition) ? void(0) : __builtin_unreachable()) 62 | 63 | #elif defined(BITMANIP_MSVC) 64 | #define BITMANIP_HAS_BUILTIN_ASSUME 65 | #define BITMANIP_ASSUME(condition) __assume(condition) 66 | 67 | #else 68 | #define BITMANIP_ASSUME(condition) 69 | #endif 70 | 71 | namespace bitmanip::builtin { 72 | 73 | // void trap(): 74 | // Executes an abnormal instruction or otherwise exits the program immediately. 75 | // This bypasses even std::terminate() and is completely unhandled. 76 | // This function always exists and will call std::terminate() by default. 77 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_trap) 78 | #define BITMANIP_HAS_BUILTIN_TRAP 79 | [[noreturn]] inline void trap() 80 | { 81 | __builtin_trap(); 82 | } 83 | #else 84 | [[noreturn]] void trap(); 85 | #endif 86 | 87 | // bool isconsteval(): 88 | // To be used in an if-statement to verify whether the current context is constant-evaluated. 89 | // This builtin can potentially not exist and it has no sane default. 90 | // clang 9+ and gcc 9+ support this builtin, even for C++17, allowing for implementation of 91 | // std::is_constant_evaluated() before C++20. 92 | // clang-format off 93 | #if defined(BITMANIP_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_is_constant_evaluated) || \ 94 | defined(BITMANIP_GNU) && BITMANIP_GNU >= 9 || \ 95 | defined(BITMANIP_MSVC) && BITMANIP_MSVC >= 1925 96 | // clang-format on 97 | #define BITMANIP_HAS_BUILTIN_ISCONSTEVAL 98 | constexpr bool isconsteval() noexcept 99 | { 100 | return __builtin_is_constant_evaluated(); 101 | } 102 | #else 103 | constexpr bool isconsteval() noexcept 104 | { 105 | return true; 106 | } 107 | #endif 108 | 109 | // To bitcast(const From &from): 110 | // Wrapper for __builtin_bit_cast. 111 | // This builtin should behave identically to std::bit_cast from the header. 112 | // clang-format off 113 | #if defined(BITMANIP_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_bit_cast) || \ 114 | defined(BITMANIP_GNU) && BITMANIP_GNU >= 11 || \ 115 | defined(BITMANIP_MSVC) && BITMANIP_MSVC >= 1926 116 | // clang-format on 117 | template 118 | constexpr bool isbitcastable_ = 119 | sizeof(To) == sizeof(From) && std::is_trivially_copyable_v &&std::is_trivially_copyable_v; 120 | 121 | template 122 | constexpr std::enable_if_t, To> bitcast(const From &from) 123 | { 124 | return __builtin_bit_cast(To, from); 125 | } 126 | #endif 127 | 128 | // BIT COUNTING ======================================================================================================== 129 | 130 | // int clrsb(unsigned ...): 131 | // Counts the number of redundant sign bits, i.o.w. the number of bits following the sign bit which are equal to it. 132 | // This is the number of leading zeros minus one for positive numbers. 133 | // For negative numbers, it is the number of leading ones - 1. 134 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_clrsb) && \ 135 | BITMANIP_HAS_BUILTIN(__builtin_clrsbl) && BITMANIP_HAS_BUILTIN(__builtin_clrsbll) 136 | #define BITMANIP_HAS_BUILTIN_CLRSB 137 | inline int clrsb(char x) noexcept 138 | { 139 | return __builtin_clrsb(x); 140 | } 141 | 142 | inline int clrsb(short x) noexcept 143 | { 144 | return __builtin_clrsb(x); 145 | } 146 | 147 | inline int clrsb(int x) noexcept 148 | { 149 | return __builtin_clrsb(x); 150 | } 151 | 152 | inline int clrsb(long x) noexcept 153 | { 154 | return __builtin_clrsbl(x); 155 | } 156 | 157 | inline int clrsb(long long x) noexcept 158 | { 159 | return __builtin_clrsbll(x); 160 | } 161 | #endif 162 | 163 | // int clz(unsigned ...): 164 | // Counts the number of leading zeros in an unsigned integer type. 165 | // The result of countLeadingZeros(0) is undefined for all types. 166 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_clz) && BITMANIP_HAS_BUILTIN(__builtin_clzl) && \ 167 | BITMANIP_HAS_BUILTIN(__builtin_clzll) 168 | #define BITMANIP_HAS_BUILTIN_CLZ 169 | inline int clz(unsigned char x) noexcept 170 | { 171 | return __builtin_clz(x) - int{(sizeof(int) - sizeof(unsigned char)) * 8}; 172 | } 173 | 174 | inline int clz(unsigned short x) noexcept 175 | { 176 | return __builtin_clz(x) - int{(sizeof(int) - sizeof(unsigned short)) * 8}; 177 | } 178 | 179 | inline int clz(unsigned int x) noexcept 180 | { 181 | return __builtin_clz(x); 182 | } 183 | 184 | inline int clz(unsigned long x) noexcept 185 | { 186 | return __builtin_clzl(x); 187 | } 188 | 189 | inline int clz(unsigned long long x) noexcept 190 | { 191 | return __builtin_clzll(x); 192 | } 193 | 194 | #elif defined(BITMANIP_MSVC) && defined(BITMANIP_X86_OR_X64) 195 | #define BITMANIP_HAS_BUILTIN_CLZ 196 | __forceinline int clz(unsigned char x) noexcept 197 | { 198 | return __lzcnt16(x) - 8; 199 | } 200 | 201 | __forceinline int clz(unsigned short x) noexcept 202 | { 203 | return __lzcnt16(x); 204 | } 205 | 206 | __forceinline int clz(unsigned int x) noexcept 207 | { 208 | return __lzcnt(x); 209 | } 210 | 211 | __forceinline int clz(unsigned long x) noexcept 212 | { 213 | static_assert(sizeof(unsigned long) == sizeof(unsigned) || sizeof(unsigned long) == sizeof(uint64_t)); 214 | 215 | if constexpr (sizeof(unsigned long) == sizeof(unsigned)) { 216 | return __lzcnt(static_cast(x)); 217 | } 218 | else { 219 | return __lzcnt64(static_cast(x)); 220 | } 221 | } 222 | 223 | #ifdef BITMANIP_64_BIT 224 | __forceinline int clz(uint64_t x) noexcept 225 | { 226 | return __lzcnt64(x); 227 | } 228 | #else 229 | [[noreturn]] __forceinline int clz(uint64_t) noexcept 230 | { 231 | trap(); 232 | } 233 | #endif 234 | #endif 235 | 236 | // int ctz(unsigned ...): 237 | // Counts the number of trailing zeros in an unsigned integer type. 238 | // The result of countTrailingZeros(0) is undefined for all types. 239 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_ctz) && BITMANIP_HAS_BUILTIN(__builtin_ctzl) && \ 240 | BITMANIP_HAS_BUILTIN(__builtin_ctzll) 241 | #define BITMANIP_HAS_BUILTIN_CTZ 242 | inline int ctz(unsigned char x) noexcept 243 | { 244 | return __builtin_ctz(x); 245 | } 246 | 247 | inline int ctz(unsigned short x) noexcept 248 | { 249 | return __builtin_ctz(x); 250 | } 251 | 252 | inline int ctz(unsigned int x) noexcept 253 | { 254 | return __builtin_ctz(x); 255 | } 256 | 257 | inline int ctz(unsigned long x) noexcept 258 | { 259 | return __builtin_ctzl(x); 260 | } 261 | 262 | inline int ctz(unsigned long long x) noexcept 263 | { 264 | return __builtin_ctzll(x); 265 | } 266 | #endif 267 | 268 | // int lsb(unsigned ...): 269 | // Returns the index of the least significant bit. 270 | // For example, leastSignificantBit(0b110) -> 1. 271 | // The result of leastSignificantBit(0) is 0 for all types. 272 | #ifdef BITMANIP_MSVC 273 | #define BITMANIP_HAS_BUILTIN_LSB 274 | __forceinline int lsb(unsigned char x) noexcept 275 | { 276 | unsigned long result; 277 | bool nonzero = _BitScanForward(&result, x); 278 | return nonzero * static_cast(result); 279 | } 280 | 281 | __forceinline int lsb(unsigned short x) noexcept 282 | { 283 | unsigned long result; 284 | bool nonzero = _BitScanForward(&result, x); 285 | return nonzero * static_cast(result); 286 | } 287 | 288 | __forceinline int lsb(unsigned int x) noexcept 289 | { 290 | unsigned long result; 291 | bool nonzero = _BitScanForward(&result, x); 292 | return nonzero * static_cast(result); 293 | } 294 | 295 | __forceinline int lsb(unsigned long x) noexcept 296 | { 297 | unsigned long result; 298 | bool nonzero = _BitScanForward(&result, x); 299 | return nonzero * static_cast(result); 300 | } 301 | 302 | #ifdef BITMANIP_64_BIT 303 | __forceinline int lsb(uint64_t x) noexcept 304 | { 305 | unsigned long result; 306 | bool nonzero = _BitScanForward64(&result, x); 307 | return nonzero * static_cast(result); 308 | } 309 | #else 310 | [[noreturn]] __forceinline int lsb(uint64_t) noexcept 311 | { 312 | trap(); 313 | } 314 | #endif 315 | #endif 316 | 317 | // int msb(unsigned ...): 318 | // Returns the index of the most significant bit. 319 | // For example, mostSignificantBit(0b110) -> 2. 320 | // The result of mostSignificantBit(0) is 0 for all types. 321 | #ifdef BITMANIP_MSVC 322 | #define BITMANIP_HAS_BUILTIN_MSB 323 | __forceinline int msb(unsigned char x) noexcept 324 | { 325 | unsigned long result; 326 | bool nonzero = _BitScanReverse(&result, x); 327 | return nonzero * static_cast(result); 328 | } 329 | 330 | __forceinline int msb(unsigned short x) noexcept 331 | { 332 | unsigned long result; 333 | bool nonzero = _BitScanReverse(&result, x); 334 | return nonzero * static_cast(result); 335 | } 336 | 337 | __forceinline int msb(unsigned int x) noexcept 338 | { 339 | unsigned long result; 340 | bool nonzero = _BitScanReverse(&result, x); 341 | return nonzero * static_cast(result); 342 | } 343 | 344 | __forceinline int msb(unsigned long x) noexcept 345 | { 346 | unsigned long result; 347 | bool nonzero = _BitScanReverse(&result, x); 348 | return nonzero * static_cast(result); 349 | } 350 | 351 | #ifdef BITMANIP_64_BIT 352 | __forceinline int msb(uint64_t x) noexcept 353 | { 354 | unsigned long result; 355 | bool nonzero = _BitScanReverse64(&result, x); 356 | return nonzero * static_cast(result); 357 | } 358 | #else 359 | [[noreturn]] __forceinline int msb(uint64_t) noexcept 360 | { 361 | trap(); 362 | } 363 | #endif 364 | #endif 365 | 366 | // int ffs(unsigned ...): 367 | // Returns one plus the number of trailing zeros. 368 | // findFirstSet(0) evaluates to zero. 369 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_ffs) && BITMANIP_HAS_BUILTIN(__builtin_ffsl) && \ 370 | BITMANIP_HAS_BUILTIN(__builtin_ffsll) 371 | #define BITMANIP_HAS_BUILTIN_FFS 372 | inline int ffs(unsigned char x) noexcept 373 | { 374 | return __builtin_ffs(x); 375 | } 376 | 377 | inline int ffs(unsigned short x) noexcept 378 | { 379 | return __builtin_ffs(x); 380 | } 381 | 382 | inline int ffs(unsigned int x) noexcept 383 | { 384 | return __builtin_ffs(static_cast(x)); 385 | } 386 | 387 | inline int ffs(unsigned long x) noexcept 388 | { 389 | return __builtin_ffsl(static_cast(x)); 390 | } 391 | 392 | inline int ffs(unsigned long long x) noexcept 393 | { 394 | return __builtin_ffsll(static_cast(x)); 395 | } 396 | #endif 397 | 398 | // int popcount(unsigned ...): 399 | // Counts the number of one-bits in an unsigned integer type. 400 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_popcount) && \ 401 | BITMANIP_HAS_BUILTIN(__builtin_popcountl) && BITMANIP_HAS_BUILTIN(__builtin_popcountll) 402 | #define BITMANIP_HAS_BUILTIN_POPCOUNT 403 | inline int popcount(unsigned char x) noexcept 404 | { 405 | return __builtin_popcount(x); 406 | } 407 | 408 | inline int popcount(unsigned short x) noexcept 409 | { 410 | return __builtin_popcount(x); 411 | } 412 | 413 | inline int popcount(unsigned int x) noexcept 414 | { 415 | return __builtin_popcount(x); 416 | } 417 | 418 | inline int popcount(unsigned long x) noexcept 419 | { 420 | return __builtin_popcountl(x); 421 | } 422 | 423 | inline int popcount(unsigned long long x) noexcept 424 | { 425 | return __builtin_popcountll(x); 426 | } 427 | #elif defined(BITMANIP_MSVC) 428 | #define BITMANIP_HAS_BUILTIN_POPCOUNT 429 | __forceinline int popcount(uint8_t x) noexcept 430 | { 431 | return static_cast(__popcnt16(x)); 432 | } 433 | 434 | __forceinline int popcount(uint16_t x) noexcept 435 | { 436 | return static_cast(__popcnt16(x)); 437 | } 438 | 439 | __forceinline int popcount(uint32_t x) noexcept 440 | { 441 | return static_cast(__popcnt(x)); 442 | } 443 | 444 | #ifdef BITMANIP_64_BIT 445 | __forceinline int popcount(uint64_t x) noexcept 446 | { 447 | return static_cast(__popcnt64(x)); 448 | } 449 | #else 450 | [[noreturn]] __forceinline int popcount(uint64_t) noexcept 451 | { 452 | trap(); 453 | } 454 | #endif 455 | #endif 456 | 457 | // int parity(unsigned ...): 458 | // Returns the parity of a number. 459 | // This is a bool which indicates whether the number of set bits in x is odd. 460 | // The parity of 0 is 0, the parity of 1 is 1. 461 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_parity) && \ 462 | BITMANIP_HAS_BUILTIN(__builtin_parityl) && BITMANIP_HAS_BUILTIN(__builtin_parityll) 463 | #define BITMANIP_HAS_BUILTIN_PARITY 464 | inline bool parity(unsigned char x) noexcept 465 | { 466 | return __builtin_parity(x); 467 | } 468 | 469 | inline bool parity(unsigned short x) noexcept 470 | { 471 | return __builtin_parity(x); 472 | } 473 | 474 | inline bool parity(unsigned int x) noexcept 475 | { 476 | return __builtin_parity(x); 477 | } 478 | 479 | inline bool parity(unsigned long x) noexcept 480 | { 481 | return __builtin_parityl(x); 482 | } 483 | 484 | inline bool parity(unsigned long long x) noexcept 485 | { 486 | return __builtin_parityll(x); 487 | } 488 | #endif 489 | 490 | // ADVANCED BITWISE OPS ================================================================================================ 491 | 492 | // unsigned rotr(unsigned ...): 493 | // Right-rotates the bits of a number. 494 | #if defined(BITMANIP_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_rotateright8) && \ 495 | BITMANIP_HAS_BUILTIN(__builtin_rotateright64) 496 | #define BITMANIP_HAS_BUILTIN_ROTR 497 | template && (sizeof(Uint) <= 8), int> = 0> 498 | Uint rotr(Uint x, unsigned char rot) noexcept 499 | { 500 | if constexpr (sizeof(x) == 1) return __builtin_rotateright8(x, rot); 501 | if constexpr (sizeof(x) == 2) return __builtin_rotateright16(x, rot); 502 | if constexpr (sizeof(x) == 4) return __builtin_rotateright32(x, rot); 503 | if constexpr (sizeof(x) == 8) return __builtin_rotateright64(x, rot); 504 | } 505 | #endif 506 | 507 | // unsigned rotl(unsigned ...): 508 | // Left-rotates the bits of a number. 509 | #if defined(BITMANIP_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_rotateleft8) && \ 510 | BITMANIP_HAS_BUILTIN(__builtin_rotateleft64) 511 | #define BITMANIP_HAS_BUILTIN_ROTL 512 | template && (sizeof(Uint) <= 8), int> = 0> 513 | Uint rotl(Uint x, unsigned char rot) noexcept 514 | { 515 | if constexpr (sizeof(x) == 1) return __builtin_rotateleft8(x, rot); 516 | if constexpr (sizeof(x) == 2) return __builtin_rotateleft16(x, rot); 517 | if constexpr (sizeof(x) == 4) return __builtin_rotateleft32(x, rot); 518 | if constexpr (sizeof(x) == 8) return __builtin_rotateleft64(x, rot); 519 | } 520 | #endif 521 | 522 | // uintXX_t bswap(uintXX_t ...): 523 | // Swaps the bytes of any uintXX type. 524 | // This reverses the byte order (little-endian/big-endian). 525 | // This does nothing for byteSwap(uint8_t). 526 | #if defined(BITMANIP_GNU_OR_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_bswap16) && \ 527 | BITMANIP_HAS_BUILTIN(__builtin_bswap32) && BITMANIP_HAS_BUILTIN(__builtin_bswap64) 528 | #define BITMANIP_HAS_BUILTIN_BSWAP 529 | template && (sizeof(Uint) <= 8), int> = 0> 530 | Uint bswap(Uint x) noexcept 531 | { 532 | if constexpr (sizeof(x) == 1) return x; 533 | if constexpr (sizeof(x) == 2) return __builtin_bswap16(x); 534 | if constexpr (sizeof(x) == 4) return __builtin_bswap32(x); 535 | if constexpr (sizeof(x) == 8) return __builtin_bswap64(x); 536 | } 537 | 538 | #elif defined(BITMANIP_MSVC) 539 | #define BITMANIP_HAS_BUILTIN_BSWAP 540 | __forceinline uint8_t bswap(uint8_t val) noexcept 541 | { 542 | return val; 543 | } 544 | 545 | __forceinline unsigned short bswap(unsigned short val) noexcept 546 | { 547 | return _byteswap_ushort(val); 548 | } 549 | 550 | __forceinline unsigned int bswap(unsigned int val) noexcept 551 | { 552 | static_assert(sizeof(unsigned int) == sizeof(unsigned short) || sizeof(unsigned int) == sizeof(unsigned long), 553 | "No viable _byteswap implementation for unsigned int"); 554 | if constexpr (sizeof(unsigned int) == sizeof(unsigned short)) { 555 | return static_cast(_byteswap_ushort(static_cast(val))); 556 | } 557 | else { 558 | return static_cast(_byteswap_ulong(static_cast(val))); 559 | } 560 | } 561 | 562 | __forceinline unsigned long bswap(unsigned long val) noexcept 563 | { 564 | return _byteswap_ulong(val); 565 | } 566 | 567 | #ifdef BITMANIP_64_BIT 568 | __forceinline uint64_t bswap(uint64_t val) noexcept 569 | { 570 | return _byteswap_uint64(val); 571 | } 572 | #else 573 | [[noreturn]] __forceinline uint64_t bswap(uint64_t) noexcept 574 | { 575 | trap(); 576 | } 577 | #endif 578 | #endif 579 | 580 | // uintXX_t bitrev(uintXX_t ...): 581 | // Reverse the order of bits in an integer 582 | #if defined(BITMANIP_CLANG) && BITMANIP_HAS_BUILTIN(__builtin_bitreverse64) 583 | #define BITMANIP_HAS_BUILTIN_BITREV 584 | template && (sizeof(Uint) <= 8), int> = 0> 585 | Uint bitrev(Uint x) noexcept 586 | { 587 | if constexpr (sizeof(x) == 1) return __builtin_bitreverse8(x); 588 | if constexpr (sizeof(x) == 2) return __builtin_bitreverse16(x); 589 | if constexpr (sizeof(x) == 4) return __builtin_bitreverse32(x); 590 | if constexpr (sizeof(x) == 8) return __builtin_bitreverse64(x); 591 | } 592 | #endif 593 | 594 | // BMI2 BITWISE OPS ==================================================================================================== 595 | 596 | // uintXX_t pdep(uintXX_t val, uintXX_t mask): 597 | // See https://www.felixcloutier.com/x86/pdep 598 | #if defined(BITMANIP_X86_OR_X64) && (defined(BITMANIP_MSVC) || defined(BITMANIP_GNU_OR_CLANG) && defined(__BMI2__)) 599 | #define BITMANIP_HAS_BUILTIN_PDEP 600 | template && sizeof(Uint) <= 8), int> = 0> 601 | Uint pdep(Uint val, Uint mask) 602 | { 603 | if constexpr (sizeof(Uint) < 8) { 604 | return _pdep_u32(static_cast(val), static_cast(mask)); 605 | } 606 | else { 607 | return _pdep_u64(val, mask); 608 | } 609 | } 610 | 611 | // uintXX_t pext(uintXX_t val, uintXX_t mask): 612 | // See https://www.felixcloutier.com/x86/pext 613 | #define BITMANIP_HAS_BUILTIN_PEXT 614 | template && sizeof(Uint) <= 8), int> = 0> 615 | Uint pext(Uint val, Uint mask) 616 | { 617 | if constexpr (sizeof(Uint) < 8) { 618 | return _pext_u32(static_cast(val), static_cast(mask)); 619 | } 620 | else { 621 | return _pext_u64(val, mask); 622 | } 623 | } 624 | #endif 625 | 626 | } // namespace bitmanip::builtin 627 | 628 | #endif // BUILTIN_HPP 629 | -------------------------------------------------------------------------------- /include/bitmanip/intlog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITMANIP_INTLOG_HPP 2 | #define BITMANIP_INTLOG_HPP 3 | /* 4 | * intlog.hpp 5 | * ----------- 6 | * Implements arithmetic related to integer logarithms or exponentiation. 7 | */ 8 | 9 | #include "bitcount.hpp" 10 | #include "builtin.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | namespace bitmanip { 20 | 21 | #if defined(BITMANIP_HAS_BUILTIN_CLZ) || defined(BITMANIP_HAS_BUILTIN_MSB) 22 | #define BITMANIP_HAS_BUILTIN_LOG2FLOOR 23 | namespace detail { 24 | 25 | template 26 | inline Int log2floor_builtin(Int v) 27 | { 28 | #ifdef BITMANIP_HAS_BUILTIN_MSB 29 | return builtin::msb(v); 30 | #elif defined(BITMANIP_HAS_BUILTIN_CLZ) 31 | constexpr int maxIndex = bits_v - 1; 32 | return (v != 0) * static_cast(maxIndex - builtin::clz(v)); 33 | #endif 34 | } 35 | 36 | } // namespace detail 37 | #endif 38 | 39 | // POWER OF 2 TESTING ================================================================================================== 40 | 41 | /** 42 | * @brief Returns whether an unsigned integer is a power of 2 or zero. 43 | * Note that this test is faster than having to test if val is a power of 2. 44 | * @param val the parameter to test 45 | * @return true if val is a power of 2 or if val is zero 46 | */ 47 | template 48 | [[nodiscard]] constexpr bool isPow2or0(Uint val) noexcept 49 | { 50 | return (val & (val - 1)) == 0; 51 | } 52 | 53 | /** 54 | * @brief Returns whether an unsigned integer is a power of 2. 55 | * @param val the parameter to test 56 | * @return true if val is a power of 2 57 | * @see is_pow2_or_zero 58 | */ 59 | template 60 | [[nodiscard]] constexpr bool isPow2(Uint val) noexcept 61 | { 62 | return val != 0 && isPow2or0(val); 63 | } 64 | 65 | // POWER OF 2 ROUNDING ================================================================================================= 66 | 67 | namespace detail { 68 | 69 | #ifdef BITMANIP_HAS_BUILTIN_LOG2FLOOR 70 | #define BITMANIP_HAS_BUILTIN_CEILPOW2 71 | template 72 | [[nodiscard]] inline Uint ceilPow2m1_builtin(Uint v) noexcept 73 | { 74 | Uint log = log2floor_builtin(v); 75 | return v | (Uint{1} << log) - Uint{1}; 76 | } 77 | #endif 78 | 79 | template 80 | [[nodiscard]] constexpr Uint ceilPow2m1_shift(Uint v) noexcept 81 | { 82 | constexpr std::size_t iterations = log2bits_v; 83 | for (std::size_t i = 0; i < iterations; ++i) { 84 | // after all iterations, all bits right to the msb will be filled with 1 85 | v |= v >> (1 << i); 86 | } 87 | return v; 88 | } 89 | 90 | } // namespace detail 91 | 92 | /** 93 | * @brief Rounds up an unsigned integer to the next power of 2, minus 1. 94 | * 0 is not rounded up and stays zero. 95 | * Examples: 100 -> 127, 1 -> 1, 3 -> 3, 3000 -> 4095, 64 -> 127 96 | * @param v the value to round up 97 | */ 98 | template 99 | [[nodiscard]] constexpr Uint ceilPow2m1(Uint v) noexcept 100 | { 101 | // The codegen on other platforms such as ARM is actually better for the repeated shift version. 102 | // Each step on ARM can be performed with a flexible operand which performs a shift. 103 | #if defined(BITMANIP_HAS_BUILTIN_CEILPOW2) && defined(BITMANIP_X86_OR_X64) 104 | if (not builtin::isconsteval()) { 105 | return detail::ceilPow2m1_builtin(v); 106 | } 107 | #endif 108 | return detail::ceilPow2m1_shift(v); 109 | } 110 | 111 | /** 112 | * @brief Rounds up an unsigned integer to the next power of 2. 113 | * Powers of two are not affected. 114 | * 0 is not rounded and stays zero. 115 | * Examples: 100 -> 128, 1 -> 1, 3 -> 4, 3000 -> 4096 116 | * @param v the value to round up 117 | */ 118 | template 119 | [[nodiscard]] constexpr Uint ceilPow2(Uint v) noexcept 120 | { 121 | return ceilPow2m1(v - 1) + 1; 122 | } 123 | 124 | /** 125 | * @brief Rounds down an unsigned integer to the next power of 2. 126 | * Powers of 2 are not affected. 127 | * The result of floorPow2(0) is undefined. 128 | * Examples: 100 -> 64, 1 -> 1, 3 -> 2, 3000 -> 2048 129 | * @param v the value to round down 130 | */ 131 | template 132 | [[nodiscard]] constexpr Uint floorPow2(Uint v) noexcept 133 | { 134 | return ceilPow2m1(v >> 1) + 1; 135 | } 136 | 137 | // BASE 2 LOGARITHMS =================================================================================================== 138 | 139 | /** 140 | * @brief Naive implementation of log2 using repeated single-bit rightshifting. 141 | */ 142 | template 143 | [[nodiscard]] constexpr Uint log2floor_naive(Uint val) noexcept 144 | { 145 | Uint result = 0; 146 | while (val >>= 1) { 147 | ++result; 148 | } 149 | return result; 150 | } 151 | 152 | /** 153 | * @brief Fast implementation of log2. 154 | * See https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog. 155 | * 156 | * Unrolled 32-bit version: 157 | * unsigned shift = (v > 0xFFFF) << 4; 158 | v >>= shift; 159 | r |= shift; 160 | 161 | shift = (v > 0xFF ) << 3; 162 | v >>= shift; 163 | r |= shift; 164 | 165 | shift = (v > 0xF ) << 2; 166 | v >>= shift; 167 | r |= shift; 168 | 169 | shift = (v > 0x3 ) << 1; 170 | v >>= shift; 171 | r |= shift; 172 | 173 | shift = (v > 1) << 0; 174 | r >>= shift; 175 | r |= shift; 176 | */ 177 | template 178 | [[nodiscard]] constexpr Uint log2floor_fast(Uint v) noexcept 179 | { 180 | constexpr std::size_t iterations = log2bits_v; 181 | unsigned result = 0; 182 | 183 | for (std::size_t i = iterations; i != 0; --i) { 184 | unsigned compBits = 1 << (i - 1); 185 | Uint compShift = Uint{1} << compBits; 186 | unsigned shift = unsigned{v >= compShift} << (i - 1); 187 | v >>= shift; 188 | result |= shift; 189 | } 190 | 191 | return result; 192 | } 193 | 194 | namespace detail { 195 | 196 | constexpr unsigned char MultiplyDeBruijnBitPosition[32] = {0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 197 | 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 198 | 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; 199 | 200 | } 201 | 202 | /** 203 | * @brief log2floor implementation using De Bruijn multiplication. 204 | * See https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn. 205 | * @param val the value 206 | */ 207 | [[nodiscard]] constexpr std::uint32_t log2floor_debruijn(std::uint32_t val) noexcept 208 | { 209 | constexpr std::uint32_t magic = 0x07C4ACDD; 210 | 211 | val = ceilPow2m1(val); 212 | val *= magic; 213 | val >>= 27; 214 | 215 | return detail::MultiplyDeBruijnBitPosition[val]; 216 | } 217 | 218 | /** 219 | * @brief Computes the floored binary logarithm of a given integer. 220 | * Example: log2floor(123) = 6 221 | * 222 | * This templated function will choose the best available method depending on the type of the integer. 223 | * It is undefined for negative values. 224 | * 225 | * Unlike a traditional log function, it is defined for 0: log2floor(0) = 0 226 | * 227 | * @param v the value 228 | * @return the floored binary logarithm 229 | */ 230 | template 231 | [[nodiscard]] constexpr Uint log2floor(Uint v) noexcept 232 | { 233 | #ifdef BITMANIP_HAS_BUILTIN_LOG2FLOOR 234 | if (not builtin::isconsteval()) { 235 | return detail::log2floor_builtin(v); 236 | } 237 | #endif 238 | if constexpr (std::is_same_v) { 239 | return log2floor_debruijn(v); 240 | } 241 | else { 242 | return log2floor_fast(v); 243 | } 244 | } 245 | 246 | /** 247 | * @brief Computes the ceiled binary logarithm of a given integer. 248 | * Example: log2ceil(123) = 7 249 | * 250 | * This templated function will choose the best available method depending on the type of the integer. 251 | * It is undefined for negative values. 252 | * 253 | * Unlike a traditional log function, it is defined for 0: log2ceil(0) = 0 254 | * 255 | * @param v the value 256 | * @return the floored binary logarithm 257 | */ 258 | template 259 | [[nodiscard]] constexpr Uint log2ceil(Uint val) noexcept 260 | { 261 | const Uint result = log2floor(val); 262 | return result + not isPow2or0(val); 263 | } 264 | 265 | /** 266 | * @brief Computes the number of bits required to represent a given number. 267 | * Examples: bitLength(0) = 1, bitLength(3) = 2, bitLength(123) = 7, bitLength(4) = 3 268 | */ 269 | template 270 | [[nodiscard]] constexpr Uint bitCount(Uint val) noexcept 271 | { 272 | return log2floor(val) + 1; 273 | } 274 | 275 | // ARBITRARY BASE LOGARITHMS =========================================================================================== 276 | 277 | namespace detail { 278 | 279 | /** 280 | * @brief Naive implementation of log base N using repeated division. 281 | */ 282 | template 283 | [[nodiscard]] constexpr Uint logFloor_naive(Uint val, unsigned base) noexcept 284 | { 285 | Uint result = 0; 286 | while (val /= base) { 287 | ++result; 288 | } 289 | return result; 290 | } 291 | 292 | } // namespace detail 293 | 294 | /** 295 | * @brief The maximum possible exponent for a given base that can still be represented by a given integer type. 296 | * Example: maxExp = 2, because 10^2 is representable by an 8-bit unsigned integer but 10^3 isn't. 297 | */ 298 | template 299 | constexpr Uint maxExp = detail::logFloor_naive(static_cast(~Uint{0u}), BASE); 300 | 301 | static_assert(maxExp<10, std::uint8_t> == 2); 302 | static_assert(maxExp<10, std::uint16_t> == 4); 303 | static_assert(maxExp<10, std::uint32_t> == 9); 304 | 305 | namespace detail { 306 | 307 | /** 308 | * @brief Simple implementation of log base N using repeated multiplication. 309 | * This method is slightly more sophisticated than logFloor_naive because it avoids division. 310 | */ 311 | template 312 | [[nodiscard]] constexpr Uint logFloor_simple(Uint val) noexcept 313 | { 314 | constexpr Uint limit = maxExp; 315 | 316 | Uint i = 0; 317 | Uint pow = BASE; 318 | for (; i <= limit; ++i, pow *= BASE) { 319 | if (val < pow) { 320 | return i; 321 | } 322 | } 323 | return i; 324 | } 325 | 326 | /** 327 | * @brief Tiny array implementation to avoid including . 328 | */ 329 | template 330 | struct [[nodiscard]] Table 331 | { 332 | static_assert(N != 0, "Can't create zero-size tables"); 333 | 334 | using value_type = T; 335 | static constexpr std::size_t size_value = N; 336 | T data[N]; 337 | 338 | constexpr std::size_t size() const noexcept 339 | { 340 | return size_value; 341 | } 342 | 343 | constexpr T front() const noexcept 344 | { 345 | return data[0]; 346 | } 347 | 348 | constexpr T back() const noexcept 349 | { 350 | return data[N - 1]; 351 | } 352 | 353 | constexpr T &operator[](std::size_t i) noexcept 354 | { 355 | return data[i]; 356 | } 357 | 358 | constexpr const T &operator[](std::size_t i) const noexcept 359 | { 360 | return data[i]; 361 | } 362 | }; 363 | 364 | /// Unsafe fixed point multiplication between Q32.32 and Q64.0 365 | /// Unsafe because for large numbers, this operation can overflow. 366 | [[nodiscard]] constexpr std::uint64_t unsafeMulQ32o32Q64(std::uint64_t q32o32, std::uint64_t q64) noexcept 367 | { 368 | return q32o32 * q64 >> std::uint64_t{32}; 369 | } 370 | 371 | /// Unsafe fixed point multiplication between Q16.16 and Q32.0 372 | /// Unsafe because for large numbers, this operation can overflow. 373 | [[nodiscard]] constexpr std::uint32_t unsafeMulQ16o16Q32(std::uint32_t q16o16, std::uint32_t q32) noexcept 374 | { 375 | return q16o16 * q32 >> std::uint32_t{16}; 376 | } 377 | 378 | /** 379 | * @brief Compares two integers a, b. 380 | * @param a the first integer 381 | * @param b the second integer 382 | * @return -1 if a is lower, 1 if a is greater, 0 otherwise (if they are equal). 383 | */ 384 | [[nodiscard]] constexpr int cmpU64(std::uint64_t a, std::uint64_t b) noexcept 385 | { 386 | return (b < a) - (a < b); 387 | } 388 | 389 | /** 390 | * @brief Creates a table of approximations of the base BASE logarithm of powers of two: log_BASE(2^i). 391 | * @tparam UInt an unsigned integer type of the numbers which's base BASE logarithm will be taken 392 | * @tparam BASE the base 393 | * @return the table of approximate logarithms 394 | */ 395 | template 396 | [[nodiscard]] constexpr Table> makeGuessTable() noexcept 397 | { 398 | Table> result{}; 399 | for (std::size_t i = 0; i < result.size(); ++i) { 400 | const auto pow2 = static_cast(Uint{1} << i); 401 | result[i] = static_cast(logFloor_naive(pow2, BASE)); 402 | } 403 | return result; 404 | } 405 | 406 | template 407 | [[nodiscard]] constexpr int compareApproximationToGuessTable(std::uint64_t approxFactor, 408 | const Table &table) noexcept 409 | { 410 | for (unsigned b = 0; b < SIZE; ++b) { 411 | std::uint64_t actualLog = table[b]; 412 | std::uint64_t approxLog = unsafeMulQ32o32Q64(approxFactor, b); 413 | if (int cmp = cmpU64(approxLog, actualLog); cmp != 0) { 414 | return cmp; 415 | } 416 | } 417 | return 0; 418 | } 419 | 420 | constexpr std::uint64_t NO_APPROXIMATION = ~std::uint64_t{0}; 421 | 422 | /** 423 | * @brief Approximates the logarithm guess table using a Q32.32 number. 424 | * This exploits the fact that to convert from the logarithm base 2, to logarithm base B, we need to multiply with a 425 | * constant factor. 426 | * This constant factor is being approximated in a bit-guessing approach. 427 | * The result of this function is guaranteed to only set the most significant bits necessary. 428 | * E.g. if the result has the lowest 16 bits not set, the approximation can also be truncated to Q16.16. 429 | * @tparam SIZE the size of the table 430 | * @param table the table to approximate, where table[i] <= i 431 | * @return the fixed-point number approximating the table 432 | */ 433 | template 434 | [[nodiscard]] constexpr std::uint64_t approximateGuessTable(const Table &table) noexcept 435 | { 436 | std::uint64_t result = 0; 437 | for (unsigned b = 33; b-- != 0;) { 438 | std::uint64_t guessedResult = result | (std::uint64_t{1} << b); 439 | int cmp = compareApproximationToGuessTable(guessedResult, table); 440 | if (cmp == 0) { 441 | return guessedResult; 442 | } 443 | if (cmp == -1) { 444 | result = guessedResult; 445 | } 446 | } 447 | return NO_APPROXIMATION; 448 | } 449 | 450 | template 451 | struct [[nodiscard]] LogFloorGuesser 452 | { 453 | static constexpr Table> guessTable = makeGuessTable(); 454 | static constexpr std::uint64_t guessTableApproximation = approximateGuessTable(guessTable); 455 | 456 | constexpr unsigned char operator()(unsigned char log2) const noexcept 457 | { 458 | if constexpr (guessTableApproximation == NO_APPROXIMATION) { 459 | return guessTable[log2]; 460 | } 461 | else if constexpr ((guessTableApproximation & makeMask(16u)) == 0) { 462 | constexpr std::uint32_t lessPreciseApproximation = guessTableApproximation >> 16; 463 | return unsafeMulQ16o16Q32(lessPreciseApproximation, log2); 464 | } 465 | else { 466 | return unsafeMulQ32o32Q64(guessTableApproximation, log2); 467 | } 468 | } 469 | 470 | constexpr unsigned char maxGuess() const noexcept 471 | { 472 | return guessTable.back(); 473 | } 474 | }; 475 | 476 | template > 477 | [[nodiscard]] constexpr auto makePowerTable() noexcept 478 | { 479 | // the size of the table is maxExp + 2 because we need to store the maximum power 480 | // +1 because we need to store maxExp, which is an index, not a size 481 | // +1 again because for narrow integers, we would like to access one beyond the "end" of the table 482 | // 483 | // as a result, the last multiplication with BASE in this function might overflow, but this is perfectly normal 484 | Table + 2> result{}; 485 | std::uintmax_t x = 1; 486 | for (std::size_t i = 0; i < result.size(); ++i, x *= BASE) { 487 | result[i] = static_cast(x); 488 | } 489 | return result; 490 | } 491 | 492 | /// table that maps from log_N(val) -> pow(N, val + 1) 493 | template 494 | constexpr auto logFloor_powers = detail::makePowerTable(); 495 | 496 | template 497 | constexpr auto powConst_powers = detail::makePowerTable(); 498 | 499 | } // namespace detail 500 | 501 | /** 502 | * @brief Computes pow(BASE, exponent) where BASE is known at compile-time. 503 | */ 504 | template 505 | [[nodiscard]] constexpr Uint powConst(const Uint exponent) noexcept 506 | { 507 | if constexpr (isPow2(BASE)) { 508 | return Uint{1} << (exponent * log2floor(BASE)); 509 | } 510 | else { 511 | return detail::powConst_powers[exponent]; 512 | } 513 | } 514 | 515 | /** 516 | * @brief Computes the floored logarithm of a number with a given base. 517 | * 518 | * Examples: 519 | * logFloor<10>(0) = 0 520 | * logFloor<10>(5) = 0 521 | * logFloor<10>(10) = 1 522 | * logFloor<10>(123) = 2 523 | * 524 | * Note that unlike a traditional logarithm function, it is defined for 0 and is equal to 0. 525 | * 526 | * @see https://stackoverflow.com/q/63411054 527 | * @tparam BASE the base (e.g. 10) 528 | * @param val the input value 529 | * @return floor(log(val, BASE)) 530 | */ 531 | template 532 | [[nodiscard]] constexpr auto logFloor(const Uint val) noexcept 533 | -> std::enable_if_t<(std::is_unsigned_v && BASE >= 2), Uint> 534 | { 535 | if constexpr (isPow2(BASE)) { 536 | return log2floor(val) / log2floor(BASE); 537 | } 538 | else { 539 | constexpr detail::LogFloorGuesser guesser; 540 | constexpr auto &powers = detail::logFloor_powers; 541 | using table_value_type = typename decltype(detail::logFloor_powers)::value_type; 542 | 543 | if constexpr (sizeof(Uint) < sizeof(table_value_type) || guesser.maxGuess() + 2 < powers.size()) { 544 | // handle the special case where our integer is narrower than the type of the powers table, 545 | // or by coincidence, the greatest guessed power of BASE is representable by the powers table. 546 | // e.g. for base 10: 547 | // greatest guess for 64-bit is floor(log10(2^63)) = 18 548 | // greatest representable power of 10 for 64-bit is pow(10, 19) 549 | // pow(10, 18 + 1) <= pow(10, 19) => we can always access powers[guess + 1] 550 | // BUT 551 | // greatest guess for 8-bit is floor(log10(2^7)) = 2 552 | // greatest representable power of 10 for 8-bit is pow(10, 2) = 100 553 | // pow(10, 2 + 1) > pow(10, 2) => we can not always access powers[guess + 1] 554 | // (however, the powers table is not made of 8-bit integers, so we actually can) 555 | const unsigned char guess = guesser(log2floor(val)); 556 | return guess + (val >= powers[guess + 1]); 557 | } 558 | else { 559 | // ALTERNATIVE from: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 560 | // This version is always safe from overflow for any non-powers of two. 561 | // However, we want zero-preservation and need an additional zero-check compared to the linked page. 562 | const unsigned char guess = guesser(log2floor(val) + 1); 563 | return (guess - (val < powers[guess])) * (val != 0); 564 | } 565 | } 566 | } 567 | 568 | /** 569 | * @brief Convenience function that forwards to logFloor<10>. 570 | */ 571 | template 572 | [[nodiscard]] constexpr auto log10floor(const Uint val) noexcept -> std::enable_if_t, Uint> 573 | { 574 | return logFloor<10, Uint>(val); 575 | } 576 | 577 | /** 578 | * @brief Computes the number of digits required to represent a number with a given base. 579 | */ 580 | template 581 | [[nodiscard]] constexpr auto digitCount(const Uint val) noexcept 582 | -> std::enable_if_t<(std::is_unsigned_v && BASE >= 2), Uint> 583 | { 584 | return logFloor(val) + 1; 585 | } 586 | 587 | } // namespace bitmanip 588 | 589 | #endif 590 | --------------------------------------------------------------------------------