Subject: Re: bug#13949: 24.4.1; `fill-paragraph' should not always put the buffer as modified

From: Lars Magne Ingebrigtsen <[email-masked]>
Date: Mon, 28 Mar 2016 13:27:49 +0200

I've now made a simple buffer hashing function (that does not take text
properties into account) to see how slow this would be.

The following form

(with-temp-buffer
  (dotimes (i 10000)
    (insert (make-string 100 ?k) "\n"))
  (benchmark-run 1000 (buffer-hash)))

takes 2.7 seconds (that's 1000 1MB buffers), and unsurprisingly

(with-temp-buffer
  (dotimes (i 10000000)
    (insert (make-string 100 ?k) "\n"))
  (benchmark-run 1 (progn (message "%s" (buffer-size) (buffer-hash)))))

(which is 1 1GB buffer) takes the same amount of time.  :-)

(This is on a 2.7GHz i5 from like five years ago.)

So on big buffers this isn't really something you'd want to run a lot,
but it's workable (especially since in the `M-q' case you'd only run it
if the buffer isn't already modified).

So...  

diff --git a/src/fns.c b/src/fns.c
index 0e3fc27..d027058 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -4737,6 +4737,22 @@ It should be the case that if (eq (funcall HASH x1) (funcall HASH x2))
 #include "sha256.h"
 #include "sha512.h"
 
+static Lisp_Object
+make_digest_string (Lisp_Object digest, int digest_size)
+{
+  unsigned char *p = SDATA (digest);
+  int i;
+
+  for (i = digest_size - 1; i >= 0; i--)
+    {
+      static char const hexdigit[16] = "0123456789abcdef";
+      int p_i = p[i];
+      p[2 * i] = hexdigit[p_i >> 4];
+      p[2 * i + 1] = hexdigit[p_i & 0xf];
+    }
+  return digest;
+}
+
 /* ALGORITHM is a symbol: md5, sha1, sha224 and so on. */
 
 static Lisp_Object
@@ -4936,17 +4952,7 @@ It should be the case that if (eq (funcall HASH x1) (funcall HASH x2))
 	     SSDATA (digest));
 
   if (NILP (binary))
-    {
-      unsigned char *p = SDATA (digest);
-      for (i = digest_size - 1; i >= 0; i--)
-	{
-	  static char const hexdigit[16] = "0123456789abcdef";
-	  int p_i = p[i];
-	  p[2 * i] = hexdigit[p_i >> 4];
-	  p[2 * i + 1] = hexdigit[p_i & 0xf];
-	}
-      return digest;
-    }
+    return make_digest_string (digest, digest_size);
   else
     return make_unibyte_string (SSDATA (digest), digest_size);
 }
@@ -4997,6 +5003,44 @@ It should be the case that if (eq (funcall HASH x1) (funcall HASH x2))
 {
   return secure_hash (algorithm, object, start, end, Qnil, Qnil, binary);
 }
+
+DEFUN ("buffer-hash", Fbuffer_hash, Sbuffer_hash, 0, 1, 0,
+       doc: /* Return a hash of the contents of BUFFER-OR-NAME.
+If nil, use the current buffer." */ )
+     (Lisp_Object buffer_or_name)
+{
+  Lisp_Object buffer;
+  struct buffer *b;
+  struct sha1_ctx ctx;
+  Lisp_Object digest = make_uninit_string (SHA1_DIGEST_SIZE * 2);
+
+  if (NILP (buffer_or_name))
+    buffer = Fcurrent_buffer ();
+  else
+    buffer = Fget_buffer (buffer_or_name);
+  if (NILP (buffer))
+    nsberror (buffer_or_name);
+
+  b = XBUFFER (buffer);
+  sha1_init_ctx (&ctx);
+
+  /* Process the first part of the buffer. */
+  sha1_process_bytes (BUF_BEG_ADDR (b),
+		      BUF_GPT_BYTE (b) - BUF_BEG_BYTE (b),
+		      &ctx);
+
+  /* If the gap is before the end of the buffer, process the last half
+     of the buffer. */
+  if (BUF_GPT_BYTE (b) < BUF_Z_BYTE (b))
+    sha1_process_bytes (BUF_GAP_END_ADDR (b),
+			BUF_Z_BYTE (b) - (BUF_GPT_BYTE (b) + BUF_GAP_SIZE (b)),
+			&ctx);
+
+  sha1_finish_ctx (&ctx, SSDATA (digest));
+  return make_digest_string (digest, SHA1_DIGEST_SIZE);
+}
+
+
 
 void
 syms_of_fns (void)
@@ -5156,6 +5200,7 @@ It should be the case that if (eq (funcall HASH x1) (funcall HASH x2))
   defsubr (&Sbase64_decode_string);
   defsubr (&Smd5);
   defsubr (&Ssecure_hash);
+  defsubr (&Sbuffer_hash);
   defsubr (&Slocale_info);
 
   hashtest_eq.name = Qeq;

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no