nalchi
Loading...
Searching...
No Matches
bit_stream.hpp
1#pragma once
2
3#include "nalchi/character.hpp"
4#include "nalchi/export.hpp"
5#include "nalchi/make_unsigned_allow_bool.hpp"
6#include "nalchi/shared_payload.hpp"
7
8#include <algorithm>
9#include <bit>
10#include <concepts>
11#include <cstdint>
12#include <limits>
13#include <span>
14#include <string>
15#include <string_view>
16#include <type_traits>
17
18#define NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(ret_val) \
19 do \
20 { \
21 if (fail()) \
22 return ret_val; \
23 } while (false)
24
25#define NALCHI_BIT_STREAM_WRITER_FAIL_IF_WRITE_AFTER_FINAL_FLUSH(ret_val) \
26 do \
27 { \
28 if (_final_flushed) \
29 { \
30 _fail = true; \
31 return ret_val; \
32 } \
33 } while (false)
34
35#define NALCHI_BIT_STREAM_FAIL_IF_MIN_MAX_RANGE_INVALID(ret_val) \
36 do \
37 { \
38 if (min >= max) \
39 { \
40 _fail = true; \
41 return ret_val; \
42 } \
43 } while (false)
44
45#define NALCHI_BIT_STREAM_WRITER_FAIL_IF_DATA_OUT_OF_RANGE(ret_val) \
46 do \
47 { \
48 if (data < min || data > max) \
49 { \
50 _fail = true; \
51 return ret_val; \
52 } \
53 } while (false)
54
55#define NALCHI_BIT_STREAM_WRITER_FAIL_IF_STR_OVERFLOW(prefix_bytes, str_len_bytes) \
56 do \
57 { \
58 if (_logical_used_bits + STR_LEN_PREFIX_PREFIX_BITS + (8 * (prefix_bytes)) + (8 * (str_len_bytes)) > \
59 _logical_total_bits) \
60 { \
61 _fail = true; \
62 return *this; \
63 } \
64 } while (false)
65
66#define NALCHI_BIT_STREAM_READER_FAIL_IF_STR_OVERFLOW(str_len_bytes) \
67 do \
68 { \
69 if (_logical_used_bits + (8 * (str_len_bytes)) > _logical_total_bits) \
70 { \
71 _fail = true; \
72 return *this; \
73 } \
74 } while (false)
75
76namespace nalchi
77{
78
90{
91public:
92 using size_type = std::uint32_t;
93
94 using scratch_type = std::uint64_t;
95 using word_type = std::uint32_t;
96
97 static_assert(std::is_unsigned_v<scratch_type>);
98 static_assert(std::is_unsigned_v<word_type>);
99 static_assert(sizeof(scratch_type) == 2 * sizeof(word_type));
100
101 static_assert(std::endian::native == std::endian::little || std::endian::native == std::endian::big,
102 "Mixed endian system is not supported");
103
104public:
105 // "prefix of string length prefix"
106 // 0: u8 / 1: u16 / 2: u32 / 3: u64
107 static constexpr size_type STR_LEN_PREFIX_PREFIX_BITS = 2u;
108 static constexpr size_type MIN_STR_LEN_PREFIX_PREFIX = 0u;
109 static constexpr size_type MAX_STR_LEN_PREFIX_PREFIX = 3u;
110
111private:
112 scratch_type _scratch;
113 std::span<word_type> _words;
114
115 int _scratch_index;
116 int _words_index;
117
118 // These fields are required, because logical user buffer size might differ from `_words` size.
119 //
120 // e.g. If user passed `shared_payload` whose size is 5 bytes,
121 // the actual allocated buffer is 8 bytes, to avoid overrun writes.
122 // But for the user's perspective, writing more than 5 bytes should be treated as an overflow.
123 size_type _logical_total_bits;
124 size_type _logical_used_bits;
125
126 bool _init_fail;
127 bool _fail;
128
129 bool _final_flushed;
130
131public:
134
137
142 NALCHI_API bit_stream_writer();
143
148 NALCHI_API bit_stream_writer(shared_payload buffer, size_type logical_bytes_length);
149
154 NALCHI_API bit_stream_writer(std::span<word_type> buffer, size_type logical_bytes_length);
155
161 NALCHI_API bit_stream_writer(word_type* begin, word_type* end, size_type logical_bytes_length);
162
168 NALCHI_API bit_stream_writer(word_type* begin, size_type words_length, size_type logical_bytes_length);
169
170public:
172 NALCHI_API void set_fail()
173 {
174 _fail = true;
175 }
176
181 NALCHI_API bool fail() const noexcept
182 {
183 return _fail;
184 }
185
190 NALCHI_API bool operator!() const noexcept
191 {
192 return fail();
193 }
194
199 NALCHI_API operator bool() const noexcept
200 {
201 return !fail();
202 }
203
204public:
207 NALCHI_API auto total_bytes() const -> size_type
208 {
209 return _logical_total_bits / 8;
210 }
211
214 NALCHI_API auto total_bits() const -> size_type
215 {
216 return _logical_total_bits;
217 }
218
221 NALCHI_API auto used_bytes() const -> size_type;
222
225 NALCHI_API auto used_bits() const -> size_type
226 {
227 return _logical_used_bits;
228 }
229
232 NALCHI_API auto unused_bytes() const -> size_type
233 {
234 return total_bytes() - used_bytes();
235 }
236
239 NALCHI_API auto unused_bits() const -> size_type
240 {
241 return total_bits() - used_bits();
242 }
243
244public:
248 NALCHI_API void restart();
249
253 NALCHI_API void reset();
254
261 NALCHI_API void reset_with(shared_payload buffer, size_type logical_bytes_length);
262
269 NALCHI_API void reset_with(std::span<word_type> buffer, size_type logical_bytes_length);
270
278 NALCHI_API void reset_with(word_type* begin, word_type* end, size_type logical_bytes_length);
279
287 NALCHI_API void reset_with(word_type* begin, size_type words_length, size_type logical_bytes_length);
288
293 NALCHI_API auto flush_final() -> bit_stream_writer&;
294
297 NALCHI_API bool flushed() const
298 {
299 return _final_flushed;
300 }
301
302public:
309 NALCHI_API auto write(const void* data, size_type size) -> bit_stream_writer&;
310
317 template <std::integral SInt>
318 requires(sizeof(SInt) <= sizeof(word_type))
319 auto write(SInt data, SInt min = std::numeric_limits<SInt>::min(), SInt max = std::numeric_limits<SInt>::max())
321 {
322 return do_write<true>(data, min, max);
323 }
324
331 template <std::integral BInt>
332 requires(sizeof(BInt) > sizeof(word_type))
333 auto write(BInt data, BInt min = std::numeric_limits<BInt>::min(), BInt max = std::numeric_limits<BInt>::max())
335 {
336 return do_write<true>(data, min, max);
337 }
338
342 NALCHI_API auto write(float data) -> bit_stream_writer&;
343
347 NALCHI_API auto write(double data) -> bit_stream_writer&;
348
354 template <character CharT, typename CharTraits>
355 auto write(std::basic_string_view<CharT, CharTraits> str) -> bit_stream_writer&
356 {
357 NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(*this);
358 NALCHI_BIT_STREAM_WRITER_FAIL_IF_WRITE_AFTER_FINAL_FLUSH(*this);
359
360 // Get the length of the string.
361 const auto len = str.length();
362
363 // Write a "prefix of length prefix" + length prefix.
364 // 0: u8 / 1: u16 / 2: u32 / 3: u64
365 if (len <= std::numeric_limits<std::uint8_t>::max())
366 {
367 NALCHI_BIT_STREAM_WRITER_FAIL_IF_STR_OVERFLOW(sizeof(std::uint8_t), len * sizeof(CharT));
368 do_write<false>(size_type(0), MIN_STR_LEN_PREFIX_PREFIX,
369 MAX_STR_LEN_PREFIX_PREFIX); // prefix of length prefix
370 do_write<false>(static_cast<std::uint8_t>(len)); // length prefix
371 }
372 else if (len <= std::numeric_limits<std::uint16_t>::max())
373 {
374 NALCHI_BIT_STREAM_WRITER_FAIL_IF_STR_OVERFLOW(sizeof(std::uint16_t), len * sizeof(CharT));
375 do_write<false>(size_type(1), MIN_STR_LEN_PREFIX_PREFIX,
376 MAX_STR_LEN_PREFIX_PREFIX); // prefix of length prefix
377 do_write<false>(static_cast<std::uint16_t>(len)); // length prefix
378 }
379 else if (len <= std::numeric_limits<std::uint32_t>::max())
380 {
381 NALCHI_BIT_STREAM_WRITER_FAIL_IF_STR_OVERFLOW(sizeof(std::uint32_t), len * sizeof(CharT));
382 do_write<false>(size_type(2), MIN_STR_LEN_PREFIX_PREFIX,
383 MAX_STR_LEN_PREFIX_PREFIX); // prefix of length prefix
384 do_write<false>(static_cast<std::uint32_t>(len)); // length prefix
385 }
386 else // len <= std::numeric_limits<std::uint64_t>::max()
387 {
388 NALCHI_BIT_STREAM_WRITER_FAIL_IF_STR_OVERFLOW(sizeof(std::uint64_t), len * sizeof(CharT));
389 do_write<false>(size_type(3), MIN_STR_LEN_PREFIX_PREFIX,
390 MAX_STR_LEN_PREFIX_PREFIX); // prefix of length prefix
391 do_write<false>(static_cast<std::uint64_t>(len)); // length prefix
392 }
393
394 // Just write every character one by one.
395 for (const auto ch : str)
396 do_write<false>(ch);
397
398 return *this;
399 }
400
407 template <character CharT, typename CharTraits, typename Allocator>
408 auto write(const std::basic_string<CharT, CharTraits, Allocator>& str) -> bit_stream_writer&
409 {
410 return write(std::basic_string_view<CharT, CharTraits>(str));
411 }
412
417 template <character CharT>
418 auto write(const CharT* str) -> bit_stream_writer&
419 {
420 return write(std::basic_string_view<CharT>(str));
421 }
422
423private:
431 template <bool Checked, std::integral SInt>
432 requires(sizeof(SInt) <= sizeof(word_type))
433 auto do_write(SInt data, SInt min = std::numeric_limits<SInt>::min(), SInt max = std::numeric_limits<SInt>::max())
435 {
436 if constexpr (Checked)
437 {
438 NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(*this);
439 NALCHI_BIT_STREAM_WRITER_FAIL_IF_WRITE_AFTER_FINAL_FLUSH(*this);
440 NALCHI_BIT_STREAM_FAIL_IF_MIN_MAX_RANGE_INVALID(*this);
441 NALCHI_BIT_STREAM_WRITER_FAIL_IF_DATA_OUT_OF_RANGE(*this);
442 }
443
444 using UInt = make_unsigned_allow_bool_t<SInt>;
445
446 // Convert `data` to `value` to actually write.
447 const scratch_type value = static_cast<UInt>(((UInt)data) - ((UInt)min));
448 const int bits = std::bit_width(static_cast<UInt>(((UInt)max) - ((UInt)min)));
449
450 if constexpr (Checked)
451 {
452 // Fail if user buffer overflows.
453 if (_logical_used_bits + bits > _logical_total_bits)
454 {
455 _fail = true;
456 return *this;
457 }
458 }
459
460 // Write `value` to `_scratch`, and flush if scratch overflow.
461 _scratch |= (value << _scratch_index);
462 _scratch_index += bits;
463 flush_if_scratch_overflow();
464
465 // Adjust used bits
466 _logical_used_bits += bits;
467
468 return *this;
469 }
470
478 template <bool Checked, std::integral BInt>
479 requires(sizeof(BInt) > sizeof(word_type))
480 auto do_write(BInt data, BInt min = std::numeric_limits<BInt>::min(), BInt max = std::numeric_limits<BInt>::max())
482 {
483 if constexpr (Checked)
484 {
485 NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(*this);
486 NALCHI_BIT_STREAM_WRITER_FAIL_IF_WRITE_AFTER_FINAL_FLUSH(*this);
487 NALCHI_BIT_STREAM_FAIL_IF_MIN_MAX_RANGE_INVALID(*this);
488 NALCHI_BIT_STREAM_WRITER_FAIL_IF_DATA_OUT_OF_RANGE(*this);
489 }
490
491 // Current logic assumes the only size here is 64 bits, but hey who wouldn't?
492 static_assert(sizeof(BInt) == 2 * sizeof(word_type));
493
494 using UInt = make_unsigned_allow_bool_t<BInt>;
495
496 // Convert `data` to `value`.
497 const scratch_type value = static_cast<UInt>(((UInt)data) - ((UInt)min));
498 const int bits = std::bit_width(static_cast<UInt>(((UInt)max) - ((UInt)min)));
499
500 if constexpr (Checked)
501 {
502 // Fail if user buffer overflows.
503 if (_logical_used_bits + bits > _logical_total_bits)
504 {
505 _fail = true;
506 return *this;
507 }
508 }
509
510 // Split lower half of `value`
511 const scratch_type low = (value << (8 * sizeof(word_type))) >> (8 * sizeof(word_type));
512 const int low_bits = std::min(bits, static_cast<int>(8 * sizeof(word_type)));
513
514 // Write lower half to `_scratch`, and flush if scratch overflow.
515 _scratch |= (low << _scratch_index);
516 _scratch_index += low_bits;
517 flush_if_scratch_overflow();
518
519 const int high_bits = bits - low_bits;
520 if (high_bits > 0)
521 {
522 // Split higher half of `value`
523 const scratch_type high = (value >> (8 * sizeof(word_type)));
524
525 // Write higher half to `_scratch`, and flush if scratch overflow.
526 _scratch |= (high << _scratch_index);
527 _scratch_index += high_bits;
528 flush_if_scratch_overflow();
529 }
530
531 // Adjust used bits
532 _logical_used_bits += bits;
533
534 return *this;
535 }
536
537private:
538 NALCHI_API void flush_if_scratch_overflow();
539
546 NALCHI_API void do_flush_word_unchecked();
547};
548
555{
556public:
558
559private:
560 size_type _logical_used_bits = 0;
561
562public:
565
568
570 NALCHI_API bit_stream_measurer() = default;
571
572public:
575 NALCHI_API auto used_bytes() const -> size_type;
576
579 NALCHI_API auto used_bits() const -> size_type
580 {
581 return _logical_used_bits;
582 }
583
584public:
586 NALCHI_API void restart()
587 {
588 _logical_used_bits = 0;
589 }
590
591public:
596 NALCHI_API auto write([[maybe_unused]] const void* data, size_type size) -> bit_stream_measurer&
597 {
598 _logical_used_bits += 8 * size;
599 return *this;
600 }
601
608 template <std::integral Int>
609 auto write([[maybe_unused]] Int data, Int min = std::numeric_limits<Int>::min(),
610 Int max = std::numeric_limits<Int>::max()) -> bit_stream_measurer&
611 {
612 using UInt = make_unsigned_allow_bool_t<Int>;
613
614 // Convert `data` to `value`.
615 const int bits = std::bit_width(static_cast<UInt>(((UInt)max) - ((UInt)min)));
616
617 // Adjust used bits
618 _logical_used_bits += static_cast<size_type>(bits);
619
620 return *this;
621 }
622
626 NALCHI_API auto write(float data) -> bit_stream_measurer&
627 {
628 _logical_used_bits += static_cast<size_type>(8 * sizeof(data));
629 return *this;
630 }
631
635 NALCHI_API auto write(double data) -> bit_stream_measurer&
636 {
637 _logical_used_bits += static_cast<size_type>(8 * sizeof(data));
638 return *this;
639 }
640
646 template <character CharT, typename CharTraits>
647 auto write(std::basic_string_view<CharT, CharTraits> str) -> bit_stream_measurer&
648 {
649 // Fake-write a prefix of length prefix.
650 _logical_used_bits += bit_stream_writer::STR_LEN_PREFIX_PREFIX_BITS;
651
652 // Get the length of the string.
653 const auto len = str.length();
654
655 // Fake-write a length prefix.
656 if (len <= std::numeric_limits<std::uint8_t>::max())
657 {
658 _logical_used_bits += (8 * sizeof(std::uint8_t));
659 }
660 else if (len <= std::numeric_limits<std::uint16_t>::max())
661 {
662 _logical_used_bits += (8 * sizeof(std::uint16_t));
663 }
664 else if (len <= std::numeric_limits<std::uint32_t>::max())
665 {
666 _logical_used_bits += (8 * sizeof(std::uint32_t));
667 }
668 else // len <= std::numeric_limits<std::uint64_t>::max()
669 {
670 _logical_used_bits += (8 * sizeof(std::uint64_t));
671 }
672
673 // Fake-write the string payload.
674 _logical_used_bits += static_cast<size_type>(8 * len * sizeof(CharT));
675
676 return *this;
677 }
678
685 template <character CharT, typename CharTraits, typename Allocator>
686 auto write(const std::basic_string<CharT, CharTraits, Allocator>& str) -> bit_stream_measurer&
687 {
688 return write(std::basic_string_view<CharT, CharTraits>(str));
689 }
690
695 template <character CharT>
696 auto write(const CharT* str) -> bit_stream_measurer&
697 {
698 return write(std::basic_string_view<CharT>(str));
699 }
700};
701
708{
709public:
711 using ssize_type = std::make_signed_t<size_type>;
712
716
717private:
718 scratch_type _scratch;
719 std::span<const word_type> _words;
720
721 int _scratch_bits;
722 int _words_index;
723
724 size_type _logical_total_bits;
725 size_type _logical_used_bits;
726
727 bool _init_fail;
728 bool _fail;
729
730public:
733
736
741 NALCHI_API bit_stream_reader();
742
747 NALCHI_API bit_stream_reader(std::span<const word_type> buffer, size_type logical_bytes_length);
748
754 NALCHI_API bit_stream_reader(const word_type* begin, const word_type* end, size_type logical_bytes_length);
755
761 NALCHI_API bit_stream_reader(const word_type* begin, size_type words_length, size_type logical_bytes_length);
762
763public:
765 NALCHI_API void set_fail()
766 {
767 _fail = true;
768 }
769
774 NALCHI_API bool fail() const noexcept
775 {
776 return _fail;
777 }
778
783 NALCHI_API bool operator!() const noexcept
784 {
785 return fail();
786 }
787
792 NALCHI_API operator bool() const noexcept
793 {
794 return !fail();
795 }
796
797public:
800 NALCHI_API auto total_bytes() const -> size_type
801 {
802 return _logical_total_bits / 8;
803 }
804
807 NALCHI_API auto total_bits() const -> size_type
808 {
809 return _logical_total_bits;
810 }
811
814 NALCHI_API auto used_bytes() const -> size_type;
815
818 NALCHI_API auto used_bits() const -> size_type
819 {
820 return _logical_used_bits;
821 }
822
825 NALCHI_API auto unused_bytes() const -> size_type
826 {
827 return total_bytes() - used_bytes();
828 }
829
832 NALCHI_API auto unused_bits() const -> size_type
833 {
834 return total_bits() - used_bits();
835 }
836
837public:
839 NALCHI_API void restart();
840
842 NALCHI_API void reset();
843
848 NALCHI_API void reset_with(std::span<const word_type> buffer, size_type logical_bytes_length);
849
855 NALCHI_API void reset_with(const word_type* begin, const word_type* end, size_type logical_bytes_length);
856
862 NALCHI_API void reset_with(const word_type* begin, size_type words_length, size_type logical_bytes_length);
863
864public:
871 NALCHI_API auto read(void* data, size_type size) -> bit_stream_reader&;
872
879 template <std::integral SInt>
880 requires(sizeof(SInt) <= sizeof(word_type))
881 auto read(SInt& data, SInt min = std::numeric_limits<SInt>::min(), SInt max = std::numeric_limits<SInt>::max())
883 {
884 return do_read<true>(data, min, max);
885 }
886
893 template <std::integral BInt>
894 requires(sizeof(BInt) > sizeof(word_type))
895 auto read(BInt& data, BInt min = std::numeric_limits<BInt>::min(), BInt max = std::numeric_limits<BInt>::max())
897 {
898 return do_read<true>(data, min, max);
899 }
900
904 NALCHI_API auto read(float& data) -> bit_stream_reader&;
905
909 NALCHI_API auto read(double& data) -> bit_stream_reader&;
910
922 template <character CharT, typename CharTraits, typename Allocator>
923 auto read(std::basic_string<CharT, CharTraits, Allocator>& str, size_type max_length) -> bit_stream_reader&
924 {
925 NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(*this);
926
927 // Read the length of the string.
928 ssize_type len = read_string_length();
929 if (len < 0 || static_cast<size_type>(len) > max_length)
930 {
931 _fail = true;
932 return *this;
933 }
934
935 NALCHI_BIT_STREAM_READER_FAIL_IF_STR_OVERFLOW(len * sizeof(CharT));
936
937 // Resize and read every character one by one.
938 str.resize(len);
939 for (auto& ch : str)
940 do_read<false>(ch);
941
942 return *this;
943 }
944
957 template <character CharT>
958 auto read(CharT* str, size_type max_length) -> bit_stream_reader&
959 {
960 NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(*this);
961
962 // Read the length of the string.
963 ssize_type len = read_string_length();
964 if (len < 0 || static_cast<size_type>(len) > max_length)
965 {
966 _fail = true;
967 return *this;
968 }
969
970 NALCHI_BIT_STREAM_READER_FAIL_IF_STR_OVERFLOW(len * sizeof(CharT));
971
972 // Read every character one by one.
973 for (ssize_type i = 0; i < len; ++i)
974 do_read<false>(str[i]);
975
976 // Insert final null character.
977 str[len] = CharT(0);
978
979 return *this;
980 }
981
988 NALCHI_API auto peek_string_length() -> ssize_type;
989
990private:
997 NALCHI_API auto read_string_length() -> ssize_type;
998
1006 template <bool Checked, std::integral SInt>
1007 requires(sizeof(SInt) <= sizeof(word_type))
1008 auto do_read(SInt& data, SInt min = std::numeric_limits<SInt>::min(), SInt max = std::numeric_limits<SInt>::max())
1010 {
1011 if constexpr (Checked)
1012 {
1013 NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(*this);
1014 NALCHI_BIT_STREAM_FAIL_IF_MIN_MAX_RANGE_INVALID(*this);
1015 }
1016
1017 using UInt = make_unsigned_allow_bool_t<SInt>;
1018
1019 // Calculate bits to read.
1020 const int bits = std::bit_width(static_cast<UInt>(((UInt)max) - ((UInt)min)));
1021
1022 if constexpr (Checked)
1023 {
1024 // Fail if no more data to be read in `_words`.
1025 if (_logical_used_bits + bits > _logical_total_bits)
1026 {
1027 _fail = true;
1028 return *this;
1029 }
1030 }
1031
1032 // Load more bits to `_scratch` if needed.
1033 if (bits > _scratch_bits)
1034 do_fetch_word_unchecked();
1035
1036 // Read raw `value` from `_scratch`.
1037 UInt value = static_cast<UInt>(_scratch & ((((scratch_type)1) << bits) - 1));
1038
1039 // Remove read bits from `_scratch`.
1040 _scratch >>= bits;
1041 _scratch_bits -= bits;
1042
1043 // Convert to original range.
1044 const SInt conv = static_cast<SInt>(((SInt)value) + min);
1045
1046 if constexpr (Checked)
1047 {
1048 // Fail if it exceeds `max`.
1049 if (conv > max)
1050 {
1051 _fail = true;
1052 return *this;
1053 }
1054 }
1055
1056 // Load `conv` to `data`.
1057 data = conv;
1058
1059 // Adjust used bits
1060 _logical_used_bits += bits;
1061
1062 return *this;
1063 }
1064
1072 template <bool Checked, std::integral BInt>
1073 requires(sizeof(BInt) > sizeof(word_type))
1074 auto do_read(BInt& data, BInt min = std::numeric_limits<BInt>::min(), BInt max = std::numeric_limits<BInt>::max())
1076 {
1077 if constexpr (Checked)
1078 {
1079 NALCHI_BIT_STREAM_RETURN_IF_STREAM_ALREADY_FAILED(*this);
1080 NALCHI_BIT_STREAM_FAIL_IF_MIN_MAX_RANGE_INVALID(*this);
1081 }
1082
1083 // Current logic assumes the only size here is 64 bits, but hey who wouldn't?
1084 static_assert(sizeof(BInt) == 2 * sizeof(word_type));
1085
1086 using UInt = make_unsigned_allow_bool_t<BInt>;
1087
1088 // Calculate bits to read.
1089 const int bits = std::bit_width(static_cast<UInt>(((UInt)max) - ((UInt)min)));
1090
1091 if constexpr (Checked)
1092 {
1093 // Fail if no more data to be read in `_words`.
1094 if (_logical_used_bits + bits > _logical_total_bits)
1095 {
1096 _fail = true;
1097 return *this;
1098 }
1099 }
1100
1101 const int low_bits = std::min(bits, static_cast<int>(8 * sizeof(word_type)));
1102
1103 // Load more bits to `_scratch` if needed.
1104 if (low_bits > _scratch_bits)
1105 do_fetch_word_unchecked();
1106
1107 // Read low bits from `_scratch`.
1108 UInt value = static_cast<UInt>(_scratch & ((((scratch_type)1) << low_bits) - 1));
1109
1110 // Remove read bits from `_scratch`.
1111 _scratch >>= low_bits;
1112 _scratch_bits -= low_bits;
1113
1114 const int high_bits = bits - low_bits;
1115 if (high_bits > 0)
1116 {
1117 // Load more bits to `_scratch` if needed.
1118 if (high_bits > _scratch_bits)
1119 do_fetch_word_unchecked();
1120
1121 // Read high bits from `_scratch`.
1122 value |= (static_cast<UInt>(_scratch & ((((scratch_type)1) << high_bits) - 1)) << low_bits);
1123
1124 // Remove read bits from `_scratch`.
1125 _scratch >>= high_bits;
1126 _scratch_bits -= high_bits;
1127 }
1128
1129 // Convert to original range.
1130 const BInt conv = static_cast<BInt>(((BInt)value) + min);
1131
1132 if constexpr (Checked)
1133 {
1134 // Fail if it exceeds `max`.
1135 if (conv > max)
1136 {
1137 _fail = true;
1138 return *this;
1139 }
1140 }
1141
1142 // Load `conv` to `data`.
1143 data = conv;
1144
1145 // Adjust used bits
1146 _logical_used_bits += bits;
1147
1148 return *this;
1149 }
1150
1151private:
1152 NALCHI_API void do_fetch_word_unchecked();
1153};
1154
1155} // namespace nalchi
Measures the bytes bit_stream_writer will use.
Definition bit_stream.hpp:555
auto used_bits() const -> size_type
Gets the number of used (measured) bits.
Definition bit_stream.hpp:579
bit_stream_measurer(const bit_stream_measurer &)=delete
Deleted copy constructor.
auto used_bytes() const -> size_type
Gets the number of used (measured) bytes.
auto write(const std::basic_string< CharT, CharTraits, Allocator > &str) -> bit_stream_measurer &
Fake-writes a string to the bit stream.
Definition bit_stream.hpp:686
auto write(const CharT *str) -> bit_stream_measurer &
Fake-writes a null-terminated string to the bit stream.
Definition bit_stream.hpp:696
auto write(const void *data, size_type size) -> bit_stream_measurer &
Fake-writes some arbitrary data to the bit stream.
Definition bit_stream.hpp:596
bit_stream_measurer()=default
Constructs a bit_stream_measurer instance.
bit_stream_writer::size_type size_type
Size type representing number of bits and bytes.
Definition bit_stream.hpp:557
auto write(float data) -> bit_stream_measurer &
Fake-writes a float value to the bit stream.
Definition bit_stream.hpp:626
auto operator=(const bit_stream_measurer &) -> bit_stream_measurer &=delete
Deleted copy assignment operator.
void restart()
Restarts the measure from zero.
Definition bit_stream.hpp:586
auto write(std::basic_string_view< CharT, CharTraits > str) -> bit_stream_measurer &
Fake-writes a string view to the bit stream.
Definition bit_stream.hpp:647
auto write(Int data, Int min=std::numeric_limits< Int >::min(), Int max=std::numeric_limits< Int >::max()) -> bit_stream_measurer &
Fake-writes an integral value to the bit stream.
Definition bit_stream.hpp:609
auto write(double data) -> bit_stream_measurer &
Fake-writes a double value to the bit stream.
Definition bit_stream.hpp:635
Helper stream to read bits from your buffer.
Definition bit_stream.hpp:708
bit_stream_reader(const bit_stream_reader &)=delete
Deleted copy constructor.
void reset_with(std::span< const word_type > buffer, size_type logical_bytes_length)
Resets the stream with a std::span<word_type> buffer.
bit_stream_writer::scratch_type scratch_type
Internal scratch type to store the temporary scratch data.
Definition bit_stream.hpp:713
void reset()
Resets the stream so that it no longer holds your buffer anymore.
bit_stream_reader(const word_type *begin, size_type words_length, size_type logical_bytes_length)
Constructs a bit_stream_reader instance with a word begin pointer and the word length.
void restart()
Restarts the stream so that it can read from the beginning again.
auto unused_bits() const -> size_type
Gets the number of unused bits in the stream.
Definition bit_stream.hpp:832
auto read(SInt &data, SInt min=std::numeric_limits< SInt >::min(), SInt max=std::numeric_limits< SInt >::max()) -> bit_stream_reader &
Reads an integral value from the bit stream.
Definition bit_stream.hpp:881
void reset_with(const word_type *begin, size_type words_length, size_type logical_bytes_length)
Resets the stream with a word begin pointer and the word length.
auto peek_string_length() -> ssize_type
Peeks the string length prefix from the current stream position.
bool fail() const noexcept
Check if reading from your buffer has been failed or not.
Definition bit_stream.hpp:774
auto operator=(const bit_stream_reader &) -> bit_stream_reader &=delete
Deleted copy assignment operator.
auto read(double &data) -> bit_stream_reader &
Reads a double value from the bit stream.
bit_stream_reader(const word_type *begin, const word_type *end, size_type logical_bytes_length)
Constructs a bit_stream_reader instance with a word range.
auto read(std::basic_string< CharT, CharTraits, Allocator > &str, size_type max_length) -> bit_stream_reader &
Reads a string from the bit stream.
Definition bit_stream.hpp:923
bit_stream_writer::word_type word_type
Internal word type used to read from your buffer.
Definition bit_stream.hpp:715
auto read(float &data) -> bit_stream_reader &
Reads a float value from the bit stream.
auto unused_bytes() const -> size_type
Gets the number of unused bytes in the stream.
Definition bit_stream.hpp:825
void reset_with(const word_type *begin, const word_type *end, size_type logical_bytes_length)
Resets the stream with a word range.
auto total_bits() const -> size_type
Gets the number of total bits in the stream.
Definition bit_stream.hpp:807
auto used_bits() const -> size_type
Gets the number of used bits in the stream.
Definition bit_stream.hpp:818
auto read(BInt &data, BInt min=std::numeric_limits< BInt >::min(), BInt max=std::numeric_limits< BInt >::max()) -> bit_stream_reader &
Reads an integral value from the bit stream.
Definition bit_stream.hpp:895
auto read(void *data, size_type size) -> bit_stream_reader &
Reads some arbitrary data from the bit stream.
auto total_bytes() const -> size_type
Gets the number of total bytes in the stream.
Definition bit_stream.hpp:800
bit_stream_writer::size_type size_type
Size type representing number of bits and bytes.
Definition bit_stream.hpp:710
bit_stream_reader()
Constructs a bit_stream_reader instance without a buffer.
auto read(CharT *str, size_type max_length) -> bit_stream_reader &
Reads a null-terminated string from the bit stream.
Definition bit_stream.hpp:958
auto used_bytes() const -> size_type
Gets the number of used bytes in the stream.
std::make_signed_t< size_type > ssize_type
Signed size type to allow negative error value.
Definition bit_stream.hpp:711
bool operator!() const noexcept
Check if there was an error in the reading to your buffer. This is effectively same as fail().
Definition bit_stream.hpp:783
void set_fail()
Force set the fail flag.
Definition bit_stream.hpp:765
bit_stream_reader(std::span< const word_type > buffer, size_type logical_bytes_length)
Constructs a bit_stream_reader instance with a std::span<word_type> buffer.
Helper stream to write bits to your buffer.
Definition bit_stream.hpp:90
bool fail() const noexcept
Check if writing to your buffer has been failed or not.
Definition bit_stream.hpp:181
auto operator=(const bit_stream_writer &) -> bit_stream_writer &=delete
Deleted copy assignment operator.
void set_fail()
Force set the fail flag.
Definition bit_stream.hpp:172
bit_stream_writer()
Constructs a bit_stream_writer instance without a buffer.
bit_stream_writer(word_type *begin, size_type words_length, size_type logical_bytes_length)
Constructs a bit_stream_writer instance with a word begin pointer and the word length.
void restart()
Restarts the stream so that it can write from the beginning again.
auto unused_bytes() const -> size_type
Gets the number of unused bytes in the stream.
Definition bit_stream.hpp:232
void reset_with(std::span< word_type > buffer, size_type logical_bytes_length)
Resets the stream with a std::span<word_type> buffer.
std::uint64_t scratch_type
Internal scratch type to store the temporary scratch data.
Definition bit_stream.hpp:94
auto used_bits() const -> size_type
Gets the number of used bits in the stream.
Definition bit_stream.hpp:225
bool operator!() const noexcept
Check if there was an error in the writing to your buffer. This is effectively same as fail().
Definition bit_stream.hpp:190
void reset()
Resets the stream so that it no longer holds your buffer anymore.
auto total_bits() const -> size_type
Gets the number of total bits in the stream.
Definition bit_stream.hpp:214
auto flush_final() -> bit_stream_writer &
Flushes the last remaining bytes on the internal scratch buffer to your buffer.
auto total_bytes() const -> size_type
Gets the number of total bytes in the stream.
Definition bit_stream.hpp:207
auto write(SInt data, SInt min=std::numeric_limits< SInt >::min(), SInt max=std::numeric_limits< SInt >::max()) -> bit_stream_writer &
Writes an integral value to the bit stream.
Definition bit_stream.hpp:319
void reset_with(shared_payload buffer, size_type logical_bytes_length)
Resets the stream with a shared_payload buffer.
bit_stream_writer(shared_payload buffer, size_type logical_bytes_length)
Constructs a bit_stream_writer instance with a shared_payload buffer.
auto write(const std::basic_string< CharT, CharTraits, Allocator > &str) -> bit_stream_writer &
Writes a string to the bit stream.
Definition bit_stream.hpp:408
auto write(const CharT *str) -> bit_stream_writer &
Writes a null-terminated string to the bit stream.
Definition bit_stream.hpp:418
auto write(BInt data, BInt min=std::numeric_limits< BInt >::min(), BInt max=std::numeric_limits< BInt >::max()) -> bit_stream_writer &
Writes an integral value to the bit stream.
Definition bit_stream.hpp:333
bit_stream_writer(const bit_stream_writer &)=delete
Deleted copy constructor.
auto used_bytes() const -> size_type
Gets the number of used bytes in the stream.
std::uint32_t size_type
Size type representing number of bits and bytes.
Definition bit_stream.hpp:92
auto write(float data) -> bit_stream_writer &
Writes a float value to the bit stream.
void reset_with(word_type *begin, size_type words_length, size_type logical_bytes_length)
Resets the stream with a word begin pointer and the word length.
auto unused_bits() const -> size_type
Gets the number of unused bits in the stream.
Definition bit_stream.hpp:239
auto write(double data) -> bit_stream_writer &
Writes a double value to the bit stream.
std::uint32_t word_type
Internal word type used to write to your buffer.
Definition bit_stream.hpp:95
bool flushed() const
Checks if flush_final() has been called or not.
Definition bit_stream.hpp:297
bit_stream_writer(std::span< word_type > buffer, size_type logical_bytes_length)
Constructs a bit_stream_writer instance with a std::span<word_type> buffer.
bit_stream_writer(word_type *begin, word_type *end, size_type logical_bytes_length)
Constructs a bit_stream_writer instance with a word range.
auto write(std::basic_string_view< CharT, CharTraits > str) -> bit_stream_writer &
Writes a string view to the bit stream.
Definition bit_stream.hpp:355
auto write(const void *data, size_type size) -> bit_stream_writer &
Writes some arbitrary data to the bit stream.
void reset_with(word_type *begin, word_type *end, size_type logical_bytes_length)
Resets the stream with a word range.
Shared payload to store data to send.
Definition shared_payload.hpp:20