ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
bitbuffer.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_bitbuffer of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8ALIB_EXPORT namespace alib { namespace bitbuffer {
9
10//==================================================================================================
11/// An array of integral values used for serializing and deserializing data on bit-level.
12/// While writing and reading bits is performed with associated classes #"BitWriter"
13/// and #"BitReader", this class is responsible for storing the data and transferring
14/// it to an <em>integral</em>-arrays, which may, for example, be written and read to and from
15/// <c>std::[i/o]stream</c> objects.
16/// With this, platform independence is guaranteed (in respect to little/big-endian storage and
17/// similar matters).
18///
19/// This class is abstract (pure virtual), and does not perform the allocation.
20/// With #"BitBuffer", #"BitBufferMA" and #"BitBufferLocal",
21/// three descendants with different allocation strategies are provided.
22/// A customized version may be easily created by implementing pure virtual methods
23/// #".Capacity" and #".EnsureCapacity".
24///
25/// \attention
26/// To avoid the use of virtual function calls bit write operations, methods #".Capacity" and
27/// #".EnsureCapacity" are <b>not invoked automatically!</b>.
28/// In contrast, it is the users' responsibility to invoke these methods (or only
29/// #".EnsureCapacity") before performing data insertions.<br>
30/// This behavior is a design decision to maximize execution performance, hence rather a feature.
31///
32/// Own Implementations have to set field #".data" when reallocating the internal buffer.
33//==================================================================================================
35 public:
36 /// The storage type of bit buffers. This is chosen as being <c>unsigned int</c>, which
37 /// should be the "fasted" integral type for any compiler/platform combination.
38 using TStorage= unsigned int;
39
40 static_assert( bitsof(TStorage) == 32 || bitsof(TStorage) == 64,
41 "Unsupported size of C++ int type" );
42
43 /// Defines a bit position within outer class #"BitBufferBase". A bit position is
44 /// determined by the index in the storage array along with the number of the currently
45 /// written (or read) bit. Types #"BitWriter" and #"BitReader"
46 /// use this type to define their current write (read) position.
47 ///
48 /// Methods #"Encode32" and #"Encode64" shorten the information, by storing the bit position in
49 /// the upper bits of a \e 32, respectively \e 64 bit value. This is useful whenever a
50 /// broader number of bit buffer indices are to be stored. The use case to mention here is
51 /// "lazy decoding of data", where only the index to the bit buffer is kept in memory.)
52 /// positions
53 class Index {
54 #if !DOXYGEN
55 friend class BitBufferBase;
56 friend class BitReader;
57 friend class BitWriter;
58 #endif
59
60 uinteger pos= 0; ///< Index of the current word to read/write.
61 lang::ShiftOpRHS bit= 0; ///< Current bit index in the current word.
62
63 public:
64
65 /// Default constructor initializing members #"pos" and #".bit" to zero.
66 Index() =default;
67
68 /// Constructor
69 /// @param pPos The initial value for member #"pos".
70 /// @param pBit The initial value for member #".bit".
72 : pos(pPos)
73 , bit(pBit) {}
74
75 /// Returns the index of the actual storage word in the buffer.
76 /// @return The index of the current word containing the next bit to read/write.
77 uinteger Pos() const { return pos; }
78
79 /// Returns the number of the actual bit in the actual word of the buffer buffer.
80 /// @return The number of the next bit to read/write.
81 lang::ShiftOpRHS Bit() const { return bit; }
82
83 /// Returns true, if the next bit to read/write is the first of the current storage
84 /// word in the buffer. Alignment of buffers may become important when buffers are
85 /// serialized (e.g., to mass storage devices). Method
86 /// #"BitBufferBase::Terminate;*" may be used to receive an aligned
87 /// index.
88 ///
89 /// @return The result of <c>Bit() == 0</c>.
90 bool IsAligned() const { return bit == 0; }
91
92 /// Sets this index to zero, hence pointing to the first bit in the buffer.
93 void Clear() { pos= 0; bit= 0; }
94
95 /// Returns the size of the memory from given \p{startIdx} to this index occupied by
96 /// the internal storage words of the buffer.
97 ///
98 /// @param startIdx The starting index. Defaults to <c>{0,0}</c>.
99 /// @return The size of the buffer (part) in bytes.
100 integer GetByteOffset( Index startIdx= Index(0, 0) ) const {
101 ALIB_ASSERT_ERROR( startIdx.pos < pos
102 || (startIdx.pos == pos && startIdx.bit < bit), "BITBUFFER",
103 "Given buffer start index is greater than this index." )
104 return integer((pos - startIdx.Pos()) * sizeof(TStorage) );
105 }
106
107 /// Sets this index to point to the word and bit given by a byte offset.<br>
108 /// This method is useful when bit buffers are deserialized from character streams.
109 /// @param byteOffset The position within the buffer in bytes.
110 void SetFromByteOffset( uinteger byteOffset )
111 {
112 pos= byteOffset / sizeof( TStorage );
113 bit= (byteOffset % sizeof( TStorage )) * 8 ;
114 }
115
116 /// Returns the number of bits used in respect to this index.
117 /// @return The number of bits written or read.
119 { return uinteger( pos * bitsof(TStorage) + uinteger(bit) ); }
120
121 /// Encodes this index information into a 32-bit variable by using the upper 5 (or 6)
122 /// bits for the bit index. As a result, the possible value range of index data is
123 /// reduced. The reduction depends on the platform's size of type \c int. In case of
124 /// 32-bit, five bits are needed to store the bit position. In the case of 64-bit,
125 /// six bits are needed.<br>
126 /// As the underlying \e TStorage type changes as well, in both cases, the resulting
127 /// addressable storage bytes is limited to the same value:
128 /// - TStorage 64-bit: <em>2^(32-6) * 8 bytes = 512 megabytes</em>
129 /// - TStorage 32-bit: <em>2^(32-5) * 4 bytes = 512 megabytes</em>
130 ///
131 /// In case bit buffers grow to over half a gigabyte, 64-bit encoding should be performed
132 /// by using alternative method #"Encode64".
133 ///
134 /// @return The encoded index.
135 uint32_t Encode32() {
137 "BITBUFFER", "32bit too narrow for endcoding BitBuffer::Index." )
138 return uint32_t(pos) | (uint32_t(bit) << (31 - lang::Log2OfSize<TStorage>() ));
139 }
140
141 /// Encodes this index information into a 64-bit value by using the upper five (or six)
142 /// bits for the bit index.
143 ///
144 /// @see For a shorter encoding, limited to bit buffer sizes of 512 megabytes,
145 /// see method #"Encode32".
146 ///
147 /// @return The encoded index.
148 uint64_t Encode64()
149 { return uint64_t(pos) | (uint64_t(bit) << (63L - lang::Log2OfSize<TStorage>() )); }
150
151 /// Static method that decodes an index information, encoded with #"Encode32", to an
152 /// instance of this class.
153 ///
154 /// @param code The encoded information.
155 /// @return The decoded index.
156 static
157 Index Decode32(uint32_t code)
158 {
159 return Index { uinteger (code & lang::LowerMask< 31 - lang::Log2OfSize<TStorage>() , uint32_t>() ),
161 }
162
163
164 /// Static method that decodes an index information, encoded with #"Encode64", to an
165 /// instance of this class.
166 ///
167 /// @param code The encoded information.
168 /// @return The decoded index.
169 static
170 Index Decode64(uint64_t code) {
171 return Index{uinteger( code & lang::LowerMask< 63L - lang::Log2OfSize<TStorage>() , uint64_t>() ),
173 }
174
175 /// Comparison operator.
176 ///
177 /// @param rhs The right-hand side argument of the comparison.
178 /// @return \c true if this object equals \p{rhs}, \c false otherwise.
179 bool operator==(const Index& rhs) const { return (pos == rhs.pos) && (bit == rhs.bit); }
180
181 /// Comparison operator.
182 ///
183 /// @param rhs The right-hand side argument of the comparison.
184 /// @return \c true if this object does not equal \p{rhs}, \c false otherwise.
185 bool operator!=(const Index& rhs) const { return !(*this == rhs); }
186
187 /// Comparison operator.
188 ///
189 /// @param rhs The right-hand side argument of the comparison.
190 /// @return \c true if this object is smaller than \p{rhs}, \c false otherwise.
191 bool operator<(const Index& rhs) const {
192 return (pos < rhs.pos) || ((pos == rhs.pos)
193 && (bit < rhs.bit) );
194 }
195
196 /// Comparison operator.
197 ///
198 /// @param rhs The right-hand side argument of the comparison.
199 /// @return \c true if this object is smaller or equal than \p{rhs}, \c false otherwise.
200 bool operator<=(const Index& rhs) const {
201 return (pos < rhs.pos) || ((pos == rhs.pos)
202 && (bit <= rhs.bit) );
203 }
204
205 /// Comparison operator.
206 ///
207 /// @param rhs The right-hand side argument of the comparison.
208 /// @return \c true if this object is greater or equal than \p{rhs}, \c false otherwise.
209 bool operator>=(const Index& rhs) const { return !(*this < rhs); }
210
211 /// Comparison operator.
212 ///
213 /// @param rhs The right-hand side argument of the comparison.
214 /// @return \c true if this object is greater than \p{rhs}, \c false otherwise.
215 bool operator>(const Index& rhs) const { return !(*this <= rhs); }
216 }; // inner struct Index
217
218 protected:
219
220 /// A pointer to the storage. Implementations of this abstract type have to set this field
221 /// when reallocating the internal buffer.
223
224 public:
225 /// Default Constructor (the only one).
226 BitBufferBase() noexcept : data(nullptr) {}
227
228 /// Virtual destructor (does nothing, needed for abstract virtual class).
229 virtual
231
232 /// Virtual function to determine the (currently allocated) capacity.
233 /// @return The size of the internal storage in bits.
235 virtual uinteger Capacity() const =0;
236
237 /// Virtual function to reserve buffer space by optionally increasing the buffer to
238 /// enable the writing of the given bits.
239 ///
240 /// @param bitsRequired The number of bits required.
241 /// @param index The index to which the capacity is currently used.
242 /// @return \c true if the space is available or could be made available,
243 /// \c false otherwise.
245 virtual bool EnsureCapacity( uinteger bitsRequired, BitBufferBase::Index index ) =0;
246
247 /// Returns the storage word at the given position
248 /// @param index The index to read the word from. Note that the bit number in this value is
249 /// ignored.
250 /// @return The word at the given index.
251 TStorage GetWord(const Index& index) const { return data[index.pos]; }
252
253 /// Stores the given \p{value} at the given \p{index}.
254 /// @param index The index to read the word at. Note that the bit number in this value is
255 /// ignored.
256 /// @param value The value to store
257 void SetWord(const Index& index, TStorage value) { data[index.pos]= value; }
258
259 /// Returns the number of remaining bits in this buffer in relation to a given index.
260 /// @param idx An actual writing/reading position.
261 /// @return The number of bits dived remaining in this buffer.
262 uinteger RemainingSize(const Index& idx) const
263 { return Capacity() - idx.CountBits(); }
264
265 /// Returns the start of the internal storage.
266 /// @return A pointer to the data array provided by the decendent types.
267 TStorage* Data() const { return data; }
268
269 /// Returns the memory address of the internal storage word denoted by \p{idx}
270 /// reinterpreted to C++ type <c>char*</c>.
271 /// @param idx The index of the word to point to. The bit position within this index is
272 /// ignored.
273 /// @return A \c char pointer to the internal storage word the given index refers to.
274 char* CharStream( Index idx= Index(0, 0) )
275 { return reinterpret_cast<char*>( &data[idx.pos] ); }
276
277 /// Writes a termination bit of value \c 1 and lets this buffer's index point to the next
278 /// buffer word.\n
279 /// Termination can be undone using the result index of this method with #".Unterminate".
280 /// This method should be invoked before serializing a buffer and method
281 /// #".Unterminate" may be used after deserialization to continue writing to the buffer without
282 /// creating a gap.
283 ///
284 /// @param writerIndex The index to the last bit before termination.
285 ///
286 /// @return The #"BitBufferBase::Index::IsAligned;aligned" index after termination
287 /// which is aligned point to the first bit behind the last used storage word.
288 /// Such index may be later fed into method #".Unterminate" to undo the termination.
290 Index Terminate (Index writerIndex);
291
292 /// Removes the termination bit found in the word before given \p{terminationIndex}.
293 /// @param terminationIndex The index returned by previous invocation of method #".Terminate".
294 ///
295 /// @return The index of the next bit to write to the now unterminated buffer.
297 Index Unterminate(Index terminationIndex);
298
299 /// Converts the internal storage words into the platform-independent
300 /// "Little Endian Encoding", which means it may change the byte order within the storage
301 /// words of the buffer.
302 ///
303 /// This method is recommended to be used before writing buffer contents to a file to
304 /// make files system independent.
305 ///
306 /// \attention The start index needs to be aligned to a storage word. This is asserted
307 /// in debug compilations.
308 /// See method #"Index::IsAligned" for more information.
309 ///
310 /// \note It is recommended to terminate the buffer before using this method.
311 /// and to pass the index returned by method #".Terminate" as second parameter
312 /// \p{endIndex}.
313 ///
314 /// \see Method #".FromLittleEndianEncoding".
315 ///
316 /// @param startIndex The first storage word to convert. (The bit position of the index
317 /// is ignored).
318 /// @param endIndex The first bit behind the storage to be converted. Hence, if the bit
319 /// position within this argument is not \c 0, then the whole word that
320 /// this index points to, will be converted. Otherwise it won't.
322 void ToLittleEndianEncoding( const Index& startIndex,
323 const Index& endIndex );
324
325 /// The counter-method to #".ToLittleEndianEncoding".
326 ///
327 /// @param startIndex The first storage word to convert. (The bit position of the index
328 /// is ignored).
329 /// @param endIndex The first bit behind the storage to be converted. Hence, if the bit
330 /// position within this argument is not \c 0, then the whole word that
331 /// this index points to, will be converted. Otherwise it won't.
333 void FromLittleEndianEncoding( const Index& startIndex, const Index& endIndex );
334}; // class BitBufferBase
335
336//==================================================================================================
337/// A bit buffer using dynamic allocation.
338/// \see
339/// - Two alternatives are provided with #"BitBufferMA", which uses
340/// #"alib_mods_contmono;monotonic allocation" and #"BitBufferLocal", which uses
341/// local (stack) memory.
342/// - For this class, a #"alibtools_debug_helpers_gdb;pretty printer" for the
343/// GNU debugger is provided.
344//==================================================================================================
346 protected:
347 /// The vector that holds the data.
348 std::vector<TStorage> storage;
349
350 public:
351 /// Constructor.
352 /// @param initialCapacity The requested initial capacity of the buffer in bits.
353 BitBuffer( uinteger initialCapacity ) { EnsureCapacity(initialCapacity, Index()); }
354
355 /// Returns the (currently allocated) capacity.
356 /// @return The size of the internal storage in bits.
357 virtual uinteger Capacity() const override { return storage.capacity() * bitsof(TStorage); }
358
359 /// Checks if the given required storage space is internally reserved. If not,
360 /// the internal capacity is doubled, or, if more is required, set to the required space.
361 ///
362 /// @param bitsRequired The number of bits required.
363 /// @param idx The index of current buffer use.
364 /// @return \c true if the space is available or could be made available,
365 /// \c false otherwise.
366 virtual bool EnsureCapacity(uinteger bitsRequired, BitBufferBase::Index idx) override {
367 uinteger capacityNeeded= (idx.CountBits() + bitsRequired + (bitsof(TStorage) - 1) )
368 / bitsof(TStorage);
369 if( capacityNeeded > storage.capacity() )
370 storage.reserve( (std::max)( capacityNeeded, uinteger(storage.capacity()) * 2 ));
371 data= storage.data();
372
373 return true;
374 }
375}; // class BitBuffer
376
377//==================================================================================================
378/// A bit buffer using #"alib_mods_contmono;monotonic allocation".
379/// \see
380/// Two alternatives are provided with #"BitBuffer" and #"BitBufferLocal".
381//==================================================================================================
383 protected:
384 /// The monotonic allocator used internally to allocate the storage. This is provided
385 /// with construction.
387
388 /// The vector that holds the data.
390
391 public:
392 /// Constructor taking an external monotonic allocator and the initial capacity.
393 /// @param monoAllocator A reference to a monotonic allocator to use to allocate buffer
394 /// storage data.
395 /// @param initialCapacity The requested initial capacity of the buffer in bits.
396 BitBufferMA( MonoAllocator& monoAllocator, uinteger initialCapacity )
397 : ma( monoAllocator )
398 , storage(ma) { EnsureCapacity(initialCapacity, Index()); }
399
400
401 /// Returns the (currently allocated) capacity.
402 /// @return The size of the internal storage in bits.
403 virtual uinteger Capacity() const override { return storage.capacity() * bitsof(TStorage); }
404
405 /// Checks if the given required storage space is internally reserved. If not,
406 /// the internal capacity is doubled, or, if more is required, set to the required space.
407 ///
408 /// @param bitsRequired The number of bits divided required.
409 /// @param idx The index of current buffer use.
410 /// @return \c true if the space is available or could be made available,
411 /// \c false otherwise.
412 virtual bool EnsureCapacity(uinteger bitsRequired, BitBufferBase::Index idx) override {
413 uinteger capacityNeeded= (idx.CountBits() + bitsRequired + (bitsof(TStorage) - 1) )
414 / bitsof(TStorage);
415 if( capacityNeeded > storage.capacity() )
416 storage.reserve( (std::max)( capacityNeeded, uinteger(storage.capacity()) * 2 ));
417 data= storage.data();
418
419 return true;
420 }
421
422 /// Returns the internal monotonic allocator for external use.
423 /// @return The monotonic allocator given on construction.
425}; // class BitBufferMA
426
427//==================================================================================================
428/// A bit buffer using local storage, which means a fixed size internal array.
429/// If used as function member, the storage is located on the stack and hence its size has
430/// platform-specific limitations.<br>
431/// This class is useful to read and write smaller pieces of data, for example, header information
432/// of binary data files which furthermore are filled/loaded with bit buffers using of other memory
433/// allocations.
434/// \see
435/// Two alternatives are provided with #"BitBuffer" and #"BitBufferMA".
436/// @tparam TCapacity The number of bits to reserve internally
437//==================================================================================================
438template<uinteger TCapacity>
440 protected:
441 /// The array that holds the data.
442 TStorage storage[ (TCapacity + bitsof(TStorage) - 1) / bitsof(TStorage) ];
443
444 public:
445 /// Constructor.
446 BitBufferLocal() noexcept { data= storage; }
447
448 /// Returns the (in this case fixed size!) capacity.
449 /// @return The size of the internal storage in bits.
450 virtual uinteger Capacity() const override { return TCapacity; }
451
452 /// Checks if the given required storage space is internally reserved.
453 /// If not, in debug compilations an \alib_assertion is raised because this is a fixed size
454 /// buffer.
455 ///
456 /// @param bitsRequired The number of bits required.
457 /// @param idx The index of current buffer use.
458 /// @return \c true if the space is available or could be made available,
459 /// \c false otherwise.
460 virtual bool EnsureCapacity(uinteger bitsRequired, BitBufferBase::Index idx) override {
461 uinteger capacityNeeded= idx.CountBits() + bitsRequired;
462 if( capacityNeeded > TCapacity ) {
463 ALIB_ERROR("BITBUFFER", "Local bit buffer cannot expand its capacity" )
464 return false;
465 }
466
467 return true;
468 }
469}; // class BitBufferLocal
470
471
472//==================================================================================================
473/// Non-instantiatable base class for types #"BitWriter" and #"BitReader".
474//==================================================================================================
476 protected:
477 BitBufferBase& bb; ///< The bit buffer to write into. Provided on construction.
478 BitBufferBase::Index idx; ///< The current reading/writing index within #"BitRWBase::bb".
479
480 /// Protected constructor, used by derived classes only.
481 /// @param buffer The bit buffer to work on.
482 explicit BitRWBase( BitBufferBase& buffer )
483 : bb ( buffer ) {}
484
485 public:
486 /// Retrieves the internal bit buffer.
487 /// @return The buffer the derived reader or writer works with.
488 BitBufferBase& GetBuffer() const { return bb; }
489
490 /// Returns a copy of the current index in the bit buffer in respect to writing or
491 /// reading progress of derived classes #"BitWriter" #"BitReader".
492 /// Such index elements may be passed to methods
493 /// #"BitWriter::Reset(const BitBufferBase::Index&)" and
494 /// #"BitReader::Reset(const BitBufferBase::Index&)"
495 /// @return The index of the next bit to write.
497
498 /// Invokes #"BitBufferBase::Index::CountBits;*" on the index returned by #".GetIndex".
499 /// @return The number of bits currently read from or written to the buffer.
500 uinteger Usage() const { return idx.CountBits(); }
501
502 /// Invokes #"BitBufferBase::RemainingSize;*" on the internal bit buffer, passing
503 /// the result of #".GetIndex".
504 /// @return The number of bits dived by 8 remaining in this buffer.
505 uinteger RemainingSize() const { return bb.RemainingSize(idx); }
506
507};
508
509//==================================================================================================
510/// Writes bits into a #"BitBufferBase".
511//==================================================================================================
512class BitWriter : public BitRWBase {
513 /// Local type alias (shortcut)
515
516 /// The current word, which is partly written and not stored in buffer, yet.
518
519 public:
520 /// Constructs a bit writer operating on the given bit buffer.
521 /// @param buffer The buffer to write to (starting at the beginning).
522 explicit BitWriter( BitBufferBase& buffer )
523 : BitRWBase( buffer ) { word= 0; }
524
525 /// Constructs a bit writer operating on the given bit buffer, starting to write at the
526 /// given #"BitBufferBase::Index".
527 /// @param buffer The buffer to write to.
528 /// @param index An index providing the postion of the first bit to (over-) write in
529 /// \p{buffer}.
530 explicit BitWriter( BitBufferBase& buffer, const BitBufferBase::Index& index )
531 : BitRWBase ( buffer )
532 {
533 idx= index;
534 word= bb.GetWord(idx) & lang::LowerMask<TStorage>( idx.bit );
535 }
536
537 /// Destructs a bit writer. Invokes #".Flush".
539
540 /// Resets the internal index of this writer to the start of the bit buffer.
541 void Reset() { idx = BitBufferBase::Index(); word= 0; }
542
543 /// Resets the internal index of this writer to the given one.
544 /// @param index The position of the next bit in the buffer to write to.
545 void Reset( const BitBufferBase::Index& index ) {
546 idx.pos= index.pos;
547 idx.bit= index.bit;
548 word= bb.GetWord(idx) & lang::LowerMask<TStorage>(idx.bit );
549 }
550
551 /// Writes the last word of bits into the underlying buffer.
552 /// This method has to be called before writing the buffer to a file or similar.
553 /// The method is automatically invoked on destruction.
554 void Flush() { bb.SetWord(idx, word); }
555
556 #if DOXYGEN
557 /// Writes the given integral value with the given number of bits to the stream.
558 /// \note
559 /// Internally, different template functions selected with keyword \c requires
560 /// for the different integral types exist.
561 ///
562 /// \see
563 /// This method uses a template parameter for the number of bits to write.
564 /// A slightly slower, non-templated version is available with
565 /// #"WriteBits(lang::ShiftOpRHS, TValue)"
566 /// which is to be used when the value is determined only at runtime.
567 ///
568 /// @tparam TWidth The number of bits in \p{value} to write.
569 /// @tparam TValue The type of \p{value} to write. (Deduced from parameter \p{value}.)
570 /// @tparam TMaskValue Determines if bits beyond \p{width} of given \p{value} may be set and
571 /// have to be masked out.
572 /// Defaults to \c false.
573 /// @param value The value to write.
574 template<ShiftOpRHS TWidth, typename TIntegral, bool TMaskValue= false>
575 void WriteBits( TIntegral value );
576 #else
577 // Version 1/2: #Bits to write are less or equal to internal buffer width
578 template<lang::ShiftOpRHS TWidth, typename TValue, bool TMaskValue= false>
579 requires ( std::unsigned_integral<TValue>
580 && !std::same_as < TValue, bool>
581 && ( TWidth <= bitsof(TStorage) ) )
582 void WriteBits(TValue value ) {
583 static_assert(bitsof(TValue) >= TWidth, "Fixed size given greater than value type.");
584 ALIB_ASSERT_ERROR(idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
585 ALIB_ASSERT_ERROR( TMaskValue
586 || TWidth == bitsof(TValue)
587 || value == (value & lang::LowerMask<TValue>(TWidth) ), "BITBUFFER",
588 "Upper bits dirty while TMaskValue flag not set." )
589
590 if constexpr ( (TWidth < bitsof(TValue)) && TMaskValue )
591 value&= lang::LowerMask<TWidth, TValue >();
592
593 word|= TStorage(value) << idx.bit ;
594 idx.bit+= TWidth;
595 if(idx.bit >= bitsof(TStorage) ) {
596 bb.SetWord(idx, word);
597 idx.pos++;
598 word= 0;
599 idx.bit-= bitsof(TStorage);
600 if( idx.bit )
601 word|= (TStorage(value) >> (TWidth - idx.bit) );
602 } }
603
604 // Version 2/2: #Bits to write is greater than internal buffer width
605 template<lang::ShiftOpRHS TWidth, typename TValue, bool TMaskValue= false>
606 requires ( std::unsigned_integral<TValue>
607 && !std::same_as < TValue, bool>
608 && ( TWidth > bitsof(TStorage) ) )
609 void WriteBits(TValue value ) {
610 static_assert(bitsof(TValue) >= TWidth, "Fixed size given greater than value type.");
611 ALIB_ASSERT_ERROR(idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
612 ALIB_ASSERT_ERROR( TMaskValue
613 || TWidth == bitsof(TValue)
614 || value == (value & lang::LowerMask<TValue>(TWidth) ), "BITBUFFER",
615 "Upper bits dirty while TMaskValue not set." )
616
617 if constexpr ( (TWidth < bitsof(TValue)) && TMaskValue )
618 value&= lang::LowerMask<TWidth, TValue>();
619
620 word|= (TStorage(value) << idx.bit);
621 lang::ShiftOpRHS bitsWritten= bitsof(TStorage) - idx.bit;
622 value>>= bitsWritten;
623 while(true) { // the loop is at least hit once and bit is 0! (but not written in bit)
624 bb.SetWord(idx, word);
625 idx.pos++;
626 word= TStorage(value);
627 bitsWritten+= bitsof(TStorage);
628 if(bitsWritten >= TWidth )
629 break;
630
631 value>>= bitsof(TStorage);
632 }
633
634 idx.bit= (idx.bit + TWidth) % bitsof(TStorage);
635 if(idx.bit == 0 ) { // next buffer value reached?
636 bb.SetWord(idx, word);
637 idx.pos++;
638 word= 0;
639 } }
640
641 // Helper-versions for signed and bool
642 template<lang::ShiftOpRHS TWidth, typename TValue, bool TMaskValue= false>
643 requires ( std::signed_integral<TValue> && !std::same_as<TValue, bool> )
644 void WriteBits(TValue value) {
645 using TUI= typename std::make_unsigned<TValue>::type;
646 WriteBits<TWidth, TUI, TMaskValue>( static_cast<TUI>( value ) );
647 }
648
649 template<typename TValue>
650 requires ( std::same_as<TValue, bool> )
651 void WriteBits(TValue value)
652 { WriteBits<1, unsigned int, false>( static_cast<unsigned int>( value ) ); }
653
654 #endif // doxygen
655
656
657// Doxygen (V1.15) would create identical entries in its tag-file, so those are not reachable.
658// On the other hand, it complains if the methods are not doxed. So, we hide them from doxygen.
659#if DOXYGEN
660 /// Writes the given integral value with the given number of bits to the stream.
661 /// \note Internally, two versions of this method, selected with keyword \c requires,
662 /// for different integral types exist.
663 /// \see
664 /// A method that uses a template parameter for the number of bits to write, is
665 /// available with #".WriteBits(TIntegral)".
666 /// This might be slightly faster and should be used instead of this method, whenever the
667 /// number of bits to write is known at compilation time.
668 ///
669 /// @tparam TValue The integral type of the value to write.
670 /// (Deduced from parameter \p{value}.)
671 /// @tparam TMaskValue Determines if bits beyond \p{width} of given \p{value} may be set and
672 /// have to be masked out. Defaults to \c false.
673 /// @param width The number of bits in \p{value}.
674 /// @param value The value to write.
675 template<typename TValue, bool TMaskValue= false>
676 requires ( std::integral<TValue> && ( sizeof(TValue) <= sizeof(TStorage) ) )
677 void WriteBits(lang::ShiftOpRHS width, TValue value);
678#else
679 template<typename TValue, bool TMaskValue= false>
680 requires ( std::integral<TValue> && ( sizeof(TValue) <= sizeof(TStorage) ) )
681 void WriteBits(lang::ShiftOpRHS width, TValue value) {
682 ALIB_ASSERT_ERROR(idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
683 ALIB_ASSERT_ERROR( width <= bitsof(TValue),
684 "BITBUFFER", "BitBufferBase::Write: Width too high: ", width )
685
686 ALIB_ASSERT_ERROR( TMaskValue
687 || width>=bitsof(TValue)
688 || value == (value & lang::LowerMask<TValue>(width) ),
689 "BITBUFFER", "Upper bits dirty while TMaskValue not set.")
690
691 if constexpr (TMaskValue)
692 if( width < bitsof(TValue) )
693 value&= lang::LowerMask<TValue>(width);
694
695 word|= TStorage(value) << idx.bit ;
696 idx.bit+= width;
697 if(idx.bit >= bitsof(TStorage) ) {
698 bb.SetWord(idx, word);
699 idx.pos++;
700 word= 0;
701 idx.bit-= bitsof(TStorage);
702 if( idx.bit )
703 word|= ( TStorage(value) >> (width - idx.bit) );
704 } }
705
706 template<typename TValue, bool TMaskValue= false>
707 requires ( std::integral<TValue> && ( sizeof(TValue) > sizeof(TStorage) ) )
708 void WriteBits(lang::ShiftOpRHS width, TValue value) {
709 ALIB_ASSERT_ERROR( idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
710 ALIB_ASSERT_ERROR( width <= bitsof(TValue), "BITBUFFER",
711 "BitBufferBase::Write: Width too high: ", width )
712 ALIB_ASSERT_ERROR( TMaskValue
713 || width==bitsof(TValue)
714 || value == (value & lang::LowerMask<TValue>(width) ),
715 "BITBUFFER", "Upper bits dirty while TMaskValue not set.")
716
717 if constexpr (TMaskValue)
718 if( width <= bitsof(TValue) )
719 value&= lang::LowerMask<TValue>(width);
720
721 if( width <= bitsof(TStorage) ) {
722 word|= TStorage(value) << idx.bit ;
723 idx.bit+= width;
724 if(idx.bit >= bitsof(TStorage) ) {
725 bb.SetWord(idx, word);
726 idx.pos++;
727 word= 0;
729 if( idx.bit )
730 word|= ( TStorage(value) >> (width - idx.bit) );
731 }
732 } else {
733 word|= (TStorage(value) << idx.bit);
734 lang::ShiftOpRHS bitsWritten= bitsof(TStorage) - idx.bit;
735 value>>= bitsWritten;
736 while(true) { // the loop is at least hit once and bit is 0! (but not written in bit)
737 bb.SetWord(idx, word);
738 idx.pos++;
739 word= TStorage(value);
740 bitsWritten+= bitsof(TStorage);
741 if(bitsWritten >= width )
742 break;
743 value>>= bitsof(TStorage);
744 }
745 idx.bit= (idx.bit + width) % bitsof(TStorage);
746 if(idx.bit == 0 ) { // next buffer value reached?
747 bb.SetWord(idx, word);
748 idx.pos++;
749 word= 0;
750 } } }
751#endif
752
753 /// Writes the given unsigned integral value to the stream by writing lower values
754 /// with smaller sizes. The general assumption behind this is that lower values are more frequent
755 /// and the average written size is less than the unencode size - despite the overhead needed
756 /// to write information about how a value is encoded.
757 ///
758 /// The encoding for \e unsigned integrals is as follows:
759 /// - For byte value types, a single bit <c>'0'</c> is written if the value is below \c 8,
760 /// followed by the three bits containing the value. Otherwise, a single bit <c>'1'</c> is
761 /// written, followed by the full 8 bits.
762 /// - For all other value types (16-, 32- and 64-bit) the number of bytes needed is written
763 /// first (one bit in the case of 16-bit values, two bits in the case of 32-bit values
764 /// and three bits in the case of 64 bit values), and then the corresponding amount of
765 /// full bytes are written.
766 ///
767 /// \e Signed integrals are converted to unsigned integrals using the sometimes called
768 /// "zig-zag coding". Here, all numbers are doubled and negative numbers are turned
769 /// positive and uneven. This way, the least significant bit becomes the sign bit.
770 /// The advantage of this approach is that small numbers, negative or positive,
771 /// remain small in respect to their bitwise representation.<p>
772 /// The conversion hence is as follows:
773 /// unsigned = signed >= 0 ? signed * 2 : ( (-signed -1 ) * 2 ) | 1
774 ///
775 /// @tparam TUIntegral The type of the given unsigned integral.
776 /// @param value The value to write.
777 template<typename TUIntegral>
778 requires ( std::unsigned_integral<TUIntegral> && !std::same_as< TUIntegral, bool> )
779 void WriteInt( TUIntegral value )
780 {
781 ALIB_ASSERT_ERROR( idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
782 writeUIntegral(value);
783 }
784
785 /// Converts the given \e signed integral to an unsigned one using the so-called
786 /// "zig-zag coding". Here, all numbers are doubled and negative numbers are turned
787 /// positive and uneven. This way, the least significant bit becomes the sign bit.
788 /// The advantage of this approach is that small numbers, negative or positive,
789 /// remain small in respect to their bitwise representation.<p>
790 /// The conversion hence is as follows:
791 /// unsigned = signed >= 0 ? signed * 2 : ( (-signed -1 ) * 2 ) | 1
792 ///
793 /// Then calls #"BitWriter::WriteInt(TUIntegral)".
794 /// @tparam TSIntegral The type of the given signed integral.
795 /// @param value The value to write.
796 template<typename TSIntegral>
797 requires ( std::signed_integral<TSIntegral> && !std::same_as< TSIntegral, bool> )
798 void WriteInt( TSIntegral value ) {
799 ALIB_ASSERT_ERROR( idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
800 using TUnsigned= typename std::make_unsigned<TSIntegral>::type;
801 writeUIntegral( value >= 0 ? TUnsigned( value << 1)
802 : TUnsigned( (TUnsigned(-(value+ 1)) << 1) | 1 ) );
803 }
804
805 protected:
806
807 /// Internal method that writes a unsigned 8-bit value.
808 /// @param value The value to write.
809 ALIB_DLL void writeUIntegral( uint8_t value );
810
811 /// Internal method that writes a unsigned 16-bit value.
812 /// @param value The value to write.
813 ALIB_DLL void writeUIntegral( uint16_t value );
814
815 /// Internal method that writes a unsigned 32-bit value.
816 /// @param value The value to write.
817 ALIB_DLL void writeUIntegral( uint32_t value );
818
819 /// Internal method that writes a unsigned 64-bit value.
820 /// @param value The value to write.
821 ALIB_DLL void writeUIntegral( uint64_t value );
822
823}; // class BitWriter
824
825
826//==================================================================================================
827/// Reads bits from a #"BitBufferBase".
828//==================================================================================================
829class BitReader : public BitRWBase {
830 protected:
831 /// Local type alias (shortcut)
833
834 /// The current word, which is partly read and shifted to start with current bit.
836
837 public:
838 /// Constructs a bit reader using the given bit buffer and starting to read at the beginning.
839 /// @param buffer The buffer to read from.
840 explicit BitReader( BitBufferBase& buffer )
841 : BitRWBase( buffer ) { word= bb.GetWord(idx); }
842
843 /// Constructs a bit reader using the given bit buffer, starting to read at the
844 /// given #"BitBufferBase::Index".
845 /// @param buffer The buffer to read from.
846 /// @param index An index providing the postion of the first bit to read in \p{buffer}.
847 explicit BitReader( BitBufferBase& buffer, const BitBufferBase::Index& index )
848 : BitRWBase( buffer ) {
849 idx.pos= index.pos;
850 idx.bit= index.bit;
851 word= bb.GetWord(idx) >> idx.bit;
852 }
853
854 /// Destructs a bit reader. In debug compilations an \alib_assertion is raised if the
855 /// read operation passed the end of the underlying buffer was performed.
857 {
858 ALIB_ASSERT_ERROR( idx.pos < bb.Capacity(), "BITBUFFER",
859 "BitBufferBase overflow detected. Ensure a higher capacity" )
860 }
861
862 /// Resets this reader to the start of the bit buffer.
863 void Reset() {
864 idx.pos= 0;
865 idx.bit= 0;
866 word= bb.GetWord(idx);
867 }
868
869 /// Resets this reader to the given index position and calls #".Sync".
870 /// @param index The next read position.
871 void Reset( const BitBufferBase::Index& index ) {
872 idx.pos= index.pos;
873 idx.bit= index.bit;
874 Sync();
875 }
876
877 /// Re-reads the currently fetched storage word from the memory.
878 /// \note This method is not needed in common use cases and implemented solely for the
879 /// purpose to support unit-tests which write and write in parallel to the same
880 /// bit buffer.
881 /// @return A reference to this \c BitReader to allow concatenated operations.
882 BitReader& Sync() { word= bb.GetWord(idx) >> idx.bit; return *this; }
883
884// Doxygen (V1.15) would create identical entries in its tag-file, so those are not reachable.
885// On the other hand, it complains if the methods are not doxed. So, we hide them from doxygen.
886#if DOXYGEN
887 /// Reads the given number of bits from the stream into the given unsigned integral value.
888 /// \note
889 /// Two different template functions (selected by keyword \c requires) for the different
890 /// integral types exist.
891 /// One to read a number of bits that are less or equal to the internal buffer width, and
892 /// one if there is more to read than the internal buffer width.
893 /// \see
894 /// This method uses a template parameter for the number of bits to read.
895 /// A slightly slower, non-templated version is available with #".ReadBits(lang::ShiftOpRHS)",
896 /// which is to be used when the number of bits to read is determined only at runtime.
897 /// @tparam TWidth The number of bits in \p{value} to write.
898 /// @tparam TResult The type of the value to return.
899 /// @return The value read.
900 template<lang::ShiftOpRHS TWidth, typename TResult= int>
901 TResult ReadBits();
902#else
903 template<lang::ShiftOpRHS TWidth, typename TResult= int>
904 requires ( std::integral<TResult> && ( TWidth <= bitsof(TStorage)) )
905 TResult ReadBits() {
906 ALIB_ASSERT_ERROR(idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
907
908 TResult result;
909
910 // the one bit case. Could have been left to the compiler to optimize, but who knows.
911 if constexpr ( TWidth == 1 ) {
912 result= word & 1;
913 word>>= 1;
914 if(++idx.bit == bitsof(TStorage)) {
915 idx.pos++;
916 word= bb.GetWord(idx);
917 idx.bit= 0;
918 }
919 return result;
920 }
921
922 static_assert(bitsof(TResult) >= TWidth, "Fixed size to read greater than given result type.");
923 if constexpr ( TWidth == bitsof(TStorage) )
924 result= TResult( word );
925 else {
926 result= TResult( word & lang::LowerMask<TWidth, TStorage>() );
927 word>>= TWidth;
928 }
929
930 idx.bit+= TWidth;
931 if(idx.bit >= bitsof(TStorage)) {
932 idx.pos++;
933 word= bb.GetWord(idx);
935 if( idx.bit ) {
936 lang::ShiftOpRHS bitsRead= TWidth - idx.bit;
937 if constexpr ( TWidth < bitsof(TStorage) )
938 result |= TResult( ( word << bitsRead )
940 else
941 result |= TResult( word << bitsRead );
942 }
943
944 word>>= idx.bit;
945 }
946
947 return result;
948 }
949
950 template<lang::ShiftOpRHS TWidth, typename TResult= int>
951 requires ( std::integral<TResult> && ( TWidth > bitsof(TStorage)) )
952 TResult ReadBits() {
953 static_assert(bitsof(TResult) >= TWidth, "Fixed size to read greater than given result type.");
954 ALIB_ASSERT_ERROR( idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
955
956 TResult result = word;
957 lang::ShiftOpRHS bitsRead= bitsof(TStorage) - idx.bit;
958 do { // the loop is at least hit once and bit is 0! (but not written in bit)
959 idx.pos++;
960 word= bb.GetWord(idx);
961 result|= TResult(word) << bitsRead;
962 bitsRead+= bitsof(TStorage);
963 }
964 while( bitsRead < TWidth);
965
966 idx.bit= (idx.bit + TWidth) % bitsof(TStorage);
967
968 // next buffer value reached?
969 if(idx.bit == 0 )
970 idx.pos++;
971 else
972 result= lang::LowerBits<TWidth, TResult>( result );
973
974 word= bb.GetWord(idx) >> idx.bit;
975
976 return result;
977 }
978#endif
979
980// Doxygen (V1.15) would create identical entries in its tag-file, so those are not reachable.
981// On the other hand, it complains if the methods are not doxed. So, we hide them from doxygen.
982#if DOXYGEN
983 /// Reads the given number of bits from the stream into the given unsigned integral value.
984 /// \note
985 /// Two different implementations for different integral types exist and are
986 /// selected with keyword \c requires.
987 ///
988 /// \see
989 /// A method that uses a template parameter for the number of bits to read, is available
990 /// with #".ReadBits"<TWidth,TResult>.
991 /// This might be slightly faster and should be used instead of this method, whenever
992 /// the number of bits to read is known at compilation time.
993 ///
994 /// @tparam TResult The type of the value to return. Defaults to type \c int.
995 /// @param width The number of bits to read.
996 /// @return The value read.
997 template<typename TResult= int>
998 TResult ReadBits( lang::ShiftOpRHS width );
999#else
1000 template<typename TResult= int>
1001 requires ( sizeof(TResult) <= sizeof(TStorage) )
1002 TResult ReadBits( lang::ShiftOpRHS width ) {
1003 ALIB_ASSERT_ERROR( idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
1004 ALIB_ASSERT_ERROR( bitsof(TResult) >= width, "BITBUFFER",
1005 "Read size given greater than value type.")
1006
1007 TResult result;
1008 if ( width < bitsof(TStorage) )
1009 result= TResult( word & lang::LowerMask<TStorage>(width) );
1010 else
1011 result= TResult( word );
1012 word>>= width;
1013
1014 idx.bit+= width;
1015 if(idx.bit >= bitsof(TStorage)) {
1016 idx.pos++;
1017 word= bb.GetWord(idx);
1018 idx.bit-= bitsof(TStorage);
1019
1020 if( idx.bit ) {
1021 lang::ShiftOpRHS bitsRead= width - idx.bit;
1022 result |= TResult( ( word << bitsRead )
1023 & lang::LowerMask<TStorage>(width) );
1024 word>>= idx.bit;
1025 } }
1026
1027 return result;
1028 }
1029
1030 template<typename TResult= int>
1031 requires ( sizeof(TResult) > sizeof(TStorage) )
1032 TResult ReadBits( lang::ShiftOpRHS width ) {
1033 ALIB_ASSERT_ERROR( idx.pos < bb.Capacity(), "BITBUFFER", "BitBufferBase overflow" )
1034 ALIB_ASSERT_ERROR( bitsof(TResult) >= width , "BITBUFFER",
1035 "Read size given greater than value type.")
1036
1037 if( width <= bitsof(TStorage)) {
1038 TResult result;
1039 if ( width < bitsof(TStorage) )
1040 result= TResult( word & lang::LowerMask<TStorage>(width) );
1041 else
1042 result= TResult( word );
1043 word>>= width;
1044
1045 idx.bit+= width;
1046 if(idx.bit >= bitsof(TStorage)) {
1047 idx.pos++;
1048 word= bb.GetWord(idx);
1050
1051 if( idx.bit ) {
1052 lang::ShiftOpRHS bitsRead= width - idx.bit;
1053 result |= TResult( ( word << bitsRead )
1054 & lang::LowerMask<TStorage>(width) );
1055 word>>= idx.bit;
1056 } }
1057
1058 return result;
1059 } else {
1060 TResult result = TResult( word );
1061 lang::ShiftOpRHS bitsRead= bitsof(TStorage) - idx.bit;
1062 do { // the loop is at least hit once and bit is 0! (but not written in bit)
1063 idx.pos++;
1064 word= bb.GetWord(idx);
1065 result|= TResult(word) << bitsRead;
1066 bitsRead+= bitsof(TStorage);
1067 }
1068 while( bitsRead < width);
1069
1070 idx.bit= (idx.bit + width) % bitsof(TStorage);
1071
1072 // next buffer value reached?
1073 if(idx.bit == 0 ) {
1074 idx.pos++;
1075 word= bb.GetWord(idx);
1076 } else {
1077 result= lang::LowerBits<TResult>( width, result );
1078 word>>= width;
1079 }
1080 return result;
1081 } }
1082
1083#endif
1084
1085
1086// Doxygen (V1.15) would create identical entries in its tag-file, so those are not reachable.
1087// On the other hand, it complains if the methods are not doxed. So, we hide them from doxygen.
1088#if DOXYGEN
1089 /// Reads the given integral value from the stream.
1090 /// Information about the encoding of the values is given with the documentation of
1091 /// #"BitWriter::WriteBits(TIntegral);WriteBits<TIntegral>(TIntegral)".<br>
1092 ///
1093 /// Note that the implementation of this method consists of various versions which
1094 /// are selected using C++20 keyword \c requires.
1095 /// @tparam TUIntegral The unsigned integral type to read.
1096 /// @return The value read from the bit buffer.
1097 template< typename TUIntegral>
1098 TUIntegral ReadInt() { return readUIntegral8(); }
1099#else
1100 template< typename TUIntegral>
1101 requires( std::unsigned_integral<TUIntegral> && (bitsof(TUIntegral) == 8) )
1102 TUIntegral ReadInt() { return readUIntegral8(); }
1103
1104 template< typename TUIntegral>
1105 requires( std::unsigned_integral<TUIntegral> && (bitsof(TUIntegral) == 16) )
1106 TUIntegral ReadInt() { return readUIntegral16(); }
1107
1108
1109 template< typename TUIntegral>
1110 requires( std::unsigned_integral<TUIntegral> && (bitsof(TUIntegral) == 32) )
1111 TUIntegral ReadInt() { return readUIntegral32(); }
1112
1113 template< typename TUIntegral>
1114 requires( std::unsigned_integral<TUIntegral> && (bitsof(TUIntegral) == 64) )
1115 TUIntegral ReadInt() { return readUIntegral64(); }
1116
1117 template< typename TSIntegral>
1118 requires( std::signed_integral<TSIntegral> )
1119 TSIntegral ReadInt() {
1120 using TUnsigned= typename std::make_unsigned<TSIntegral>::type;
1121 TUnsigned result= ReadInt<TUnsigned>();
1122 return result & 1 ? TSIntegral( -TSIntegral( result >> 1 ) - 1 )
1123 : TSIntegral( result >> 1 );
1124 }
1125#endif
1126
1127 protected:
1128 /// Internal method that reads a unsigned 8-bit value.
1129 /// @return The value read.
1130 ALIB_DLL uint8_t readUIntegral8();
1131
1132 /// Internal method that reads a unsigned 16-bit value.
1133 /// @return The value read.
1134 ALIB_DLL uint16_t readUIntegral16();
1135
1136 /// Internal method that reads a unsigned 32-bit value.
1137 /// @return The value read.
1138 ALIB_DLL uint32_t readUIntegral32();
1139
1140 /// Internal method that reads a unsigned 64-bit value.
1141 /// @return The value read.
1142 ALIB_DLL uint64_t readUIntegral64();
1143
1144
1145}; // class BitReader
1146
1147} // namespace alib[::bitbuffer]
1148
1149/// Type alias in namespace #"%alib".
1151
1152/// Type alias in namespace #"%alib".
1154
1155/// Type alias in namespace #"%alib".
1156template<uinteger TCapacity>
1158
1159/// Type alias in namespace #"%alib".
1161
1162/// Type alias in namespace #"%alib".
1164
1165/// Type alias in namespace #"%alib".
1166using ShiftOpRHS = int;
1167
1168} // namespace [alib]
#define bitsof(type)
#define ALIB_DLL
#define ALIB_ERROR(domain,...)
#define ALIB_EXPORT
#define ALIB_ASSERT_ERROR(cond, domain,...)
bool operator!=(const Index &rhs) const
bool operator<=(const Index &rhs) const
void SetFromByteOffset(uinteger byteOffset)
lang::ShiftOpRHS bit
Current bit index in the current word.
Definition bitbuffer.hpp:61
void Clear()
Sets this index to zero, hence pointing to the first bit in the buffer.
Definition bitbuffer.hpp:93
integer GetByteOffset(Index startIdx=Index(0, 0)) const
bool operator<(const Index &rhs) const
static Index Decode64(uint64_t code)
bool operator>(const Index &rhs) const
uinteger pos
Index of the current word to read/write.
Definition bitbuffer.hpp:60
bool operator==(const Index &rhs) const
bool operator>=(const Index &rhs) const
Index()=default
Default constructor initializing members #"pos" and #".bit" to zero.
static Index Decode32(uint32_t code)
Index(uinteger pPos, lang::ShiftOpRHS pBit)
Definition bitbuffer.hpp:71
lang::ShiftOpRHS Bit() const
Definition bitbuffer.hpp:81
uinteger RemainingSize(const Index &idx) const
Index Unterminate(Index terminationIndex)
Definition bitbuffer.cpp:30
virtual uinteger Capacity() const =0
void SetWord(const Index &index, TStorage value)
virtual ~BitBufferBase()
Virtual destructor (does nothing, needed for abstract virtual class).
BitBufferBase() noexcept
Default Constructor (the only one).
Index Terminate(Index writerIndex)
Definition bitbuffer.cpp:14
void ToLittleEndianEncoding(const Index &startIndex, const Index &endIndex)
Definition bitbuffer.cpp:99
void FromLittleEndianEncoding(const Index &startIndex, const Index &endIndex)
TStorage GetWord(const Index &index) const
virtual bool EnsureCapacity(uinteger bitsRequired, BitBufferBase::Index index)=0
char * CharStream(Index idx=Index(0, 0))
BitBufferLocal() noexcept
Constructor.
virtual bool EnsureCapacity(uinteger bitsRequired, BitBufferBase::Index idx) override
TStorage storage[(TCapacity+bitsof(TStorage) - 1)/bitsof(TStorage)]
The array that holds the data.
virtual uinteger Capacity() const override
StdVectorMA< TStorage > storage
The vector that holds the data.
MonoAllocator & GetAllocator()
virtual uinteger Capacity() const override
BitBufferMA(MonoAllocator &monoAllocator, uinteger initialCapacity)
virtual bool EnsureCapacity(uinteger bitsRequired, BitBufferBase::Index idx) override
std::vector< TStorage > storage
The vector that holds the data.
virtual bool EnsureCapacity(uinteger bitsRequired, BitBufferBase::Index idx) override
virtual uinteger Capacity() const override
BitBuffer(uinteger initialCapacity)
BitBufferBase & bb
The bit buffer to write into. Provided on construction.
uinteger RemainingSize() const
BitBufferBase::Index idx
The current reading/writing index within #"BitRWBase::bb".
BitBufferBase & GetBuffer() const
BitBufferBase::Index GetIndex() const
BitRWBase(BitBufferBase &buffer)
Reads bits from a #"BitBufferBase".
BitReader(BitBufferBase &buffer)
BitBufferBase::TStorage TStorage
Local type alias (shortcut).
BitReader(BitBufferBase &buffer, const BitBufferBase::Index &index)
BitBufferBase::TStorage word
The current word, which is partly read and shifted to start with current bit.
void Reset(const BitBufferBase::Index &index)
TResult ReadBits(lang::ShiftOpRHS width)
void Reset()
Resets this reader to the start of the bit buffer.
Writes bits into a #"BitBufferBase".
void Reset(const BitBufferBase::Index &index)
~BitWriter()
Destructs a bit writer. Invokes #".Flush".
void WriteInt(TUIntegral value)
void Reset()
Resets the internal index of this writer to the start of the bit buffer.
void WriteBits(lang::ShiftOpRHS width, TValue value)
BitBufferBase::TStorage word
The current word, which is partly written and not stored in buffer, yet.
BitWriter(BitBufferBase &buffer)
BitWriter(BitBufferBase &buffer, const BitBufferBase::Index &index)
void writeUIntegral(uint8_t value)
void WriteInt(TSIntegral value)
BitBufferBase::TStorage TStorage
Local type alias (shortcut).
void WriteBits(TIntegral value)
constexpr TIntegral LowerMask()
int ShiftOpRHS
Definition bits.hpp:20
constexpr int Log2OfSize()
Definition bits.hpp:194
constexpr TIntegral LowerBits(TIntegral value)
Definition bits.hpp:159
Definition alox.cpp:14
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
bitbuffer::BitReader BitReader
Type alias in namespace #"%alib".
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
bitbuffer::BitBufferMA BitBufferMA
Type alias in namespace #"%alib".
bitbuffer::BitBufferLocal< TCapacity > BitBufferLocal
Type alias in namespace #"%alib".
bitbuffer::BitBuffer BitBuffer
Type alias in namespace #"%alib".
bitbuffer::BitWriter BitWriter
Type alias in namespace #"%alib".
lang::uinteger uinteger
Type alias in namespace #"%alib".
Definition integers.hpp:152
std::vector< T, StdMA< T > > StdVectorMA
Type alias in namespace #"%alib".
int ShiftOpRHS
Type alias in namespace #"%alib".