Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}