Ejemplo n.º 1
0
*/  REBSER *Compress(REBSER *input, REBINT index, REBINT len, REBFLG use_crc)
/*
**      Compress a binary (only).
**		data
**		/part
**		length
**		/crc32
**
**      Note: If the file length is "small", it can't overrun on
**      compression too much so we use our magic numbers; otherwise,
**      we'll just be safe by a percentage of the file size.  This may
**      be a bit much, though.
**
***********************************************************************/
{
    // NOTE: The use_crc flag is not present in Zlib 1.2.8
    // Instead, compress's fifth paramter is the compression level
    // It can be a value from 1 to 9, or Z_DEFAULT_COMPRESSION if you
    // want it to pick what the library author considers the "worth it"
    // tradeoff of time to generally suggest.

    uLongf size;
    REBSER *output;
    REBINT err;
    REBYTE out_size[sizeof(REBCNT)];

    if (len < 0) Trap_DEAD_END(RE_PAST_END); // !!! better msg needed
    size = len + (len > STERLINGS_MAGIC_NUMBER ? len / 10 + 12 : STERLINGS_MAGIC_FIX);
    output = Make_Binary(size);

    //DISABLE_GC;	// !!! why??
    // dest, dest-len, src, src-len, level
    err = z_compress2(BIN_HEAD(output), &size, BIN_HEAD(input) + index, len, Z_DEFAULT_COMPRESSION);
    if (err) {
        REBVAL arg;
        if (err == Z_MEM_ERROR) Trap_DEAD_END(RE_NO_MEMORY);
        SET_INTEGER(&arg, err);
        Trap1_DEAD_END(RE_BAD_PRESS, &arg); //!!!provide error string descriptions
    }
    SET_STR_END(output, size);
    SERIES_TAIL(output) = size;
    REBCNT_To_Bytes(out_size, (REBCNT)len); // Tag the size to the end.
    Append_Series(output, (REBYTE*)out_size, sizeof(REBCNT));
    if (SERIES_AVAIL(output) > 1024) // Is there wasted space?
        output = Copy_Series(output); // Trim it down if too big. !!! Revisit this based on mem alloc alg.
    //ENABLE_GC;

    return output;
}
Ejemplo n.º 2
0
*/  REBSER *Compress(REBSER *input, REBINT index, REBCNT len, REBFLG gzip, REBFLG raw)
/*
**		This is a wrapper over Zlib which will compress a BINARY!
**		series to produce another BINARY!.  It can use either gzip
**		or zlib envelopes, and has a "raw" option for no header.
**
**		!!! Adds 32-bit size info to zlib non-raw compressions for
**		compatibility with Rebol2 and R3-Alpha, at the cost of
**		inventing yet-another-format.  Consider removing.
**
**		!!! Does not expose the "streaming" ability of zlib.
**
***********************************************************************/
{
	REBCNT buf_size;
	REBSER *output;
	int ret;
	z_stream strm;

	assert(BYTE_SIZE(input)); // must be BINARY!

	// compression level can be a value from 1 to 9, or Z_DEFAULT_COMPRESSION
	// if you want it to pick what the library author considers the "worth it"
	// tradeoff of time to generally suggest.
	//
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	strm.opaque = Z_NULL;

	ret = deflateInit2(
		&strm,
		Z_DEFAULT_COMPRESSION,
		Z_DEFLATED,
		raw
			? (gzip ? window_bits_gzip_raw : window_bits_zlib_raw)
			: (gzip ? window_bits_gzip : window_bits_zlib),
		8,
		Z_DEFAULT_STRATEGY
	);

	if (ret != Z_OK)
		raise Error_Compression(&strm, ret);

	// http://stackoverflow.com/a/4938401/211160
	buf_size = deflateBound(&strm, len);

	strm.avail_in = len;
	strm.next_in = BIN_HEAD(input) + index;

	output = Make_Binary(buf_size);
	strm.avail_out = buf_size;
	strm.next_out = BIN_HEAD(output);

	ret = deflate(&strm, Z_FINISH);
	deflateEnd(&strm);

	if (ret != Z_STREAM_END)
		raise Error_Compression(&strm, ret);

	SET_STR_END(output, buf_size - strm.avail_out);
	SERIES_TAIL(output) = buf_size - strm.avail_out;

	if (gzip) {
		// GZIP contains its own CRC.  It also has a 32-bit uncompressed
		// length (and CRC), conveniently (and perhaps confusingly) at the
		// tail in the same format that Rebol used.

		REBCNT gzip_len = Bytes_To_REBCNT(
			SERIES_DATA(output) + buf_size - strm.avail_out - sizeof(REBCNT)
		);
		assert(len == gzip_len);
	}
	else if (!raw) {
		// Add 32-bit length to the end.
		//
		// !!! In ZLIB format the length can be found by decompressing, but
		// not known a priori.  So this is for efficiency.  It would likely be
		// better to not include this as it only confuses matters for those
		// expecting the data to be in a known format...though it means that
		// clients who wanted to decompress to a known allocation size would
		// have to save the size somewhere.

		REBYTE out_size[sizeof(REBCNT)];
		REBCNT_To_Bytes(out_size, cast(REBCNT, len));
		Append_Series(output, cast(REBYTE*, out_size), sizeof(REBCNT));
	}