all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: "Juan José García-Ripoll" <juanjose.garciaripoll@gmail.com>
To: emacs-devel@gnu.org
Subject: decompress.c now also compresses
Date: Sun, 29 Mar 2020 17:52:03 +0200	[thread overview]
Message-ID: <86zhbz17mk.fsf@csic.es> (raw)

[-- Attachment #1: Type: text/plain, Size: 1039 bytes --]

Hi,

I attach a patch that adds support for compressing buffers using
zlib. It is a minor extension to the file src/decompress.c but it may be
useful because of two reasons (i) in Windows, Emasc is shipped without
g[un]zip.exe, (ii) the whole process of compression takes about 20 times
less time than calling gzip.

(benchmark 1
  '(mapc 'simple-zlib-compress
      (directory-files  "~/emacs-build/git/emacs-27/lisp/" t ".*\\.el")))
;; => Elapsed time: 2.602588s (0.014894s in 1 GCs)

(benchmark 1
  '(mapc 'simple-gzip-compress
       (directory-files  "~/emacs-build/git/emacs-27/lisp/" t ".*\\.el")))
;; => Elapsed time: 61.986128s (0.039815s in 3 GCs)

I attach a patch that was produced against emacs-27 but also seems to
work against emacs-28 (at least the decompress.c part, I am unsure about
how NEWS should be edited).

I also attach a lisp file that test and benchmark the
compression/decompression using Emacs' lisp sources.

Best

Juanjo

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

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: decompress.diff --]
[-- Type: text/x-patch, Size: 20186 bytes --]

diff --git a/etc/NEWS b/etc/NEWS
index d3f27e3..96ddf5c 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3656,6 +3656,10 @@ easier to undo immediately afterwards.
 ** When called interactively, 'next-buffer' and 'previous-buffer' now
 signal 'user-error' if there is no buffer to switch to.
 
+---
+** New function 'zlib-compress-region' compresses a unibyte buffer region using
+gzip's format, via the zlib library.
+
 \f
 * Changes in Emacs 27.1 on Non-Free Operating Systems
 
