LibCompress: Don't assume zlib header is available right away
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run

Instead of checking the header in ZlibDecompressor::create(), we now
check it in read_some() when it is called for the first time. This
resolves a FIXME in the new DecompressionStream implementation.
This commit is contained in:
Valtteri Koskivuori 2024-11-18 20:16:55 +02:00 committed by Tim Flynn
parent be09893fa7
commit 135daeb8bb
Notes: github-actions[bot] 2024-11-19 00:56:46 +00:00
6 changed files with 45 additions and 24 deletions

View file

@ -17,31 +17,35 @@ namespace Compress {
ErrorOr<NonnullOwnPtr<ZlibDecompressor>> ZlibDecompressor::create(MaybeOwned<Stream> stream)
{
auto header = TRY(stream->read_value<ZlibHeader>());
if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7)
return Error::from_string_literal("Non-DEFLATE compression inside Zlib is not supported");
if (header.present_dictionary)
return Error::from_string_literal("Zlib compression with a pre-defined dictionary is currently not supported");
if (header.as_u16 % 31 != 0)
return Error::from_string_literal("Zlib error correction code does not match");
auto bit_stream = make<LittleEndianInputBitStream>(move(stream));
auto deflate_stream = TRY(Compress::DeflateDecompressor::construct(move(bit_stream)));
return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(header, move(deflate_stream)));
return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(move(stream)));
}
ZlibDecompressor::ZlibDecompressor(ZlibHeader header, NonnullOwnPtr<Stream> stream)
: m_header(header)
ZlibDecompressor::ZlibDecompressor(MaybeOwned<Stream> stream)
: m_has_seen_header(false)
, m_stream(move(stream))
{
}
ErrorOr<Bytes> ZlibDecompressor::read_some(Bytes bytes)
{
if (!m_has_seen_header) {
auto header = TRY(m_stream->read_value<ZlibHeader>());
if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7)
return Error::from_string_literal("Non-DEFLATE compression inside Zlib is not supported");
if (header.present_dictionary)
return Error::from_string_literal("Zlib compression with a pre-defined dictionary is currently not supported");
if (header.as_u16 % 31 != 0)
return Error::from_string_literal("Zlib error correction code does not match");
auto bit_stream = make<LittleEndianInputBitStream>(move(m_stream));
auto deflate_stream = TRY(Compress::DeflateDecompressor::construct(move(bit_stream)));
m_stream = move(deflate_stream);
m_has_seen_header = true;
}
return m_stream->read_some(bytes);
}

View file

@ -55,10 +55,10 @@ public:
virtual void close() override;
private:
ZlibDecompressor(ZlibHeader, NonnullOwnPtr<Stream>);
ZlibDecompressor(MaybeOwned<Stream>);
ZlibHeader m_header;
NonnullOwnPtr<Stream> m_stream;
bool m_has_seen_header { false };
MaybeOwned<Stream> m_stream;
};
class ZlibCompressor : public Stream {

View file

@ -32,8 +32,6 @@ WebIDL::ExceptionOr<GC::Ref<DecompressionStream>> DecompressionStream::construct
auto decompressor = [&, input_stream = MaybeOwned<Stream> { *input_stream }]() mutable -> ErrorOr<Decompressor> {
switch (format) {
case Bindings::CompressionFormat::Deflate:
// FIXME: Our zlib decompressor assumes the initial data contains the zlib header. We don't have any data yet,
// so this will always fail.
return TRY(Compress::ZlibDecompressor::create(move(input_stream)));
case Bindings::CompressionFormat::DeflateRaw:
return TRY(Compress::DeflateDecompressor::construct(make<LittleEndianInputBitStream>(move(input_stream))));

View file

@ -27,6 +27,25 @@ TEST_CASE(zlib_decompress_simple)
EXPECT(decompressed.bytes() == (ReadonlyBytes { uncompressed, sizeof(uncompressed) - 1 }));
}
TEST_CASE(zlib_decompress_stream)
{
Array<u8, 40> const compressed {
0x78, 0x01, 0x01, 0x1D, 0x00, 0xE2, 0xFF, 0x54, 0x68, 0x69, 0x73, 0x20,
0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x20,
0x74, 0x65, 0x78, 0x74, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x20, 0x3A, 0x29,
0x99, 0x5E, 0x09, 0xE8
};
u8 const uncompressed[] = "This is a simple text file :)";
auto stream = make<AllocatingMemoryStream>();
auto input = MaybeOwned<Stream> { *stream };
auto decompressor = TRY_OR_FAIL(Compress::ZlibDecompressor::create(move(input)));
TRY_OR_FAIL(stream->write_until_depleted(compressed));
auto decompressed = TRY_OR_FAIL(decompressor->read_until_eof());
EXPECT(decompressed.bytes() == (ReadonlyBytes { uncompressed, sizeof(uncompressed) - 1 }));
}
TEST_CASE(zlib_compress_simple)
{
// Note: This is just the output of our compression function from an arbitrary point in time.

View file

@ -1,2 +1,3 @@
format=deflate: Well hello friends!
format=deflate-raw: Well hello friends!
format=gzip: Well hello friends!

View file

@ -2,8 +2,7 @@
<script>
asyncTest(async done => {
const data = [
// FIXME: Enable this case when our DecompressionStream is functioning with the zlib format.
// { format: "deflate", text: "eJwLT83JUchIzcnJV0grykzNSylWBABGEQb1" },
{ format: "deflate", text: "eJwLT83JUchIzcnJV0grykzNSylWBABGEQb1" },
{ format: "deflate-raw", text: "C0/NyVHISM3JyVdIK8pMzUspVgQA" },
{ format: "gzip", text: "H4sIAAAAAAADAwtPzclRyEjNyclXSCvKTM1LKVYEAHN0w4sTAAAA" },
];