libfly  6.2.2
C++20 utility library for Linux, macOS, and Windows
bit_stream_reader.hpp
1 #pragma once
2 
3 #include "fly/types/bit_stream/detail/bit_stream.hpp"
4 #include "fly/types/bit_stream/detail/concepts.hpp"
5 #include "fly/types/bit_stream/types.hpp"
6 #include "fly/types/numeric/endian.hpp"
7 
8 #include <algorithm>
9 #include <bit>
10 #include <cstdint>
11 #include <istream>
12 
13 namespace fly {
14 
27 {
28 public:
35  explicit BitStreamReader(std::istream &stream) noexcept;
36 
48  bool read_word(word_type &word);
49 
61  bool read_byte(byte_type &byte);
62 
80  template <detail::BitStreamInteger DataType>
81  byte_type read_bits(DataType &bits, byte_type size);
82 
98  template <detail::BitStreamInteger DataType>
99  byte_type peek_bits(DataType &bits, byte_type size);
100 
107  void discard_bits(byte_type size);
108 
114  bool fully_consumed() const;
115 
119  byte_type header() const;
120 
121 private:
125  void refill_buffer();
126 
137  template <detail::BitStreamInteger DataType>
138  byte_type fill(DataType &buffer, byte_type bytes);
139 
140  std::istream &m_stream;
141 
142  byte_type m_header {0};
143  byte_type m_remainder {0};
144 };
145 
146 //==================================================================================================
147 template <detail::BitStreamInteger DataType>
148 byte_type BitStreamReader::read_bits(DataType &bits, byte_type size)
149 {
150  if constexpr (detail::BitStreamBuffer<DataType>)
151  {
152  // See peek_bits for why buffer_type reads must be split.
153  const byte_type size_high = size / 2;
154  const byte_type size_low = size - size_high;
155  std::uint32_t bits_high, bits_low;
156 
157  const byte_type bits_read_high = peek_bits(bits_high, size_high);
158  discard_bits(bits_read_high);
159 
160  const byte_type bits_read_low = peek_bits(bits_low, size_low);
161  discard_bits(bits_read_low);
162 
163  bits = (static_cast<DataType>(bits_high) << size_low) | bits_low;
164  return bits_read_high + bits_read_low;
165  }
166  else
167  {
168  const byte_type bits_read = peek_bits(bits, size);
169  discard_bits(bits_read);
170 
171  return bits_read;
172  }
173 }
174 
175 //==================================================================================================
176 template <detail::BitStreamInteger DataType>
177 byte_type BitStreamReader::peek_bits(DataType &bits, byte_type size)
178 {
179  // Peek operations the site of the byte buffer are not supported because the byte buffer could
180  // be in a state where it cannot be refilled.
181  //
182  // For example, consider a 64-bit byte buffer, and reading 6 bits and then 64 bits. After the
183  // 6-bit read, there will be 58 bits left in the buffer; not enough for the next 64 bit read.
184  // The buffer must then be refilled, but it cannot because there is less than 1 byte free in the
185  // byte buffer.
186  //
187  // Ideally, given the split peek operations below, the given bits could be filled with the 58
188  // available, then the byte buffer entirely refilled. But the caller then cannot discard more
189  // than 6 bits, which invalidates the whole peek_bits/discard_bits semantic. BitStreamReader
190  // would need to support putting bits back onto the stream.
191  static_assert(
192  !detail::BitStreamBuffer<DataType>,
193  "peek_bits only supports types smaller than buffer_type");
194 
195  byte_type peeked = 0, lshift = 0;
196  bits = 0;
197 
198  // If there are more bits to peek than are available in the byte buffer, break the peek into two
199  // peeks.
200  if (size > m_position)
201  {
202  peeked = m_position;
203 
204  const DataType buffer = static_cast<DataType>(m_buffer);
205  lshift = size - m_position;
206 
207  // Fill the input bits with the remainder of byte buffer.
208  bits = buffer & bit_mask<DataType>(m_position);
209  bits <<= lshift - 1;
210  bits <<= 1;
211 
212  // Refill the buffer from the stream.
213  refill_buffer();
214 
215  // Then update the input to only peek any remaining bits next.
216  size = std::min(lshift, static_cast<byte_type>(m_position - peeked));
217  lshift -= size;
218  }
219 
220  const byte_type rshift = m_position - peeked - size;
221  const DataType buffer = static_cast<DataType>(m_buffer >> rshift);
222 
223  bits |= (buffer & bit_mask<DataType>(size)) << lshift;
224  peeked += size;
225 
226  return peeked;
227 }
228 
229 //==================================================================================================
230 inline void BitStreamReader::discard_bits(byte_type size)
231 {
232  m_position -= size;
233 }
234 
235 //==================================================================================================
236 template <detail::BitStreamInteger DataType>
237 byte_type BitStreamReader::fill(DataType &buffer, byte_type bytes)
238 {
239  if (m_stream)
240  {
241  const std::streamsize bytes_read = m_stream_buffer->sgetn(
242  reinterpret_cast<std::ios::char_type *>(&buffer),
243  static_cast<std::streamsize>(bytes));
244 
245  buffer = endian_swap_if_non_native<std::endian::big>(buffer);
246  return static_cast<byte_type>(bytes_read);
247  }
248 
249  return 0;
250 }
251 
252 } // namespace fly
Definition: bit_stream_reader.hpp:27
bool read_word(word_type &word)
Definition: bit_stream_reader.cpp:31
bool fully_consumed() const
Definition: bit_stream_reader.cpp:43
byte_type read_bits(DataType &bits, byte_type size)
Definition: bit_stream_reader.hpp:148
byte_type peek_bits(DataType &bits, byte_type size)
Definition: bit_stream_reader.hpp:177
BitStreamReader(std::istream &stream) noexcept
Definition: bit_stream_reader.cpp:9
void discard_bits(byte_type size)
Definition: bit_stream_reader.hpp:230
byte_type header() const
Definition: bit_stream_reader.cpp:54
bool read_byte(byte_type &byte)
Definition: bit_stream_reader.cpp:37
Definition: bit_stream.hpp:34