diff options
author | Marcel Schneider <marcelschneider5@outlook.de> | 2019-11-09 18:29:50 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-11-10 12:52:23 +0100 |
commit | 4fe5503b17d5cf8a99d69e8ad95a82e67c975d86 (patch) | |
tree | fcd0ff1222fa10e6486fa14bcd2ffc0bd72f34cd /Libraries/LibCore | |
parent | 07806d12732e45b8e42350307e25fcb36c2f0f52 (diff) | |
download | serenity-4fe5503b17d5cf8a99d69e8ad95a82e67c975d86.zip |
LibCore: Add a gzip implementation
Diffstat (limited to 'Libraries/LibCore')
-rw-r--r-- | Libraries/LibCore/CGzip.cpp | 123 | ||||
-rw-r--r-- | Libraries/LibCore/CGzip.h | 9 | ||||
-rw-r--r-- | Libraries/LibCore/Makefile | 3 |
3 files changed, 134 insertions, 1 deletions
diff --git a/Libraries/LibCore/CGzip.cpp b/Libraries/LibCore/CGzip.cpp new file mode 100644 index 0000000000..b0dfcb35d6 --- /dev/null +++ b/Libraries/LibCore/CGzip.cpp @@ -0,0 +1,123 @@ +#include "CGzip.h" + +#include <AK/ByteBuffer.h> +#include <AK/Optional.h> +#include <limits.h> +#include <stddef.h> + +#include <LibDraw/puff.h> + +bool Gzip::is_compressed(const ByteBuffer& data) +{ + return data.size() > 2 && data[0] == 0x1F && data[1] == 0x8b; +} + +// skips the gzip header +// see: https://tools.ietf.org/html/rfc1952#page-5 +static Optional<ByteBuffer> get_gzip_payload(const ByteBuffer& data) +{ + int current = 0; + auto read_byte = [&]() { + if (current >= data.size()) { + ASSERT_NOT_REACHED(); + return (u8)0; + } + // dbg() << "read_byte: " << String::format("%x", data[current]); + return data[current++]; + }; + + dbg() << "get_gzip_payload: Skipping over gzip header."; + + // Magic Header + if (read_byte() != 0x1F || read_byte() != 0x8B) { + dbg() << "get_gzip_payload: Wrong magic number."; + return Optional<ByteBuffer>(); + } + + // Compression method + auto method = read_byte(); + if (method != 8) { + dbg() << "get_gzip_payload: Wrong compression method = " << method; + return Optional<ByteBuffer>(); + } + + u8 flags = read_byte(); + + // Timestamp, Extra flags, OS + current += 6; + + // FEXTRA + if (flags & 4) { + u16 length = read_byte() & read_byte() << 8; + dbg() << "get_gzip_payload: Header has FEXTRA flag set. Length = " << length; + current += length; + } + + // FNAME + if (flags & 8) { + dbg() << "get_gzip_payload: Header has FNAME flag set."; + while (read_byte() != '\0') + ; + } + + // FCOMMENT + if (flags & 16) { + dbg() << "get_gzip_payload: Header has FCOMMENT flag set."; + while (read_byte() != '\0') + ; + } + + // FHCRC + if (flags & 2) { + dbg() << "get_gzip_payload: Header has FHCRC flag set."; + current += 2; + } + + auto new_size = data.size() - current; + dbg() << "get_gzip_payload: Returning slice from " << current << " with size " << new_size; + return data.slice(current, new_size); +} + +Optional<ByteBuffer> Gzip::decompress(const ByteBuffer& data) +{ + ASSERT(is_compressed(data)); + + dbg() << "Gzip::decompress: Decompressing gzip compressed data. Size = " << data.size(); + auto optional_payload = get_gzip_payload(data); + if (!optional_payload.has_value()) { + return Optional<ByteBuffer>(); + } + + auto source = optional_payload.value(); + unsigned long source_len = source.size(); + auto destination = ByteBuffer::create_uninitialized(1024); + while (true) { + unsigned long destination_len = destination.size(); + // FIXME: dbg() cannot take ulong? + // dbg() << "Gzip::decompress: Calling puff()\n" + // << " destination_data = " << destination.data() << "\n" + // << " destination_len = " << (int)destination_len << "\n" + // << " source_data = " << source.data() << "\n" + // << " source_len = " << (int)source_len; + + auto puff_ret = puff( + destination.data(), &destination_len, + source.data(), &source_len); + + if (puff_ret == 0) { + dbg() << "Gzip::decompress: Decompression success."; + break; + } + + if (puff_ret == 1) { + // FIXME: Find a better way of decompressing without needing to try over and over again. + dbg() << "Gzip::decompress: Output buffer exhausted. Growing."; + destination.grow(destination.size() * 2); + } else { + dbg() << "Gzip::decompress: Error. puff() returned: " << puff_ret; + ASSERT_NOT_REACHED(); + } + } + + return destination; +}
\ No newline at end of file diff --git a/Libraries/LibCore/CGzip.h b/Libraries/LibCore/CGzip.h new file mode 100644 index 0000000000..020edf90b2 --- /dev/null +++ b/Libraries/LibCore/CGzip.h @@ -0,0 +1,9 @@ +#include <AK/ByteBuffer.h> +#include <AK/Optional.h> +#include <AK/String.h> + +class Gzip { +public: + static bool is_compressed(const ByteBuffer& data); + static Optional<ByteBuffer> decompress(const ByteBuffer& data); +};
\ No newline at end of file diff --git a/Libraries/LibCore/Makefile b/Libraries/LibCore/Makefile index a4a641bf3e..c9f25fadef 100644 --- a/Libraries/LibCore/Makefile +++ b/Libraries/LibCore/Makefile @@ -23,7 +23,8 @@ OBJS = \ CEvent.o \ CProcessStatisticsReader.o \ CDirIterator.o \ - CUserInfo.o + CUserInfo.o \ + CGzip.o LIBRARY = libcore.a DEFINES += -DUSERLAND |