#include "config.h"
#include "Gunzip.h"
#include <compression.h>
namespace PAL {
Vector<LChar> gunzip(const unsigned char* data, size_t length)
{
Vector<LChar> result;
auto checks = [&]() {
return length >= 10 && data[0] == 0x1f && data[1] == 0x8b && data[2] == 0x8 && data[3] == 0x0;
};
ASSERT(checks());
if (!checks())
return { };
constexpr auto ignoredByteCount = 10;
compression_stream stream;
auto status = compression_stream_init(&stream, COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB);
ASSERT(status == COMPRESSION_STATUS_OK);
if (status != COMPRESSION_STATUS_OK)
return { };
stream.dst_ptr = result.data();
stream.dst_size = result.size();
stream.src_ptr = data + ignoredByteCount;
stream.src_size = length - ignoredByteCount;
size_t offset = 0;
do {
uint8_t* originalDestinationPointer = stream.dst_ptr;
status = compression_stream_process(&stream, COMPRESSION_STREAM_FINALIZE);
uint8_t* newDestinationPointer = stream.dst_ptr;
offset += newDestinationPointer - originalDestinationPointer;
switch (status) {
case COMPRESSION_STATUS_OK: {
auto newSize = offset * 1.5 + 1;
if (newSize > result.size())
result.grow(newSize);
stream.dst_ptr = result.data() + offset;
stream.dst_size = result.size() - offset;
break;
}
case COMPRESSION_STATUS_END:
status = compression_stream_destroy(&stream);
ASSERT(status == COMPRESSION_STATUS_OK);
if (status == COMPRESSION_STATUS_OK) {
result.shrink(stream.dst_ptr - result.data());
return result;
}
return { };
case COMPRESSION_STATUS_ERROR:
default:
ASSERT_NOT_REACHED();
compression_stream_destroy(&stream);
return { };
}
} while (true);
}
}