Coverage Report

Created: 2025-03-04 07:22

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