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