master 565639eb32e 1/4: Simplify character comparisons in Freplace_region_contents

Pip Cet via Mailing list for Emacs changes <[email protected]>
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: master
commit 565639eb32e85a22c09422ee1d8e172821191763
Author: Pip Cet <[email protected]>
Commit: Pip Cet <[email protected]>

    Simplify character comparisons in Freplace_region_contents
    
    We expect 'compareseq' to call 'buffer_chars_equal' very often, so
    it's worth it to use some memory and make it as fast as possible.
    
    * src/editfns.c (EXTRA_CONTEXT_FIELDS): Drop buffer, beg, and unibyte
    fields; add arrays of character codes stored as C ints.
    (Freplace_region_contents): Generate such arrays from the two regions
    to be compared.
    (buffer_chars_equal): Compare array elements instead of buffer
    characters.
---
 src/editfns.c | 68 +++++++++++++++++++++--------------------------------------
 1 file changed, 24 insertions(+), 44 deletions(-)

diff --git a/src/editfns.c b/src/editfns.c
index 84f1e5cef03..50391a7ce0b 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -1895,15 +1895,9 @@ determines whether case is significant or ignored.  */)
 #define OFFSET ptrdiff_t
 
 #define EXTRA_CONTEXT_FIELDS                    \
-  /* Buffers to compare.  */                    \
-  struct buffer *buffer_a;                      \
-  struct buffer *buffer_b;                      \
-  /* BEGV of each buffer */			\
-  ptrdiff_t beg_a;				\
-  ptrdiff_t beg_b;				\
-  /* Whether each buffer is unibyte/plain-ASCII or not.  */ \
-  bool a_unibyte;				\
-  bool b_unibyte;				\
+  /* Arrays of character codes to compare.  */	\
+  int *chars_a;					\
+  int *chars_b;					\
   /* Bit vectors recording for each character whether it was deleted
      or inserted.  */                           \
   unsigned char *deletions;                     \
@@ -2098,17 +2092,25 @@ a buffer or a string.  But this is deprecated.  */)
     }
   Lisp_Object source_buffer = make_lisp_ptr (b, Lisp_Vectorlike);
 
+  /* Copy the characters to arrays of C integers.  This speeds up
+     comparison dramatically in multibyte buffers.  */
+  int *chars_a = SAFE_ALLOCA (sizeof (chars_a[0]) * size_a);
+  for (ptrdiff_t p = min_a; p < min_a + size_a; p++)
+    chars_a[p - min_a]
+      = BUF_FETCH_CHAR_AS_MULTIBYTE (a, buf_charpos_to_bytepos (a, p));
+
+  int *chars_b = SAFE_ALLOCA (sizeof (chars_b[0]) * size_b);
+  for (ptrdiff_t p = min_b; p < min_b + size_b; p++)
+    chars_b[p - min_b]
+      = BUF_FETCH_CHAR_AS_MULTIBYTE (b, buf_charpos_to_bytepos (b, p));
+
   /* FIXME: It is not documented how to initialize the contents of the
      context structure.  This code cargo-cults from the existing
      caller in src/analyze.c of GNU Diffutils, which appears to
      work.  */
   struct context ctx = {
-    .buffer_a = a,
-    .buffer_b = b,
-    .beg_a = min_a,
-    .beg_b = min_b,
-    .a_unibyte = BUF_ZV (a) == BUF_ZV_BYTE (a),
-    .b_unibyte = BUF_ZV (b) == BUF_ZV_BYTE (b),
+    .chars_a = chars_a,
+    .chars_b = chars_b,
     .deletions = deletions_insertions,
     .insertions = deletions_insertions + del_bytes,
     .fdiag = buffer + size_b + 1,
@@ -2219,17 +2221,17 @@ bit_is_set (const unsigned char *a, ptrdiff_t i)
   return a[i / CHAR_BIT] & (1 << (i % CHAR_BIT));
 }
 
-/* Return true if the characters at position POS_A of buffer
-   CTX->buffer_a and at position POS_B of buffer CTX->buffer_b are
+/* Return true if the characters at position POS_A in the first stretch
+   of text to be compared and at position POS_B of the second one are
    equal.  POS_A and POS_B are zero-based.  Text properties are
    ignored.
 
    Implementation note: this function is called inside the inner-most
    loops of compareseq, so it absolutely must be optimized for speed,
-   every last bit of it.  E.g., each additional use of BEGV or such
-   likes will slow down replace-buffer-contents by dozens of percents,
-   because builtin_lisp_symbol will be called one more time in the
-   innermost loop.  */
+   every last bit of it.  E.g., each use of BEGV or such likes will slow
+   down replace-buffer-contents by dozens of percents, because
+   builtin_lisp_symbol will be called one more time in the innermost
+   loop.  */
 
 static bool
 buffer_chars_equal (struct context *ctx,
@@ -2242,29 +2244,7 @@ buffer_chars_equal (struct context *ctx,
 	sys_longjmp (ctx->jmp, 1);
     }
 
-  pos_a += ctx->beg_a;
-  pos_b += ctx->beg_b;
-
-  ptrdiff_t bpos_a =
-    ctx->a_unibyte ? pos_a : buf_charpos_to_bytepos (ctx->buffer_a, pos_a);
-  ptrdiff_t bpos_b =
-    ctx->b_unibyte ? pos_b : buf_charpos_to_bytepos (ctx->buffer_b, pos_b);
-
-  /* We make the below a series of specific test to avoid using
-     BUF_FETCH_CHAR_AS_MULTIBYTE, which references Lisp symbols, and
-     is therefore significantly slower (see the note in the commentary
-     to this function).  */
-  if (ctx->a_unibyte && ctx->b_unibyte)
-    return BUF_FETCH_BYTE (ctx->buffer_a, bpos_a)
-      == BUF_FETCH_BYTE (ctx->buffer_b, bpos_b);
-  if (ctx->a_unibyte && !ctx->b_unibyte)
-    return UNIBYTE_TO_CHAR (BUF_FETCH_BYTE (ctx->buffer_a, bpos_a))
-      == BUF_FETCH_MULTIBYTE_CHAR (ctx->buffer_b, bpos_b);
-  if (!ctx->a_unibyte && ctx->b_unibyte)
-    return BUF_FETCH_MULTIBYTE_CHAR (ctx->buffer_a, bpos_a)
-      == UNIBYTE_TO_CHAR (BUF_FETCH_BYTE (ctx->buffer_b, bpos_b));
-  return BUF_FETCH_MULTIBYTE_CHAR (ctx->buffer_a, bpos_a)
-    == BUF_FETCH_MULTIBYTE_CHAR (ctx->buffer_b, bpos_b);
+  return ctx->chars_a[pos_a] == ctx->chars_b[pos_b];
 }
 
 static bool
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.