diff --git a/src/decompress.c b/src/decompress.c
index 5d24638..68178db 100644
--- a/src/decompress.c
+++ b/src/decompress.c
@@ -1,247 +1,394 @@
-/* Interface to zlib.
-   Copyright (C) 2013-2020 Free Software Foundation, Inc.
-
-This file is part of GNU Emacs.
-
-GNU Emacs is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or (at
-your option) any later version.
-
-GNU Emacs is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
-
-#include <config.h>
-
-#ifdef HAVE_ZLIB
-
-#include <zlib.h>
-
-#include "lisp.h"
-#include "buffer.h"
-#include "composite.h"
-
-#include <verify.h>
-
-#ifdef WINDOWSNT
-# include <windows.h>
-# include "w32common.h"
-# include "w32.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));
-
-static bool zlib_initialized;
-
-static bool
-init_zlib_functions (void)
-{
-  HMODULE library = w32_delayed_load (Qzlib);
-
-  if (!library)
-    return false;
-
-  LOAD_DLL_FN (library, inflateInit2_);
-  LOAD_DLL_FN (library, inflate);
-  LOAD_DLL_FN (library, inflateEnd);
-  return true;
-}
-
-# undef inflate
-# undef inflateEnd
-# undef inflateInit2_
-
-# define inflate fn_inflate
-# define inflateEnd fn_inflateEnd
-# define inflateInit2_ fn_inflateInit2_
-
-#endif	/* WINDOWSNT */
-
-\f
-struct decompress_unwind_data
-{
-  ptrdiff_t old_point, orig, start, nbytes;
-  z_stream *stream;
-};
-
-static void
-unwind_decompress (void *ddata)
-{
-  struct decompress_unwind_data *data = ddata;
-  inflateEnd (data->stream);
-
-  /* Delete any uncompressed data already inserted on error, but
-     without calling the change hooks.  */
-  if (data->start)
-    {
-      del_range_2 (data->start, data->start, /* byte, char offsets the same */
-                   data->start + data->nbytes, data->start + data->nbytes,
-                   0);
-      update_compositions (data->start, data->start, CHECK_HEAD);
-      /* "Balance" the before-change-functions call, which would
-         otherwise be left "hanging". */
-      signal_after_change (data->orig, data->start - data->orig,
-                           data->start - data->orig);
-    }
-  /* Put point where it was, or if the buffer has shrunk because the
-     compressed data is bigger than the uncompressed, at
-     point-max.  */
-  SET_PT (min (data->old_point, ZV));
-}
-
-DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
-       doc: /* Return t if zlib decompression is available in this instance of Emacs.  */)
-     (void)
-{
-#ifdef WINDOWSNT
-  Lisp_Object found = Fassq (Qzlib, Vlibrary_cache);
-  if (CONSP (found))
-    return XCDR (found);
-  else
-    {
-      Lisp_Object status;
-      zlib_initialized = init_zlib_functions ();
-      status = zlib_initialized ? Qt : Qnil;
-      Vlibrary_cache = Fcons (Fcons (Qzlib, status), Vlibrary_cache);
-      return status;
-    }
-#else
-  return Qt;
-#endif
-}
-
-DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
-       Szlib_decompress_region,
-       2, 3, 0,
-       doc: /* Decompress a gzip- or zlib-compressed region.
-Replace the text in the region by the decompressed data.
-
-If optional parameter ALLOW-PARTIAL is nil or omitted, then on
-failure, return nil and leave the data in place.  Otherwise, return
-the number of bytes that were not decompressed and replace the region
-text by whatever data was successfully decompressed (similar to gzip).
-If decompression is completely successful return t.
-
-This function can be called only in unibyte buffers.  */)
-  (Lisp_Object start, Lisp_Object end, Lisp_Object allow_partial)
-{
-  ptrdiff_t istart, iend, pos_byte;
-  z_stream stream;
-  int inflate_status;
-  struct decompress_unwind_data unwind_data;
-  ptrdiff_t count = SPECPDL_INDEX ();
-
-  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;
-
-  /* The magic number 32 apparently means "autodetect both the gzip and
-     zlib formats" according to zlib.h.  */
-  if (inflateInit2 (&stream, MAX_WBITS + 32) != 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;
-  record_unwind_protect_ptr (unwind_decompress, &unwind_data);
-
-  /* Insert the decompressed data at the end of the compressed data.  */
-  SET_PT (iend);
-
-  pos_byte = istart;
-
-  /* Keep calling 'inflate' until it reports an error or end-of-input.  */
-  do
-    {
-      /* Maximum number of bytes that one 'inflate' 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 decompressed;
-
-      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;
-      inflate_status = inflate (&stream, Z_NO_FLUSH);
-      pos_byte += avail_in - stream.avail_in;
-      decompressed = avail_out - stream.avail_out;
-      insert_from_gap (decompressed, decompressed, 0);
-      unwind_data.nbytes += decompressed;
-      maybe_quit ();
-    }
-  while (inflate_status == Z_OK);
-
-  Lisp_Object ret = Qt;
-  if (inflate_status != Z_STREAM_END)
-    {
-      if (!NILP (allow_partial))
-        ret = make_int (iend - pos_byte);
-      else
-        return unbind_to (count, Qnil);
-    }
-
-  unwind_data.start = 0;
-
-  /* Delete the compressed 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);
-}
-
-\f
-/***********************************************************************
-			    Initialization
- ***********************************************************************/
-void
-syms_of_decompress (void)
-{
-  defsubr (&Szlib_decompress_region);
-  defsubr (&Szlib_available_p);
-}
-
-#endif /* HAVE_ZLIB */
+/* Interface to zlib.
+   Copyright (C) 2013-2020 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#ifdef HAVE_ZLIB
+
+#include <zlib.h>
+
+#include "lisp.h"
+#include "buffer.h"
+#include "composite.h"
+
+#include <verify.h>
+
+#ifdef WINDOWSNT
+# include <windows.h>
+# 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;
+
+static bool
+init_zlib_functions (void)
+{
+  HMODULE library = w32_delayed_load (Qzlib);
+
+  if (!library)
+    return false;
+
+  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 */
+
+\f
+struct decompress_unwind_data
+{
+  ptrdiff_t old_point, orig, start, nbytes;
+  z_stream *stream;
+  int deflating;
+};
+
+static void
+unwind_zlib (void *ddata)
+{
+  struct decompress_unwind_data *data = ddata;
+  (data->deflating? deflateEnd : inflateEnd) (data->stream);
+
+  /* Delete any uncompressed data already inserted on error, but
+     without calling the change hooks.  */
+  if (data->start)
+    {
+      del_range_2 (data->start, data->start, /* byte, char offsets the same */
+                   data->start + data->nbytes, data->start + data->nbytes,
+                   0);
+      update_compositions (data->start, data->start, CHECK_HEAD);
+      /* "Balance" the before-change-functions call, which would
+         otherwise be left "hanging". */
+      signal_after_change (data->orig, data->start - data->orig,
+                           data->start - data->orig);
+    }
+  /* Put point where it was, or if the buffer has shrunk because the
+     compressed data is bigger than the uncompressed, at
+     point-max.  */
+  SET_PT (min (data->old_point, ZV));
+}
+
+DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
+       doc: /* Return t if zlib decompression is available in this instance of Emacs.  */)
+     (void)
+{
+#ifdef WINDOWSNT
+  Lisp_Object found = Fassq (Qzlib, Vlibrary_cache);
+  if (CONSP (found))
+    return XCDR (found);
+  else
+    {
+      Lisp_Object status;
+      zlib_initialized = init_zlib_functions ();
+      status = zlib_initialized ? Qt : Qnil;
+      Vlibrary_cache = Fcons (Fcons (Qzlib, status), Vlibrary_cache);
+      return status;
+    }
+#else
+  return Qt;
+#endif
+}
+
+DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
+       Szlib_decompress_region,
+       2, 3, 0,
+       doc: /* Decompress a gzip- or zlib-compressed region.
+Replace the text in the region by the decompressed data.
+
+If optional parameter ALLOW-PARTIAL is nil or omitted, then on
+failure, return nil and leave the data in place.  Otherwise, return
+the number of bytes that were not decompressed and replace the region
+text by whatever data was successfully decompressed (similar to gzip).
+If decompression is completely successful return t.
+
+This function can be called only in unibyte buffers.  */)
+  (Lisp_Object start, Lisp_Object end, Lisp_Object allow_partial)
+{
+  ptrdiff_t istart, iend, pos_byte;
+  z_stream stream;
+  int inflate_status;
+  struct decompress_unwind_data unwind_data;
+  ptrdiff_t count = SPECPDL_INDEX ();
+
+  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;
+
+  /* The magic number 32 apparently means "autodetect both the gzip and
+     zlib formats" according to zlib.h.  */
+  if (inflateInit2 (&stream, MAX_WBITS + 32) != 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 = 0;
+  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 'inflate' until it reports an error or end-of-input.  */
+  do
+    {
+      /* Maximum number of bytes that one 'inflate' 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 decompressed;
+
+      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;
+      inflate_status = inflate (&stream, Z_NO_FLUSH);
+      pos_byte += avail_in - stream.avail_in;
+      decompressed = avail_out - stream.avail_out;
+      insert_from_gap (decompressed, decompressed, 0);
+      unwind_data.nbytes += decompressed;
+      maybe_quit ();
+    }
+  while (inflate_status == Z_OK);
+
+  Lisp_Object ret = Qt;
+  if (inflate_status != Z_STREAM_END)
+    {
+      if (!NILP (allow_partial))
+        ret = make_int (iend - pos_byte);
+      else
+        return unbind_to (count, Qnil);
+    }
+
+  unwind_data.start = 0;
+
+  /* Delete the compressed 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);
+}
+
+\f
+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);
+}
+
+\f
+/***********************************************************************
+			    Initialization
+ ***********************************************************************/
+void
+syms_of_decompress (void)
+{
+  defsubr (&Szlib_decompress_region);
+  defsubr (&Szlib_compress_region);
+  defsubr (&Szlib_available_p);
+}
+
+#endif /* HAVE_ZLIB */

[-- Attachment #3: test.lisp --]
[-- Type: application/octet-stream, Size: 3637 bytes --]

(switch-to-buffer "*Messages*")

(setq debug-on-error t)

(defun test-zlib-compress (filename &optional show-diff)
  "Test compression passing the output of zlib-compress-region to
gzip -d and comparing the output. If SHOW-DIFF is not nil, save the
failing files and output a diff.

Returns T on success, NIL on failure of the comparison."
  (with-temp-buffer
    (set-buffer-multibyte nil)
    (insert-file-contents-literally filename nil nil nil t)
    (let ((b-zlib (current-buffer))
          (s-orig (buffer-string))
          (coding buffer-file-coding-system))
      (unless (zlib-compress-region (point-min) (point-max))
        (error "Failed to compress %s" filename))
      (with-temp-buffer
        (let ((b-gunzipped (current-buffer)))
          (set-buffer-multibyte nil)
          (with-current-buffer b-zlib
            (let ((coding-system-for-write 'raw-text-unix)
                  (coding-system-for-read 'raw-text-unix))
              (call-process-region (point-min) (point-max)
                                   "gzip.exe" nil b-gunzipped nil
                                   "-d" "-c" "-q" "-"))
            (delete-region (point-min) (point-max))
            (insert-file filename)
            (let* ((s-uncomp (with-current-buffer b-gunzipped (buffer-string)))
                   (success (string-equal s-orig s-uncomp)))
              (when show-diff
                (message "Compression/decompression with %S %s "
                         filename
                         (if success "succeeded!" "failed!"))
                (unless success
                  (with-current-buffer b-zlib
                    (write-region (point-min) (point-max) "test-file-a"))
                  (with-current-buffer b-gunzipped
                    (write-region (point-min) (point-max) "test-file-b"))
                  (diff "test-file-a" "test-file-b")))
              success
              )))))))

(defun test-directory (dir pattern)
  "Runs TEST-ZLIB-COMPRESS on all files in DIR that match PATTERN.
Returns a list of files that fail the test."
  (let ((failures nil))
    (dolist (f (directory-files dir t pattern))
      (unless (test-zlib-compress f)
        (push f failures)))
    (message "Files that failed test: %S" failures)))

(test-directory "~/emacs-build/git/emacs-27/lisp/" ".*\\.el")

;; Run this if some file fails to pass
;; (test-zlib-compress "c:/Users/juanj/emacs-build/git/emacs-27/lisp/finder-inf.el" t)


(defun simple-zlib-compress (filename)
  "Compress the buffer directly with ZLIB-COMPRESS-REGION"
  (with-temp-buffer
    (set-buffer-multibyte nil)
    (insert-file-contents-literally filename nil nil nil t)
    (unless (zlib-compress-region (point-min) (point-max))
      (error "Failed to compress %s" filename))))

(defun simple-gzip-compress (filename)
  "Compress the buffer indirectly, passing its content through gzip."
  (with-temp-buffer
    (set-buffer-multibyte nil)
    (insert-file-contents-literally filename nil nil nil t)
    (let ((coding-system-for-write 'raw-text-unix)
          (coding-system-for-read 'raw-text-unix))
      (call-process-region (point-min) (point-max)
                           "gzip.exe" t t))))

(benchmark 1 '(mapc 'simple-zlib-compress
                    (directory-files  "~/emacs-build/git/emacs-27/lisp/" t ".*\\.el")))
;; => Elapsed time: 2.602588s (0.014894s in 1 GCs)

(benchmark 1 '(mapc 'simple-gzip-compress
                    (directory-files  "~/emacs-build/git/emacs-27/lisp/" t ".*\\.el")))
;; => Elapsed time: 61.986128s (0.039815s in 3 GCs)

             reply	other threads:[~2020-03-29 15:52 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-29 15:52 Juan José García-Ripoll [this message]
2020-03-29 15:57 ` decompress.c now also compresses Juan José García-Ripoll
2020-03-29 16:11 ` Eli Zaretskii
2020-03-29 16:48   ` Juan José García-Ripoll
2020-03-29 16:54 ` Stefan Monnier
2020-03-29 19:27   ` Juan José García-Ripoll

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86zhbz17mk.fsf@csic.es \
    --to=juanjose.garciaripoll@gmail.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.