/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 | | } |