/src/serenity/AK/BitStream.h
Line | Count | Source (jump to first uncovered line) |
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 | 126k | { |
30 | 126k | 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 | 126k | align_to_byte_boundary(); |
36 | 126k | return m_stream->read_some(bytes); |
37 | 126k | } |
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 | 36.9M | { |
49 | 36.9M | return read_bits<bool>(1); |
50 | 36.9M | } |
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 | 515M | { |
58 | 515M | if constexpr (IsSame<bool, T>) { |
59 | 36.9M | VERIFY(count == 1); |
60 | 36.9M | } |
61 | 36.9M | T result = 0; |
62 | | |
63 | 515M | size_t nread = 0; |
64 | 1.54G | while (nread < count) { |
65 | 1.03G | if (m_current_byte.has_value()) { |
66 | 776M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { |
67 | | // read as many bytes as possible directly |
68 | 737M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { |
69 | | // shift existing data over |
70 | 182M | result <<= 8; |
71 | 182M | result |= m_current_byte.value(); |
72 | 182M | nread += 8; |
73 | 182M | m_current_byte.clear(); |
74 | 554M | } else { |
75 | 554M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; |
76 | 554M | result <<= 1; |
77 | 554M | result |= bit; |
78 | 554M | ++nread; |
79 | 554M | if (m_bit_offset++ == 7) |
80 | 67.2M | m_current_byte.clear(); |
81 | 554M | } |
82 | 737M | } else { |
83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit |
84 | 39.5M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; |
85 | | if constexpr (IsSame<bool, T>) |
86 | 36.9M | result = bit; |
87 | 2.53M | else { |
88 | 2.53M | result <<= 1; |
89 | 2.53M | result |= bit; |
90 | 2.53M | } |
91 | 39.5M | ++nread; |
92 | 39.5M | if (m_bit_offset++ == 7) |
93 | 6.24M | m_current_byte.clear(); |
94 | 39.5M | } |
95 | 776M | } else { |
96 | 257M | m_current_byte = TRY(m_stream->read_value<u8>()); |
97 | 0 | m_bit_offset = 0; |
98 | 257M | } |
99 | 1.03G | } |
100 | | |
101 | 515M | return result; |
102 | 515M | } _ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 36.9M | { | 58 | 36.9M | if constexpr (IsSame<bool, T>) { | 59 | 36.9M | VERIFY(count == 1); | 60 | 36.9M | } | 61 | 36.9M | T result = 0; | 62 | | | 63 | 36.9M | size_t nread = 0; | 64 | 78.0M | while (nread < count) { | 65 | 41.1M | 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 | 36.9M | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | 36.9M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | 36.9M | result = bit; | 87 | | else { | 88 | | result <<= 1; | 89 | | result |= bit; | 90 | | } | 91 | 36.9M | ++nread; | 92 | 36.9M | if (m_bit_offset++ == 7) | 93 | 6.03M | m_current_byte.clear(); | 94 | 36.9M | } | 95 | 36.9M | } else { | 96 | 4.14M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 0 | m_bit_offset = 0; | 98 | 4.14M | } | 99 | 41.1M | } | 100 | | | 101 | 36.9M | return result; | 102 | 36.9M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 29.0M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 29.0M | T result = 0; | 62 | | | 63 | 29.0M | size_t nread = 0; | 64 | 116M | while (nread < count) { | 65 | 87.6M | if (m_current_byte.has_value()) { | 66 | 58.5M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 58.5M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 23.7M | result <<= 8; | 71 | 23.7M | result |= m_current_byte.value(); | 72 | 23.7M | nread += 8; | 73 | 23.7M | m_current_byte.clear(); | 74 | 34.8M | } else { | 75 | 34.8M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 34.8M | result <<= 1; | 77 | 34.8M | result |= bit; | 78 | 34.8M | ++nread; | 79 | 34.8M | if (m_bit_offset++ == 7) | 80 | 2.31M | m_current_byte.clear(); | 81 | 34.8M | } | 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 | 58.5M | } else { | 96 | 29.0M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 0 | m_bit_offset = 0; | 98 | 29.0M | } | 99 | 87.6M | } | 100 | | | 101 | 29.0M | return result; | 102 | 29.0M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 511k | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 511k | T result = 0; | 62 | | | 63 | 511k | size_t nread = 0; | 64 | 3.29M | while (nread < count) { | 65 | 2.77M | 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 | 2.53M | } else { | 83 | | // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit | 84 | 2.53M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 85 | | if constexpr (IsSame<bool, T>) | 86 | | result = bit; | 87 | 2.53M | else { | 88 | 2.53M | result <<= 1; | 89 | 2.53M | result |= bit; | 90 | 2.53M | } | 91 | 2.53M | ++nread; | 92 | 2.53M | if (m_bit_offset++ == 7) | 93 | 211k | m_current_byte.clear(); | 94 | 2.53M | } | 95 | 2.53M | } else { | 96 | 245k | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 0 | m_bit_offset = 0; | 98 | 245k | } | 99 | 2.77M | } | 100 | | | 101 | 511k | return result; | 102 | 511k | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 2.13M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 2.13M | T result = 0; | 62 | | | 63 | 2.13M | size_t nread = 0; | 64 | 9.22M | while (nread < count) { | 65 | 7.09M | if (m_current_byte.has_value()) { | 66 | 4.57M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 4.57M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 2.20M | result <<= 8; | 71 | 2.20M | result |= m_current_byte.value(); | 72 | 2.20M | nread += 8; | 73 | 2.20M | m_current_byte.clear(); | 74 | 2.37M | } else { | 75 | 2.37M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 2.37M | result <<= 1; | 77 | 2.37M | result |= bit; | 78 | 2.37M | ++nread; | 79 | 2.37M | if (m_bit_offset++ == 7) | 80 | 243k | m_current_byte.clear(); | 81 | 2.37M | } | 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.57M | } else { | 96 | 2.51M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 0 | m_bit_offset = 0; | 98 | 2.51M | } | 99 | 7.09M | } | 100 | | | 101 | 2.13M | return result; | 102 | 2.13M | } |
_ZN2AK23BigEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEjEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 57 | 446M | { | 58 | | if constexpr (IsSame<bool, T>) { | 59 | | VERIFY(count == 1); | 60 | | } | 61 | 446M | T result = 0; | 62 | | | 63 | 446M | size_t nread = 0; | 64 | 1.34G | while (nread < count) { | 65 | 895M | if (m_current_byte.has_value()) { | 66 | 674M | if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) { | 67 | | // read as many bytes as possible directly | 68 | 674M | if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { | 69 | | // shift existing data over | 70 | 156M | result <<= 8; | 71 | 156M | result |= m_current_byte.value(); | 72 | 156M | nread += 8; | 73 | 156M | m_current_byte.clear(); | 74 | 517M | } else { | 75 | 517M | auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; | 76 | 517M | result <<= 1; | 77 | 517M | result |= bit; | 78 | 517M | ++nread; | 79 | 517M | if (m_bit_offset++ == 7) | 80 | 64.6M | m_current_byte.clear(); | 81 | 517M | } | 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 | 674M | } else { | 96 | 221M | m_current_byte = TRY(m_stream->read_value<u8>()); | 97 | 0 | m_bit_offset = 0; | 98 | 221M | } | 99 | 895M | } | 100 | | | 101 | 446M | return result; | 102 | 446M | } |
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 | 641k | { |
108 | 641k | m_current_byte.clear(); |
109 | 641k | m_bit_offset = 0; |
110 | 641k | } |
111 | | |
112 | | /// Whether we are (accidentally or intentionally) at a byte boundary right now. |
113 | 263M | ALWAYS_INLINE bool is_aligned_to_byte_boundary() const { return m_bit_offset % 8 == 0; } |
114 | 16 | 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 | 269k | : m_stream(move(stream)) |
131 | 269k | { |
132 | 269k | } |
133 | | |
134 | | template<Unsigned T> |
135 | | static constexpr T lsb_mask(T bits) |
136 | 1.20G | { |
137 | 1.20G | constexpr auto max = NumericLimits<T>::max(); |
138 | 1.20G | constexpr auto digits = NumericLimits<T>::digits(); |
139 | | |
140 | 1.20G | return bits == 0 ? 0 : max >> (digits - bits); |
141 | 1.20G | } _ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEhEET_S3_ Line | Count | Source | 136 | 83.7k | { | 137 | 83.7k | constexpr auto max = NumericLimits<T>::max(); | 138 | 83.7k | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 83.7k | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 83.7k | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEmEET_S3_ Line | Count | Source | 136 | 1.10G | { | 137 | 1.10G | constexpr auto max = NumericLimits<T>::max(); | 138 | 1.10G | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 1.10G | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 1.10G | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEbEET_S3_ Line | Count | Source | 136 | 80.8M | { | 137 | 80.8M | constexpr auto max = NumericLimits<T>::max(); | 138 | 80.8M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 80.8M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 80.8M | } |
_ZN2AK21LittleEndianBitStream8lsb_maskITkNS_8Concepts8UnsignedEtEET_S3_ Line | Count | Source | 136 | 22.2M | { | 137 | 22.2M | constexpr auto max = NumericLimits<T>::max(); | 138 | 22.2M | constexpr auto digits = NumericLimits<T>::digits(); | 139 | | | 140 | 22.2M | return bits == 0 ? 0 : max >> (digits - bits); | 141 | 22.2M | } |
|
142 | | |
143 | 13.2k | 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 | 265k | : LittleEndianBitStream(move(stream)) |
162 | 265k | , m_unsatisfiable_read_behavior(unsatisfiable_read_behavior) |
163 | 265k | { |
164 | 265k | } |
165 | | |
166 | | // ^Stream |
167 | | virtual ErrorOr<Bytes> read_some(Bytes bytes) override |
168 | 5.14M | { |
169 | 5.14M | align_to_byte_boundary(); |
170 | | |
171 | 5.14M | size_t bytes_read = 0; |
172 | 5.14M | auto buffer = bytes; |
173 | | |
174 | 5.14M | if (m_bit_count > 0) { |
175 | 141k | auto bits_to_read = min(buffer.size() * bits_per_byte, m_bit_count); |
176 | 141k | auto result = TRY(read_bits(bits_to_read)); |
177 | | |
178 | 0 | bytes_read = bits_to_read / bits_per_byte; |
179 | 141k | buffer.overwrite(0, &result, bytes_read); |
180 | | |
181 | 141k | buffer = buffer.slice(bytes_read); |
182 | 141k | } |
183 | | |
184 | 5.14M | buffer = TRY(m_stream->read_some(buffer)); |
185 | 0 | bytes_read += buffer.size(); |
186 | | |
187 | 5.14M | return bytes.trim(bytes_read); |
188 | 5.14M | } AK::LittleEndianInputBitStream::read_some(AK::Span<unsigned char>) Line | Count | Source | 168 | 5.14M | { | 169 | 5.14M | align_to_byte_boundary(); | 170 | | | 171 | 5.14M | size_t bytes_read = 0; | 172 | 5.14M | auto buffer = bytes; | 173 | | | 174 | 5.14M | if (m_bit_count > 0) { | 175 | 141k | auto bits_to_read = min(buffer.size() * bits_per_byte, m_bit_count); | 176 | 141k | auto result = TRY(read_bits(bits_to_read)); | 177 | | | 178 | 0 | bytes_read = bits_to_read / bits_per_byte; | 179 | 141k | buffer.overwrite(0, &result, bytes_read); | 180 | | | 181 | 141k | buffer = buffer.slice(bytes_read); | 182 | 141k | } | 183 | | | 184 | 5.14M | buffer = TRY(m_stream->read_some(buffer)); | 185 | 0 | bytes_read += buffer.size(); | 186 | | | 187 | 5.14M | return bytes.trim(bytes_read); | 188 | 5.14M | } |
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.34M | 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 | 80.8M | { |
201 | 80.8M | return read_bits<bool>(1); |
202 | 80.8M | } |
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 | 374M | { |
210 | 374M | auto result = TRY(peek_bits<T>(count)); |
211 | 0 | discard_previously_peeked_bits(count); |
212 | | |
213 | 374M | return result; |
214 | 374M | } _ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 271M | { | 210 | 271M | auto result = TRY(peek_bits<T>(count)); | 211 | 0 | discard_previously_peeked_bits(count); | 212 | | | 213 | 271M | return result; | 214 | 271M | } |
_ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 80.8M | { | 210 | 80.8M | auto result = TRY(peek_bits<T>(count)); | 211 | 0 | discard_previously_peeked_bits(count); | 212 | | | 213 | 80.8M | return result; | 214 | 80.8M | } |
_ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 209 | 22.2M | { | 210 | 22.2M | auto result = TRY(peek_bits<T>(count)); | 211 | 0 | discard_previously_peeked_bits(count); | 212 | | | 213 | 22.2M | return result; | 214 | 22.2M | } |
Unexecuted instantiation: _ZN2AK26LittleEndianInputBitStream9read_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIT_NS_5ErrorEEEm |
215 | | |
216 | | template<Unsigned T = u64> |
217 | | ErrorOr<T> peek_bits(size_t count) |
218 | 1.19G | { |
219 | 1.19G | if (count > m_bit_count) |
220 | 619M | TRY(refill_buffer_from_stream(count)); |
221 | | |
222 | 1.19G | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); |
223 | 1.19G | } _ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 1.08G | { | 219 | 1.08G | if (count > m_bit_count) | 220 | 615M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 1.08G | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 1.08G | } |
_ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 80.8M | { | 219 | 80.8M | if (count > m_bit_count) | 220 | 1.41M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 80.8M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 80.8M | } |
_ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIT_NS_5ErrorEEEm Line | Count | Source | 218 | 22.2M | { | 219 | 22.2M | if (count > m_bit_count) | 220 | 2.08M | TRY(refill_buffer_from_stream(count)); | 221 | | | 222 | 22.2M | return m_bit_buffer & lsb_mask<T>(min(count, m_bit_count)); | 223 | 22.2M | } |
Unexecuted instantiation: _ZN2AK26LittleEndianInputBitStream9peek_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIT_NS_5ErrorEEEm |
224 | | |
225 | | ALWAYS_INLINE void discard_previously_peeked_bits(u8 count) |
226 | 1.17G | { |
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.17G | if (count > m_bit_count) |
230 | 0 | count = m_bit_count; |
231 | | |
232 | 1.17G | m_bit_buffer >>= count; |
233 | 1.17G | m_bit_count -= count; |
234 | 1.17G | } |
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.18M | { |
240 | 5.18M | u8 remaining_bits = 0; |
241 | | |
242 | 5.18M | if (auto offset = m_bit_count % bits_per_byte; offset != 0) { |
243 | 83.7k | remaining_bits = m_bit_buffer & lsb_mask<u8>(offset); |
244 | 83.7k | discard_previously_peeked_bits(offset); |
245 | 83.7k | } |
246 | | |
247 | 5.18M | return remaining_bits; |
248 | 5.18M | } |
249 | | |
250 | | private: |
251 | | ErrorOr<void> refill_buffer_from_stream(size_t requested_bit_count) |
252 | 619M | { |
253 | 636M | while (requested_bit_count > m_bit_count) [[likely]] { |
254 | 619M | if (m_stream->is_eof()) [[unlikely]] { |
255 | 602M | if (m_unsatisfiable_read_behavior == UnsatisfiableReadBehavior::FillWithZero) { |
256 | 602M | m_bit_count = requested_bit_count; |
257 | 602M | return {}; |
258 | 602M | } |
259 | | |
260 | 6.39k | return Error::from_string_literal("Reached end-of-stream without collecting the required number of bits"); |
261 | 602M | } |
262 | | |
263 | 16.7M | size_t bits_to_read = bit_buffer_size - m_bit_count; |
264 | 16.7M | size_t bytes_to_read = bits_to_read / bits_per_byte; |
265 | | |
266 | 16.7M | BufferType buffer = 0; |
267 | 16.7M | auto bytes = TRY(m_stream->read_some({ &buffer, bytes_to_read })); |
268 | | |
269 | 0 | m_bit_buffer |= (buffer << m_bit_count); |
270 | 16.7M | m_bit_count += bytes.size() * bits_per_byte; |
271 | 16.7M | } |
272 | | |
273 | 16.7M | return {}; |
274 | 619M | } AK::LittleEndianInputBitStream::refill_buffer_from_stream(unsigned long) Line | Count | Source | 252 | 619M | { | 253 | 636M | while (requested_bit_count > m_bit_count) [[likely]] { | 254 | 619M | if (m_stream->is_eof()) [[unlikely]] { | 255 | 602M | if (m_unsatisfiable_read_behavior == UnsatisfiableReadBehavior::FillWithZero) { | 256 | 602M | m_bit_count = requested_bit_count; | 257 | 602M | return {}; | 258 | 602M | } | 259 | | | 260 | 6.39k | return Error::from_string_literal("Reached end-of-stream without collecting the required number of bits"); | 261 | 602M | } | 262 | | | 263 | 16.7M | size_t bits_to_read = bit_buffer_size - m_bit_count; | 264 | 16.7M | size_t bytes_to_read = bits_to_read / bits_per_byte; | 265 | | | 266 | 16.7M | BufferType buffer = 0; | 267 | 16.7M | auto bytes = TRY(m_stream->read_some({ &buffer, bytes_to_read })); | 268 | | | 269 | 0 | m_bit_buffer |= (buffer << m_bit_count); | 270 | 16.7M | m_bit_count += bytes.size() * bits_per_byte; | 271 | 16.7M | } | 272 | | | 273 | 16.7M | return {}; | 274 | 619M | } |
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.15k | : m_stream(move(stream)) |
285 | 2.15k | { |
286 | 2.15k | } |
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 | 671M | { |
302 | 671M | VERIFY(m_bit_offset <= 7); |
303 | | |
304 | 1.50G | while (bit_count > 0) { |
305 | 829M | u8 next_bit = (value >> (bit_count - 1)) & 1; |
306 | 829M | bit_count--; |
307 | | |
308 | 829M | m_current_byte <<= 1; |
309 | 829M | m_current_byte |= next_bit; |
310 | 829M | m_bit_offset++; |
311 | | |
312 | 829M | if (m_bit_offset > 7) { |
313 | 103M | TRY(m_stream->write_value(m_current_byte)); |
314 | 0 | m_bit_offset = 0; |
315 | 103M | m_current_byte = 0; |
316 | 103M | } |
317 | 829M | } |
318 | | |
319 | 671M | return {}; |
320 | 671M | } |
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 | 148k | { |
343 | 148k | if (m_bit_offset == 0) |
344 | 82.8k | return {}; |
345 | | |
346 | 131k | TRY(write_bits(0u, 8 - m_bit_offset)); |
347 | 65.8k | VERIFY(m_bit_offset == 0); |
348 | 65.8k | return {}; |
349 | 131k | } AK::BigEndianOutputBitStream::align_to_byte_boundary() Line | Count | Source | 342 | 148k | { | 343 | 148k | if (m_bit_offset == 0) | 344 | 82.8k | return {}; | 345 | | | 346 | 131k | TRY(write_bits(0u, 8 - m_bit_offset)); | 347 | 65.8k | VERIFY(m_bit_offset == 0); | 348 | 65.8k | return {}; | 349 | 131k | } |
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.70k | : LittleEndianBitStream(move(stream)) |
363 | 3.70k | { |
364 | 3.70k | } |
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.2k | { |
373 | 13.2k | VERIFY(is_aligned_to_byte_boundary()); |
374 | | |
375 | 13.2k | if (m_bit_count > 0) |
376 | 4.39k | TRY(flush_buffer_to_stream()); |
377 | | |
378 | 13.2k | return m_stream->write_some(bytes); |
379 | 13.2k | } AK::LittleEndianOutputBitStream::write_some(AK::Span<unsigned char const>) Line | Count | Source | 372 | 13.2k | { | 373 | 13.2k | VERIFY(is_aligned_to_byte_boundary()); | 374 | | | 375 | 13.2k | if (m_bit_count > 0) | 376 | 4.39k | TRY(flush_buffer_to_stream()); | 377 | | | 378 | 13.2k | return m_stream->write_some(bytes); | 379 | 13.2k | } |
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 | 102M | { |
384 | 102M | if (m_bit_count == bit_buffer_size) { |
385 | 0 | TRY(flush_buffer_to_stream()); |
386 | 102M | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { |
387 | 10.5M | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; |
388 | 10.5M | m_bit_count = bit_buffer_size; |
389 | | |
390 | 10.5M | if (remaining != sizeof(value) * bits_per_byte) |
391 | 10.5M | value >>= remaining; |
392 | 10.5M | count -= remaining; |
393 | | |
394 | 10.5M | TRY(flush_buffer_to_stream()); |
395 | 10.5M | } |
396 | | |
397 | 102M | if (count == 0) |
398 | 5.07M | return {}; |
399 | | |
400 | 97.1M | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; |
401 | 97.1M | m_bit_count += count; |
402 | | |
403 | 97.1M | return {}; |
404 | 102M | } _ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 362k | { | 384 | 362k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 362k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 16.9k | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 16.9k | m_bit_count = bit_buffer_size; | 389 | | | 390 | 16.9k | if (remaining != sizeof(value) * bits_per_byte) | 391 | 16.9k | value >>= remaining; | 392 | 16.9k | count -= remaining; | 393 | | | 394 | 16.9k | TRY(flush_buffer_to_stream()); | 395 | 16.9k | } | 396 | | | 397 | 362k | if (count == 0) | 398 | 5.34k | return {}; | 399 | | | 400 | 357k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 357k | m_bit_count += count; | 402 | | | 403 | 357k | return {}; | 404 | 362k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEtEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 101M | { | 384 | 101M | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 101M | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 10.5M | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 10.5M | m_bit_count = bit_buffer_size; | 389 | | | 390 | 10.5M | if (remaining != sizeof(value) * bits_per_byte) | 391 | 10.5M | value >>= remaining; | 392 | 10.5M | count -= remaining; | 393 | | | 394 | 10.5M | TRY(flush_buffer_to_stream()); | 395 | 10.5M | } | 396 | | | 397 | 101M | if (count == 0) | 398 | 5.06M | return {}; | 399 | | | 400 | 96.6M | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 96.6M | m_bit_count += count; | 402 | | | 403 | 96.6M | return {}; | 404 | 101M | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEmEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 32.4k | { | 384 | 32.4k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 32.4k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 2.02k | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 2.02k | m_bit_count = bit_buffer_size; | 389 | | | 390 | 2.02k | if (remaining != sizeof(value) * bits_per_byte) | 391 | 2.02k | value >>= remaining; | 392 | 2.02k | count -= remaining; | 393 | | | 394 | 2.02k | TRY(flush_buffer_to_stream()); | 395 | 2.02k | } | 396 | | | 397 | 32.4k | if (count == 0) | 398 | 519 | return {}; | 399 | | | 400 | 31.8k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 31.8k | m_bit_count += count; | 402 | | | 403 | 31.8k | return {}; | 404 | 32.4k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEbEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 16.6k | { | 384 | 16.6k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 16.6k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 302 | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 302 | m_bit_count = bit_buffer_size; | 389 | | | 390 | 302 | if (remaining != sizeof(value) * bits_per_byte) | 391 | 302 | value >>= remaining; | 392 | 302 | count -= remaining; | 393 | | | 394 | 302 | TRY(flush_buffer_to_stream()); | 395 | 302 | } | 396 | | | 397 | 16.6k | if (count == 0) | 398 | 302 | return {}; | 399 | | | 400 | 16.3k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 16.3k | m_bit_count += count; | 402 | | | 403 | 16.3k | return {}; | 404 | 16.6k | } |
_ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEjEENS_7ErrorOrIvNS_5ErrorEEET_m Line | Count | Source | 383 | 16.7k | { | 384 | 16.7k | if (m_bit_count == bit_buffer_size) { | 385 | 0 | TRY(flush_buffer_to_stream()); | 386 | 16.7k | } else if (auto remaining = bit_buffer_size - m_bit_count; count >= remaining) { | 387 | 457 | m_bit_buffer |= (static_cast<BufferType>(value) & lsb_mask<BufferType>(remaining)) << m_bit_count; | 388 | 457 | m_bit_count = bit_buffer_size; | 389 | | | 390 | 457 | if (remaining != sizeof(value) * bits_per_byte) | 391 | 457 | value >>= remaining; | 392 | 457 | count -= remaining; | 393 | | | 394 | 457 | TRY(flush_buffer_to_stream()); | 395 | 457 | } | 396 | | | 397 | 16.7k | if (count == 0) | 398 | 266 | return {}; | 399 | | | 400 | 16.4k | m_bit_buffer |= static_cast<BufferType>(value) << m_bit_count; | 401 | 16.4k | m_bit_count += count; | 402 | | | 403 | 16.4k | return {}; | 404 | 16.7k | } |
Unexecuted instantiation: _ZN2AK27LittleEndianOutputBitStream10write_bitsITkNS_8Concepts8UnsignedEhEENS_7ErrorOrIvNS_5ErrorEEET_m |
405 | | |
406 | | ALWAYS_INLINE ErrorOr<void> flush_buffer_to_stream() |
407 | 10.6M | { |
408 | 10.6M | auto bytes_to_write = m_bit_count / bits_per_byte; |
409 | 10.6M | TRY(m_stream->write_until_depleted({ &m_bit_buffer, bytes_to_write })); |
410 | | |
411 | 10.6M | if (m_bit_count == bit_buffer_size) { |
412 | 10.5M | m_bit_buffer = 0; |
413 | 10.5M | m_bit_count = 0; |
414 | 10.5M | } else { |
415 | 8.09k | auto bits_written = bytes_to_write * bits_per_byte; |
416 | | |
417 | 8.09k | m_bit_buffer >>= bits_written; |
418 | 8.09k | m_bit_count -= bits_written; |
419 | 8.09k | } |
420 | | |
421 | 10.6M | return {}; |
422 | 10.6M | } |
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 | 16.6k | { |
440 | 16.6k | return m_bit_count; |
441 | 16.6k | } |
442 | | |
443 | | ErrorOr<void> align_to_byte_boundary() |
444 | 8.13k | { |
445 | 8.13k | if (auto offset = m_bit_count % bits_per_byte; offset != 0) |
446 | 6.93k | TRY(write_bits<u8>(0u, bits_per_byte - offset)); |
447 | | |
448 | 8.13k | return {}; |
449 | 8.13k | } |
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 |