diff --git a/Libraries/LibCompress/Gzip.cpp b/Libraries/LibCompress/Gzip.cpp index 884b31a60c4..dd0a20924cd 100644 --- a/Libraries/LibCompress/Gzip.cpp +++ b/Libraries/LibCompress/Gzip.cpp @@ -189,8 +189,25 @@ ErrorOr GzipDecompressor::write_some(ReadonlyBytes) return Error::from_errno(EBADF); } -GzipCompressor::GzipCompressor(MaybeOwned stream) - : m_output_stream(move(stream)) +ErrorOr> GzipCompressor::create(MaybeOwned output_stream) +{ + BlockHeader header; + header.identification_1 = 0x1f; + header.identification_2 = 0x8b; + header.compression_method = 0x08; + header.flags = 0; + header.modification_time = 0; + header.extra_flags = 3; // DEFLATE sets 2 for maximum compression and 4 for minimum compression + header.operating_system = 3; // unix + TRY(output_stream->write_until_depleted({ &header, sizeof(header) })); + + auto deflate_compressor = TRY(DeflateCompressor::construct(MaybeOwned(*output_stream))); + return adopt_own(*new GzipCompressor { move(output_stream), move(deflate_compressor) }); +} + +GzipCompressor::GzipCompressor(MaybeOwned output_stream, NonnullOwnPtr deflate_compressor) + : m_output_stream(move(output_stream)) + , m_deflate_compressor(move(deflate_compressor)) { } @@ -201,25 +218,27 @@ ErrorOr GzipCompressor::read_some(Bytes) ErrorOr GzipCompressor::write_some(ReadonlyBytes bytes) { - BlockHeader header; - header.identification_1 = 0x1f; - header.identification_2 = 0x8b; - header.compression_method = 0x08; - header.flags = 0; - header.modification_time = 0; - header.extra_flags = 3; // DEFLATE sets 2 for maximum compression and 4 for minimum compression - header.operating_system = 3; // unix - TRY(m_output_stream->write_until_depleted({ &header, sizeof(header) })); - auto compressed_stream = TRY(DeflateCompressor::construct(MaybeOwned(*m_output_stream))); - TRY(compressed_stream->write_until_depleted(bytes)); - TRY(compressed_stream->final_flush()); - Crypto::Checksum::CRC32 crc32; - crc32.update(bytes); - TRY(m_output_stream->write_value>(crc32.digest())); - TRY(m_output_stream->write_value>(bytes.size())); + VERIFY(!m_finished); + + TRY(m_deflate_compressor->write_until_depleted(bytes)); + m_total_bytes += bytes.size(); + m_crc32.update(bytes); + return bytes.size(); } +ErrorOr GzipCompressor::finish() +{ + VERIFY(!m_finished); + m_finished = true; + + TRY(m_deflate_compressor->final_flush()); + TRY(m_output_stream->write_value>(m_crc32.digest())); + TRY(m_output_stream->write_value>(m_total_bytes)); + + return {}; +} + bool GzipCompressor::is_eof() const { return true; @@ -237,12 +256,14 @@ void GzipCompressor::close() ErrorOr GzipCompressor::compress_all(ReadonlyBytes bytes) { auto output_stream = TRY(try_make()); - GzipCompressor gzip_stream { MaybeOwned(*output_stream) }; + auto gzip_stream = TRY(GzipCompressor::create(MaybeOwned { *output_stream })); - TRY(gzip_stream.write_until_depleted(bytes)); + TRY(gzip_stream->write_until_depleted(bytes)); + TRY(gzip_stream->finish()); auto buffer = TRY(ByteBuffer::create_uninitialized(output_stream->used_buffer_size())); TRY(output_stream->read_until_filled(buffer.bytes())); + return buffer; } diff --git a/Libraries/LibCompress/Gzip.h b/Libraries/LibCompress/Gzip.h index 0b1fa9160b5..736a077fec8 100644 --- a/Libraries/LibCompress/Gzip.h +++ b/Libraries/LibCompress/Gzip.h @@ -49,7 +49,7 @@ public: virtual ErrorOr write_some(ReadonlyBytes) override; virtual bool is_eof() const override; virtual bool is_open() const override { return true; } - virtual void close() override {}; + virtual void close() override { } static ErrorOr decompress_all(ReadonlyBytes); @@ -83,7 +83,7 @@ private: class GzipCompressor final : public Stream { public: - GzipCompressor(MaybeOwned); + static ErrorOr> create(MaybeOwned); virtual ErrorOr read_some(Bytes) override; virtual ErrorOr write_some(ReadonlyBytes) override; @@ -93,8 +93,17 @@ public: static ErrorOr compress_all(ReadonlyBytes bytes); + ErrorOr finish(); + private: + GzipCompressor(MaybeOwned, NonnullOwnPtr); + MaybeOwned m_output_stream; + NonnullOwnPtr m_deflate_compressor; + + Crypto::Checksum::CRC32 m_crc32; + size_t m_total_bytes { 0 }; + bool m_finished { false }; }; }