mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 01:41:59 -05:00
LibCompress: Implement the XZ delta filter
This commit is contained in:
parent
f0b08e9dea
commit
786e654dfd
3 changed files with 102 additions and 3 deletions
|
@ -1183,8 +1183,8 @@ TEST_CASE(xz_utils_good_1_3delta_lzma2)
|
|||
|
||||
auto stream = MUST(try_make<FixedMemoryStream>(compressed));
|
||||
auto decompressor = MUST(Compress::XzDecompressor::create(move(stream)));
|
||||
// TODO: This uses the currently unimplemented delta filter.
|
||||
(void)decompressor->read_until_eof(PAGE_SIZE);
|
||||
auto buffer = TRY_OR_FAIL(decompressor->read_until_eof(PAGE_SIZE));
|
||||
EXPECT_EQ(buffer.span(), xz_utils_lorem_ipsum.bytes());
|
||||
}
|
||||
|
||||
TEST_CASE(xz_utils_good_1_arm64_lzma2_1)
|
||||
|
@ -1864,7 +1864,9 @@ TEST_CASE(xz_utils_unsupported_filter_flags_2)
|
|||
auto stream = MUST(try_make<FixedMemoryStream>(compressed));
|
||||
auto decompressor = MUST(Compress::XzDecompressor::create(move(stream)));
|
||||
|
||||
// TODO: The delta filter is currently unimplemented. However, once it is implemented, we likely want to mirror the recommended behavior of the specification anyways.
|
||||
// TODO: We don't yet check whether the filter chain satisfies the "can't be the last filter"
|
||||
// requirement. We just happen to get the result right because we try to uncompress the
|
||||
// test case and fail.
|
||||
auto buffer_or_error = decompressor->read_until_eof(PAGE_SIZE);
|
||||
EXPECT(buffer_or_error.is_error());
|
||||
}
|
||||
|
|
|
@ -161,6 +161,66 @@ u32 XzFilterLzma2Properties::dictionary_size() const
|
|||
return dictionary_size;
|
||||
}
|
||||
|
||||
u32 XzFilterDeltaProperties::distance() const
|
||||
{
|
||||
// "The Properties byte indicates the delta distance, which can be
|
||||
// 1-256 bytes backwards from the current byte: 0x00 indicates
|
||||
// distance of 1 byte and 0xFF distance of 256 bytes."
|
||||
return encoded_distance + 1;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<XzFilterDelta>> XzFilterDelta::create(MaybeOwned<Stream> stream, u32 distance)
|
||||
{
|
||||
auto buffer = TRY(CircularBuffer::create_empty(distance));
|
||||
auto filter = TRY(adopt_nonnull_own_or_enomem(new (nothrow) XzFilterDelta(move(stream), move(buffer))));
|
||||
return filter;
|
||||
}
|
||||
|
||||
XzFilterDelta::XzFilterDelta(MaybeOwned<Stream> stream, CircularBuffer buffer)
|
||||
: m_stream(move(stream))
|
||||
, m_buffer(move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<Bytes> XzFilterDelta::read_some(Bytes bytes)
|
||||
{
|
||||
bytes = TRY(m_stream->read_some(bytes));
|
||||
|
||||
auto distance = m_buffer.capacity();
|
||||
|
||||
for (auto& byte : bytes) {
|
||||
if (m_buffer.seekback_limit() >= distance) {
|
||||
u8 byte_at_distance { 0 };
|
||||
MUST(m_buffer.read_with_seekback({ &byte_at_distance, 1 }, distance));
|
||||
byte = byte_at_distance + byte;
|
||||
}
|
||||
|
||||
m_buffer.write({ &byte, 1 });
|
||||
MUST(m_buffer.discard(1));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
ErrorOr<size_t> XzFilterDelta::write_some(ReadonlyBytes)
|
||||
{
|
||||
return EBADF;
|
||||
}
|
||||
|
||||
bool XzFilterDelta::is_eof() const
|
||||
{
|
||||
return m_stream->is_eof();
|
||||
}
|
||||
|
||||
bool XzFilterDelta::is_open() const
|
||||
{
|
||||
return m_stream->is_open();
|
||||
}
|
||||
|
||||
void XzFilterDelta::close()
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<XzDecompressor>> XzDecompressor::create(MaybeOwned<Stream> stream)
|
||||
{
|
||||
auto counting_stream = TRY(try_make<CountingStream>(move(stream)));
|
||||
|
@ -353,6 +413,17 @@ ErrorOr<void> XzDecompressor::load_next_block(u8 encoded_block_header_size)
|
|||
continue;
|
||||
}
|
||||
|
||||
// 5.3.3. Delta
|
||||
if (filter.id == 0x03) {
|
||||
if (filter.properties.size() < sizeof(XzFilterDeltaProperties))
|
||||
return Error::from_string_literal("XZ Delta filter has a smaller-than-needed properties size");
|
||||
|
||||
auto const* properties = reinterpret_cast<XzFilterDeltaProperties*>(filter.properties.data());
|
||||
|
||||
new_block_stream = TRY(XzFilterDelta::create(move(new_block_stream), properties->distance()));
|
||||
continue;
|
||||
}
|
||||
|
||||
return Error::from_string_literal("XZ block header contains unknown filter ID");
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/CircularBuffer.h>
|
||||
#include <AK/ConstrainedStream.h>
|
||||
#include <AK/CountingStream.h>
|
||||
#include <AK/Endian.h>
|
||||
|
@ -98,6 +99,31 @@ struct [[gnu::packed]] XzFilterLzma2Properties {
|
|||
};
|
||||
static_assert(sizeof(XzFilterLzma2Properties) == 1);
|
||||
|
||||
// 5.3.3. Delta
|
||||
struct [[gnu::packed]] XzFilterDeltaProperties {
|
||||
u8 encoded_distance;
|
||||
|
||||
u32 distance() const;
|
||||
};
|
||||
static_assert(sizeof(XzFilterDeltaProperties) == 1);
|
||||
|
||||
class XzFilterDelta : public Stream {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<XzFilterDelta>> create(MaybeOwned<Stream>, u32 distance);
|
||||
|
||||
virtual ErrorOr<Bytes> read_some(Bytes) override;
|
||||
virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
|
||||
virtual bool is_eof() const override;
|
||||
virtual bool is_open() const override;
|
||||
virtual void close() override;
|
||||
|
||||
private:
|
||||
XzFilterDelta(MaybeOwned<Stream>, CircularBuffer);
|
||||
|
||||
MaybeOwned<Stream> m_stream;
|
||||
CircularBuffer m_buffer;
|
||||
};
|
||||
|
||||
class XzDecompressor : public Stream {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<XzDecompressor>> create(MaybeOwned<Stream>);
|
||||
|
|
Loading…
Reference in a new issue