]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve performance when a string's byte count changes
authorPaul Eggert <eggert@Penguin.CS.UCLA.EDU>
Sat, 18 Jan 2020 07:59:51 +0000 (23:59 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Sat, 18 Jan 2020 08:02:12 +0000 (00:02 -0800)
* src/alloc.c (allocate_string_data): Now static.
Remove code for when Faset calls this function when S
already has data assigned, as that can no longer happen.
(resize_string_data): New function, which avoids relocation in
more cases than the old code did, by not bothering to relocate
when the size changes falls within the alignment slop.
* src/data.c (Faset): Use resize_string_data.
Change a while to a do-while since it must iterate at least once.

src/alloc.c
src/data.c
src/lisp.h

index 7eb37bb12da9fc8fcb0617bc72e6a00f32ed43c4..9dc6ef79e39c2339ae6a248781c1864fa7d151b6 100644 (file)
@@ -1786,13 +1786,12 @@ allocate_string (void)
 
    If CLEARIT, also clear the other bytes of S->u.s.data.  */
 
-void
+static void
 allocate_string_data (struct Lisp_String *s,
                      EMACS_INT nchars, EMACS_INT nbytes, bool clearit)
 {
-  sdata *data, *old_data;
+  sdata *data;
   struct sblock *b;
-  ptrdiff_t old_nbytes;
 
   if (STRING_BYTES_MAX < nbytes)
     string_overflow ();
@@ -1800,13 +1799,6 @@ allocate_string_data (struct Lisp_String *s,
   /* Determine the number of bytes needed to store NBYTES bytes
      of string data.  */
   ptrdiff_t needed = sdata_size (nbytes);
-  if (s->u.s.data)
-    {
-      old_data = SDATA_OF_STRING (s);
-      old_nbytes = STRING_BYTES (s);
-    }
-  else
-    old_data = NULL;
 
   MALLOC_BLOCK_INPUT;
 
@@ -1875,16 +1867,53 @@ allocate_string_data (struct Lisp_String *s,
          GC_STRING_OVERRUN_COOKIE_SIZE);
 #endif
 
-  /* Note that Faset may call to this function when S has already data
-     assigned.  In this case, mark data as free by setting it's string
-     back-pointer to null, and record the size of the data in it.  */
-  if (old_data)
+  tally_consing (needed);
+}
+
+/* Reallocate the data for STRING when a single character is replaced.
+   The character is at byte offset CIDX_BYTE in the string.
+   The character being replaced is CLEN bytes long,
+   and the character that will replace it is NEW_CLEN bytes long.
+   Return the address of where the caller should store the
+   the new character.  */
+
+unsigned char *
+resize_string_data (Lisp_Object string, ptrdiff_t cidx_byte,
+                   int clen, int new_clen)
+{
+  sdata *old_sdata = SDATA_OF_STRING (XSTRING (string));
+  ptrdiff_t nchars = SCHARS (string);
+  ptrdiff_t nbytes = SBYTES (string);
+  ptrdiff_t new_nbytes = nbytes + (new_clen - clen);
+  unsigned char *data = SDATA (string);
+  unsigned char *new_charaddr;
+
+  if (sdata_size (nbytes) == sdata_size (new_nbytes))
+    {
+      /* No need to reallocate, as the size change falls within the
+        alignment slop.  */
+      new_charaddr = data + cidx_byte;
+      memmove (new_charaddr + new_clen, new_charaddr + clen,
+              nbytes - (cidx_byte + (clen - 1)));
+    }
+  else
     {
-      SDATA_NBYTES (old_data) = old_nbytes;
-      old_data->string = NULL;
+      allocate_string_data (XSTRING (string), nchars, new_nbytes, false);
+      unsigned char *new_data = SDATA (string);
+      new_charaddr = new_data + cidx_byte;
+      memcpy (new_charaddr + new_clen, data + cidx_byte + clen,
+             nbytes - (cidx_byte + clen));
+      memcpy (new_data, data, cidx_byte);
+
+      /* Mark old string data as free by setting its string back-pointer
+        to null, and record the size of the data in it.  */
+      SDATA_NBYTES (old_sdata) = nbytes;
+      old_sdata->string = NULL;
     }
 
-  tally_consing (needed);
+  clear_string_char_byte_cache ();
+
+  return new_charaddr;
 }
 
 
index c8445e7d874b3be3c76055183e6f1126a88e8c62..cd7db6a0bb9461317660b9a2917441b82c495035 100644 (file)
@@ -2303,34 +2303,18 @@ bool-vector.  IDX starts at 0.  */)
 
       if (STRING_MULTIBYTE (array))
        {
-         ptrdiff_t idxval_byte, nbytes;
-         int prev_bytes, new_bytes;
-         unsigned char workbuf[MAX_MULTIBYTE_LENGTH], *p0 = workbuf, *p1;
-
-         nbytes = SBYTES (array);
-         idxval_byte = string_char_to_byte (array, idxval);
-         p1 = SDATA (array) + idxval_byte;
-         prev_bytes = BYTES_BY_CHAR_HEAD (*p1);
-         new_bytes = CHAR_STRING (c, p0);
+         unsigned char workbuf[MAX_MULTIBYTE_LENGTH], *p0 = workbuf;
+         ptrdiff_t idxval_byte = string_char_to_byte (array, idxval);
+         unsigned char *p1 = SDATA (array) + idxval_byte;
+
+         int prev_bytes = BYTES_BY_CHAR_HEAD (*p1);
+         int new_bytes = CHAR_STRING (c, p0);
          if (prev_bytes != new_bytes)
-           {
-             /* We must relocate the string data.  */
-             ptrdiff_t nchars = SCHARS (array);
-             USE_SAFE_ALLOCA;
-             unsigned char *str = SAFE_ALLOCA (nbytes);
-
-             memcpy (str, SDATA (array), nbytes);
-             allocate_string_data (XSTRING (array), nchars,
-                                   nbytes + new_bytes - prev_bytes, false);
-             memcpy (SDATA (array), str, idxval_byte);
-             p1 = SDATA (array) + idxval_byte;
-             memcpy (p1 + new_bytes, str + idxval_byte + prev_bytes,
-                     nbytes - (idxval_byte + prev_bytes));
-             SAFE_FREE ();
-             clear_string_char_byte_cache ();
-           }
-         while (new_bytes--)
+           p1 = resize_string_data (array, idxval_byte, prev_bytes, new_bytes);
+
+         do
            *p1++ = *p0++;
+         while (--new_bytes != 0);
        }
       else
        {
index 3681b7b2a7c3864bd022a85846c8f41fd02d5cd9..4bcd1228443693fafc7eafbaa72372aaad91db0c 100644 (file)
@@ -3812,8 +3812,7 @@ extern void parse_str_as_multibyte (const unsigned char *, ptrdiff_t,
 /* Defined in alloc.c.  */
 extern void *my_heap_start (void);
 extern void check_pure_size (void);
-extern void allocate_string_data (struct Lisp_String *, EMACS_INT, EMACS_INT,
-                                 bool);
+unsigned char *resize_string_data (Lisp_Object, ptrdiff_t, int, int);
 extern void malloc_warning (const char *);
 extern AVOID memory_full (size_t);
 extern AVOID buffer_memory_full (ptrdiff_t);