michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/Compression.h" michael@0: michael@0: #include "js/Utility.h" michael@0: michael@0: using namespace js; michael@0: michael@0: #if USE_ZLIB michael@0: static void * michael@0: zlib_alloc(void *cx, uInt items, uInt size) michael@0: { michael@0: return js_calloc(items, size); michael@0: } michael@0: michael@0: static void michael@0: zlib_free(void *cx, void *addr) michael@0: { michael@0: js_free(addr); michael@0: } michael@0: michael@0: Compressor::Compressor(const unsigned char *inp, size_t inplen) michael@0: : inp(inp), michael@0: inplen(inplen), michael@0: outbytes(0), michael@0: initialized(false) michael@0: { michael@0: JS_ASSERT(inplen > 0); michael@0: zs.opaque = nullptr; michael@0: zs.next_in = (Bytef *)inp; michael@0: zs.avail_in = 0; michael@0: zs.next_out = nullptr; michael@0: zs.avail_out = 0; michael@0: zs.zalloc = zlib_alloc; michael@0: zs.zfree = zlib_free; michael@0: } michael@0: michael@0: michael@0: Compressor::~Compressor() michael@0: { michael@0: if (initialized) { michael@0: int ret = deflateEnd(&zs); michael@0: if (ret != Z_OK) { michael@0: // If we finished early, we can get a Z_DATA_ERROR. michael@0: JS_ASSERT(ret == Z_DATA_ERROR); michael@0: JS_ASSERT(uInt(zs.next_in - inp) < inplen || !zs.avail_out); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: Compressor::init() michael@0: { michael@0: if (inplen >= UINT32_MAX) michael@0: return false; michael@0: // zlib is slow and we'd rather be done compression sooner michael@0: // even if it means decompression is slower which penalizes michael@0: // Function.toString() michael@0: int ret = deflateInit(&zs, Z_BEST_SPEED); michael@0: if (ret != Z_OK) { michael@0: JS_ASSERT(ret == Z_MEM_ERROR); michael@0: return false; michael@0: } michael@0: initialized = true; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: Compressor::setOutput(unsigned char *out, size_t outlen) michael@0: { michael@0: JS_ASSERT(outlen > outbytes); michael@0: zs.next_out = out + outbytes; michael@0: zs.avail_out = outlen - outbytes; michael@0: } michael@0: michael@0: Compressor::Status michael@0: Compressor::compressMore() michael@0: { michael@0: JS_ASSERT(zs.next_out); michael@0: uInt left = inplen - (zs.next_in - inp); michael@0: bool done = left <= CHUNKSIZE; michael@0: if (done) michael@0: zs.avail_in = left; michael@0: else if (zs.avail_in == 0) michael@0: zs.avail_in = CHUNKSIZE; michael@0: Bytef *oldout = zs.next_out; michael@0: int ret = deflate(&zs, done ? Z_FINISH : Z_NO_FLUSH); michael@0: outbytes += zs.next_out - oldout; michael@0: if (ret == Z_MEM_ERROR) { michael@0: zs.avail_out = 0; michael@0: return OOM; michael@0: } michael@0: if (ret == Z_BUF_ERROR || (done && ret == Z_OK)) { michael@0: JS_ASSERT(zs.avail_out == 0); michael@0: return MOREOUTPUT; michael@0: } michael@0: JS_ASSERT_IF(!done, ret == Z_OK); michael@0: JS_ASSERT_IF(done, ret == Z_STREAM_END); michael@0: return done ? DONE : CONTINUE; michael@0: } michael@0: michael@0: bool michael@0: js::DecompressString(const unsigned char *inp, size_t inplen, unsigned char *out, size_t outlen) michael@0: { michael@0: JS_ASSERT(inplen <= UINT32_MAX); michael@0: z_stream zs; michael@0: zs.zalloc = zlib_alloc; michael@0: zs.zfree = zlib_free; michael@0: zs.opaque = nullptr; michael@0: zs.next_in = (Bytef *)inp; michael@0: zs.avail_in = inplen; michael@0: zs.next_out = out; michael@0: JS_ASSERT(outlen); michael@0: zs.avail_out = outlen; michael@0: int ret = inflateInit(&zs); michael@0: if (ret != Z_OK) { michael@0: JS_ASSERT(ret == Z_MEM_ERROR); michael@0: return false; michael@0: } michael@0: ret = inflate(&zs, Z_FINISH); michael@0: JS_ASSERT(ret == Z_STREAM_END); michael@0: ret = inflateEnd(&zs); michael@0: JS_ASSERT(ret == Z_OK); michael@0: return true; michael@0: } michael@0: #endif /* USE_ZLIB */ michael@0: