Coverage Report

Created: 2025-12-18 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibCompress/Brotli.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/BitStream.h>
10
#include <AK/CircularQueue.h>
11
#include <AK/FixedArray.h>
12
#include <AK/Vector.h>
13
14
namespace Compress {
15
16
namespace Brotli {
17
18
class CanonicalCode {
19
public:
20
257k
    CanonicalCode() = default;
21
    CanonicalCode(Vector<size_t> codes, Vector<size_t> values)
22
0
        : m_symbol_codes(move(codes))
23
0
        , m_symbol_values(move(values))
24
0
    {
25
0
    }
26
27
    static ErrorOr<CanonicalCode> read_prefix_code(LittleEndianInputBitStream&, size_t alphabet_size);
28
    static ErrorOr<CanonicalCode> read_simple_prefix_code(LittleEndianInputBitStream&, size_t alphabet_size);
29
    static ErrorOr<CanonicalCode> read_complex_prefix_code(LittleEndianInputBitStream&, size_t alphabet_size, size_t hskip);
30
31
    ErrorOr<size_t> read_symbol(LittleEndianInputBitStream&) const;
32
33
private:
34
    static ErrorOr<size_t> read_complex_prefix_code_length(LittleEndianInputBitStream&);
35
36
    Vector<size_t> m_symbol_codes;
37
    Vector<size_t> m_symbol_values;
38
};
39
40
}
41
42
class BrotliDecompressionStream : public Stream {
43
    using CanonicalCode = Brotli::CanonicalCode;
44
45
public:
46
    enum class State {
47
        WindowSize,
48
        Idle,
49
        UncompressedData,
50
        CompressedCommand,
51
        CompressedLiteral,
52
        CompressedDistance,
53
        CompressedCopy,
54
        CompressedDictionary,
55
    };
56
57
    struct Block {
58
        size_t type;
59
        size_t type_previous;
60
        size_t number_of_types;
61
62
        size_t length;
63
64
        CanonicalCode type_code;
65
        CanonicalCode length_code;
66
    };
67
68
    class LookbackBuffer {
69
    private:
70
        LookbackBuffer(FixedArray<u8>& buffer)
71
5.53k
            : m_buffer(move(buffer))
72
5.53k
        {
73
5.53k
        }
74
75
    public:
76
        static ErrorOr<LookbackBuffer> try_create(size_t size)
77
5.53k
        {
78
5.53k
            auto buffer = TRY(FixedArray<u8>::create(size));
79
5.53k
            return LookbackBuffer { buffer };
80
5.53k
        }
81
82
        void write(u8 value)
83
1.27G
        {
84
1.27G
            m_buffer[m_offset] = value;
85
1.27G
            m_offset = (m_offset + 1) % m_buffer.size();
86
1.27G
            m_total_written++;
87
1.27G
        }
88
89
        u8 lookback(size_t offset) const
90
791M
        {
91
791M
            VERIFY(offset <= m_total_written);
92
791M
            VERIFY(offset <= m_buffer.size());
93
791M
            size_t index = (m_offset + m_buffer.size() - offset) % m_buffer.size();
94
791M
            return m_buffer[index];
95
791M
        }
96
97
        u8 lookback(size_t offset, u8 fallback) const
98
577M
        {
99
577M
            if (offset > m_total_written || offset > m_buffer.size())
100
4.71k
                return fallback;
101
577M
            VERIFY(offset <= m_total_written);
102
577M
            VERIFY(offset <= m_buffer.size());
103
577M
            size_t index = (m_offset + m_buffer.size() - offset) % m_buffer.size();
104
577M
            return m_buffer[index];
105
577M
        }
106
107
117M
        size_t total_written() { return m_total_written; }
108
109
    private:
110
        FixedArray<u8> m_buffer;
111
        size_t m_offset { 0 };
112
        size_t m_total_written { 0 };
113
    };
114
115
public:
116
    BrotliDecompressionStream(MaybeOwned<Stream>);
117
118
    ErrorOr<Bytes> read_some(Bytes output_buffer) override;
119
0
    ErrorOr<size_t> write_some(ReadonlyBytes bytes) override { return m_input_stream.write_some(bytes); }
120
    bool is_eof() const override;
121
0
    bool is_open() const override { return m_input_stream.is_open(); }
122
0
    void close() override { m_input_stream.close(); }
123
124
private:
125
    ErrorOr<size_t> read_window_length();
126
    ErrorOr<size_t> read_size_number_of_nibbles();
127
    ErrorOr<size_t> read_variable_length();
128
129
    ErrorOr<void> read_context_map(size_t number_of_codes, Vector<u8>& context_map, size_t context_map_size);
130
    ErrorOr<void> read_block_configuration(Block&);
131
132
    ErrorOr<void> block_update_length(Block&);
133
    ErrorOr<void> block_read_new_state(Block&);
134
135
    size_t literal_code_index_from_context();
136
137
    LittleEndianInputBitStream m_input_stream;
138
    State m_current_state { State::WindowSize };
139
    Optional<LookbackBuffer> m_lookback_buffer;
140
141
    size_t m_window_size { 0 };
142
    bool m_read_final_block { false };
143
    size_t m_postfix_bits { 0 };
144
    size_t m_direct_distances { 0 };
145
    size_t m_distances[4] { 4, 11, 15, 16 };
146
147
    size_t m_bytes_left { 0 };
148
    size_t m_insert_length { 0 };
149
    size_t m_copy_length { 0 };
150
    bool m_implicit_zero_distance { false };
151
    size_t m_distance { 0 };
152
    ByteBuffer m_dictionary_data;
153
154
    Block m_literal_block;
155
    Vector<u8> m_literal_context_modes;
156
    Block m_insert_and_copy_block;
157
    Block m_distance_block;
158
159
    Vector<u8> m_context_mapping_literal;
160
    Vector<u8> m_context_mapping_distance;
161
162
    Vector<CanonicalCode> m_literal_codes;
163
    Vector<CanonicalCode> m_insert_and_copy_codes;
164
    Vector<CanonicalCode> m_distance_codes;
165
};
166
167
}