mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 02:12:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			177 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2023, Tim Schumacher <timschumi@gmx.de>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <AK/CircularBuffer.h>
 | |
| #include <AK/ConstrainedStream.h>
 | |
| #include <AK/CountingStream.h>
 | |
| #include <AK/Endian.h>
 | |
| #include <AK/Error.h>
 | |
| #include <AK/MaybeOwned.h>
 | |
| #include <AK/NonnullOwnPtr.h>
 | |
| #include <AK/OwnPtr.h>
 | |
| #include <AK/Stream.h>
 | |
| #include <AK/Vector.h>
 | |
| 
 | |
| namespace Compress {
 | |
| 
 | |
| // This implementation is based on the "The .xz File Format" specification version 1.1.0:
 | |
| // https://tukaani.org/xz/xz-file-format-1.1.0.txt
 | |
| 
 | |
| // 1.2. Multibyte Integers
 | |
| class [[gnu::packed]] XzMultibyteInteger {
 | |
| public:
 | |
|     constexpr XzMultibyteInteger() = default;
 | |
| 
 | |
|     constexpr XzMultibyteInteger(u64 value)
 | |
|         : m_value(value)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     constexpr operator u64() const { return m_value; }
 | |
| 
 | |
|     static ErrorOr<XzMultibyteInteger> read_from_stream(Stream& stream);
 | |
| 
 | |
| private:
 | |
|     u64 m_value { 0 };
 | |
| };
 | |
| 
 | |
| // 2.1.1.2. Stream Flags
 | |
| enum XzStreamCheckType : u8 {
 | |
|     None = 0x00,
 | |
|     CRC32 = 0x01,
 | |
|     CRC64 = 0x04,
 | |
|     SHA256 = 0x0A,
 | |
| };
 | |
| 
 | |
| // 2.1.1.2. Stream Flags
 | |
| struct [[gnu::packed]] XzStreamFlags {
 | |
|     u8 reserved;
 | |
|     XzStreamCheckType check_type : 4;
 | |
|     u8 reserved_bits : 4;
 | |
| };
 | |
| static_assert(sizeof(XzStreamFlags) == 2);
 | |
| 
 | |
| // 2.1.1. Stream Header
 | |
| struct [[gnu::packed]] XzStreamHeader {
 | |
|     u8 magic[6];
 | |
|     XzStreamFlags flags;
 | |
|     LittleEndian<u32> flags_crc32;
 | |
| 
 | |
|     ErrorOr<void> validate() const;
 | |
| };
 | |
| static_assert(sizeof(XzStreamHeader) == 12);
 | |
| 
 | |
| // 2.1.2. Stream Footer
 | |
| struct [[gnu::packed]] XzStreamFooter {
 | |
|     LittleEndian<u32> size_and_flags_crc32;
 | |
|     LittleEndian<u32> encoded_backward_size;
 | |
|     XzStreamFlags flags;
 | |
|     u8 magic[2];
 | |
| 
 | |
|     ErrorOr<void> validate() const;
 | |
|     u32 backward_size() const;
 | |
| };
 | |
| static_assert(sizeof(XzStreamFooter) == 12);
 | |
| 
 | |
| // 3.1.2. Block Flags
 | |
| struct [[gnu::packed]] XzBlockFlags {
 | |
|     u8 encoded_number_of_filters : 2;
 | |
|     u8 reserved : 4;
 | |
|     bool compressed_size_present : 1;
 | |
|     bool uncompressed_size_present : 1;
 | |
| 
 | |
|     size_t number_of_filters() const;
 | |
| };
 | |
| static_assert(sizeof(XzBlockFlags) == 1);
 | |
| 
 | |
| // 5.3.1. LZMA2
 | |
| struct [[gnu::packed]] XzFilterLzma2Properties {
 | |
|     u8 encoded_dictionary_size : 6;
 | |
|     u8 reserved : 2;
 | |
| 
 | |
|     ErrorOr<void> validate() const;
 | |
|     u32 dictionary_size() const;
 | |
| };
 | |
| static_assert(sizeof(XzFilterLzma2Properties) == 1);
 | |
| 
 | |
| // 5.3.3. Delta
 | |
| struct [[gnu::packed]] XzFilterDeltaProperties {
 | |
|     u8 encoded_distance;
 | |
| 
 | |
|     u32 distance() const;
 | |
| };
 | |
| static_assert(sizeof(XzFilterDeltaProperties) == 1);
 | |
| 
 | |
| class XzFilterDelta : public Stream {
 | |
| public:
 | |
|     static ErrorOr<NonnullOwnPtr<XzFilterDelta>> create(MaybeOwned<Stream>, u32 distance);
 | |
| 
 | |
|     virtual ErrorOr<Bytes> read_some(Bytes) override;
 | |
|     virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
 | |
|     virtual bool is_eof() const override;
 | |
|     virtual bool is_open() const override;
 | |
|     virtual void close() override;
 | |
| 
 | |
| private:
 | |
|     XzFilterDelta(MaybeOwned<Stream>, CircularBuffer);
 | |
| 
 | |
|     MaybeOwned<Stream> m_stream;
 | |
|     CircularBuffer m_buffer;
 | |
| };
 | |
| 
 | |
| class XzDecompressor : public Stream {
 | |
| public:
 | |
|     static ErrorOr<NonnullOwnPtr<XzDecompressor>> create(MaybeOwned<Stream>);
 | |
| 
 | |
|     virtual ErrorOr<Bytes> read_some(Bytes) override;
 | |
|     virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
 | |
|     virtual bool is_eof() const override;
 | |
|     virtual bool is_open() const override;
 | |
|     virtual void close() override;
 | |
| 
 | |
| private:
 | |
|     XzDecompressor(NonnullOwnPtr<CountingStream>);
 | |
| 
 | |
|     ErrorOr<bool> load_next_stream();
 | |
|     ErrorOr<void> load_next_block(u8 encoded_block_header_size);
 | |
|     ErrorOr<void> finish_current_block();
 | |
|     ErrorOr<void> finish_current_stream();
 | |
| 
 | |
|     NonnullOwnPtr<CountingStream> m_stream;
 | |
|     Optional<XzStreamFlags> m_stream_flags;
 | |
|     bool m_found_first_stream_header { false };
 | |
|     bool m_found_last_stream_footer { false };
 | |
| 
 | |
|     Optional<MaybeOwned<Stream>> m_current_block_stream {};
 | |
|     Optional<u64> m_current_block_expected_uncompressed_size {};
 | |
|     u64 m_current_block_uncompressed_size {};
 | |
|     u64 m_current_block_start_offset {};
 | |
| 
 | |
|     struct BlockMetadata {
 | |
|         u64 uncompressed_size {};
 | |
|         u64 unpadded_size {};
 | |
|     };
 | |
|     Vector<BlockMetadata> m_processed_blocks;
 | |
| };
 | |
| 
 | |
| }
 | |
| 
 | |
| template<>
 | |
| struct AK::Traits<Compress::XzStreamHeader> : public AK::GenericTraits<Compress::XzStreamHeader> {
 | |
|     static constexpr bool is_trivially_serializable() { return true; }
 | |
| };
 | |
| 
 | |
| template<>
 | |
| struct AK::Traits<Compress::XzStreamFooter> : public AK::GenericTraits<Compress::XzStreamFooter> {
 | |
|     static constexpr bool is_trivially_serializable() { return true; }
 | |
| };
 | |
| 
 | |
| template<>
 | |
| struct AK::Traits<Compress::XzBlockFlags> : public AK::GenericTraits<Compress::XzBlockFlags> {
 | |
|     static constexpr bool is_trivially_serializable() { return true; }
 | |
| };
 | 
