LibWeb: Refactor {Dec,C}ompressionStream to handle split chunks

This commit is contained in:
devgianlu 2025-03-01 17:57:52 +01:00 committed by Jelle Raaijmakers
commit e02521911a
Notes: github-actions[bot] 2025-03-19 12:48:02 +00:00
4 changed files with 90 additions and 83 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -113,21 +114,22 @@ WebIDL::ExceptionOr<void> CompressionStream::compress_and_enqueue_chunk(JS::Valu
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Chunk is not a BufferSource type"sv };
// 2. Let buffer be the result of compressing chunk with cs's format and context.
auto buffer = [&]() -> ErrorOr<ByteBuffer> {
auto maybe_buffer = [&]() -> ErrorOr<ByteBuffer> {
if (auto buffer = WebIDL::underlying_buffer_source(chunk.as_object()))
return compress(buffer->buffer(), Finish::No);
return ByteBuffer {};
}();
if (maybe_buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress chunk: {}", maybe_buffer.error())) };
if (buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress chunk: {}", buffer.error())) };
auto buffer = maybe_buffer.release_value();
// 3. If buffer is empty, return.
if (buffer.value().is_empty())
if (buffer.is_empty())
return {};
// 4. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays.
auto array_buffer = JS::ArrayBuffer::create(realm, buffer.release_value());
auto array_buffer = JS::ArrayBuffer::create(realm, move(buffer));
auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 5. For each Uint8Array array, enqueue array in cs's transform.
@ -141,17 +143,18 @@ WebIDL::ExceptionOr<void> CompressionStream::compress_flush_and_enqueue()
auto& realm = this->realm();
// 1. Let buffer be the result of compressing an empty input with cs's format and context, with the finish flag.
auto buffer = compress({}, Finish::Yes);
auto maybe_buffer = compress({}, Finish::Yes);
if (maybe_buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress flush: {}", maybe_buffer.error())) };
if (buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress flush: {}", buffer.error())) };
auto buffer = maybe_buffer.release_value();
// 2. If buffer is empty, return.
if (buffer.value().is_empty())
if (buffer.is_empty())
return {};
// 3. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays.
auto array_buffer = JS::ArrayBuffer::create(realm, buffer.release_value());
auto array_buffer = JS::ArrayBuffer::create(realm, move(buffer));
auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 4. For each Uint8Array array, enqueue array in cs's transform.

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -115,21 +116,30 @@ WebIDL::ExceptionOr<void> DecompressionStream::decompress_and_enqueue_chunk(JS::
// 2. Let buffer be the result of decompressing chunk with ds's format and context. If this results in an error,
// then throw a TypeError.
auto buffer = [&]() -> ErrorOr<ByteBuffer> {
if (auto buffer = WebIDL::underlying_buffer_source(chunk.as_object()))
return decompress(buffer->buffer());
auto maybe_buffer = [&]() -> ErrorOr<ByteBuffer> {
auto chunk_buffer = WebIDL::underlying_buffer_source(chunk.as_object());
if (!chunk_buffer)
return ByteBuffer {};
}();
if (buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to decompress chunk: {}", buffer.error())) };
TRY(m_input_stream->write_until_depleted(chunk_buffer->buffer()));
auto decompressed = TRY(ByteBuffer::create_uninitialized(4096));
auto size = TRY(m_decompressor.visit([&](auto const& decompressor) -> ErrorOr<size_t> {
return TRY(decompressor->read_some(decompressed.bytes())).size();
}));
return decompressed.slice(0, size);
}();
if (maybe_buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to decompress chunk: {}", maybe_buffer.error())) };
auto buffer = maybe_buffer.release_value();
// 3. If buffer is empty, return.
if (buffer.value().is_empty())
if (buffer.is_empty())
return {};
// 4. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays.
auto array_buffer = JS::ArrayBuffer::create(realm, buffer.release_value());
auto array_buffer = JS::ArrayBuffer::create(realm, move(buffer));
auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 5. For each Uint8Array array, enqueue array in ds's transform.
@ -143,19 +153,24 @@ WebIDL::ExceptionOr<void> DecompressionStream::decompress_flush_and_enqueue()
auto& realm = this->realm();
// 1. Let buffer be the result of decompressing an empty input with ds's format and context, with the finish flag.
auto buffer = decompress({});
auto maybe_buffer = m_decompressor.visit([&](auto const& decompressor) -> ErrorOr<ByteBuffer> {
return TRY(decompressor->read_until_eof());
});
if (maybe_buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress flush: {}", maybe_buffer.error())) };
if (buffer.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress flush: {}", buffer.error())) };
auto buffer = maybe_buffer.release_value();
// FIXME: 2. If the end of the compressed input has not been reached, then throw a TypeError.
// 2. If the end of the compressed input has not been reached, then throw a TypeError.
if (m_decompressor.visit([](auto const& decompressor) { return !decompressor->is_eof(); }))
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "End of compressed input has not been reached"sv };
// 3. If buffer is empty, return.
if (buffer.value().is_empty())
if (buffer.is_empty())
return {};
// 4. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays.
auto array_buffer = JS::ArrayBuffer::create(realm, buffer.release_value());
auto array_buffer = JS::ArrayBuffer::create(realm, move(buffer));
auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 5. For each Uint8Array array, enqueue array in ds's transform.
@ -163,13 +178,4 @@ WebIDL::ExceptionOr<void> DecompressionStream::decompress_flush_and_enqueue()
return {};
}
ErrorOr<ByteBuffer> DecompressionStream::decompress(ReadonlyBytes bytes)
{
TRY(m_input_stream->write_until_depleted(bytes));
return TRY(m_decompressor.visit([&](auto const& decompressor) {
return decompressor->read_until_eof();
}));
}
}

View file

@ -44,8 +44,6 @@ private:
WebIDL::ExceptionOr<void> decompress_and_enqueue_chunk(JS::Value);
WebIDL::ExceptionOr<void> decompress_flush_and_enqueue();
ErrorOr<ByteBuffer> decompress(ReadonlyBytes);
Decompressor m_decompressor;
NonnullOwnPtr<AllocatingMemoryStream> m_input_stream;
};

View file

@ -1,50 +1,50 @@
Harness status: Error
Harness status: OK
Found 45 tests
45 Fail
Fail decompressing splitted chunk into pieces of size 1 should work in deflate
Fail decompressing splitted chunk into pieces of size 1 should work in gzip
Fail decompressing splitted chunk into pieces of size 1 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 2 should work in deflate
Fail decompressing splitted chunk into pieces of size 2 should work in gzip
Fail decompressing splitted chunk into pieces of size 2 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 3 should work in deflate
Fail decompressing splitted chunk into pieces of size 3 should work in gzip
Fail decompressing splitted chunk into pieces of size 3 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 4 should work in deflate
Fail decompressing splitted chunk into pieces of size 4 should work in gzip
Fail decompressing splitted chunk into pieces of size 4 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 5 should work in deflate
Fail decompressing splitted chunk into pieces of size 5 should work in gzip
Fail decompressing splitted chunk into pieces of size 5 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 6 should work in deflate
Fail decompressing splitted chunk into pieces of size 6 should work in gzip
Fail decompressing splitted chunk into pieces of size 6 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 7 should work in deflate
Fail decompressing splitted chunk into pieces of size 7 should work in gzip
Fail decompressing splitted chunk into pieces of size 7 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 8 should work in deflate
Fail decompressing splitted chunk into pieces of size 8 should work in gzip
Fail decompressing splitted chunk into pieces of size 8 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 9 should work in deflate
Fail decompressing splitted chunk into pieces of size 9 should work in gzip
Fail decompressing splitted chunk into pieces of size 9 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 10 should work in deflate
Fail decompressing splitted chunk into pieces of size 10 should work in gzip
Fail decompressing splitted chunk into pieces of size 10 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 11 should work in deflate
Fail decompressing splitted chunk into pieces of size 11 should work in gzip
Fail decompressing splitted chunk into pieces of size 11 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 12 should work in deflate
Fail decompressing splitted chunk into pieces of size 12 should work in gzip
Fail decompressing splitted chunk into pieces of size 12 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 13 should work in deflate
Fail decompressing splitted chunk into pieces of size 13 should work in gzip
Fail decompressing splitted chunk into pieces of size 13 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 14 should work in deflate
Fail decompressing splitted chunk into pieces of size 14 should work in gzip
Fail decompressing splitted chunk into pieces of size 14 should work in deflate-raw
Fail decompressing splitted chunk into pieces of size 15 should work in deflate
Fail decompressing splitted chunk into pieces of size 15 should work in gzip
Fail decompressing splitted chunk into pieces of size 15 should work in deflate-raw
45 Pass
Pass decompressing splitted chunk into pieces of size 1 should work in deflate
Pass decompressing splitted chunk into pieces of size 1 should work in gzip
Pass decompressing splitted chunk into pieces of size 1 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 2 should work in deflate
Pass decompressing splitted chunk into pieces of size 2 should work in gzip
Pass decompressing splitted chunk into pieces of size 2 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 3 should work in deflate
Pass decompressing splitted chunk into pieces of size 3 should work in gzip
Pass decompressing splitted chunk into pieces of size 3 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 4 should work in deflate
Pass decompressing splitted chunk into pieces of size 4 should work in gzip
Pass decompressing splitted chunk into pieces of size 4 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 5 should work in deflate
Pass decompressing splitted chunk into pieces of size 5 should work in gzip
Pass decompressing splitted chunk into pieces of size 5 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 6 should work in deflate
Pass decompressing splitted chunk into pieces of size 6 should work in gzip
Pass decompressing splitted chunk into pieces of size 6 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 7 should work in deflate
Pass decompressing splitted chunk into pieces of size 7 should work in gzip
Pass decompressing splitted chunk into pieces of size 7 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 8 should work in deflate
Pass decompressing splitted chunk into pieces of size 8 should work in gzip
Pass decompressing splitted chunk into pieces of size 8 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 9 should work in deflate
Pass decompressing splitted chunk into pieces of size 9 should work in gzip
Pass decompressing splitted chunk into pieces of size 9 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 10 should work in deflate
Pass decompressing splitted chunk into pieces of size 10 should work in gzip
Pass decompressing splitted chunk into pieces of size 10 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 11 should work in deflate
Pass decompressing splitted chunk into pieces of size 11 should work in gzip
Pass decompressing splitted chunk into pieces of size 11 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 12 should work in deflate
Pass decompressing splitted chunk into pieces of size 12 should work in gzip
Pass decompressing splitted chunk into pieces of size 12 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 13 should work in deflate
Pass decompressing splitted chunk into pieces of size 13 should work in gzip
Pass decompressing splitted chunk into pieces of size 13 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 14 should work in deflate
Pass decompressing splitted chunk into pieces of size 14 should work in gzip
Pass decompressing splitted chunk into pieces of size 14 should work in deflate-raw
Pass decompressing splitted chunk into pieces of size 15 should work in deflate
Pass decompressing splitted chunk into pieces of size 15 should work in gzip
Pass decompressing splitted chunk into pieces of size 15 should work in deflate-raw