/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.61M | : m_stream(move(stream)) |
24 | 1.61M | { |
25 | 1.61M | } |
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 | 508k | 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.5M | { |
49 | 33.5M | return read_bits<bool>(1); |
50 | 33.5M | } |
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<Unsigned T = u64> |
56 | | ErrorOr<T> read_bits(size_t count) |
57 | 425M | { |
58 | 425M | if constexpr (IsSame<bool, T>) { |
59 | 33.5M | VERIFY(count == 1); |
60 | 33.5M | } |
61 | 33.5M | T result = 0; |
62 | | |
63 | 425M | size_t nread = 0; |
64 | 1.33G | while (nread < count) { |
65 | 908M | if (m_current_byte.has_value()) { |
66 | 682M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { |
67 | | // read as many bytes as possible directly |
68 | 645M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { |
69 | | // shift existing data over |
70 | 160M | result <<= 8; |
71 | 160M | result |= m_current_byte.value(); |
72 | 160M | nread += 8; |
73 | 160M | m_current_byte.clear(); |
74 | 484M | } else { |
75 | 484M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; |
76 | 484M | result <<= 1; |
77 | 484M | result |= bit; |
78 | 484M | ++nread; |
79 | 484M | if (m_bit_offset++ == 7) |
80 | 58.4M | m_current_byte.clear(); |
81 | 484M | } |
82 | 645M | } else { |
83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit |
84 | 37.6M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; |
85 | | if constexpr (IsSame<bool, T>) |
86 | 33.5M | result = bit; |
87 | 4.08M | else { |
88 | 4.08M | result <<= 1; |
89 | 4.08M | result |= bit; |
90 | 4.08M | } |
91 | 37.6M | ++nread; |
92 | 37.6M | if (m_bit_offset++ == 7) |
93 | 5.99M | m_current_byte.clear(); |
94 | 37.6M | } |
95 | 682M | } else { |
96 | 226M | m_current_byte = TRY(m_stream->read_value<u8>()); |
97 | 226M | m_bit_offset = 0; |
98 | 226M | } |
99 | 908M | } |
100 | | |
101 | 425M | return result; |
102 | 425M | } _ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 33.5M | { | 58 | 33.5M | if constexpr (IsSame<bool, T>) { | 59 | 33.5M | VERIFY(count == 1); | 60 | 33.5M | } | 61 | 33.5M | T result = 0; | 62 | | | 63 | 33.5M | size_t nread = 0; | 64 | 70.9M | while (nread < count) { | 65 | 37.3M | 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.5M | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | 33.5M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | 33.5M | result = bit; | 87 | | else { | 88 | | result <<= 1; | 89 | | result |= bit; | 90 | | } | 91 | 33.5M | ++nread; | 92 | 33.5M | if (m_bit_offset++ == 7) | 93 | 5.66M | m_current_byte.clear(); | 94 | 33.5M | } | 95 | 33.5M | } else { | 96 | 3.79M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 3.79M | m_bit_offset = 0; | 98 | 3.79M | } | 99 | 37.3M | } | 100 | | | 101 | 33.5M | return result; | 102 | 33.5M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 37.5M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 37.5M | T result = 0; | 62 | | | 63 | 37.5M | size_t nread = 0; | 64 | 123M | while (nread < count) { | 65 | 85.5M | if (m_current_byte.has_value()) { | 66 | 61.7M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 61.7M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 17.2M | result <<= 8; | 71 | 17.2M | result |= m_current_byte.value(); | 72 | 17.2M | nread += 8; | 73 | 17.2M | m_current_byte.clear(); | 74 | 44.5M | } else { | 75 | 44.5M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 44.5M | result <<= 1; | 77 | 44.5M | result |= bit; | 78 | 44.5M | ++nread; | 79 | 44.5M | if (m_bit_offset++ == 7) | 80 | 3.59M | m_current_byte.clear(); | 81 | 44.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 | 61.7M | } else { | 96 | 23.7M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 23.7M | m_bit_offset = 0; | 98 | 23.7M | } | 99 | 85.5M | } | 100 | | | 101 | 37.5M | return result; | 102 | 37.5M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 815k | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 815k | T result = 0; | 62 | | | 63 | 815k | size_t nread = 0; | 64 | 5.27M | while (nread < count) { | 65 | 4.46M | 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 | 4.08M | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | 4.08M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | | result = bit; | 87 | 4.08M | else { | 88 | 4.08M | result <<= 1; | 89 | 4.08M | result |= bit; | 90 | 4.08M | } | 91 | 4.08M | ++nread; | 92 | 4.08M | if (m_bit_offset++ == 7) | 93 | 334k | m_current_byte.clear(); | 94 | 4.08M | } | 95 | 4.08M | } else { | 96 | 379k | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 379k | m_bit_offset = 0; | 98 | 379k | } | 99 | 4.46M | } | 100 | | | 101 | 815k | return result; | 102 | 815k | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 2.06M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 2.06M | T result = 0; | 62 | | | 63 | 2.06M | size_t nread = 0; | 64 | 9.47M | while (nread < count) { | 65 | 7.40M | if (m_current_byte.has_value()) { | 66 | 4.90M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 4.90M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 2.12M | result <<= 8; | 71 | 2.12M | result |= m_current_byte.value(); | 72 | 2.12M | nread += 8; | 73 | 2.12M | m_current_byte.clear(); | 74 | 2.77M | } else { | 75 | 2.77M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 2.77M | result <<= 1; | 77 | 2.77M | result |= bit; | 78 | 2.77M | ++nread; | 79 | 2.77M | if (m_bit_offset++ == 7) | 80 | 258k | m_current_byte.clear(); | 81 | 2.77M | } | 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 | 4.90M | } else { | 96 | 2.50M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 2.50M | m_bit_offset = 0; | 98 | 2.50M | } | 99 | 7.40M | } | 100 | | | 101 | 2.06M | return result; | 102 | 2.06M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEjEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 351M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 351M | T result = 0; | 62 | | | 63 | 351M | size_t nread = 0; | 64 | 1.12G | while (nread < count) { | 65 | 774M | if (m_current_byte.has_value()) { | 66 | 578M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 578M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 141M | result <<= 8; | 71 | 141M | result |= m_current_byte.value(); | 72 | 141M | nread += 8; | 73 | 141M | m_current_byte.clear(); | 74 | 437M | } else { | 75 | 437M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 437M | result <<= 1; | 77 | 437M | result |= bit; | 78 | 437M | ++nread; | 79 | 437M | if (m_bit_offset++ == 7) | 80 | 54.6M | m_current_byte.clear(); | 81 | 437M | } | 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 | 578M | } else { | 96 | 195M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 195M | m_bit_offset = 0; | 98 | 195M | } | 99 | 774M | } | 100 | | | 101 | 351M | return result; | 102 | 351M | } |
Unexecuted instantiation: _ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEbEENS_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 | 936k | { |
108 | 936k | m_current_byte.clear(); |
109 | 936k | m_bit_offset = 0; |
110 | 936k | } |
111 | | |
112 | | /// Whether we are (accidentally or intentionally) at a byte boundary right now. |
113 | 242M | ALWAYS_INLINE bool is_aligned_to_byte_boundary() const { return m_bit_offset % 8 == 0; } |
114 | 19 | 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 | 342k | : m_stream(move(stream)) |
131 | 342k | { |
132 | 342k | } |
133 | | |
134 | | template<Unsigned T> |
135 | | static constexpr T lsb_mask(T bits) |
136 | 1.02G | { |
137 | 1.02G | constexpr auto max = NumericLimits<T>::max(); |
138 | 1.02G | constexpr auto digits = NumericLimits<T>::digits(); |
139 | | |
140 | 1.02G | return bits == 0 ? 0 : max >> (digits - bits); |
141 | 1.02G | } _ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEhEET_S3_ Line | Count | Source | 136 | 115k | { | 137 | 115k | constexpr auto max = NumericLimits<T>::max(); | 138 | 115k | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 115k | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 115k | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEmEET_S3_ Line | Count | Source | 136 | 911M | { | 137 | 911M | constexpr auto max = NumericLimits<T>::max(); | 138 | 911M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 911M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 911M | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEbEET_S3_ Line | Count | Source | 136 | 97.4M | { | 137 | 97.4M | constexpr auto max = NumericLimits<T>::max(); | 138 | 97.4M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 97.4M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 97.4M | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEtEET_S3_ Line | Count | Source | 136 | 20.1M | { | 137 | 20.1M | constexpr auto max = NumericLimits<T>::max(); | 138 | 20.1M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 20.1M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 20.1M | } |
|
142 | | |
143 | 12.4k | 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 | 339k | : LittleEndianBitStream(move(stream)) |
162 | 339k | , m_unsatisfiable_read_behavior(unsatisfiable_read_behavior) |
163 | 339k | { |
164 | 339k | } |
165 | | |
166 | | // ^Stream |
167 | | virtual ErrorOr<Bytes> read_some(Bytes bytes) override |
168 | 5.42M | { |
169 | 5.42M | align_to_byte_boundary(); |
170 | | |
171 | 5.42M | size_t bytes_read = 0; |
172 | 5.42M | auto buffer = bytes; |
173 | | |
174 | 5.42M | if (m_bit_count > 0) { |
175 | 138k | auto bits_to_read = min(buffer.size() * bits_per_byte, m_bit_count); |
176 | 138k | auto result = TRY(read_bits(bits_to_read)); |
177 | | |
178 | 138k | bytes_read = bits_to_read / bits_per_byte; |
179 | 138k | buffer.overwrite(0, &result, bytes_read); |
180 | | |
181 | 138k | buffer = buffer.slice(bytes_read); |
182 | 138k | } |
183 | | |
184 | 5.42M | buffer = TRY(m_stream->read_some(buffer)); |
185 | 5.42M | bytes_read += buffer.size(); |
186 | | |
187 | 5.42M | return bytes.trim(bytes_read); |
188 | 5.42M | } AK::LittleEndianInputBitStream::read_some(AK::Span<unsigned char>) Line | Count | Source | 168 | 5.42M | { | 169 | 5.42M | align_to_byte_boundary(); | 170 | | | 171 | 5.42M | size_t bytes_read = 0; | 172 | 5.42M | auto buffer = bytes; | 173 | | | 174 | 5.42M | if (m_bit_count > 0) { | 175 | 138k | auto bits_to_read = min(buffer.size() * bits_per_byte, m_bit_count); | 176 | 138k | auto result = TRY(read_bits(bits_to_read)); | 177 | | | 178 | 138k | bytes_read = bits_to_read / bits_per_byte; | 179 | 138k | buffer.overwrite(0, &result, bytes_read); | 180 | | | 181 | 138k | buffer = buffer.slice(bytes_read); | 182 | 138k | } | 183 | | | 184 | 5.42M | buffer = TRY(m_stream->read_some(buffer)); | 185 | 5.42M | bytes_read += buffer.size(); | 186 | | | 187 | 5.42M | return bytes.trim(bytes_read); | 188 | 5.42M | } |
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 | 9.38M | 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 | 97.4M | { |
201 | 97.4M | return read_bits<bool>(1); |
202 | 97.4M | } |
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<Unsigned T = u64> |
208 | | ErrorOr<T> read_bits(size_t count) |
209 | 366M | { |
210 | 366M | auto result = TRY(peek_bits<T>(count)); |
211 | 366M | discard_previously_peeked_bits(count); |
212 | | |
213 | 366M | return result; |
214 | 366M | } _ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 248M | { | 210 | 248M | auto result = TRY(peek_bits<T>(count)); | 211 | 248M | discard_previously_peeked_bits(count); | 212 | | | 213 | 248M | return result; | 214 | 248M | } |
_ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 97.4M | { | 210 | 97.4M | auto result = TRY(peek_bits<T>(count)); | 211 | 97.4M | discard_previously_peeked_bits(count); | 212 | | | 213 | 97.4M | return result; | 214 | 97.4M | } |
_ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 20.1M | { | 210 | 20.1M | auto result = TRY(peek_bits<T>(count)); | 211 | 20.1M | discard_previously_peeked_bits(count); | 212 | | | 213 | 20.1M | return result; | 214 | 20.1M | } |
Unexecuted instantiation: _ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIT_NS_5ErrorEEEm |
215 | | |
216 | | template<Unsigned T = u64> |
217 | | ErrorOr<T> peek_bits(size_t count) |
218 | 1.01G | { |
219 | 1.01G | if (count > m_bit_count) |
220 | 470M | TRY(refill_buffer_from_stream(count)); |
221 | | |
222 | 1.01G | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); |
223 | 1.01G | } _ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 901M | { | 219 | 901M | if (count > m_bit_count) | 220 | 466M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 901M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 901M | } |
_ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 97.4M | { | 219 | 97.4M | if (count > m_bit_count) | 220 | 1.66M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 97.4M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 97.4M | } |
_ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 20.1M | { | 219 | 20.1M | if (count > m_bit_count) | 220 | 1.94M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 20.1M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 20.1M | } |
Unexecuted instantiation: _ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIT_NS_5ErrorEEEm |
224 | | |
225 | | ALWAYS_INLINE void discard_previously_peeked_bits(u8 count) |
226 | 1.00G | { |
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 | 1.00G | if (count > m_bit_count) |
230 | 0 | count = m_bit_count; |
231 | | |
232 | 1.00G | m_bit_buffer >>= count; |
233 | 1.00G | m_bit_count -= count; |
234 | 1.00G | } |
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 | 5.50M | { |
240 | 5.50M | u8 remaining_bits = 0; |
241 | | |
242 | 5.50M | if (auto offset = m_bit_count % bits_per_byte; offset != 0) { |
243 | 115k | remaining_bits = m_bit_buffer & lsb_mask<u8>(offset); |
244 | 115k | discard_previously_peeked_bits(offset); |
245 | 115k | } |
246 | | |
247 | 5.50M | return remaining_bits; |
248 | 5.50M | } |
249 | | |
250 | | private: |
251 | | ErrorOr<void> refill_buffer_from_stream(size_t requested_bit_count) |
252 | 470M | { |
253 | 485M | while (requested_bit_count > m_bit_count) [[likely]] { |
254 | 470M | if (m_stream->is_eof()) [[unlikely]] { |
255 | 454M | if (m_unsatisfiable_read_behavior == UnsatisfiableReadBehavior::FillWithZero) { |
256 | 454M | m_bit_count = requested_bit_count; |
257 | 454M | return {}; |
258 | 454M | } |
259 | | |
260 | 6.41k | return Error::from_string_literal("Reached end-of-stream without collecting the required number of bits"); |
261 | 454M | } |
262 | | |
263 | 15.5M | size_t bits_to_read = bit_buffer_size - m_bit_count; |
264 | 15.5M | size_t bytes_to_read = bits_to_read / bits_per_byte; |
265 | | |
266 | 15.5M | BufferType buffer = 0; |
267 | 15.5M | auto bytes = TRY(m_stream->read_some({ &buffer, bytes_to_read })); |
268 | | |
269 | 15.5M | m_bit_buffer |= (buffer << m_bit_count); |
270 | 15.5M | m_bit_count += bytes.size() * bits_per_byte; |
271 | 15.5M | } |
272 | | |
273 | 15.5M | return {}; |
274 | 470M | } AK::LittleEndianInputBitStream::refill_buffer_from_stream(unsigned long) Line | Count | Source | 252 | 470M | { | 253 | 485M | while (requested_bit_count > m_bit_count) [[likely]] { | 254 | 470M | if (m_stream->is_eof()) [[unlikely]] { | 255 | 454M | if (m_unsatisfiable_read_behavior == UnsatisfiableReadBehavior::FillWithZero) { | 256 | 454M | m_bit_count = requested_bit_count; | 257 | 454M | return {}; | 258 | 454M | } | 259 | | | 260 | 6.41k | return Error::from_string_literal("Reached end-of-stream without collecting the required number of bits"); | 261 | 454M | } | 262 | | | 263 | 15.5M | size_t bits_to_read = bit_buffer_size - m_bit_count; | 264 | 15.5M | size_t bytes_to_read = bits_to_read / bits_per_byte; | 265 | | | 266 | 15.5M | BufferType buffer = 0; | 267 | 15.5M | auto bytes = TRY(m_stream->read_some({ &buffer, bytes_to_read })); | 268 | | | 269 | 15.5M | m_bit_buffer |= (buffer << m_bit_count); | 270 | 15.5M | m_bit_count += bytes.size() * bits_per_byte; | 271 | 15.5M | } | 272 | | | 273 | 15.5M | return {}; | 274 | 470M | } |
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 | 2.12k | : m_stream(move(stream)) |
285 | 2.12k | { |
286 | 2.12k | } |
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<Unsigned T> |
300 | | ErrorOr<void> write_bits(T value, size_t bit_count) |
301 | 615M | { |
302 | 615M | VERIFY(m_bit_offset <= 7); |
303 | | |
304 | 1.34G | while (bit_count > 0) { |
305 | 729M | u8 next_bit = (value >> (bit_count - 1)) & 1; |
306 | 729M | bit_count--; |
307 | | |
308 | 729M | m_current_byte <<= 1; |
309 | 729M | m_current_byte |= next_bit; |
310 | 729M | m_bit_offset++; |
311 | | |
312 | 729M | if (m_bit_offset > 7) { |
313 | 91.1M | TRY(m_stream->write_value(m_current_byte)); |
314 | 91.1M | m_bit_offset = 0; |
315 | 91.1M | m_current_byte = 0; |
316 | 91.1M | } |
317 | 729M | } |
318 | | |
319 | 615M | return {}; |
320 | 615M | } |
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 | 360k | { |
343 | 360k | if (m_bit_offset == 0) |
344 | 297k | return {}; |
345 | | |
346 | 360k | TRY(write_bits(0u, 8 - m_bit_offset)); |
347 | 126k | VERIFY(m_bit_offset == 0); |
348 | 63.0k | return {}; |
349 | 126k | } AK::BigEndianOutputBitStream::align_to_byte_boundary() Line | Count | Source | 342 | 360k | { | 343 | 360k | if (m_bit_offset == 0) | 344 | 297k | return {}; | 345 | | | 346 | 360k | TRY(write_bits(0u, 8 - m_bit_offset)); | 347 | 126k | VERIFY(m_bit_offset == 0); | 348 | 63.0k | return {}; | 349 | 126k | } |
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.48k | : LittleEndianBitStream(move(stream)) |
363 | 3.48k | { |
364 | 3.48k | } |
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 | 12.4k | { |
373 | 12.4k | VERIFY(is_aligned_to_byte_boundary()); |
374 | | |
375 | 12.4k | if (m_bit_count > 0) |
376 | 4.10k | TRY(flush_buffer_to_stream()); |
377 | | |
378 | 12.4k | return m_stream->write_some(bytes); |
379 | 12.4k | } AK::LittleEndianOutputBitStream::write_some(AK::Span<unsigned char const>) Line | Count | Source | 372 | 12.4k | { | 373 | 12.4k | VERIFY(is_aligned_to_byte_boundary()); | 374 | | | 375 | 12.4k | if (m_bit_count > 0) | 376 | 4.10k | TRY(flush_buffer_to_stream()); | 377 | | | 378 | 12.4k | return m_stream->write_some(bytes); | 379 | 12.4k | } |
Unexecuted instantiation: AK::LittleEndianOutputBitStream::write_some(AK::Span<unsigned char const>) |
380 | | |
381 | | template<Unsigned T> |
382 | | ErrorOr<void> write_bits(T value, size_t count) |
383 | 92.4M | { |
384 | 92.4M | if (m_bit_count == bit_buffer_size) { |
385 | 0 | TRY(flush_buffer_to_stream()); |
386 | 92.4M | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { |
387 | 9.57M | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; |
388 | 9.57M | m_bit_count = bit_buffer_size; |
389 | | |
390 | 9.57M | if (remaining != sizeof(value) * bits_per_byte) |
391 | 9.57M | value >>= remaining; |
392 | 9.57M | count -= remaining; |
393 | | |
394 | 9.57M | TRY(flush_buffer_to_stream()); |
395 | 9.57M | } |
396 | | |
397 | 92.4M | if (count == 0) |
398 | 4.32M | return {}; |
399 | | |
400 | 88.1M | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; |
401 | 88.1M | m_bit_count += count; |
402 | | |
403 | 88.1M | return {}; |
404 | 92.4M | } _ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 338k | { | 384 | 338k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 338k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 15.8k | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 15.8k | m_bit_count = bit_buffer_size; | 389 | | | 390 | 15.8k | if (remaining != sizeof(value) * bits_per_byte) | 391 | 15.8k | value >>= remaining; | 392 | 15.8k | count -= remaining; | 393 | | | 394 | 15.8k | TRY(flush_buffer_to_stream()); | 395 | 15.8k | } | 396 | | | 397 | 338k | if (count == 0) | 398 | 5.02k | return {}; | 399 | | | 400 | 333k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 333k | m_bit_count += count; | 402 | | | 403 | 333k | return {}; | 404 | 338k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 92.0M | { | 384 | 92.0M | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 92.0M | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 9.55M | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 9.55M | m_bit_count = bit_buffer_size; | 389 | | | 390 | 9.55M | if (remaining != sizeof(value) * bits_per_byte) | 391 | 9.55M | value >>= remaining; | 392 | 9.55M | count -= remaining; | 393 | | | 394 | 9.55M | TRY(flush_buffer_to_stream()); | 395 | 9.55M | } | 396 | | | 397 | 92.0M | if (count == 0) | 398 | 4.32M | return {}; | 399 | | | 400 | 87.7M | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 87.7M | m_bit_count += count; | 402 | | | 403 | 87.7M | return {}; | 404 | 92.0M | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 30.5k | { | 384 | 30.5k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 30.5k | } 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 | 30.5k | if (count == 0) | 398 | 496 | return {}; | 399 | | | 400 | 30.0k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 30.0k | m_bit_count += count; | 402 | | | 403 | 30.0k | return {}; | 404 | 30.5k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 15.6k | { | 384 | 15.6k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 15.6k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 295 | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 295 | m_bit_count = bit_buffer_size; | 389 | | | 390 | 295 | if (remaining != sizeof(value) * bits_per_byte) | 391 | 295 | value >>= remaining; | 392 | 295 | count -= remaining; | 393 | | | 394 | 295 | TRY(flush_buffer_to_stream()); | 395 | 295 | } | 396 | | | 397 | 15.6k | if (count == 0) | 398 | 295 | return {}; | 399 | | | 400 | 15.3k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 15.3k | m_bit_count += count; | 402 | | | 403 | 15.3k | return {}; | 404 | 15.6k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEjEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 15.7k | { | 384 | 15.7k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 15.7k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 438 | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 438 | m_bit_count = bit_buffer_size; | 389 | | | 390 | 438 | if (remaining != sizeof(value) * bits_per_byte) | 391 | 438 | value >>= remaining; | 392 | 438 | count -= remaining; | 393 | | | 394 | 438 | TRY(flush_buffer_to_stream()); | 395 | 438 | } | 396 | | | 397 | 15.7k | if (count == 0) | 398 | 259 | return {}; | 399 | | | 400 | 15.4k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 15.4k | m_bit_count += count; | 402 | | | 403 | 15.4k | return {}; | 404 | 15.7k | } |
Unexecuted instantiation: _ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIvNS_5ErrorEEET_m |
405 | | |
406 | | ALWAYS_INLINE ErrorOr<void> flush_buffer_to_stream() |
407 | 9.58M | { |
408 | 9.58M | auto bytes_to_write = m_bit_count / bits_per_byte; |
409 | 9.58M | TRY(m_stream->write_until_depleted({ &m_bit_buffer, bytes_to_write })); |
410 | | |
411 | 9.58M | if (m_bit_count == bit_buffer_size) { |
412 | 9.57M | m_bit_buffer = 0; |
413 | 9.57M | m_bit_count = 0; |
414 | 9.57M | } else { |
415 | 7.59k | auto bits_written = bytes_to_write * bits_per_byte; |
416 | | |
417 | 7.59k | m_bit_buffer >>= bits_written; |
418 | 7.59k | m_bit_count -= bits_written; |
419 | 7.59k | } |
420 | | |
421 | 9.58M | return {}; |
422 | 9.58M | } |
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 | 15.6k | { |
440 | 15.6k | return m_bit_count; |
441 | 15.6k | } |
442 | | |
443 | | ErrorOr<void> align_to_byte_boundary() |
444 | 7.62k | { |
445 | 7.62k | if (auto offset = m_bit_count % bits_per_byte; offset != 0) |
446 | 6.50k | TRY(write_bits<u8>(0u, bits_per_byte - offset)); |
447 | | |
448 | 7.62k | return {}; |
449 | 7.62k | } |
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 |