Coverage Report

Created: 2025-08-28 06:26

/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