From: Paul Eggert Date: Sat, 18 Jan 2020 07:59:51 +0000 (-0800) Subject: Improve performance when a string's byte count changes X-Git-Tag: emacs-28.0.90~7908^2~111 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=c1b6d5c5b9f8eee8aa3a8071292e8b3281ecf28a;p=emacs.git Improve performance when a string's byte count changes * 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. --- diff --git a/src/alloc.c b/src/alloc.c index 7eb37bb12da..9dc6ef79e39 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -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; } diff --git a/src/data.c b/src/data.c index c8445e7d874..cd7db6a0bb9 100644 --- a/src/data.c +++ b/src/data.c @@ -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 { diff --git a/src/lisp.h b/src/lisp.h index 3681b7b2a7c..4bcd1228443 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -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);