mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 07:24:58 +00:00

Similar to POSIX read, the basic read and write functions of AK::Stream do not have a lower limit of how much data they read or write (apart from "none at all"). Rename the functions to "read some [data]" and "write some [data]" (with "data" being omitted, since everything here is reading and writing data) to make them sufficiently distinct from the functions that ensure to use the entire buffer (which should be the go-to function for most usages). No functional changes, just a lot of new FIXMEs.
181 lines
5 KiB
C++
181 lines
5 KiB
C++
/*
|
|
* Copyright (c) 2020, the SerenityOS developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/IntegralMath.h>
|
|
#include <AK/MemoryStream.h>
|
|
#include <AK/Span.h>
|
|
#include <AK/TypeCasts.h>
|
|
#include <AK/Types.h>
|
|
#include <LibCompress/Deflate.h>
|
|
#include <LibCompress/Zlib.h>
|
|
|
|
namespace Compress {
|
|
|
|
constexpr static size_t Adler32Size = sizeof(u32);
|
|
|
|
Optional<ZlibDecompressor> ZlibDecompressor::try_create(ReadonlyBytes data)
|
|
{
|
|
if (data.size() < (sizeof(ZlibHeader) + Adler32Size))
|
|
return {};
|
|
|
|
ZlibHeader header { .as_u16 = data.at(0) << 8 | data.at(1) };
|
|
|
|
if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7)
|
|
return {}; // non-deflate compression
|
|
|
|
if (header.present_dictionary)
|
|
return {}; // we dont support pre-defined dictionaries
|
|
|
|
if (header.as_u16 % 31 != 0)
|
|
return {}; // error correction code doesn't match
|
|
|
|
ZlibDecompressor zlib { header, data };
|
|
zlib.m_data_bytes = data.slice(2, data.size() - sizeof(ZlibHeader) - Adler32Size);
|
|
return zlib;
|
|
}
|
|
|
|
ZlibDecompressor::ZlibDecompressor(ZlibHeader header, ReadonlyBytes data)
|
|
: m_header(header)
|
|
, m_input_data(data)
|
|
{
|
|
}
|
|
|
|
Optional<ByteBuffer> ZlibDecompressor::decompress()
|
|
{
|
|
auto buffer_or_error = DeflateDecompressor::decompress_all(m_data_bytes);
|
|
if (buffer_or_error.is_error())
|
|
return {};
|
|
return buffer_or_error.release_value();
|
|
}
|
|
|
|
Optional<ByteBuffer> ZlibDecompressor::decompress_all(ReadonlyBytes bytes)
|
|
{
|
|
auto zlib = try_create(bytes);
|
|
if (!zlib.has_value())
|
|
return {};
|
|
return zlib->decompress();
|
|
}
|
|
|
|
u32 ZlibDecompressor::checksum()
|
|
{
|
|
if (!m_checksum) {
|
|
auto bytes = m_input_data.slice_from_end(Adler32Size);
|
|
m_checksum = bytes.at(0) << 24 | bytes.at(1) << 16 | bytes.at(2) << 8 || bytes.at(3);
|
|
}
|
|
|
|
return m_checksum;
|
|
}
|
|
|
|
ErrorOr<NonnullOwnPtr<ZlibCompressor>> ZlibCompressor::construct(MaybeOwned<Stream> stream, ZlibCompressionLevel compression_level)
|
|
{
|
|
// Zlib only defines Deflate as a compression method.
|
|
auto compression_method = ZlibCompressionMethod::Deflate;
|
|
|
|
// FIXME: Find a way to compress with Deflate's "Best" compression level.
|
|
auto compressor_stream = TRY(DeflateCompressor::construct(MaybeOwned(*stream), static_cast<DeflateCompressor::CompressionLevel>(compression_level)));
|
|
|
|
auto zlib_compressor = TRY(adopt_nonnull_own_or_enomem(new (nothrow) ZlibCompressor(move(stream), move(compressor_stream))));
|
|
TRY(zlib_compressor->write_header(compression_method, compression_level));
|
|
|
|
return zlib_compressor;
|
|
}
|
|
|
|
ZlibCompressor::ZlibCompressor(MaybeOwned<Stream> stream, NonnullOwnPtr<Stream> compressor_stream)
|
|
: m_output_stream(move(stream))
|
|
, m_compressor(move(compressor_stream))
|
|
{
|
|
}
|
|
|
|
ZlibCompressor::~ZlibCompressor()
|
|
{
|
|
VERIFY(m_finished);
|
|
}
|
|
|
|
ErrorOr<void> ZlibCompressor::write_header(ZlibCompressionMethod compression_method, ZlibCompressionLevel compression_level)
|
|
{
|
|
u8 compression_info = 0;
|
|
if (compression_method == ZlibCompressionMethod::Deflate) {
|
|
compression_info = AK::log2(DeflateCompressor::window_size) - 8;
|
|
VERIFY(compression_info <= 7);
|
|
}
|
|
|
|
ZlibHeader header {
|
|
.compression_method = compression_method,
|
|
.compression_info = compression_info,
|
|
.check_bits = 0,
|
|
.present_dictionary = false,
|
|
.compression_level = compression_level,
|
|
};
|
|
header.check_bits = 0b11111 - header.as_u16 % 31;
|
|
|
|
// FIXME: Support pre-defined dictionaries.
|
|
|
|
// FIXME: This should write the entire span.
|
|
TRY(m_output_stream->write_some(header.as_u16.bytes()));
|
|
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<Bytes> ZlibCompressor::read_some(Bytes)
|
|
{
|
|
return Error::from_errno(EBADF);
|
|
}
|
|
|
|
ErrorOr<size_t> ZlibCompressor::write_some(ReadonlyBytes bytes)
|
|
{
|
|
VERIFY(!m_finished);
|
|
|
|
size_t n_written = TRY(m_compressor->write_some(bytes));
|
|
m_adler32_checksum.update(bytes.trim(n_written));
|
|
return n_written;
|
|
}
|
|
|
|
bool ZlibCompressor::is_eof() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool ZlibCompressor::is_open() const
|
|
{
|
|
return m_output_stream->is_open();
|
|
}
|
|
|
|
void ZlibCompressor::close()
|
|
{
|
|
}
|
|
|
|
ErrorOr<void> ZlibCompressor::finish()
|
|
{
|
|
VERIFY(!m_finished);
|
|
|
|
if (is<DeflateCompressor>(m_compressor.ptr()))
|
|
TRY(static_cast<DeflateCompressor*>(m_compressor.ptr())->final_flush());
|
|
|
|
NetworkOrdered<u32> adler_sum = m_adler32_checksum.digest();
|
|
// FIXME: This should write the entire span.
|
|
TRY(m_output_stream->write_some(adler_sum.bytes()));
|
|
|
|
m_finished = true;
|
|
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<ByteBuffer> ZlibCompressor::compress_all(ReadonlyBytes bytes, ZlibCompressionLevel compression_level)
|
|
{
|
|
auto output_stream = TRY(try_make<AllocatingMemoryStream>());
|
|
auto zlib_stream = TRY(ZlibCompressor::construct(MaybeOwned<Stream>(*output_stream), compression_level));
|
|
|
|
TRY(zlib_stream->write_entire_buffer(bytes));
|
|
|
|
TRY(zlib_stream->finish());
|
|
|
|
auto buffer = TRY(ByteBuffer::create_uninitialized(output_stream->used_buffer_size()));
|
|
TRY(output_stream->read_entire_buffer(buffer.bytes()));
|
|
|
|
return buffer;
|
|
}
|
|
|
|
}
|