Worldstone
 All Classes Files Functions Variables Enumerations Enumerator Macros Pages
BitStream.h
1 //
2 // Created by Lectem on 12/11/2016.
3 //
4 
5 #pragma once
6 
7 #include <stdint.h>
8 #include <SystemUtils.h>
9 #include <algorithm>
10 #include <assert.h>
11 #include <climits>
12 #include <type_traits>
13 #include "IOBase.h"
14 namespace WorldStone
15 {
16 
33 class BitStreamView : public IOBase
34 {
35  using byte = unsigned char;
36  size_t size = 0;
37  size_t firstBitOffset = 0;
38  size_t currentBitPosition = 0;
39  const byte* buffer = nullptr;
40 
41 public:
42  BitStreamView() = default;
44  BitStreamView(const void* inputBuffer, size_t sizeInBits, size_t firstBitOffsetInBuffer = 0)
45  : size(sizeInBits),
46  firstBitOffset(firstBitOffsetInBuffer),
47  currentBitPosition(firstBitOffsetInBuffer),
48  buffer(static_cast<const byte*>(inputBuffer))
49  {
50  assert(firstBitOffset + size <= bufferSizeInBits());
51  }
52 
53  BitStreamView createSubView(size_t newbufferSizeInBits) const
54  {
55  // 0Bits is a special case as it should always have a size of 0
56  if (newbufferSizeInBits == 0) return {};
57  const size_t curBytesPos = currentBitPosition / CHAR_BIT;
58  const size_t bitPosInCurByte = currentBitPosition % CHAR_BIT;
59  assert(tell() + newbufferSizeInBits <= size);
60  assert(currentBitPosition + newbufferSizeInBits <= bufferSizeInBits());
61  return {buffer + curBytesPos, newbufferSizeInBits, bitPosInCurByte};
62  }
63 
65  size_t tell() const { return currentBitPosition - firstBitOffset; }
67  void setPosition(size_t newPosition)
68  {
69  assert(newPosition >= 0_z && newPosition < size);
70  currentBitPosition = newPosition + firstBitOffset;
71  }
73  size_t bitPositionInBuffer() const { return currentBitPosition; }
75  void skip(size_t nbBits)
76  {
77  assert(currentBitPosition + nbBits < bufferSizeInBits());
78  currentBitPosition += nbBits;
79  }
80 
81  void alignToByte()
82  {
83  // Remove the last 3 bytes to be a multiple of 8, but add 7 before so that we round up
84  currentBitPosition = (currentBitPosition + 7_z) & (~0x7_z);
85  }
86 
87  size_t bufferSizeInBytes() const { return (size + firstBitOffset + 7) / CHAR_BIT; }
91  size_t bufferSizeInBits() const { return bufferSizeInBytes() * CHAR_BIT; }
93  size_t sizeInBits() const { return size; }
94 
96  bool readBool()
97  {
98  const size_t currentBytesPos = currentBitPosition / CHAR_BIT;
99  const size_t bitPosInCurrentByte = currentBitPosition % CHAR_BIT;
100  const int mask = (1 << bitPosInCurrentByte);
101  currentBitPosition++;
102  return (buffer[currentBytesPos] & mask) == mask;
103  }
105  uint32_t readBit() { return uint32_t(readBool()); }
106 
111  template<typename RetType = uint32_t>
112  RetType readUnsigned(unsigned nbBits)
113  {
114  static_assert(std::is_unsigned<RetType>::value, "You must return an unsigned type !");
115  RetType value = 0;
116  size_t curBytesPos = currentBitPosition / CHAR_BIT;
117  size_t bitPosInCurByte = currentBitPosition % CHAR_BIT;
118  currentBitPosition += nbBits;
119  // The following variable is needed because we are reading a little endian input.
120  size_t curDestBitPosition = 0;
121  while (curDestBitPosition < nbBits)
122  {
123  // How many bits we can read in this byte ?
124  const size_t bitsToReadInCurByte =
125  std::min(CHAR_BIT - bitPosInCurByte, nbBits - curDestBitPosition);
126  const RetType mask = RetType((1U << bitsToReadInCurByte) - 1U);
127  // The bits we got from the current byte
128  const RetType inBits = (RetType(buffer[curBytesPos++]) >> bitPosInCurByte) & mask;
129  // Place them at the right position
130  value |= inBits << curDestBitPosition;
131  curDestBitPosition += bitsToReadInCurByte;
132  // Next time we start at the beginning of the byte, perhaps we could optimize by
133  // Treating the first byte as a special case ?
134  bitPosInCurByte = 0;
135  }
136  return value;
137  }
138 
139  uint8_t readUnsigned8OrLess(const int nbBits)
140  {
141  const size_t curBytesPos = currentBitPosition / CHAR_BIT;
142  const int bitPosInCurByte = currentBitPosition % CHAR_BIT;
143  currentBitPosition += size_t(nbBits);
144  // Note: Could get rid of the condition by having one extra byte at the end of the stream
145  const uint16_t shortFromBuffer =
146  buffer[curBytesPos] |
147  ((bitPosInCurByte + nbBits > 8) ? uint16_t(buffer[curBytesPos + 1] << CHAR_BIT) : 0);
148  const unsigned mask = 0xFF >> (CHAR_BIT - nbBits);
149  const uint8_t value = uint8_t((shortFromBuffer >> bitPosInCurByte) & mask);
150  return value;
151  }
152 
154  uint32_t read0Bits() { return 0u; }
155 
160  template<unsigned NbBits, typename std::enable_if<(NbBits > 0)>::type* = nullptr>
161  int32_t readSigned()
162  {
163  return Utils::signExtend<int32_t, NbBits>(readUnsigned(NbBits));
164  }
165 
171  template<unsigned NbBits, typename std::enable_if<(NbBits == 0)>::type* = nullptr>
172  int32_t readSigned()
173  {
174  currentBitPosition += NbBits;
175  return 0;
176  }
177 };
178 }
This class reuses the parts of std::ios API but doesn't provide heavy stream functionnality (ie...
Definition: IOBase.h:16
uint32_t readBit()
Reads a single bit from the stream (uint32_t version)
Definition: BitStream.h:105
RetType readUnsigned(unsigned nbBits)
Reads an unsigned value of variable bit size.
Definition: BitStream.h:112
bool readBool()
Reads a single bit from the stream.
Definition: BitStream.h:96
BitStreamView(const void *inputBuffer, size_t sizeInBits, size_t firstBitOffsetInBuffer=0)
Creates a bitstream from raw memory.
Definition: BitStream.h:44
size_t bitPositionInBuffer() const
Returns the current position in the buffer (ignoring the first bit position) in bits.
Definition: BitStream.h:73
size_t bufferSizeInBits() const
Returns the total size of the current stream buffer in bits.
Definition: BitStream.h:91
void setPosition(size_t newPosition)
Set the current position, in bits.
Definition: BitStream.h:67
size_t tell() const
Returns the current position in the stream in bits.
Definition: BitStream.h:65
size_t sizeInBits() const
Returns the total size of the current stream buffer in bytes.
Definition: BitStream.h:93
int32_t readSigned()
Reads an signed value of variable bit size.
Definition: BitStream.h:161
void skip(size_t nbBits)
Skips the next nbBits bits.
Definition: BitStream.h:75
Provides access to a variable bitsize values stream.
Definition: BitStream.h:33
uint32_t read0Bits()
Return 0u, used for member function tables as replacement for BitStream::readUnsigned<0> ...
Definition: BitStream.h:154