Embedded Template Library  1.0
bit_stream.h
Go to the documentation of this file.
1 
3 /******************************************************************************
4 The MIT License(MIT)
5 Embedded Template Library.
6 https://github.com/ETLCPP/etl
7 https://www.etlcpp.com
8 Copyright(c) 2018 jwellbelove
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files(the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions :
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 ******************************************************************************/
25 
26 #ifndef ETL_BIT_STREAM_INCLUDED
27 #define ETL_BIT_STREAM_INCLUDED
28 
29 #include <stdint.h>
30 #include <limits.h>
31 
32 #include "platform.h"
33 #include "type_traits.h"
34 #include "nullptr.h"
35 #include "endianness.h"
36 #include "integral_limits.h"
37 #include "binary.h"
38 #include "algorithm.h"
39 #include "iterator.h"
40 
41 #include "private/minmax_push.h"
42 
43 namespace etl
44 {
45  //***************************************************************************
48  //***************************************************************************
49  class bit_stream
50  {
51  public:
52 
53  typedef const unsigned char* const_iterator;
54 
55  //***************************************************************************
57  //***************************************************************************
59  : pdata(ETL_NULLPTR),
60  length(0U)
61  {
62  restart();
63  }
64 
65  //***************************************************************************
67  //***************************************************************************
68  bit_stream(char* begin_, char* end_)
69  : pdata(reinterpret_cast<unsigned char*>(begin_)),
70  length(etl::distance(begin_, end_))
71  {
72  restart();
73  }
74 
75  //***************************************************************************
77  //***************************************************************************
78  bit_stream(unsigned char* begin_, unsigned char* end_)
79  : pdata(begin_),
80  length(etl::distance(begin_, end_))
81  {
82  restart();
83  }
84 
85  //***************************************************************************
87  //***************************************************************************
88  bit_stream(char* begin_, size_t length_)
89  : pdata(reinterpret_cast<unsigned char*>(begin_)),
90  length(length_)
91  {
92  restart();
93  }
94 
95  //***************************************************************************
97  //***************************************************************************
98  bit_stream(unsigned char* begin_, size_t length_)
99  : pdata(begin_),
100  length(length_)
101  {
102  restart();
103  }
104 
105  //***************************************************************************
107  //***************************************************************************
108  void set_stream(char* begin_, size_t length_)
109  {
110  pdata = reinterpret_cast<unsigned char*>(begin_);
111  length = length_;
112  restart();
113  }
114 
115  //***************************************************************************
117  //***************************************************************************
118  void set_stream(unsigned char* begin_, size_t length_)
119  {
120  pdata = begin_;
121  length = length_;
122  restart();
123  }
124 
125  //***************************************************************************
127  //***************************************************************************
128  void set_stream(char* begin_, char* end_)
129  {
130  set_stream(begin_, etl::distance(begin_, end_));
131  }
132 
133  //***************************************************************************
135  //***************************************************************************
136  void set_stream(unsigned char* begin_, unsigned char* end_)
137  {
138  set_stream(begin_, etl::distance(begin_, end_));
139  }
140 
141  //***************************************************************************
143  //***************************************************************************
144  void restart()
145  {
146  bits_in_byte = 8;
147  byte_index = 0U;
148  bits_remaining = CHAR_BIT * length;
149  }
150 
151  //***************************************************************************
153  //***************************************************************************
154  bool at_end() const
155  {
156  return (bits_remaining == 0U);
157  }
158 
159  //***************************************************************************
161  //***************************************************************************
162  bool put(bool value)
163  {
164  bool success = false;
165 
166  if (pdata != ETL_NULLPTR)
167  {
168  if (bits_remaining > 0)
169  {
170  unsigned char chunk = value ? 1 : 0;
171  put_integral(uint32_t(chunk), 1);
172  success = true;
173  }
174  }
175 
176  return success;
177  }
178 
179  //***************************************************************************
181  //***************************************************************************
182  template <typename T>
183  typename etl::enable_if<etl::is_integral<T>::value, bool>::type
184  put(T value, uint_least8_t width = CHAR_BIT * sizeof(T))
185  {
186  return put_integral(static_cast<uint32_t>(value), width);
187  }
188 
189 #if ETL_USING_64BIT_TYPES
190  //***************************************************************************
192  //***************************************************************************
193  bool put(int64_t value, uint_least8_t width = CHAR_BIT * sizeof(int64_t))
194  {
195  return put_integral(uint64_t(value), width);
196  }
197 
198  //***************************************************************************
200  //***************************************************************************
201  bool put(uint64_t value, uint_least8_t width = CHAR_BIT * sizeof(uint64_t))
202  {
203  return put_integral(value, width);
204  }
205 #endif
206 
207  //***************************************************************************
209  //***************************************************************************
210  template <typename T>
212  put(T value)
213  {
214  bool success = true;
215 
216  unsigned char data[sizeof(T)];
217  to_bytes(value, data);
218 
219  for (size_t i = 0; i < sizeof(T); ++i)
220  {
221  if (!put_integral(uint32_t(data[i]), CHAR_BIT))
222  {
223  success = false;
224  }
225  }
226 
227  return success;
228  }
229 
230  //***************************************************************************
232  //***************************************************************************
233  bool get(bool& value)
234  {
235  bool success = false;
236 
237  if (pdata != ETL_NULLPTR)
238  {
239  // Do we have enough bits?
240  if (bits_remaining > 0)
241  {
242  value = get_bit();
243  success = true;
244  }
245  }
246 
247  return success;
248  }
249 
250  //***************************************************************************
252  //***************************************************************************
253  template <typename T>
254  typename etl::enable_if<etl::is_integral<T>::value, bool>::type
255  get(T& value, uint_least8_t width = CHAR_BIT * sizeof(T))
256  {
257  bool success = false;
258  uint_least8_t bits = width;
259 
260  if (pdata != ETL_NULLPTR)
261  {
262  // Do we have enough bits?
263  if (bits_remaining >= width)
264  {
265  value = 0;
266 
267  // Get the bits from the stream.
268  while (width != 0)
269  {
270  unsigned char mask_width = static_cast<unsigned char>(etl::min(width, bits_in_byte));
271  unsigned char chunk = get_chunk(mask_width);
272 
273  width -= mask_width;
274  value |= static_cast<T>(chunk) << width;
275  }
276 
277  success = true;
278  }
279  }
280 
281  // Sign extend if signed type and not already full bit width.
282  if (etl::is_signed<T>::value && (bits != (CHAR_BIT * sizeof(T))))
283  {
284  typedef typename etl::make_signed<T>::type ST;
285  value = etl::sign_extend<ST, ST>(value, bits);
286  }
287 
288  return success;
289  }
290 
291  //***************************************************************************
293  //***************************************************************************
294  template <typename T>
296  get(T& value)
297  {
298  bool success = false;
299 
300  if (pdata != ETL_NULLPTR)
301  {
302  uint_least8_t width = CHAR_BIT * sizeof(T);
303 
304  // Do we have enough bits?
305  if (bits_remaining >= width)
306  {
307  // Temporary storage.
308  unsigned char data[sizeof(T)];
309 
310  for (size_t i = 0; i < sizeof(T); ++i)
311  {
312  get(data[i], CHAR_BIT);
313  }
314 
315  from_bytes(data, value);
316 
317  success = true;
318  }
319  }
320 
321  return success;
322  }
323 
324  //***************************************************************************
326  //***************************************************************************
327  size_t size() const
328  {
329  size_t s = byte_index;
330 
331  // Current byte is used?
332  if (bits_in_byte != CHAR_BIT)
333  {
334  ++s;
335  }
336 
337  return s;
338  }
339 
340  //***************************************************************************
342  //***************************************************************************
343  size_t bits() const
344  {
345  return (length * CHAR_BIT) - bits_remaining;
346  }
347 
348  //***************************************************************************
350  //***************************************************************************
351  const_iterator begin() const
352  {
353  return pdata;
354  }
355 
356  //***************************************************************************
358  //***************************************************************************
359  const_iterator end() const
360  {
361  return pdata + size();
362  }
363 
364  private:
365 
366  //***************************************************************************
368  //***************************************************************************
369  bool put_integral(uint32_t value, uint_least8_t width)
370  {
371  bool success = false;
372 
373  if (pdata != ETL_NULLPTR)
374  {
375  // Do we have enough bits?
376  if (bits_remaining >= width)
377  {
378  // Send the bits to the stream.
379  while (width != 0)
380  {
381  unsigned char mask_width = static_cast<unsigned char>(etl::min(width, bits_in_byte));
382  width -= mask_width;
383  uint32_t mask = ((uint32_t(1U) << mask_width) - 1U) << width;
384 
385  // Move chunk to lowest char bits.
386  // Chunks are never larger than one char.
387  uint32_t chunk = ((value & mask) >> width) << (bits_in_byte - mask_width);
388 
389  put_chunk(static_cast<unsigned char>(chunk), mask_width);
390  }
391 
392  success = true;
393  }
394  }
395 
396  return success;
397  }
398 
399 #if ETL_USING_64BIT_TYPES
400  //***************************************************************************
402  //***************************************************************************
403  bool put_integral(uint64_t value, uint_least8_t width)
404  {
405  bool success = false;
406 
407  if (pdata != ETL_NULLPTR)
408  {
409  // Do we have enough bits?
410  if (bits_remaining >= width)
411  {
412  // Send the bits to the stream.
413  while (width != 0)
414  {
415  unsigned char mask_width = static_cast<unsigned char>(etl::min(width, bits_in_byte));
416  width -= mask_width;
417  uint64_t mask = ((uint64_t(1U) << mask_width) - 1U) << width;
418 
419  // Move chunk to lowest char bits.
420  // Chunks are never larger than one char.
421  uint64_t chunk = ((value & mask) >> width) << (bits_in_byte - mask_width);
422 
423  put_chunk(static_cast<unsigned char>(chunk), mask_width);
424  }
425 
426  success = true;
427  }
428  }
429 
430  return success;
431  }
432 #endif
433 
434  //***************************************************************************
436  //***************************************************************************
437  void put_chunk(unsigned char chunk, unsigned char width)
438  {
439  // Clear if new byte.
440  if (bits_in_byte == 8)
441  {
442  pdata[byte_index] = 0U;
443  }
444 
445  pdata[byte_index] |= chunk;
446  step(width);
447  }
448 
449  //***************************************************************************
451  //***************************************************************************
452  unsigned char get_chunk(unsigned char width)
453  {
454  unsigned char value = pdata[byte_index];
455 
456  value >>= (bits_in_byte - width);
457 
458  unsigned char mask;
459 
460  if (width == CHAR_BIT)
461  {
463  }
464  else
465  {
466  mask = (1U << width) - 1;
467  }
468 
469  value &= mask;
470 
471  step(width);
472 
473  return value;
474  }
475 
476  //***************************************************************************
478  //***************************************************************************
479  bool get_bit()
480  {
481  bool result = (pdata[byte_index] & (1 << (bits_in_byte - 1))) != 0;
482 
483  step(1U);
484 
485  return result;
486  }
487 
488  //***************************************************************************
490  //***************************************************************************
491  template <typename T>
492  void from_bytes(const unsigned char* data, T& value)
493  {
494  unsigned char temp[sizeof(T)];
495 
496  // Network to host.
497  if (etl::endianness::value() == etl::endian::little)
498  {
499  etl::reverse_copy(data, data + sizeof(T), temp);
500  }
501  else
502  {
503  etl::copy(data, data + sizeof(T), temp);
504  }
505 
506  value = *reinterpret_cast<T*>(temp);
507  }
508 
509  //***************************************************************************
511  //***************************************************************************
512  template <typename T>
513  void to_bytes(T value, unsigned char* data)
514  {
515  unsigned char* pf = reinterpret_cast<unsigned char*>(&value);
516 
517  // Host to network.
518  if (etl::endianness::value() == etl::endian::little)
519  {
520  etl::reverse_copy(pf, pf + sizeof(T), data);
521  }
522  else
523  {
524  etl::copy(pf, pf + sizeof(T), data);
525  }
526  }
527 
528  //***************************************************************************
531  //***************************************************************************
532  void step(unsigned char width)
533  {
534  bits_in_byte -= width;
535 
536  if (bits_in_byte == 0)
537  {
538  ++byte_index;
539  bits_in_byte = 8;
540  }
541 
542  bits_remaining -= width;
543  }
544 
545  unsigned char *pdata;
546  size_t length;
547  unsigned char bits_in_byte;
548  size_t byte_index;
549  size_t bits_remaining;
550  };
551 }
552 
553 #include "private/minmax_pop.h"
554 
555 #endif
Definition: bit_stream.h:50
bool put(uint64_t value, uint_least8_t width=CHAR_BIT *sizeof(uint64_t))
For 64bit integral types.
Definition: bit_stream.h:201
etl::enable_if< etl::is_integral< T >::value, bool >::type get(T &value, uint_least8_t width=CHAR_BIT *sizeof(T))
For integral types.
Definition: bit_stream.h:255
void set_stream(char *begin_, char *end_)
Construct from range.
Definition: bit_stream.h:128
bit_stream(char *begin_, size_t length_)
Construct from begin and length.
Definition: bit_stream.h:88
void restart()
Sets the indexes back to the beginning of the stream.
Definition: bit_stream.h:144
void set_stream(unsigned char *begin_, size_t length_)
Construct from begin and length.
Definition: bit_stream.h:118
bool put(int64_t value, uint_least8_t width=CHAR_BIT *sizeof(int64_t))
For 64bit integral types.
Definition: bit_stream.h:193
void set_stream(unsigned char *begin_, unsigned char *end_)
Construct from range.
Definition: bit_stream.h:136
size_t size() const
Returns the number of bytes used in the stream.
Definition: bit_stream.h:327
size_t bits() const
Returns the number of bits used in the stream.
Definition: bit_stream.h:343
etl::enable_if< etl::is_floating_point< T >::value, bool >::type get(T &value)
For floating point types.
Definition: bit_stream.h:296
bit_stream(unsigned char *begin_, size_t length_)
Construct from begin and length.
Definition: bit_stream.h:98
etl::enable_if< etl::is_floating_point< T >::value, bool >::type put(T value)
For floating point types.
Definition: bit_stream.h:212
bit_stream(unsigned char *begin_, unsigned char *end_)
Construct from range.
Definition: bit_stream.h:78
void set_stream(char *begin_, size_t length_)
Construct from begin and length.
Definition: bit_stream.h:108
bool at_end() const
Returns true if the bitsteam indexes have reached the end.
Definition: bit_stream.h:154
bool put(bool value)
Puts a boolean to the stream.
Definition: bit_stream.h:162
bit_stream()
Default constructor.
Definition: bit_stream.h:58
bool get(bool &value)
For bool types.
Definition: bit_stream.h:233
etl::enable_if< etl::is_integral< T >::value, bool >::type put(T value, uint_least8_t width=CHAR_BIT *sizeof(T))
For integral types.
Definition: bit_stream.h:184
const_iterator end() const
Returns end of the stream.
Definition: bit_stream.h:359
const_iterator begin() const
Returns start of the stream.
Definition: bit_stream.h:351
bit_stream(char *begin_, char *end_)
Construct from range.
Definition: bit_stream.h:68
Definition: integral_limits.h:54
enable_if
Definition: type_traits_generator.h:1228
is_signed
Definition: type_traits_generator.h:951
Definition: absolute.h:37