/* * Copyright (c) 2022, Michiel Visser * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace Compress { namespace Brotli { class CanonicalCode { public: CanonicalCode() = default; CanonicalCode(Vector codes, Vector values) : m_symbol_codes(move(codes)) , m_symbol_values(move(values)) {}; static ErrorOr read_prefix_code(LittleEndianInputBitStream&, size_t alphabet_size); static ErrorOr read_simple_prefix_code(LittleEndianInputBitStream&, size_t alphabet_size); static ErrorOr read_complex_prefix_code(LittleEndianInputBitStream&, size_t alphabet_size, size_t hskip); ErrorOr read_symbol(LittleEndianInputBitStream&) const; private: static ErrorOr read_complex_prefix_code_length(LittleEndianInputBitStream&); Vector m_symbol_codes; Vector m_symbol_values; }; } class BrotliDecompressionStream : public Stream { using CanonicalCode = Brotli::CanonicalCode; public: enum class State { WindowSize, Idle, UncompressedData, CompressedCommand, CompressedLiteral, CompressedDistance, CompressedCopy, CompressedDictionary, }; 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& buffer) : m_buffer(move(buffer)) { } public: static ErrorOr try_create(size_t size) { auto buffer = TRY(FixedArray::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 m_buffer; size_t m_offset { 0 }; size_t m_total_written { 0 }; }; public: BrotliDecompressionStream(MaybeOwned); ErrorOr read_some(Bytes output_buffer) override; ErrorOr write_some(ReadonlyBytes bytes) override { return m_input_stream.write_some(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 read_window_length(); ErrorOr read_size_number_of_nibbles(); ErrorOr read_variable_length(); ErrorOr read_context_map(size_t number_of_codes, Vector& context_map, size_t context_map_size); ErrorOr read_block_configuration(Block&); ErrorOr block_update_length(Block&); ErrorOr block_read_new_state(Block&); size_t literal_code_index_from_context(); LittleEndianInputBitStream m_input_stream; State m_current_state { State::WindowSize }; Optional 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 m_literal_context_modes; Block m_insert_and_copy_block; Block m_distance_block; Vector m_context_mapping_literal; Vector m_context_mapping_distance; Vector m_literal_codes; Vector m_insert_and_copy_codes; Vector m_distance_codes; }; }