Coverage Report

Created: 2026-07-25 07:46

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