diff --git a/src/decompress.c b/src/decompress.c index 5d24638..7f20cb0 100644 --- a/src/decompress.c +++ b/src/decompress.c @@ -33,11 +33,23 @@ # include "w32common.h" # include "w32.h" +/* We import inflateInit2_ and deflateInit2_ because inflateInit and + deflateInit are macros defined on top of these symbols by zlib.h */ DEF_DLL_FN (int, inflateInit2_, (z_streamp strm, int windowBits, const char *version, int stream_size)); DEF_DLL_FN (int, inflate, (z_streamp strm, int flush)); DEF_DLL_FN (int, inflateEnd, (z_streamp strm)); +DEF_DLL_FN (int, deflateInit2_, + (z_streamp strm, int level, int method, int windowBits, + int memLevel, int strategy, const char *version, + int stream_size)); +DEF_DLL_FN (int, deflateInit2_, + (z_streamp strm, int level, int method, int windowBits, + int memLevel, int strategy, const char *version, + int stream_size)); +DEF_DLL_FN (int, deflate, (z_streamp strm, int flush)); +DEF_DLL_FN (int, deflateEnd, (z_streamp strm)); static bool zlib_initialized; @@ -52,16 +64,25 @@ init_zlib_functions (void) LOAD_DLL_FN (library, inflateInit2_); LOAD_DLL_FN (library, inflate); LOAD_DLL_FN (library, inflateEnd); + LOAD_DLL_FN (library, deflateInit2_); + LOAD_DLL_FN (library, deflate); + LOAD_DLL_FN (library, deflateEnd); return true; } # undef inflate # undef inflateEnd # undef inflateInit2_ +# undef deflate +# undef deflateEnd +# undef deflateInit2_ # define inflate fn_inflate # define inflateEnd fn_inflateEnd # define inflateInit2_ fn_inflateInit2_ +# define deflate fn_deflate +# define deflateEnd fn_deflateEnd +# define deflateInit2_ fn_deflateInit2_ #endif /* WINDOWSNT */ @@ -70,13 +91,14 @@ init_zlib_functions (void) { ptrdiff_t old_point, orig, start, nbytes; z_stream *stream; + int deflating; }; static void -unwind_decompress (void *ddata) +unwind_zlib (void *ddata) { struct decompress_unwind_data *data = ddata; - inflateEnd (data->stream); + (data->deflating? deflateEnd : inflateEnd) (data->stream); /* Delete any uncompressed data already inserted on error, but without calling the change hooks. */ @@ -180,7 +202,8 @@ DEFUN ("zlib-decompress-region", Fzlib_decompress_region, unwind_data.stream = &stream; unwind_data.old_point = PT; unwind_data.nbytes = 0; - record_unwind_protect_ptr (unwind_decompress, &unwind_data); + unwind_data.deflating = 0; + record_unwind_protect_ptr (unwind_zlib, &unwind_data); /* Insert the decompressed data at the end of the compressed data. */ SET_PT (iend); @@ -233,6 +256,129 @@ DEFUN ("zlib-decompress-region", Fzlib_decompress_region, return unbind_to (count, ret); } + +DEFUN ("zlib-compress-region", Fzlib_compress_region, + Szlib_compress_region, + 2, 3, 0, + doc: /* Compress a region to a gzip or zlib stream. +Replace the text in the region by the compressed data. + +If optional parameter NO-WRAPPER is nil or omitted, use the GZIP +wrapper format; otherwise, output just a deflated stream of +bytes. If decompression is completely successful return t. + +This function can be called only in unibyte buffers.*/) + (Lisp_Object start, Lisp_Object end, Lisp_Object zlib) +{ + ptrdiff_t istart, iend, pos_byte; + z_stream stream; + int deflate_status, flush; + struct decompress_unwind_data unwind_data; + ptrdiff_t count = SPECPDL_INDEX (); + bool gzipp = NILP (zlib); + + validate_region (&start, &end); + + if (! NILP (BVAR (current_buffer, enable_multibyte_characters))) + error ("This function can be called only in unibyte buffers"); + +#ifdef WINDOWSNT + if (!zlib_initialized) + zlib_initialized = init_zlib_functions (); + if (!zlib_initialized) + { + message1 ("zlib library not found"); + return Qnil; + } +#endif + + /* This is a unibyte buffer, so character positions and bytes are + the same. */ + istart = XFIXNUM (start); + iend = XFIXNUM (end); + + /* Do the following before manipulating the gap. */ + modify_text (istart, iend); + + move_gap_both (iend, iend); + + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.avail_in = 0; + stream.next_in = Z_NULL; + + /* Initiate the deflate() process, choosing the format, compression + strategy and level (9), and amount of memory used. */ + if (deflateInit2 (&stream, 9, Z_DEFLATED, MAX_WBITS + (gzipp? 16: 0), + 8, Z_DEFAULT_STRATEGY) != Z_OK) + return Qnil; + + unwind_data.orig = istart; + unwind_data.start = iend; + unwind_data.stream = &stream; + unwind_data.old_point = PT; + unwind_data.nbytes = 0; + unwind_data.deflating = 1; + record_unwind_protect_ptr (unwind_zlib, &unwind_data); + + /* Insert the decompressed data at the end of the compressed data. */ + SET_PT (iend); + + pos_byte = istart; + + /* Keep calling 'deflate' until it reports an error or end-of-input. */ + flush = Z_NO_FLUSH; + do + { + /* Maximum number of bytes that one 'deflate' call should read and write. + Do not make avail_out too large, as that might unduly delay C-g. + zlib requires that avail_in and avail_out not exceed UINT_MAX. */ + ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX); + int avail_out = 16 * 1024; + int compressed; + + if (GAP_SIZE < avail_out) + make_gap (avail_out - GAP_SIZE); + stream.next_in = BYTE_POS_ADDR (pos_byte); + stream.avail_in = avail_in; + stream.next_out = GPT_ADDR; + stream.avail_out = avail_out; + deflate_status = deflate (&stream, flush); + + pos_byte += avail_in - stream.avail_in; + compressed = avail_out - stream.avail_out; + insert_from_gap (compressed, compressed, 0); + unwind_data.nbytes += compressed; + if (deflate_status == Z_BUF_ERROR && flush == Z_NO_FLUSH) { + /* When we run out of input, zlib returns Z_BUF_ERROR. + We then have to flush all output. */ + flush = Z_FINISH; + deflate_status = Z_OK; + } + maybe_quit (); + } + while (deflate_status == Z_OK); + + Lisp_Object ret = Qt; + if (deflate_status != Z_STREAM_END) + { + /* When compression did not succeed, delete output. */ + ret = make_int (iend - pos_byte); + } + + unwind_data.start = 0; + + /* Delete the uncompressed data. */ + del_range_2 (istart, istart, /* byte and char offsets are the same. */ + iend, iend, 0); + + signal_after_change (istart, iend - istart, unwind_data.nbytes); + update_compositions (istart, istart, CHECK_HEAD); + + return unbind_to (count, ret); +} + /*********************************************************************** Initialization @@ -241,6 +387,7 @@ DEFUN ("zlib-decompress-region", Fzlib_decompress_region, syms_of_decompress (void) { defsubr (&Szlib_decompress_region); + defsubr (&Szlib_compress_region); defsubr (&Szlib_available_p); }