forked from SerenityOS/serenity
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
07806d1
commit 4fe5503
Showing
3 changed files
with
134 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters