blob: 2703e943d48c6e11d2ff1233442bdff0314e7431 [file] [log] [blame]
#ifndef HB_ZLIB_CC
#define HB_ZLIB_CC
#include "hb.hh"
#include "hb-zlib.hh"
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
hb_blob_t *
hb_blob_decompress_gzip (hb_blob_t *blob,
unsigned max_output_len)
{
#ifndef HAVE_ZLIB
return nullptr;
#else
unsigned compressed_len = 0;
const uint8_t *compressed = (const uint8_t *) hb_blob_get_data (blob, &compressed_len);
if (!compressed || !compressed_len)
return nullptr;
z_stream stream = {};
stream.next_in = (Bytef *) compressed;
stream.avail_in = compressed_len;
if (inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK)
return nullptr;
uint32_t expected_size = 0;
hb_gzip_get_uncompressed_size ((const char *) compressed,
compressed_len,
&expected_size);
size_t allocated = hb_min ((size_t) hb_max (expected_size, 4096u),
(size_t) max_output_len);
char *output = (char *) hb_malloc (allocated);
if (!output)
{
inflateEnd (&stream);
return nullptr;
}
int status = Z_OK;
while (true)
{
size_t produced = (size_t) stream.total_out;
if (unlikely (produced >= (size_t) max_output_len))
goto fail;
if (produced == allocated)
{
size_t new_allocated = hb_min (allocated * 2, (size_t) max_output_len);
if (unlikely (new_allocated <= allocated))
goto fail;
char *new_output = (char *) hb_realloc (output, new_allocated);
if (unlikely (!new_output))
goto fail;
output = new_output;
allocated = new_allocated;
}
stream.next_out = (Bytef *) output + stream.total_out;
stream.avail_out = (uInt) (allocated - (size_t) stream.total_out);
status = inflate (&stream, Z_FINISH);
if (status == Z_STREAM_END)
break;
if ((status == Z_OK || status == Z_BUF_ERROR) && stream.avail_out == 0)
continue;
goto fail;
}
inflateEnd (&stream);
if (unlikely ((size_t) stream.total_out > (size_t) max_output_len))
goto fail_free;
return hb_blob_create_or_fail (output,
(unsigned) stream.total_out,
HB_MEMORY_MODE_WRITABLE,
output,
hb_free);
fail:
inflateEnd (&stream);
fail_free:
hb_free (output);
return nullptr;
#endif
}
#endif /* HB_ZLIB_CC */