/* * librd - Rapid Development C library * * Copyright (c) 2012, Magnus Edenhill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "rd.h" #include "rdgz.h" #include #define RD_GZ_CHUNK 262144 void *rd_gz_decompress (const void *compressed, int compressed_len, uint64_t *decompressed_lenp) { int pass = 1; char *decompressed = NULL; /* First pass (1): calculate decompressed size. * (pass-1 is skipped if *decompressed_lenp is * non-zero). * Second pass (2): perform actual decompression. */ if (*decompressed_lenp != 0LLU) pass++; for (; pass <= 2 ; pass++) { z_stream strm = RD_ZERO_INIT; gz_header hdr; char buf[512]; char *p; int len; int r; if ((r = inflateInit2(&strm, 15+32)) != Z_OK) goto fail; strm.next_in = (void *)compressed; strm.avail_in = compressed_len; if ((r = inflateGetHeader(&strm, &hdr)) != Z_OK) { inflateEnd(&strm); goto fail; } if (pass == 1) { /* Use dummy output buffer */ p = buf; len = sizeof(buf); } else { /* Use real output buffer */ p = decompressed; len = (int)*decompressed_lenp; } do { strm.next_out = (unsigned char *)p; strm.avail_out = len; r = inflate(&strm, Z_NO_FLUSH); switch (r) { case Z_STREAM_ERROR: case Z_NEED_DICT: case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd(&strm); goto fail; } if (pass == 2) { /* Advance output pointer (in pass 2). */ p += len - strm.avail_out; len -= len - strm.avail_out; } } while (strm.avail_out == 0 && r != Z_STREAM_END); if (pass == 1) { *decompressed_lenp = strm.total_out; if (!(decompressed = malloc((size_t)(*decompressed_lenp)+1))) { inflateEnd(&strm); return NULL; } /* For convenience of the caller we nul-terminate * the buffer. If it happens to be a string there * is no need for extra copies. */ decompressed[*decompressed_lenp] = '\0'; } inflateEnd(&strm); } return decompressed; fail: if (decompressed) free(decompressed); return NULL; }