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