summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibCompress/Xz.h
blob: 2bfdf7c1ddb162a8f477b1bc1ec26846c6c38435 (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
/*
 * Copyright (c) 2023, Tim Schumacher <timschumi@gmx.de>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#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>

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();
};
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();
    u32 backward_size();
};
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;

    u8 number_of_filters();
};
static_assert(sizeof(XzBlockFlags) == 1);

// 5.3.1. LZMA2
struct [[gnu::packed]] XzFilterLzma2Properties {
    u8 encoded_dictionary_size : 6;
    u8 reserved : 2;

    ErrorOr<void> validate();
    u32 dictionary_size();
};
static_assert(sizeof(XzFilterLzma2Properties) == 1);

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>);

    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_uncompressed_size {};
};

}

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