Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
bitfield.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022-2025 Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11#ifndef JAU_BITFIELD_HPP_
12#define JAU_BITFIELD_HPP_
13
14#include <unistd.h>
15
16#include <array>
17#include <climits>
18#include <cmath>
19#include <cstring>
20#include <limits>
21#include <string_view>
22
23#include <jau/basic_types.hpp>
24#include <jau/byte_util.hpp>
25#include <jau/cpp_lang_util.hpp>
26#include <jau/int_math.hpp>
27#include <jau/int_math_ct.hpp>
28#include <jau/int_types.hpp>
29#include <jau/string_util.hpp>
30#include <jau/type_concepts.hpp>
31
32namespace jau {
33
34 /** \addtogroup ByteUtils
35 *
36 * @{
37 */
38
39 /**
40 * Simple statically sized bitfield template for efficient bit storage access.
41 *
42 * Bit-position and bit-order are in least-significant-bits (lsb) first.
43 *
44 * Implementations utilizes an in-memory `std::array<StorageType, (BitSize+StorageTypeBits-1)/StorageTypeBits>`
45 * with unsigned integral StorageType of sizeof(StorageType) <= sizeof(size_t).
46 *
47 * Similar to std::bitset, but providing custom methods.
48 *
49 * @see jau::bitheap
50 */
51 template<jau::req::unsigned_integral StorageType, size_t BitSize>
52 requires requires (StorageType) { sizeof(StorageType) <= sizeof(size_t); }
53 class bitfield_t {
54 public:
55 typedef StorageType unit_type; ///< Unit data type
56 typedef size_t size_type; ///< size_t data type, bit position and count
57 static constexpr size_type unit_byte_size = sizeof(unit_type); ///< One unit size in bytes
58 static constexpr size_type unit_bit_size = unit_byte_size * CHAR_BIT; ///< One unit size in bits
59 static constexpr size_type unit_shift = jau::log2_byteshift(unit_byte_size); ///< One unit shift amount
60 static constexpr size_type bit_size = BitSize; ///< Storage size in bits
61
62 /** Returns storage size in bits */
63 constexpr size_type size() const noexcept { return bit_size; }
64
65 /// Storage size in units
66 static constexpr size_type unit_size = std::max<size_type>(1, (bit_size + unit_bit_size - 1) >> unit_shift);
67
68 private:
69 static constexpr unit_type one_u = 1;
70
71 typedef std::array<unit_type, unit_size> storage_t;
72 storage_t storage;
73
74 public:
75 static constexpr bool in_range(size_type bitpos) { return bitpos < bit_size; }
76 static constexpr bool in_range(size_type bitpos, size_type length) {
77 return bitpos + length <= bit_size;
78 }
79
80 /** Constructs an empty bitfield instance */
81 constexpr bitfield_t() noexcept { clear(); }
82
83 /**
84 * Constructs a bitfield instance, initialized with `bitstr` msb bit-pattern.
85 * @param bitstr most-significat (msb) bit string pattern
86 * @throws jau::IllegalArgumentError if bitstr put failed
87 * @see put(std::string_view)
88 */
89 bitfield_t(std::string_view bitstr) {
90 clear();
91 if( !put(0, bitstr) ) {
92 throw jau::IllegalArgumentError("Invalid bit-patter "+std::string(bitstr), E_FILE_LINE);
93 }
94 }
95
96 /*** Clears whole bitfield, i.e. sets all bits to zero. */
97 constexpr void clear() noexcept {
98 std::memset(storage.data(), 0, unit_byte_size * unit_size);
99 }
100 /*** Clears whole bitfield, i.e. sets all bits to zero. */
101 constexpr bitfield_t &reset() noexcept {
102 clear();
103 return *this;
104 }
105
106 /*** Reads the bit value at position `bitpos`. */
107 constexpr bool operator[](size_type bitpos) const noexcept { return get(bitpos); }
108
109 /*** Reads the bit value at position `bitpos`. */
110 constexpr bool get(size_type bitpos) const noexcept {
111 if ( !in_range(bitpos) ) {
112 return false;
113 }
114 const size_type u = bitpos >> unit_shift;
115 const size_type b = bitpos - (u << unit_shift);
116 return storage[u] & (one_u << b);
117 }
118
119 /**
120 * Writes the bit value `v` to position `bitpos` into this storage.
121 * @returns true on success, otherwise false
122 */
123 constexpr bool put(size_type bitpos, bool v) noexcept {
124 if ( !in_range(bitpos) ) {
125 return false;
126 }
127 const size_type u = bitpos >> unit_shift;
128 const size_type b = bitpos - (u << unit_shift);
129 const unit_type m = one_u << b;
130 if ( v ) {
131 storage[u] |= m;
132 } else {
133 storage[u] &= ~m;
134 }
135 return true;
136 }
137
138 /**
139 * Flips the bit value at position `bitpos` in this storage.
140 * @returns true on success, otherwise false
141 */
142 constexpr bool flip(size_type bitpos) noexcept {
143 if ( !in_range(bitpos) ) {
144 return false;
145 }
146 const size_type u = bitpos >> unit_shift;
147 const size_type b = bitpos - (u << unit_shift);
148 const unit_type m = one_u << b;
149 if ( storage[u] & m ) {
150 storage[u] &= ~m;
151 } else {
152 storage[u] |= m;
153 }
154 return true;
155 }
156
157 /*** Flips all bits in this storage. */
158 constexpr bitfield_t &flip() noexcept {
159 size_type remaining = bit_size;
160 for ( size_type i = 0; i < unit_size; ++i ) {
161 storage[i] = ~storage[i] & bit_mask<unit_type>(remaining);
162 remaining -= unit_bit_size;
163 }
164 return *this;
165 }
166
167 /*** Reverse all bits in this storage. */
168 constexpr bitfield_t &reverse() noexcept {
169 const size_type s0 = bit_size & (unit_bit_size-1); // % unit_bit_size;
170 if( 0 == s0 ) { // fast-path, swap units
171 size_type l = 0, r = unit_size-1;
172 while ( l < r ) {
173 const unit_type v_l = jau::rev_bits(storage[l]);
174 const unit_type v_r = jau::rev_bits(storage[r]);
175 storage[l++] = v_r;
176 storage[r--] = v_l;
177 }
178 if( l == r ) { // last odd middle
179 storage[l] = jau::rev_bits(storage[l]);
180 }
181 } else { // slow-path, swap bits
182 for(size_type l = 0, r = bit_size-1; l < r; ++l, --r) {
183 const bool s = get(l);
184 (void)put(l, get(r));
185 (void)put(r, s);
186 }
187 }
188 return *this;
189 }
190 /**
191 * Sets the bit at position `bitpos` of this storage.
192 * @returns true on success, otherwise false
193 */
194 constexpr bool set(size_type bitpos) noexcept { return put(bitpos, true); }
195 /**
196 * Clear the bit at position `bitpos` of this storage.
197 * @returns true on success, otherwise false
198 */
199 constexpr bool clr(size_type bitpos) noexcept { return put(bitpos, false); }
200 /**
201 * Clear the bit at position `bitpos` of this storage.
202 * @returns true on success, otherwise false
203 */
204 constexpr bool reset(size_type bitpos) noexcept { return put(bitpos, false); }
205
206 /**
207 * Copies the bit at position `srcBitpos` to position `dstBitpos`, returning the copied bit-value.
208 * @returns true on success, otherwise false
209 */
210 bool copy(size_type srcBitpos, size_type dstBitpos) noexcept {
211 return put(dstBitpos, get(srcBitpos));
212 }
213
214 /*** Reads `length` bits from this storage, starting with the lowest bit from the storage position `bitpos`.*/
215 unit_type getUnit(size_type bitpos, size_type length) const noexcept {
216 if ( 0 == length || length > unit_bit_size || !in_range(bitpos, length) ) {
217 return 0;
218 }
219 const size_type u = bitpos >> unit_shift;
220 const size_type b = bitpos - (u << unit_shift);
221 if ( 0 == b ) {
222 // fast path
223 const unit_type m = bit_mask<unit_type>(length); // mask of chunk
224 return m & storage[u];
225 } else {
226 // slow path
227 const size_type left = unit_bit_size - b; // remaining bits of first chunk storage
228 const size_type l1 = std::min(length, left); // length of first chunk < unit_bit_size
229 const unit_type m1 = (one_u << l1) - one_u; // mask of first chunk
230 const unit_type d1 = m1 & (storage[u] >> b); // data of first chunk
231 const size_type l2 = length - l1; // length of second chunk < unit_bit_size
232 if ( l2 > 0 ) {
233 const unit_type m2 = (one_u << l2) - one_u; // mask of second chunk
234 return d1 | ((m2 & storage[u + 1]) << l1); // data combined chunk 1+2
235 } else {
236 return d1; // data of chunk 1 only
237 }
238 }
239 }
240
241 /**
242 * Writes `length` bits of given `data` into this storage, starting with the lowest bit from the storage position `bitpos`.
243 * @returns true on success, otherwise false
244 */
245 bool putUnit(size_type bitpos, size_type length, unit_type data) noexcept {
246 if ( 0 == length ) {
247 return true;
248 } else if ( length > unit_bit_size || !in_range(bitpos, length) ) {
249 return false;
250 }
251 const size_type u = bitpos >> unit_shift;
252 const size_type b = bitpos - (u << unit_shift);
253 if ( 0 == b ) {
254 // fast path
255 const unit_type m = bit_mask<unit_type>(length); // mask of chunk
256 storage[u] = ((~m) & storage[u]) // keep non-written storage bits
257 | (m & data); // overwrite storage w/ used data bits
258 } else {
259 // slow path
260 const size_type left = unit_bit_size - b; // remaining bits of first chunk storage
261 const size_type l1 = std::min(length, left); // length of first chunk < unit_bit_size
262 const unit_type m1 = (one_u << l1) - one_u; // mask of first chunk
263 storage[u] = ((~(m1 << b)) & storage[u]) // keep non-written storage bits
264 | ((m1 & data) << b); // overwrite storage w/ used data bits
265 const size_type l2 = length - l1; // length of second chunk < unit_bit_size
266 if ( l2 > 0 ) {
267 const unit_type m2 = (one_u << l2) - one_u; // mask of second chunk
268 storage[u + 1] = ((~m2) & storage[u + 1]) // keep non-written storage bits
269 | (m2 & (data >> l1)); // overwrite storage w/ used data bits
270 }
271 }
272 return true;
273 }
274
275 /**
276 * Writes `bitstr` msb bit-pattern into this storage, starting with the lowest bit from the storage position `bitpos`.
277 * @param bitpos bit position to insert
278 * @param bitstr most-significat (msb) bit string pattern
279 * @returns true on success, otherwise false
280 */
281 bool put(size_type bitpos, std::string_view bitstr) noexcept {
282 if ( 0 == bitstr.size() ) {
283 return true;
284 } else if ( !in_range(bitpos, bitstr.size()) ) {
285 return false;
286 }
287 size_type left = bitstr.size();
288 size_type strPos = left - std::min(unit_bit_size, left);
289 while(left > 0) {
290 size_type len = std::min(unit_bit_size, left);
291 std::string_view segment = bitstr.substr(strPos, len);
292 const auto [val, sz, ok] = jau::fromBitString(segment, bit_order_t::msb);
293 if( !ok || sz != len || !putUnit(bitpos, len, unit_type(val)) ) {
294 return false;
295 }
296 bitpos += len;
297 strPos -= len;
298 left -= len;
299 }
300 return true;
301 }
302
303 /**
304 * Set `length` bits starting at `bitpos` of this bitfield to the given value `bit`.
305 * @returns true on success, otherwise false
306 */
307 bool set(size_type bitpos, size_type length, bool bit) noexcept {
308 if ( 0 == length ) {
309 return true;
310 } else if ( !in_range(bitpos, length) ) {
311 return false;
312 }
313 const unit_type v = bit ? std::numeric_limits<unit_type>::max() : 0;
314 size_type remaining = length;
315 size_type u = bitpos >> unit_shift;
316 {
317 const size_type b = bitpos - (u << unit_shift);
318 if ( b > 0 ) {
319 const size_type l = std::min(unit_bit_size-b, remaining);
320 if (!putUnit(bitpos, l, v)) { // first incomplete unit
321 return false;
322 }
323 remaining -= l;
324 bitpos += l;
325 u = bitpos >> unit_shift;
326 }
327 }
328 while ( remaining > unit_bit_size ) {
329 storage[u++] = v;
330 bitpos += unit_bit_size;
331 remaining -= unit_bit_size;
332 }
333 if ( remaining > 0 ) {
334 const size_type l = std::min(unit_bit_size, remaining);
335 if (!putUnit(bitpos, l, v)) { // last incomplete unit
336 return false;
337 }
338 remaining -= l;
339 }
340 assert(0 == remaining);
341 (void)remaining;
342 return true;
343 }
344 /** Set all bits of this bitfield to the given value `bit`. */
345 bitfield_t &setAll(bool bit) noexcept {
346 (void)set(0, bit_size, bit);
347 return *this;
348 }
349
350 /**
351 * Copies `length` bits at position `srcBitpos` to position `dstBitpos`, returning the copied bits.
352 * @returns true on success, otherwise false
353 */
354 bool copyUnit(size_type srcBitpos, size_type dstBitpos, size_type length) noexcept {
355 return putUnit(dstBitpos, length, getUnit(srcBitpos, length));
356 }
357
358 /*** Returns the number of set bits within this bitfield. */
359 size_type count() const noexcept {
360 size_type c = 0;
361 for ( size_type i = 0; i < unit_size; ++i ) {
362 c += jau::bit_count(storage[i]);
363 }
364 return c;
365 }
366 constexpr bool operator==(const bitfield_t &rhs) const noexcept {
367 if ( this == &rhs ) {
368 return true;
369 }
370 if ( unit_size != rhs.unit_size ) {
371 return false;
372 }
373 size_type i = 0;
374 while ( i < unit_size && storage[i] == rhs.storage[i] ) {
375 ++i;
376 }
377 return i == unit_size;
378 }
379
380 template<size_t OBitSize>
381 bool put(size_t bitpos, const bitfield_t<StorageType, OBitSize>& o) {
382 size_t length = o.bit_size;
383 if ( 0 == length ) {
384 return true;
385 } else if ( !in_range(bitpos, length) ) {
386 return false;
387 }
388 const size_type unit_count = ( length + unit_bit_size - 1 ) >> unit_shift;
389 const size_type unit_pos = bitpos >> unit_shift;
390 const size_type unit_bit_pos = bitpos - (unit_pos << unit_shift);
391 if ( 0 == unit_bit_pos ) {
392 size_type l = std::min(length, unit_bit_size);
393 size_type i = bitpos;
394 for ( size_type u = 0; u < unit_count && 0 < length; ++u ) {
395 if( !putUnit( i, l, o.storage[u] ) ) {
396 return false;
397 }
398 length -= l;
399 i += l;
400 l = std::min(length, unit_bit_size);
401 }
402 } else {
403 for ( size_type i = 0; i < length; ++i ) {
404 if( !put(bitpos + i, o.get(i)) ) {
405 return false;
406 }
407 }
408 }
409 return true;
410 }
411
412 template<size_t BitLength>
413 std::pair<bitfield_t<StorageType, BitLength>, bool> subbits(size_type bitpos) const noexcept {
414 if ( 0 == BitLength ) {
415 return { bitfield_t<StorageType, BitLength>(), true };
416 } else if ( !in_range(bitpos, BitLength) ) {
417 return { bitfield_t<StorageType, BitLength>(), false };
418 }
419 std::pair<bitfield_t<StorageType, BitLength>, bool> r{ bitfield_t<StorageType, BitLength>(), true };
420 size_type length = BitLength;
421 const size_type unit_count = ( BitLength + unit_bit_size - 1 ) >> unit_shift;
422 const size_type unit_pos = bitpos >> unit_shift;
423 const size_type unit_bit_pos = bitpos - (unit_pos << unit_shift);
424 if ( 0 == unit_bit_pos ) {
425 size_type l = std::min(length, unit_bit_size);
426 size_type i = 0;
427 for ( size_type u = unit_pos; u < unit_count && 0 < length; ++u ) {
428 if( !r.first.putUnit( i, l, storage[u] ) ) {
429 return { bitfield_t<StorageType, BitLength>(), false };
430 }
431 length -= l;
432 i += l;
433 l = std::min(length, unit_bit_size);
434 }
435 } else {
436 for(size_type i = 0; i < length; ++i) {
437 if( !r.first.put(i, get(bitpos+i)) ) {
438 return { bitfield_t<StorageType, BitLength>(), false };
439 }
440 }
441 }
442 return r;
443 }
444
445 std::string toString(size_type bitpos, size_type length, PrefixOpt prefix = PrefixOpt::none) const noexcept {
446 if ( 0 == length || !in_range(bitpos, length) ) {
447 return "";
448 }
449 std::string r;
450 r.reserve(length + (prefix == PrefixOpt::none ? 0 : 2));
451 if ( prefix == PrefixOpt::prefix ) {
452 r.append("0b");
453 }
454 const size_type unit_count = ( length + unit_bit_size - 1 ) >> unit_shift;
455 const size_type unit_pos = bitpos >> unit_shift;
456 const size_type bit_pos = bitpos - (unit_pos << unit_shift);
457 if ( 0 == bit_pos ) {
458 // fast path
459 const size_type sz0 = (unit_size - 1) * unit_bit_size;;
460 size_type l = length > sz0 ? length - sz0 : std::min(length, unit_bit_size);
461 for ( size_type i = unit_pos + unit_count; i-- > unit_pos && 0 < length; ) {
462 r.append( jau::toBitString(storage[i], bit_order_t::msb, PrefixOpt::none, l) );
463 length -= l;
464 l = std::min(length, unit_bit_size);
465 }
466 } else {
467 size_type i = bitpos + length;
468 while(length-- > 0) {
469 r.push_back(get(--i) ? '1' : '0');
470 }
471 }
472 return r;
473 }
474 std::string toString(PrefixOpt prefix = PrefixOpt::none) const noexcept {
475 return toString(0, bit_size, prefix);
476 }
477
478 std::string infoString() const noexcept {
479 return "bitfield[unit[bits "+std::to_string(unit_bit_size)+", count "+std::to_string(unit_size)+
480 "], bits"+std::to_string(bit_size)+": "+toString()+"]";
481 }
482 };
483
484 template<jau::req::unsigned_integral StorageType, size_t BitSize>
485 requires requires (StorageType) { sizeof(StorageType) <= sizeof(size_t); }
486 inline std::ostream &operator<<(std::ostream &out, const bitfield_t<StorageType, BitSize> &v) {
487 return out << v.toString();
488 }
489
490 /**
491 * Simple bitfield template for efficient bit storage access.
492 *
493 * Implementations utilizes an in-memory `std::array<StorageType = jau::nsize_t, (BitSize+StorageTypeBits-1)/StorageTypeBits>`.
494 *
495 * @see jau::bitfield_t
496 * @see jau::bitheap
497 * @see jau::nsize_t
498 */
499 template<size_t BitSize>
501
502 /**@}*/
503
504} // namespace jau
505
506#endif /* JAU_BITFIELD_HPP_ */
String toString()
#define E_FILE_LINE
Simple statically sized bitfield template for efficient bit storage access.
Definition bitfield.hpp:53
constexpr bitfield_t & reset() noexcept
Definition bitfield.hpp:101
static constexpr bool in_range(size_type bitpos)
Definition bitfield.hpp:75
size_t size_type
size_t data type, bit position and count
Definition bitfield.hpp:56
constexpr bitfield_t & flip() noexcept
Definition bitfield.hpp:158
std::pair< bitfield_t< StorageType, BitLength >, bool > subbits(size_type bitpos) const noexcept
Definition bitfield.hpp:413
constexpr bool operator==(const bitfield_t &rhs) const noexcept
Definition bitfield.hpp:366
bool set(size_type bitpos, size_type length, bool bit) noexcept
Definition bitfield.hpp:307
bool putUnit(size_type bitpos, size_type length, unit_type data) noexcept
Definition bitfield.hpp:245
bitfield_t(std::string_view bitstr)
Definition bitfield.hpp:89
bool put(size_t bitpos, const bitfield_t< StorageType, OBitSize > &o)
Definition bitfield.hpp:381
bitfield_t & setAll(bool bit) noexcept
Definition bitfield.hpp:345
constexpr bool put(size_type bitpos, bool v) noexcept
Writes the bit value v to position bitpos into this storage.
Definition bitfield.hpp:123
constexpr void clear() noexcept
Definition bitfield.hpp:97
std::string toString(PrefixOpt prefix=PrefixOpt::none) const noexcept
Definition bitfield.hpp:474
bool put(size_type bitpos, std::string_view bitstr) noexcept
Definition bitfield.hpp:281
std::string infoString() const noexcept
Definition bitfield.hpp:478
constexpr bool clr(size_type bitpos) noexcept
Definition bitfield.hpp:199
constexpr bool set(size_type bitpos) noexcept
Definition bitfield.hpp:194
constexpr bitfield_t() noexcept
Constructs an empty bitfield instance.
Definition bitfield.hpp:81
constexpr bitfield_t & reverse() noexcept
Definition bitfield.hpp:168
bool copyUnit(size_type srcBitpos, size_type dstBitpos, size_type length) noexcept
Definition bitfield.hpp:354
static constexpr size_type unit_byte_size
Definition bitfield.hpp:57
std::string toString(size_type bitpos, size_type length, PrefixOpt prefix=PrefixOpt::none) const noexcept
Definition bitfield.hpp:445
static constexpr bool in_range(size_type bitpos, size_type length)
Definition bitfield.hpp:76
size_type count() const noexcept
Definition bitfield.hpp:359
bool copy(size_type srcBitpos, size_type dstBitpos) noexcept
Definition bitfield.hpp:210
StorageType unit_type
Unit data type.
Definition bitfield.hpp:55
constexpr bool operator[](size_type bitpos) const noexcept
Definition bitfield.hpp:107
unit_type getUnit(size_type bitpos, size_type length) const noexcept
Definition bitfield.hpp:215
constexpr bool reset(size_type bitpos) noexcept
Definition bitfield.hpp:204
constexpr bool get(size_type bitpos) const noexcept
Definition bitfield.hpp:110
constexpr size_type size() const noexcept
Definition bitfield.hpp:63
constexpr bool flip(size_type bitpos) noexcept
Definition bitfield.hpp:142
bitfield_t< jau::nsize_t, BitSize > bitfield
Simple bitfield template for efficient bit storage access.
Definition bitfield.hpp:500
static constexpr T bit_mask(size_t n) noexcept
Returns the T bit mask of n-bits, i.e.
std::ostream & operator<<(std::ostream &out, const bitfield_t< StorageType, BitSize > &v)
Definition bitfield.hpp:486
constexpr uint8_t rev_bits(uint8_t v) noexcept
Reverse bits of one byte.
constexpr size_t log2_byteshift(const size_t bytesize) noexcept
Returns log2(bytesize*8), e.g.
Definition int_math.hpp:150
constexpr size_t bit_count(T n) noexcept
Returns the number of set bits within given unsigned integral.
Definition int_math.hpp:220
std::string toBitString(const void *data, const nsize_t length, const bit_order_t bitOrder=bit_order_t::msb, const PrefixOpt prefix=PrefixOpt::prefix, size_t bit_len=0) noexcept
Produce a binary string representation of the given lsb-first byte values.
SizeBoolPair fromBitString(std::vector< uint8_t > &out, const uint8_t bitstr[], const size_t bitstr_len, const bit_order_t bitOrder=bit_order_t::msb, const Bool checkPrefix=Bool::True) noexcept
Converts a given binary string representation into a byte vector, lsb-first.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
@ msb
Identifier for most-significant-bit (msb) first.
static const Mat4f m1(m1_0)
static const Mat4f m2(m2_0)