/src/serenity/AK/BitStream.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/ByteBuffer.h> |
10 | | #include <AK/Concepts.h> |
11 | | #include <AK/MaybeOwned.h> |
12 | | #include <AK/NumericLimits.h> |
13 | | #include <AK/OwnPtr.h> |
14 | | #include <AK/Stream.h> |
15 | | |
16 | | namespace AK { |
17 | | |
18 | | /// A stream wrapper class that allows you to read arbitrary amounts of bits |
19 | | /// in big-endian order from another stream. |
20 | | class BigEndianInputBitStream : public Stream { |
21 | | public: |
22 | | explicit BigEndianInputBitStream(MaybeOwned<Stream> stream) |
23 | 1.64M | : m_stream(move(stream)) |
24 | 1.64M | { |
25 | 1.64M | } |
26 | | |
27 | | // ^Stream |
28 | | virtual ErrorOr<Bytes> read_some(Bytes bytes) override |
29 | 168k | { |
30 | 168k | if (m_current_byte.has_value() && is_aligned_to_byte_boundary()) { |
31 | 0 | bytes[0] = m_current_byte.release_value(); |
32 | 0 | auto freshly_read_bytes = TRY(m_stream->read_some(bytes.slice(1))); |
33 | 0 | return bytes.trim(1 + freshly_read_bytes.size()); |
34 | 0 | } |
35 | 168k | align_to_byte_boundary(); |
36 | 168k | return m_stream->read_some(bytes); |
37 | 168k | } |
38 | 0 | virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override { return m_stream->write_some(bytes); } |
39 | 251k | virtual bool is_eof() const override { return m_stream->is_eof() && !m_current_byte.has_value(); } |
40 | 0 | virtual bool is_open() const override { return m_stream->is_open(); } |
41 | | virtual void close() override |
42 | 0 | { |
43 | 0 | m_stream->close(); |
44 | 0 | align_to_byte_boundary(); |
45 | 0 | } |
46 | | |
47 | | ErrorOr<bool> read_bit() |
48 | 33.2M | { |
49 | 33.2M | return read_bits<bool>(1); |
50 | 33.2M | } |
51 | | |
52 | | /// Depending on the number of bits to read, the return type can be chosen appropriately. |
53 | | /// This avoids a bunch of static_cast<>'s for the user. |
54 | | // TODO: Support u128, u256 etc. as well: The concepts would be quite complex. |
55 | | template<UnsignedIntegral T = u64> |
56 | | ErrorOr<T> read_bits(size_t count) |
57 | 74.5M | { |
58 | 74.5M | if constexpr (IsSame<bool, T>) { |
59 | 33.2M | VERIFY(count == 1); |
60 | 33.2M | } |
61 | 33.2M | T result = 0; |
62 | | |
63 | 74.5M | size_t nread = 0; |
64 | 226M | while (nread < count) { |
65 | 151M | if (m_current_byte.has_value()) { |
66 | 123M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { |
67 | | // read as many bytes as possible directly |
68 | 87.2M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { |
69 | | // shift existing data over |
70 | 13.7M | result <<= 8; |
71 | 13.7M | result |= m_current_byte.value(); |
72 | 13.7M | nread += 8; |
73 | 13.7M | m_current_byte.clear(); |
74 | 73.4M | } else { |
75 | 73.4M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; |
76 | 73.4M | result <<= 1; |
77 | 73.4M | result |= bit; |
78 | 73.4M | ++nread; |
79 | 73.4M | if (m_bit_offset++ == 7) |
80 | 7.13M | m_current_byte.clear(); |
81 | 73.4M | } |
82 | 87.2M | } else { |
83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit |
84 | 36.6M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; |
85 | | if constexpr (IsSame<bool, T>) |
86 | 33.2M | result = bit; |
87 | 3.49M | else { |
88 | 3.49M | result <<= 1; |
89 | 3.49M | result |= bit; |
90 | 3.49M | } |
91 | 36.6M | ++nread; |
92 | 36.6M | if (m_bit_offset++ == 7) |
93 | 5.87M | m_current_byte.clear(); |
94 | 36.6M | } |
95 | 123M | } else { |
96 | 27.9M | m_current_byte = TRY(m_stream->read_value<u8>()); |
97 | 27.9M | m_bit_offset = 0; |
98 | 27.9M | } |
99 | 151M | } |
100 | | |
101 | 74.5M | return result; |
102 | 74.5M | } _ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 33.2M | { | 58 | 33.2M | if constexpr (IsSame<bool, T>) { | 59 | 33.2M | VERIFY(count == 1); | 60 | 33.2M | } | 61 | 33.2M | T result = 0; | 62 | | | 63 | 33.2M | size_t nread = 0; | 64 | 70.1M | while (nread < count) { | 65 | 36.9M | if (m_current_byte.has_value()) { | 66 | | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | | result <<= 8; | 71 | | result |= m_current_byte.value(); | 72 | | nread += 8; | 73 | | m_current_byte.clear(); | 74 | | } else { | 75 | | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | | result <<= 1; | 77 | | result |= bit; | 78 | | ++nread; | 79 | | if (m_bit_offset++ == 7) | 80 | | m_current_byte.clear(); | 81 | | } | 82 | 33.2M | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | 33.2M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | 33.2M | result = bit; | 87 | | else { | 88 | | result <<= 1; | 89 | | result |= bit; | 90 | | } | 91 | 33.2M | ++nread; | 92 | 33.2M | if (m_bit_offset++ == 7) | 93 | 5.59M | m_current_byte.clear(); | 94 | 33.2M | } | 95 | 33.2M | } else { | 96 | 3.73M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 3.73M | m_bit_offset = 0; | 98 | 3.73M | } | 99 | 36.9M | } | 100 | | | 101 | 33.2M | return result; | 102 | 33.2M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 18.1M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 18.1M | T result = 0; | 62 | | | 63 | 18.1M | size_t nread = 0; | 64 | 63.3M | while (nread < count) { | 65 | 45.2M | if (m_current_byte.has_value()) { | 66 | 35.8M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 35.8M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 4.57M | result <<= 8; | 71 | 4.57M | result |= m_current_byte.value(); | 72 | 4.57M | nread += 8; | 73 | 4.57M | m_current_byte.clear(); | 74 | 31.2M | } else { | 75 | 31.2M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 31.2M | result <<= 1; | 77 | 31.2M | result |= bit; | 78 | 31.2M | ++nread; | 79 | 31.2M | if (m_bit_offset++ == 7) | 80 | 1.94M | m_current_byte.clear(); | 81 | 31.2M | } | 82 | | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | | result = bit; | 87 | | else { | 88 | | result <<= 1; | 89 | | result |= bit; | 90 | | } | 91 | | ++nread; | 92 | | if (m_bit_offset++ == 7) | 93 | | m_current_byte.clear(); | 94 | | } | 95 | 35.8M | } else { | 96 | 9.38M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 9.38M | m_bit_offset = 0; | 98 | 9.38M | } | 99 | 45.2M | } | 100 | | | 101 | 18.1M | return result; | 102 | 18.1M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEhEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 708k | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 708k | T result = 0; | 62 | | | 63 | 708k | size_t nread = 0; | 64 | 4.54M | while (nread < count) { | 65 | 3.83M | if (m_current_byte.has_value()) { | 66 | | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | | result <<= 8; | 71 | | result |= m_current_byte.value(); | 72 | | nread += 8; | 73 | | m_current_byte.clear(); | 74 | | } else { | 75 | | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | | result <<= 1; | 77 | | result |= bit; | 78 | | ++nread; | 79 | | if (m_bit_offset++ == 7) | 80 | | m_current_byte.clear(); | 81 | | } | 82 | 3.49M | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | 3.49M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | | result = bit; | 87 | 3.49M | else { | 88 | 3.49M | result <<= 1; | 89 | 3.49M | result |= bit; | 90 | 3.49M | } | 91 | 3.49M | ++nread; | 92 | 3.49M | if (m_bit_offset++ == 7) | 93 | 278k | m_current_byte.clear(); | 94 | 3.49M | } | 95 | 3.49M | } else { | 96 | 340k | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 340k | m_bit_offset = 0; | 98 | 340k | } | 99 | 3.83M | } | 100 | | | 101 | 708k | return result; | 102 | 708k | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 2.99M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 2.99M | T result = 0; | 62 | | | 63 | 2.99M | size_t nread = 0; | 64 | 16.0M | while (nread < count) { | 65 | 13.0M | if (m_current_byte.has_value()) { | 66 | 9.40M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 9.40M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 2.77M | result <<= 8; | 71 | 2.77M | result |= m_current_byte.value(); | 72 | 2.77M | nread += 8; | 73 | 2.77M | m_current_byte.clear(); | 74 | 6.62M | } else { | 75 | 6.62M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 6.62M | result <<= 1; | 77 | 6.62M | result |= bit; | 78 | 6.62M | ++nread; | 79 | 6.62M | if (m_bit_offset++ == 7) | 80 | 748k | m_current_byte.clear(); | 81 | 6.62M | } | 82 | | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | | result = bit; | 87 | | else { | 88 | | result <<= 1; | 89 | | result |= bit; | 90 | | } | 91 | | ++nread; | 92 | | if (m_bit_offset++ == 7) | 93 | | m_current_byte.clear(); | 94 | | } | 95 | 9.40M | } else { | 96 | 3.63M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 3.63M | m_bit_offset = 0; | 98 | 3.63M | } | 99 | 13.0M | } | 100 | | | 101 | 2.99M | return result; | 102 | 2.99M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEjEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 19.4M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 19.4M | T result = 0; | 62 | | | 63 | 19.4M | size_t nread = 0; | 64 | 72.2M | while (nread < count) { | 65 | 52.7M | if (m_current_byte.has_value()) { | 66 | 41.9M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 41.9M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 6.38M | result <<= 8; | 71 | 6.38M | result |= m_current_byte.value(); | 72 | 6.38M | nread += 8; | 73 | 6.38M | m_current_byte.clear(); | 74 | 35.5M | } else { | 75 | 35.5M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 35.5M | result <<= 1; | 77 | 35.5M | result |= bit; | 78 | 35.5M | ++nread; | 79 | 35.5M | if (m_bit_offset++ == 7) | 80 | 4.43M | m_current_byte.clear(); | 81 | 35.5M | } | 82 | | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | | result = bit; | 87 | | else { | 88 | | result <<= 1; | 89 | | result |= bit; | 90 | | } | 91 | | ++nread; | 92 | | if (m_bit_offset++ == 7) | 93 | | m_current_byte.clear(); | 94 | | } | 95 | 41.9M | } else { | 96 | 10.8M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 10.8M | m_bit_offset = 0; | 98 | 10.8M | } | 99 | 52.7M | } | 100 | | | 101 | 19.4M | return result; | 102 | 19.4M | } |
Unexecuted instantiation: _ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEbEENS_7ErrorOrIT_NS_5ErrorEEEm |
103 | | |
104 | | /// Discards any sub-byte stream positioning the input stream may be keeping track of. |
105 | | /// Non-bitwise reads will implicitly call this. |
106 | | void align_to_byte_boundary() |
107 | 383k | { |
108 | 383k | m_current_byte.clear(); |
109 | 383k | m_bit_offset = 0; |
110 | 383k | } |
111 | | |
112 | | /// Whether we are (accidentally or intentionally) at a byte boundary right now. |
113 | 22.8M | ALWAYS_INLINE bool is_aligned_to_byte_boundary() const { return m_bit_offset % 8 == 0; } |
114 | 3 | ALWAYS_INLINE u8 bits_until_next_byte_boundary() const { return m_bit_offset % 8 == 0 ? 0 : 8 - m_bit_offset; } |
115 | | |
116 | | private: |
117 | | Optional<u8> m_current_byte; |
118 | | size_t m_bit_offset { 0 }; |
119 | | MaybeOwned<Stream> m_stream; |
120 | | }; |
121 | | |
122 | | class LittleEndianBitStream : public Stream { |
123 | | protected: |
124 | | using BufferType = u64; |
125 | | |
126 | | static constexpr size_t bits_per_byte = 8u; |
127 | | static constexpr size_t bit_buffer_size = sizeof(BufferType) * bits_per_byte; |
128 | | |
129 | | explicit LittleEndianBitStream(MaybeOwned<Stream> stream) |
130 | 565k | : m_stream(move(stream)) |
131 | 565k | { |
132 | 565k | } |
133 | | |
134 | | template<UnsignedIntegral T> |
135 | | static constexpr T lsb_mask(T bits) |
136 | 986M | { |
137 | 986M | constexpr auto max = NumericLimits<T>::max(); |
138 | 986M | constexpr auto digits = NumericLimits<T>::digits(); |
139 | | |
140 | 986M | return bits == 0 ? 0 : max >> (digits - bits); |
141 | 986M | } _ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts16UnsignedIntegralEhEET_S3_ Line | Count | Source | 136 | 56.7k | { | 137 | 56.7k | constexpr auto max = NumericLimits<T>::max(); | 138 | 56.7k | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 56.7k | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 56.7k | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts16UnsignedIntegralEmEET_S3_ Line | Count | Source | 136 | 843M | { | 137 | 843M | constexpr auto max = NumericLimits<T>::max(); | 138 | 843M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 843M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 843M | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts16UnsignedIntegralEbEET_S3_ Line | Count | Source | 136 | 123M | { | 137 | 123M | constexpr auto max = NumericLimits<T>::max(); | 138 | 123M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 123M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 123M | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts16UnsignedIntegralEtEET_S3_ Line | Count | Source | 136 | 19.1M | { | 137 | 19.1M | constexpr auto max = NumericLimits<T>::max(); | 138 | 19.1M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 19.1M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 19.1M | } |
|
142 | | |
143 | 13.7k | ALWAYS_INLINE bool is_aligned_to_byte_boundary() const { return m_bit_count % bits_per_byte == 0; } |
144 | | |
145 | | MaybeOwned<Stream> m_stream; |
146 | | |
147 | | BufferType m_bit_buffer { 0 }; |
148 | | u8 m_bit_count { 0 }; |
149 | | }; |
150 | | |
151 | | /// A stream wrapper class that allows you to read arbitrary amounts of bits |
152 | | /// in little-endian order from another stream. |
153 | | class LittleEndianInputBitStream : public LittleEndianBitStream { |
154 | | public: |
155 | | enum UnsatisfiableReadBehavior { |
156 | | Reject, |
157 | | FillWithZero, |
158 | | }; |
159 | | |
160 | | explicit LittleEndianInputBitStream(MaybeOwned<Stream> stream, UnsatisfiableReadBehavior unsatisfiable_read_behavior = UnsatisfiableReadBehavior::Reject) |
161 | 561k | : LittleEndianBitStream(move(stream)) |
162 | 561k | , m_unsatisfiable_read_behavior(unsatisfiable_read_behavior) |
163 | 561k | { |
164 | 561k | } |
165 | | |
166 | | // ^Stream |
167 | | virtual ErrorOr<Bytes> read_some(Bytes bytes) override |
168 | 73.4k | { |
169 | 73.4k | align_to_byte_boundary(); |
170 | | |
171 | 73.4k | size_t bytes_read = 0; |
172 | 73.4k | auto buffer = bytes; |
173 | | |
174 | 73.4k | if (m_bit_count > 0) { |
175 | 41.5k | auto bits_to_read = min(buffer.size() * bits_per_byte, m_bit_count); |
176 | 41.5k | auto result = TRY(read_bits(bits_to_read)); |
177 | | |
178 | 41.5k | bytes_read = bits_to_read / bits_per_byte; |
179 | 41.5k | buffer.overwrite(0, &result, bytes_read); |
180 | | |
181 | 41.5k | buffer = buffer.slice(bytes_read); |
182 | 41.5k | } |
183 | | |
184 | 73.4k | buffer = TRY(m_stream->read_some(buffer)); |
185 | 73.4k | bytes_read += buffer.size(); |
186 | | |
187 | 73.4k | return bytes.trim(bytes_read); |
188 | 73.4k | } AK::LittleEndianInputBitStream::read_some(AK::Span<unsigned char>) Line | Count | Source | 168 | 73.4k | { | 169 | 73.4k | align_to_byte_boundary(); | 170 | | | 171 | 73.4k | size_t bytes_read = 0; | 172 | 73.4k | auto buffer = bytes; | 173 | | | 174 | 73.4k | if (m_bit_count > 0) { | 175 | 41.5k | auto bits_to_read = min(buffer.size() * bits_per_byte, m_bit_count); | 176 | 41.5k | auto result = TRY(read_bits(bits_to_read)); | 177 | | | 178 | 41.5k | bytes_read = bits_to_read / bits_per_byte; | 179 | 41.5k | buffer.overwrite(0, &result, bytes_read); | 180 | | | 181 | 41.5k | buffer = buffer.slice(bytes_read); | 182 | 41.5k | } | 183 | | | 184 | 73.4k | buffer = TRY(m_stream->read_some(buffer)); | 185 | 73.4k | bytes_read += buffer.size(); | 186 | | | 187 | 73.4k | return bytes.trim(bytes_read); | 188 | 73.4k | } |
Unexecuted instantiation: AK::LittleEndianInputBitStream::read_some(AK::Span<unsigned char>) |
189 | | |
190 | 0 | virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override { return m_stream->write_some(bytes); } |
191 | 222k | virtual bool is_eof() const override { return m_stream->is_eof() && m_bit_count == 0; } |
192 | 0 | virtual bool is_open() const override { return m_stream->is_open(); } |
193 | | virtual void close() override |
194 | 0 | { |
195 | 0 | m_stream->close(); |
196 | 0 | align_to_byte_boundary(); |
197 | 0 | } |
198 | | |
199 | | ErrorOr<bool> read_bit() |
200 | 123M | { |
201 | 123M | return read_bits<bool>(1); |
202 | 123M | } |
203 | | |
204 | | /// Depending on the number of bits to read, the return type can be chosen appropriately. |
205 | | /// This avoids a bunch of static_cast<>'s for the user. |
206 | | // TODO: Support u128, u256 etc. as well: The concepts would be quite complex. |
207 | | template<UnsignedIntegral T = u64> |
208 | | ErrorOr<T> read_bits(size_t count) |
209 | 446M | { |
210 | 446M | auto result = TRY(peek_bits<T>(count)); |
211 | 446M | discard_previously_peeked_bits(count); |
212 | | |
213 | 446M | return result; |
214 | 446M | } _ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 303M | { | 210 | 303M | auto result = TRY(peek_bits<T>(count)); | 211 | 303M | discard_previously_peeked_bits(count); | 212 | | | 213 | 303M | return result; | 214 | 303M | } |
_ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 123M | { | 210 | 123M | auto result = TRY(peek_bits<T>(count)); | 211 | 123M | discard_previously_peeked_bits(count); | 212 | | | 213 | 123M | return result; | 214 | 123M | } |
_ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 19.1M | { | 210 | 19.1M | auto result = TRY(peek_bits<T>(count)); | 211 | 19.1M | discard_previously_peeked_bits(count); | 212 | | | 213 | 19.1M | return result; | 214 | 19.1M | } |
Unexecuted instantiation: _ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts16UnsignedIntegralEhEENS_7ErrorOrIT_NS_5ErrorEEEm |
215 | | |
216 | | template<UnsignedIntegral T = u64> |
217 | | ErrorOr<T> peek_bits(size_t count) |
218 | 976M | { |
219 | 976M | if (count > m_bit_count) |
220 | 405M | TRY(refill_buffer_from_stream(count)); |
221 | | |
222 | 976M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); |
223 | 976M | } _ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts16UnsignedIntegralEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 833M | { | 219 | 833M | if (count > m_bit_count) | 220 | 401M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 833M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 833M | } |
_ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts16UnsignedIntegralEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 123M | { | 219 | 123M | if (count > m_bit_count) | 220 | 2.02M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 123M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 123M | } |
_ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts16UnsignedIntegralEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 19.1M | { | 219 | 19.1M | if (count > m_bit_count) | 220 | 2.04M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 19.1M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 19.1M | } |
Unexecuted instantiation: _ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts16UnsignedIntegralEhEENS_7ErrorOrIT_NS_5ErrorEEEm |
224 | | |
225 | | ALWAYS_INLINE void discard_previously_peeked_bits(u8 count) |
226 | 965M | { |
227 | | // We allow "retrieving" more bits than we can provide, but we need to make sure that we don't underflow the current bit counter. |
228 | | // This only affects certain "modes", but all the relevant checks have been handled in the respective `peek_bits` call. |
229 | 965M | if (count > m_bit_count) |
230 | 0 | count = m_bit_count; |
231 | | |
232 | 965M | m_bit_buffer >>= count; |
233 | 965M | m_bit_count -= count; |
234 | 965M | } |
235 | | |
236 | | /// Discards any sub-byte stream positioning the input stream may be keeping track of. |
237 | | /// Non-bitwise reads will implicitly call this. |
238 | | u8 align_to_byte_boundary() |
239 | 132k | { |
240 | 132k | u8 remaining_bits = 0; |
241 | | |
242 | 132k | if (auto offset = m_bit_count % bits_per_byte; offset != 0) { |
243 | 56.7k | remaining_bits = m_bit_buffer & lsb_mask<u8>(offset); |
244 | 56.7k | discard_previously_peeked_bits(offset); |
245 | 56.7k | } |
246 | | |
247 | 132k | return remaining_bits; |
248 | 132k | } |
249 | | |
250 | | private: |
251 | | ErrorOr<void> refill_buffer_from_stream(size_t requested_bit_count) |
252 | 405M | { |
253 | 419M | while (requested_bit_count > m_bit_count) [[likely]] { |
254 | 405M | if (m_stream->is_eof()) [[unlikely]] { |
255 | 390M | if (m_unsatisfiable_read_behavior == UnsatisfiableReadBehavior::FillWithZero) { |
256 | 390M | m_bit_count = requested_bit_count; |
257 | 390M | return {}; |
258 | 390M | } |
259 | | |
260 | 5.01k | return Error::from_string_literal("Reached end-of-stream without collecting the required number of bits"); |
261 | 390M | } |
262 | | |
263 | 14.5M | size_t bits_to_read = bit_buffer_size - m_bit_count; |
264 | 14.5M | size_t bytes_to_read = bits_to_read / bits_per_byte; |
265 | | |
266 | 14.5M | BufferType buffer = 0; |
267 | 14.5M | auto bytes = TRY(m_stream->read_some({ &buffer, bytes_to_read })); |
268 | | |
269 | 14.5M | m_bit_buffer |= (buffer << m_bit_count); |
270 | 14.5M | m_bit_count += bytes.size() * bits_per_byte; |
271 | 14.5M | } |
272 | | |
273 | 14.5M | return {}; |
274 | 405M | } AK::LittleEndianInputBitStream::refill_buffer_from_stream(unsigned long) Line | Count | Source | 252 | 405M | { | 253 | 419M | while (requested_bit_count > m_bit_count) [[likely]] { | 254 | 405M | if (m_stream->is_eof()) [[unlikely]] { | 255 | 390M | if (m_unsatisfiable_read_behavior == UnsatisfiableReadBehavior::FillWithZero) { | 256 | 390M | m_bit_count = requested_bit_count; | 257 | 390M | return {}; | 258 | 390M | } | 259 | | | 260 | 5.01k | return Error::from_string_literal("Reached end-of-stream without collecting the required number of bits"); | 261 | 390M | } | 262 | | | 263 | 14.5M | size_t bits_to_read = bit_buffer_size - m_bit_count; | 264 | 14.5M | size_t bytes_to_read = bits_to_read / bits_per_byte; | 265 | | | 266 | 14.5M | BufferType buffer = 0; | 267 | 14.5M | auto bytes = TRY(m_stream->read_some({ &buffer, bytes_to_read })); | 268 | | | 269 | 14.5M | m_bit_buffer |= (buffer << m_bit_count); | 270 | 14.5M | m_bit_count += bytes.size() * bits_per_byte; | 271 | 14.5M | } | 272 | | | 273 | 14.5M | return {}; | 274 | 405M | } |
Unexecuted instantiation: AK::LittleEndianInputBitStream::refill_buffer_from_stream(unsigned long) |
275 | | |
276 | | UnsatisfiableReadBehavior m_unsatisfiable_read_behavior; |
277 | | }; |
278 | | |
279 | | /// A stream wrapper class that allows you to write arbitrary amounts of bits |
280 | | /// in big-endian order to another stream. |
281 | | class BigEndianOutputBitStream : public Stream { |
282 | | public: |
283 | | explicit BigEndianOutputBitStream(MaybeOwned<Stream> stream) |
284 | 152 | : m_stream(move(stream)) |
285 | 152 | { |
286 | 152 | } |
287 | | |
288 | | virtual ErrorOr<Bytes> read_some(Bytes) override |
289 | 0 | { |
290 | 0 | return Error::from_errno(EBADF); |
291 | 0 | } |
292 | | |
293 | | virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override |
294 | 0 | { |
295 | 0 | VERIFY(m_bit_offset == 0); |
296 | 0 | return m_stream->write_some(bytes); |
297 | 0 | } Unexecuted instantiation: AK::BigEndianOutputBitStream::write_some(AK::Span<unsigned char const>) Unexecuted instantiation: AK::BigEndianOutputBitStream::write_some(AK::Span<unsigned char const>) |
298 | | |
299 | | template<UnsignedIntegral T> |
300 | | ErrorOr<void> write_bits(T value, size_t bit_count) |
301 | 49.2M | { |
302 | 49.2M | VERIFY(m_bit_offset <= 7); |
303 | | |
304 | 116M | while (bit_count > 0) { |
305 | 67.2M | u8 next_bit = (value >> (bit_count - 1)) & 1; |
306 | 67.2M | bit_count--; |
307 | | |
308 | 67.2M | m_current_byte <<= 1; |
309 | 67.2M | m_current_byte |= next_bit; |
310 | 67.2M | m_bit_offset++; |
311 | | |
312 | 67.2M | if (m_bit_offset > 7) { |
313 | 8.40M | TRY(m_stream->write_value(m_current_byte)); |
314 | 8.40M | m_bit_offset = 0; |
315 | 8.40M | m_current_byte = 0; |
316 | 8.40M | } |
317 | 67.2M | } |
318 | | |
319 | 49.2M | return {}; |
320 | 49.2M | } |
321 | | |
322 | | virtual bool is_eof() const override |
323 | 0 | { |
324 | 0 | return true; |
325 | 0 | } |
326 | | |
327 | | virtual bool is_open() const override |
328 | 0 | { |
329 | 0 | return m_stream->is_open(); |
330 | 0 | } |
331 | | |
332 | | virtual void close() override |
333 | 0 | { |
334 | 0 | } |
335 | | |
336 | | size_t bit_offset() const |
337 | 0 | { |
338 | 0 | return m_bit_offset; |
339 | 0 | } |
340 | | |
341 | | ErrorOr<void> align_to_byte_boundary() |
342 | 83.5k | { |
343 | 83.5k | if (m_bit_offset == 0) |
344 | 74.3k | return {}; |
345 | | |
346 | 83.5k | TRY(write_bits(0u, 8 - m_bit_offset)); |
347 | 18.4k | VERIFY(m_bit_offset == 0); |
348 | 9.20k | return {}; |
349 | 18.4k | } AK::BigEndianOutputBitStream::align_to_byte_boundary() Line | Count | Source | 342 | 83.5k | { | 343 | 83.5k | if (m_bit_offset == 0) | 344 | 74.3k | return {}; | 345 | | | 346 | 83.5k | TRY(write_bits(0u, 8 - m_bit_offset)); | 347 | 18.4k | VERIFY(m_bit_offset == 0); | 348 | 9.20k | return {}; | 349 | 18.4k | } |
Unexecuted instantiation: AK::BigEndianOutputBitStream::align_to_byte_boundary() |
350 | | |
351 | | private: |
352 | | MaybeOwned<Stream> m_stream; |
353 | | u8 m_current_byte { 0 }; |
354 | | size_t m_bit_offset { 0 }; |
355 | | }; |
356 | | |
357 | | /// A stream wrapper class that allows you to write arbitrary amounts of bits |
358 | | /// in little-endian order to another stream. |
359 | | class LittleEndianOutputBitStream : public LittleEndianBitStream { |
360 | | public: |
361 | | explicit LittleEndianOutputBitStream(MaybeOwned<Stream> stream) |
362 | 3.85k | : LittleEndianBitStream(move(stream)) |
363 | 3.85k | { |
364 | 3.85k | } |
365 | | |
366 | | virtual ErrorOr<Bytes> read_some(Bytes) override |
367 | 0 | { |
368 | 0 | return Error::from_errno(EBADF); |
369 | 0 | } |
370 | | |
371 | | virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override |
372 | 13.7k | { |
373 | 13.7k | VERIFY(is_aligned_to_byte_boundary()); |
374 | | |
375 | 13.7k | if (m_bit_count > 0) |
376 | 4.55k | TRY(flush_buffer_to_stream()); |
377 | | |
378 | 13.7k | return m_stream->write_some(bytes); |
379 | 13.7k | } AK::LittleEndianOutputBitStream::write_some(AK::Span<unsigned char const>) Line | Count | Source | 372 | 13.7k | { | 373 | 13.7k | VERIFY(is_aligned_to_byte_boundary()); | 374 | | | 375 | 13.7k | if (m_bit_count > 0) | 376 | 4.55k | TRY(flush_buffer_to_stream()); | 377 | | | 378 | 13.7k | return m_stream->write_some(bytes); | 379 | 13.7k | } |
Unexecuted instantiation: AK::LittleEndianOutputBitStream::write_some(AK::Span<unsigned char const>) |
380 | | |
381 | | template<UnsignedIntegral T> |
382 | | ErrorOr<void> write_bits(T value, size_t count) |
383 | 98.3M | { |
384 | 98.3M | if (m_bit_count == bit_buffer_size) { |
385 | 0 | TRY(flush_buffer_to_stream()); |
386 | 98.3M | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { |
387 | 10.2M | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; |
388 | 10.2M | m_bit_count = bit_buffer_size; |
389 | | |
390 | 10.2M | if (remaining != sizeof(value) * bits_per_byte) |
391 | 10.2M | value >>= remaining; |
392 | 10.2M | count -= remaining; |
393 | | |
394 | 10.2M | TRY(flush_buffer_to_stream()); |
395 | 10.2M | } |
396 | | |
397 | 98.3M | if (count == 0) |
398 | 4.40M | return {}; |
399 | | |
400 | 93.9M | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; |
401 | 93.9M | m_bit_count += count; |
402 | | |
403 | 93.9M | return {}; |
404 | 98.3M | } _ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts16UnsignedIntegralEhEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 371k | { | 384 | 371k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 371k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 17.6k | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 17.6k | m_bit_count = bit_buffer_size; | 389 | | | 390 | 17.6k | if (remaining != sizeof(value) * bits_per_byte) | 391 | 17.6k | value >>= remaining; | 392 | 17.6k | count -= remaining; | 393 | | | 394 | 17.6k | TRY(flush_buffer_to_stream()); | 395 | 17.6k | } | 396 | | | 397 | 371k | if (count == 0) | 398 | 5.54k | return {}; | 399 | | | 400 | 365k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 365k | m_bit_count += count; | 402 | | | 403 | 365k | return {}; | 404 | 371k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts16UnsignedIntegralEtEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 97.8M | { | 384 | 97.8M | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 97.8M | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 10.2M | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 10.2M | m_bit_count = bit_buffer_size; | 389 | | | 390 | 10.2M | if (remaining != sizeof(value) * bits_per_byte) | 391 | 10.2M | value >>= remaining; | 392 | 10.2M | count -= remaining; | 393 | | | 394 | 10.2M | TRY(flush_buffer_to_stream()); | 395 | 10.2M | } | 396 | | | 397 | 97.8M | if (count == 0) | 398 | 4.39M | return {}; | 399 | | | 400 | 93.4M | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 93.4M | m_bit_count += count; | 402 | | | 403 | 93.4M | return {}; | 404 | 97.8M | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts16UnsignedIntegralEmEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 33.1k | { | 384 | 33.1k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 33.1k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 1.91k | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 1.91k | m_bit_count = bit_buffer_size; | 389 | | | 390 | 1.91k | if (remaining != sizeof(value) * bits_per_byte) | 391 | 1.91k | value >>= remaining; | 392 | 1.91k | count -= remaining; | 393 | | | 394 | 1.91k | TRY(flush_buffer_to_stream()); | 395 | 1.91k | } | 396 | | | 397 | 33.1k | if (count == 0) | 398 | 499 | return {}; | 399 | | | 400 | 32.6k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 32.6k | m_bit_count += count; | 402 | | | 403 | 32.6k | return {}; | 404 | 33.1k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts16UnsignedIntegralEbEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 17.1k | { | 384 | 17.1k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 17.1k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 286 | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 286 | m_bit_count = bit_buffer_size; | 389 | | | 390 | 286 | if (remaining != sizeof(value) * bits_per_byte) | 391 | 286 | value >>= remaining; | 392 | 286 | count -= remaining; | 393 | | | 394 | 286 | TRY(flush_buffer_to_stream()); | 395 | 286 | } | 396 | | | 397 | 17.1k | if (count == 0) | 398 | 286 | return {}; | 399 | | | 400 | 16.8k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 16.8k | m_bit_count += count; | 402 | | | 403 | 16.8k | return {}; | 404 | 17.1k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts16UnsignedIntegralEjEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 17.2k | { | 384 | 17.2k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 17.2k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 502 | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 502 | m_bit_count = bit_buffer_size; | 389 | | | 390 | 502 | if (remaining != sizeof(value) * bits_per_byte) | 391 | 502 | value >>= remaining; | 392 | 502 | count -= remaining; | 393 | | | 394 | 502 | TRY(flush_buffer_to_stream()); | 395 | 502 | } | 396 | | | 397 | 17.2k | if (count == 0) | 398 | 301 | return {}; | 399 | | | 400 | 16.9k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 16.9k | m_bit_count += count; | 402 | | | 403 | 16.9k | return {}; | 404 | 17.2k | } |
Unexecuted instantiation: _ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts16UnsignedIntegralEhEENS_7ErrorOrIvNS_5ErrorEEET_m |
405 | | |
406 | | ALWAYS_INLINE ErrorOr<void> flush_buffer_to_stream() |
407 | 10.2M | { |
408 | 10.2M | auto bytes_to_write = m_bit_count / bits_per_byte; |
409 | 10.2M | TRY(m_stream->write_until_depleted({ &m_bit_buffer, bytes_to_write })); |
410 | | |
411 | 10.2M | if (m_bit_count == bit_buffer_size) { |
412 | 10.2M | m_bit_buffer = 0; |
413 | 10.2M | m_bit_count = 0; |
414 | 10.2M | } else { |
415 | 8.40k | auto bits_written = bytes_to_write * bits_per_byte; |
416 | | |
417 | 8.40k | m_bit_buffer >>= bits_written; |
418 | 8.40k | m_bit_count -= bits_written; |
419 | 8.40k | } |
420 | | |
421 | 10.2M | return {}; |
422 | 10.2M | } |
423 | | |
424 | | virtual bool is_eof() const override |
425 | 0 | { |
426 | 0 | return true; |
427 | 0 | } |
428 | | |
429 | | virtual bool is_open() const override |
430 | 0 | { |
431 | 0 | return m_stream->is_open(); |
432 | 0 | } |
433 | | |
434 | | virtual void close() override |
435 | 0 | { |
436 | 0 | } |
437 | | |
438 | | size_t bit_offset() const |
439 | 17.0k | { |
440 | 17.0k | return m_bit_count; |
441 | 17.0k | } |
442 | | |
443 | | ErrorOr<void> align_to_byte_boundary() |
444 | 8.43k | { |
445 | 8.43k | if (auto offset = m_bit_count % bits_per_byte; offset != 0) |
446 | 7.18k | TRY(write_bits<u8>(0u, bits_per_byte - offset)); |
447 | | |
448 | 8.43k | return {}; |
449 | 8.43k | } |
450 | | }; |
451 | | |
452 | | template<typename T> |
453 | | concept InputBitStream = OneOf<T, BigEndianInputBitStream, LittleEndianInputBitStream>; |
454 | | |
455 | | } |
456 | | |
457 | | #if USING_AK_GLOBALLY |
458 | | using AK::InputBitStream; |
459 | | #endif |