emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: decompress.c now also compresses


From: Juan José García-Ripoll
Subject: Re: decompress.c now also compresses
Date: Sun, 29 Mar 2020 17:57:23 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.3 (windows-nt)

Apologies for the corrupted patch: I used emacs' C-c C-v and it looked
good, but "git diff" failed because of cr/lf issues. Here it goes again.

-- 
Juan José García Ripoll
http://juanjose.garciaripoll.com
http://quinfog.hbar.es

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);
 }

reply via email to

[Prev in Thread] Current Thread [Next in Thread]