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