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