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) 2024, Tim Flynn <trflynn89@ladybird.org>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
* *
* SPDX-License-Identifier: BSD-2-Clause * 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 }; 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. // 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())) if (auto buffer = WebIDL::underlying_buffer_source(chunk.as_object()))
return compress(buffer->buffer(), Finish::No); return compress(buffer->buffer(), Finish::No);
return ByteBuffer {}; 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()) auto buffer = maybe_buffer.release_value();
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress chunk: {}", buffer.error())) };
// 3. If buffer is empty, return. // 3. If buffer is empty, return.
if (buffer.value().is_empty()) if (buffer.is_empty())
return {}; return {};
// 4. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays. // 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); auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 5. For each Uint8Array array, enqueue array in cs's transform. // 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(); 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. // 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()) auto buffer = maybe_buffer.release_value();
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress flush: {}", buffer.error())) };
// 2. If buffer is empty, return. // 2. If buffer is empty, return.
if (buffer.value().is_empty()) if (buffer.is_empty())
return {}; return {};
// 3. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays. // 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); auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 4. For each Uint8Array array, enqueue array in cs's transform. // 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) 2024, Tim Flynn <trflynn89@ladybird.org>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
* *
* SPDX-License-Identifier: BSD-2-Clause * 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, // 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. // then throw a TypeError.
auto buffer = [&]() -> ErrorOr<ByteBuffer> { auto maybe_buffer = [&]() -> ErrorOr<ByteBuffer> {
if (auto buffer = WebIDL::underlying_buffer_source(chunk.as_object())) auto chunk_buffer = WebIDL::underlying_buffer_source(chunk.as_object());
return decompress(buffer->buffer()); if (!chunk_buffer)
return ByteBuffer {}; return ByteBuffer {};
}();
if (buffer.is_error()) TRY(m_input_stream->write_until_depleted(chunk_buffer->buffer()));
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to decompress chunk: {}", buffer.error())) };
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. // 3. If buffer is empty, return.
if (buffer.value().is_empty()) if (buffer.is_empty())
return {}; return {};
// 4. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays. // 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); auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 5. For each Uint8Array array, enqueue array in ds's transform. // 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(); 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. // 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()) auto buffer = maybe_buffer.release_value();
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unable to compress flush: {}", buffer.error())) };
// 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. // 3. If buffer is empty, return.
if (buffer.value().is_empty()) if (buffer.is_empty())
return {}; return {};
// 4. Split buffer into one or more non-empty pieces and convert them into Uint8Arrays. // 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); auto array = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
// 5. For each Uint8Array array, enqueue array in ds's transform. // 5. For each Uint8Array array, enqueue array in ds's transform.
@ -163,13 +178,4 @@ WebIDL::ExceptionOr<void> DecompressionStream::decompress_flush_and_enqueue()
return {}; 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_and_enqueue_chunk(JS::Value);
WebIDL::ExceptionOr<void> decompress_flush_and_enqueue(); WebIDL::ExceptionOr<void> decompress_flush_and_enqueue();
ErrorOr<ByteBuffer> decompress(ReadonlyBytes);
Decompressor m_decompressor; Decompressor m_decompressor;
NonnullOwnPtr<AllocatingMemoryStream> m_input_stream; NonnullOwnPtr<AllocatingMemoryStream> m_input_stream;
}; };

View file

@ -1,50 +1,50 @@
Harness status: Error Harness status: OK
Found 45 tests Found 45 tests
45 Fail 45 Pass
Fail decompressing splitted chunk into pieces of size 1 should work in deflate Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass 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 Pass decompressing splitted chunk into pieces of size 15 should work in deflate-raw