/* SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once /** \file * \ingroup bli * * This file provides the basis for processing "indexed bits" (i.e. every bit has an index). * The main purpose of this file is to define how bits are indexed within a memory buffer. * For example, one has to define whether the first bit is the least or most significant bit and * how endianness affect the bit order. * * The order is defined as follows: * - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual. * - Within each #BitInt, the bits are ordered from least to most significant. */ #include "BLI_index_range.hh" #include "BLI_utildefines.h" #include namespace blender::bits { /** Using a large integer type is better because then it's easier to process many bits at once. */ using BitInt = uint64_t; /** Number of bits that fit into #BitInt. */ static constexpr int64_t BitsPerInt = int64_t(sizeof(BitInt) * 8); /** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */ static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(BitInt) >= 2) + (sizeof(BitInt) >= 4) + (sizeof(BitInt) >= 8); /** Bit mask containing a 1 for the last few bits that index a bit inside of an #BitInt. */ static constexpr BitInt BitIndexMask = (BitInt(1) << BitToIntIndexShift) - 1; inline BitInt mask_first_n_bits(const int64_t n) { BLI_assert(n >= 0); BLI_assert(n <= BitsPerInt); if (n == BitsPerInt) { return BitInt(-1); } return (BitInt(1) << n) - 1; } inline BitInt mask_last_n_bits(const int64_t n) { return ~mask_first_n_bits(BitsPerInt - n); } inline BitInt mask_range_bits(const int64_t start, const int64_t size) { BLI_assert(start >= 0); BLI_assert(size >= 0); const int64_t end = start + size; BLI_assert(end <= BitsPerInt); if (end == BitsPerInt) { return mask_last_n_bits(size); } return ((BitInt(1) << end) - 1) & ~((BitInt(1) << start) - 1); } inline BitInt mask_single_bit(const int64_t bit_index) { BLI_assert(bit_index >= 0); BLI_assert(bit_index < BitsPerInt); return BitInt(1) << bit_index; } inline BitInt *int_containing_bit(BitInt *data, const int64_t bit_index) { return data + (bit_index >> BitToIntIndexShift); } inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_index) { return data + (bit_index >> BitToIntIndexShift); } /** * This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but * not changed. */ class BitRef { private: /** Points to the exact integer that the bit is in. */ const BitInt *int_; /** All zeros except for a single one at the bit that is referenced. */ BitInt mask_; friend class MutableBitRef; public: BitRef() = default; /** * Reference a specific bit in an array. Note that #data does *not* have to point to the * exact integer the bit is in. */ BitRef(const BitInt *data, const int64_t bit_index) { int_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); } /** * Return true when the bit is currently 1 and false otherwise. */ bool test() const { const BitInt value = *int_; const BitInt masked_value = value & mask_; return masked_value != 0; } operator bool() const { return this->test(); } }; /** * Similar to #BitRef, but also allows changing the referenced bit. */ class MutableBitRef { private: /** Points to the integer that the bit is in. */ BitInt *int_; /** All zeros except for a single one at the bit that is referenced. */ BitInt mask_; public: MutableBitRef() = default; /** * Reference a specific bit in an array. Note that #data does *not* have to point to the * exact int the bit is in. */ MutableBitRef(BitInt *data, const int64_t bit_index) { int_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); } /** * Support implicitly casting to a read-only #BitRef. */ operator BitRef() const { BitRef bit_ref; bit_ref.int_ = int_; bit_ref.mask_ = mask_; return bit_ref; } /** * Return true when the bit is currently 1 and false otherwise. */ bool test() const { const BitInt value = *int_; const BitInt masked_value = value & mask_; return masked_value != 0; } operator bool() const { return this->test(); } /** * Change the bit to a 1. */ void set() { *int_ |= mask_; } /** * Change the bit to a 0. */ void reset() { *int_ &= ~mask_; } /** * Change the bit to a 1 if #value is true and 0 otherwise. If the value is highly unpredictable * by the CPU branch predictor, it can be faster to use #set_branchless instead. */ void set(const bool value) { if (value) { this->set(); } else { this->reset(); } } /** * Does the same as #set, but does not use a branch. This is faster when the input value is * unpredictable for the CPU branch predictor (best case for this function is a uniform random * distribution with 50% probability for true and false). If the value is predictable, this is * likely slower than #set. */ void set_branchless(const bool value) { const BitInt value_int = BitInt(value); BLI_assert(ELEM(value_int, 0, 1)); const BitInt old = *int_; *int_ = /* Unset bit. */ (~mask_ & old) /* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */ | (mask_ & ~(value_int - 1)); } MutableBitRef &operator|=(const bool value) { if (value) { this->set(); } return *this; } MutableBitRef &operator&=(const bool value) { if (!value) { this->reset(); } return *this; } }; inline std::ostream &operator<<(std::ostream &stream, const BitRef &bit) { return stream << (bit ? "1" : "0"); } inline std::ostream &operator<<(std::ostream &stream, const MutableBitRef &bit) { return stream << BitRef(bit); } } // namespace blender::bits namespace blender { using bits::BitRef; using bits::MutableBitRef; } // namespace blender