/src/serenity/Userland/Libraries/LibCompress/Zlib.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, the SerenityOS developers. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/BitStream.h> |
8 | | #include <AK/IntegralMath.h> |
9 | | #include <AK/MemoryStream.h> |
10 | | #include <AK/Span.h> |
11 | | #include <AK/TypeCasts.h> |
12 | | #include <AK/Types.h> |
13 | | #include <LibCompress/Deflate.h> |
14 | | #include <LibCompress/Zlib.h> |
15 | | |
16 | | namespace Compress { |
17 | | |
18 | | ErrorOr<NonnullOwnPtr<ZlibDecompressor>> ZlibDecompressor::create(MaybeOwned<Stream> stream) |
19 | 8.70k | { |
20 | 8.70k | auto header = TRY(stream->read_value<ZlibHeader>()); |
21 | | |
22 | 8.63k | if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7) |
23 | 70 | return Error::from_string_literal("Non-DEFLATE compression inside Zlib is not supported"); |
24 | | |
25 | 8.56k | if (header.present_dictionary) |
26 | 8 | return Error::from_string_literal("Zlib compression with a pre-defined dictionary is currently not supported"); |
27 | | |
28 | 8.56k | if (header.as_u16 % 31 != 0) |
29 | 45 | return Error::from_string_literal("Zlib error correction code does not match"); |
30 | | |
31 | 8.51k | auto bit_stream = make<LittleEndianInputBitStream>(move(stream)); |
32 | 8.51k | auto deflate_stream = TRY(Compress::DeflateDecompressor::construct(move(bit_stream))); |
33 | | |
34 | 8.51k | return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(header, move(deflate_stream))); |
35 | 8.51k | } |
36 | | |
37 | | ZlibDecompressor::ZlibDecompressor(ZlibHeader header, NonnullOwnPtr<Stream> stream) |
38 | 8.51k | : m_header(header) |
39 | 8.51k | , m_stream(move(stream)) |
40 | 8.51k | { |
41 | 8.51k | } |
42 | | |
43 | | ErrorOr<Bytes> ZlibDecompressor::read_some(Bytes bytes) |
44 | 1.48M | { |
45 | 1.48M | return m_stream->read_some(bytes); |
46 | 1.48M | } |
47 | | |
48 | | ErrorOr<size_t> ZlibDecompressor::write_some(ReadonlyBytes) |
49 | 0 | { |
50 | 0 | return Error::from_errno(EBADF); |
51 | 0 | } |
52 | | |
53 | | bool ZlibDecompressor::is_eof() const |
54 | 1.49M | { |
55 | 1.49M | return m_stream->is_eof(); |
56 | 1.49M | } |
57 | | |
58 | | bool ZlibDecompressor::is_open() const |
59 | 0 | { |
60 | 0 | return m_stream->is_open(); |
61 | 0 | } |
62 | | |
63 | | void ZlibDecompressor::close() |
64 | 0 | { |
65 | 0 | } |
66 | | |
67 | | ErrorOr<NonnullOwnPtr<ZlibCompressor>> ZlibCompressor::construct(MaybeOwned<Stream> stream, ZlibCompressionLevel compression_level) |
68 | 0 | { |
69 | | // Zlib only defines Deflate as a compression method. |
70 | 0 | auto compression_method = ZlibCompressionMethod::Deflate; |
71 | | |
72 | | // FIXME: Find a way to compress with Deflate's "Best" compression level. |
73 | 0 | auto compressor_stream = TRY(DeflateCompressor::construct(MaybeOwned(*stream), static_cast<DeflateCompressor::CompressionLevel>(compression_level))); |
74 | |
|
75 | 0 | auto zlib_compressor = TRY(adopt_nonnull_own_or_enomem(new (nothrow) ZlibCompressor(move(stream), move(compressor_stream)))); |
76 | 0 | TRY(zlib_compressor->write_header(compression_method, compression_level)); |
77 | |
|
78 | 0 | return zlib_compressor; |
79 | 0 | } |
80 | | |
81 | | ZlibCompressor::ZlibCompressor(MaybeOwned<Stream> stream, NonnullOwnPtr<Stream> compressor_stream) |
82 | 0 | : m_output_stream(move(stream)) |
83 | 0 | , m_compressor(move(compressor_stream)) |
84 | 0 | { |
85 | 0 | } |
86 | | |
87 | | ZlibCompressor::~ZlibCompressor() |
88 | 0 | { |
89 | 0 | VERIFY(m_finished); |
90 | 0 | } |
91 | | |
92 | | ErrorOr<void> ZlibCompressor::write_header(ZlibCompressionMethod compression_method, ZlibCompressionLevel compression_level) |
93 | 0 | { |
94 | 0 | u8 compression_info = 0; |
95 | 0 | if (compression_method == ZlibCompressionMethod::Deflate) { |
96 | 0 | compression_info = AK::log2(DeflateCompressor::window_size) - 8; |
97 | 0 | VERIFY(compression_info <= 7); |
98 | 0 | } |
99 | | |
100 | 0 | ZlibHeader header { |
101 | 0 | .compression_method = compression_method, |
102 | 0 | .compression_info = compression_info, |
103 | 0 | .check_bits = 0, |
104 | 0 | .present_dictionary = false, |
105 | 0 | .compression_level = compression_level, |
106 | 0 | }; |
107 | 0 | header.check_bits = 0b11111 - header.as_u16 % 31; |
108 | | |
109 | | // FIXME: Support pre-defined dictionaries. |
110 | |
|
111 | 0 | TRY(m_output_stream->write_value(header.as_u16)); |
112 | |
|
113 | 0 | return {}; |
114 | 0 | } |
115 | | |
116 | | ErrorOr<Bytes> ZlibCompressor::read_some(Bytes) |
117 | 0 | { |
118 | 0 | return Error::from_errno(EBADF); |
119 | 0 | } |
120 | | |
121 | | ErrorOr<size_t> ZlibCompressor::write_some(ReadonlyBytes bytes) |
122 | 0 | { |
123 | 0 | VERIFY(!m_finished); |
124 | | |
125 | 0 | size_t n_written = TRY(m_compressor->write_some(bytes)); |
126 | 0 | m_adler32_checksum.update(bytes.trim(n_written)); |
127 | 0 | return n_written; |
128 | 0 | } |
129 | | |
130 | | bool ZlibCompressor::is_eof() const |
131 | 0 | { |
132 | 0 | return false; |
133 | 0 | } |
134 | | |
135 | | bool ZlibCompressor::is_open() const |
136 | 0 | { |
137 | 0 | return m_output_stream->is_open(); |
138 | 0 | } |
139 | | |
140 | | void ZlibCompressor::close() |
141 | 0 | { |
142 | 0 | } |
143 | | |
144 | | ErrorOr<void> ZlibCompressor::finish() |
145 | 0 | { |
146 | 0 | VERIFY(!m_finished); |
147 | | |
148 | 0 | if (is<DeflateCompressor>(m_compressor.ptr())) |
149 | 0 | TRY(static_cast<DeflateCompressor*>(m_compressor.ptr())->final_flush()); |
150 | |
|
151 | 0 | NetworkOrdered<u32> adler_sum = m_adler32_checksum.digest(); |
152 | 0 | TRY(m_output_stream->write_value(adler_sum)); |
153 | |
|
154 | 0 | m_finished = true; |
155 | |
|
156 | 0 | return {}; |
157 | 0 | } |
158 | | |
159 | | ErrorOr<ByteBuffer> ZlibCompressor::compress_all(ReadonlyBytes bytes, ZlibCompressionLevel compression_level) |
160 | 0 | { |
161 | 0 | auto output_stream = TRY(try_make<AllocatingMemoryStream>()); |
162 | 0 | auto zlib_stream = TRY(ZlibCompressor::construct(MaybeOwned<Stream>(*output_stream), compression_level)); |
163 | |
|
164 | 0 | TRY(zlib_stream->write_until_depleted(bytes)); |
165 | |
|
166 | 0 | TRY(zlib_stream->finish()); |
167 | |
|
168 | 0 | return output_stream->read_until_eof(); |
169 | 0 | } |
170 | | |
171 | | } |