summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibCompress/Brotli.h
blob: cc321f18b5a91beb2efb4e1ee379219d011a9335 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/CircularQueue.h>
#include <AK/FixedArray.h>
#include <LibCore/InputBitStream.h>
#include <LibCore/Stream.h>

namespace Compress {

using Core::Stream::LittleEndianInputBitStream;
using Core::Stream::Stream;

class BrotliDecompressionStream : public Stream {
public:
    enum class State {
        WindowSize,
        Idle,
        UncompressedData,
        CompressedCommand,
        CompressedLiteral,
        CompressedDistance,
        CompressedCopy,
        CompressedDictionary,
    };

    class CanonicalCode {
        friend class BrotliDecompressionStream;

    public:
        CanonicalCode() = default;
        ErrorOr<size_t> read_symbol(LittleEndianInputBitStream&);
        void clear()
        {
            m_symbol_codes.clear();
            m_symbol_values.clear();
        }

    private:
        Vector<size_t> m_symbol_codes;
        Vector<size_t> m_symbol_values;
    };

    struct Block {
        size_t type;
        size_t type_previous;
        size_t number_of_types;

        size_t length;

        CanonicalCode type_code;
        CanonicalCode length_code;
    };

    class LookbackBuffer {
    private:
        LookbackBuffer(FixedArray<u8>& buffer)
            : m_buffer(move(buffer))
        {
        }

    public:
        static ErrorOr<LookbackBuffer> try_create(size_t size)
        {
            auto buffer = TRY(FixedArray<u8>::try_create(size));
            return LookbackBuffer { buffer };
        }

        void write(u8 value)
        {
            m_buffer[m_offset] = value;
            m_offset = (m_offset + 1) % m_buffer.size();
            m_total_written++;
        }

        u8 lookback(size_t offset) const
        {
            VERIFY(offset <= m_total_written);
            VERIFY(offset <= m_buffer.size());
            size_t index = (m_offset + m_buffer.size() - offset) % m_buffer.size();
            return m_buffer[index];
        }

        u8 lookback(size_t offset, u8 fallback) const
        {
            if (offset > m_total_written || offset > m_buffer.size())
                return fallback;
            VERIFY(offset <= m_total_written);
            VERIFY(offset <= m_buffer.size());
            size_t index = (m_offset + m_buffer.size() - offset) % m_buffer.size();
            return m_buffer[index];
        }

        size_t total_written() { return m_total_written; }

    private:
        FixedArray<u8> m_buffer;
        size_t m_offset { 0 };
        size_t m_total_written { 0 };
    };

public:
    BrotliDecompressionStream(Stream&);

    bool is_readable() const override { return m_input_stream.is_readable(); }
    ErrorOr<Bytes> read(Bytes output_buffer) override;
    bool is_writable() const override { return m_input_stream.is_writable(); }
    ErrorOr<size_t> write(ReadonlyBytes bytes) override { return m_input_stream.write(bytes); }
    bool is_eof() const override;
    bool is_open() const override { return m_input_stream.is_open(); }
    void close() override { m_input_stream.close(); }

private:
    ErrorOr<size_t> read_window_length();
    ErrorOr<size_t> read_size_number_of_nibbles();
    ErrorOr<size_t> read_variable_length();
    ErrorOr<size_t> read_complex_prefix_code_length();

    ErrorOr<void> read_prefix_code(CanonicalCode&, size_t alphabet_size);
    ErrorOr<void> read_simple_prefix_code(CanonicalCode&, size_t alphabet_size);
    ErrorOr<void> read_complex_prefix_code(CanonicalCode&, size_t alphabet_size, size_t hskip);
    ErrorOr<void> read_context_map(size_t number_of_codes, Vector<u8>& context_map, size_t context_map_size);
    ErrorOr<void> read_block_configuration(Block&);

    ErrorOr<void> block_update_length(Block&);
    ErrorOr<void> block_read_new_state(Block&);

    size_t literal_code_index_from_context();

    LittleEndianInputBitStream m_input_stream;
    State m_current_state { State::WindowSize };
    Optional<LookbackBuffer> m_lookback_buffer;

    size_t m_window_size { 0 };
    bool m_read_final_block { false };
    size_t m_postfix_bits { 0 };
    size_t m_direct_distances { 0 };
    size_t m_distances[4] { 4, 11, 15, 16 };

    size_t m_bytes_left { 0 };
    size_t m_insert_length { 0 };
    size_t m_copy_length { 0 };
    bool m_implicit_zero_distance { false };
    size_t m_distance { 0 };
    ByteBuffer m_dictionary_data;

    Block m_literal_block;
    Vector<u8> m_literal_context_modes;
    Block m_insert_and_copy_block;
    Block m_distance_block;

    Vector<u8> m_context_mapping_literal;
    Vector<u8> m_context_mapping_distance;

    Vector<CanonicalCode> m_literal_codes;
    Vector<CanonicalCode> m_insert_and_copy_codes;
    Vector<CanonicalCode> m_distance_codes;
};

}