master f5cb95423e7 4/4: replace-region-contents malloc fallback

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

    replace-region-contents malloc fallback
    
    * src/editfns.c (Freplace_region_contents): If the temporary
    storage is so large that malloc fails, do not signal an error.
    Instead, fall back on destructive replacement.
---
 doc/lispref/text.texi |   2 +-
 src/editfns.c         | 126 ++++++++++++++++++++++++++++----------------------
 2 files changed, 72 insertions(+), 56 deletions(-)

diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index a5535df601b..0b9dc4951a5 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -4863,7 +4863,7 @@ faster but suboptimal solution.  The default value is 1000000.
 
 @code{replace-region-contents} returns @code{t} if a non-destructive
 replacement could be performed.  Otherwise, i.e., if @var{max-secs}
-was exceeded, it returns @code{nil}.
+was exceeded or too much memory would have been needed, it returns @code{nil}.
 
 Note: When using the refined replacement algorithm, if the replacement
 is a string, it will be internally copied to a temporary buffer.
diff --git a/src/editfns.c b/src/editfns.c
index 31bf911a8be..191ea0d9053 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -1961,8 +1961,8 @@ in a temporary buffer.  Therefore, all else being equal, it is preferable
 to pass a buffer rather than a string as SOURCE argument.
 
 This function returns t if a non-destructive replacement could be
-performed.  Otherwise, i.e., if MAX-SECS was exceeded, it returns
-nil.
+performed.  Otherwise, i.e., if MAX-SECS was exceeded or too much
+memory would have been needed, it returns nil.
 
 SOURCE can also be a function that will be called with no arguments
 and with current buffer narrowed to BEG..END, and should return
@@ -2065,6 +2065,18 @@ a buffer or a string.  But this is deprecated.  */)
 	time_limit = tlim;
     }
 
+  /* The rest of the code is not prepared to handle a string SOURCE.  */
+  if (!b)
+    {
+      Lisp_Object workbuf
+	= code_conversion_save (true, STRING_MULTIBYTE (source));
+      b = XBUFFER (workbuf);
+      set_buffer_internal (b);
+      CALLN (Finsert, source);
+      set_buffer_internal (a);
+    }
+  Lisp_Object source_buffer = make_lisp_ptr (b, Lisp_Vectorlike);
+
   specpdl_ref count = SPECPDL_INDEX ();
 
   ptrdiff_t diags = size_a + size_b + 3;
@@ -2078,60 +2090,65 @@ a buffer or a string.  But this is deprecated.  */)
   ptrdiff_t bytes_needed;
   if (ckd_mul (&bytes_needed, diags, bytes_per_diag)
       || ckd_add (&bytes_needed, bytes_needed,
-		  align_bytes - surplus_char_bytes + del_bytes + ins_bytes))
-    memory_full_up ();
-  USE_SAFE_ALLOCA;
-  buffer = SAFE_ALLOCA (bytes_needed);
-
-  /* The rest of the code is not prepared to handle a string SOURCE.  */
-  if (!b)
+		  align_bytes - surplus_char_bytes + del_bytes + ins_bytes)
+      || SIZE_MAX < bytes_needed)
+    buffer = NULL;
+  else if (bytes_needed <= MAX_ALLOCA)
+    buffer = alloca (bytes_needed);
+  else
     {
-      Lisp_Object workbuf
-	= code_conversion_save (true, STRING_MULTIBYTE (source));
-      b = XBUFFER (workbuf);
-      set_buffer_internal (b);
-      CALLN (Finsert, source);
-      set_buffer_internal (a);
+      buffer = malloc (bytes_needed);
+      if (buffer)
+	{
+	  if (profiler_memory_running)
+	    malloc_probe (bytes_needed);
+	  record_unwind_protect_ptr (xfree, buffer);
+	}
     }
-  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 = (int *) (((uintptr_t) (buffer + 2 * diags) + align_bytes)
-			  & ~align_bytes);
-  for (ptrdiff_t i = 0; i < size_a; i++)
-    chars_a[i]
-      = BUF_FETCH_CHAR_AS_MULTIBYTE (a, buf_charpos_to_bytepos (a, min_a + i));
-
-  int *chars_b = chars_a + size_a;
-  for (ptrdiff_t i = 0; i < size_b; i++)
-    chars_b[i]
-      = BUF_FETCH_CHAR_AS_MULTIBYTE (b, buf_charpos_to_bytepos (b, min_b + i));
-
-  unsigned char *deletions_insertions = memset (chars_b + size_b, 0,
-						del_bytes + ins_bytes);
-
-  /* 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 = {
-    .chars_a = chars_a,
-    .chars_b = chars_b,
-    .deletions = deletions_insertions,
-    .insertions = deletions_insertions + del_bytes,
-    .fdiag = buffer + size_b + 1,
-    .bdiag = buffer + diags + size_b + 1,
-    .heuristic = true,
-    .too_expensive = too_expensive,
-    .time_limit = time_limit,
-  };
 
-  /* compareseq requires indices to be zero-based.  We add BEGV back
-     later.  */
   bool early_abort;
-  if (! sys_setjmp (ctx.jmp))
-    early_abort = compareseq (0, size_a, 0, size_b, false, &ctx);
+  struct context ctx;
+
+  if (buffer)
+    {
+      /* Copy the characters to arrays of C ints.  This speeds up
+	 comparison dramatically in multibyte buffers.  */
+      int *chars_a = (int *) (((uintptr_t) (buffer + 2 * diags) + align_bytes)
+			      & ~align_bytes);
+      for (ptrdiff_t i = 0; i < size_a; i++)
+	chars_a[i] = (BUF_FETCH_CHAR_AS_MULTIBYTE
+		      (a, buf_charpos_to_bytepos (a, min_a + i)));
+
+      int *chars_b = chars_a + size_a;
+      for (ptrdiff_t i = 0; i < size_b; i++)
+	chars_b[i] = (BUF_FETCH_CHAR_AS_MULTIBYTE
+		      (b, buf_charpos_to_bytepos (b, min_b + i)));
+
+      unsigned char *deletions_insertions = memset (chars_b + size_b, 0,
+						    del_bytes + ins_bytes);
+
+      /* 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.  */
+      ctx = (struct context) {
+	.chars_a = chars_a,
+	.chars_b = chars_b,
+	.deletions = deletions_insertions,
+	.insertions = deletions_insertions + del_bytes,
+	.fdiag = buffer + size_b + 1,
+	.bdiag = buffer + diags + size_b + 1,
+	.heuristic = true,
+	.too_expensive = too_expensive,
+	.time_limit = time_limit,
+      };
+
+      /* compareseq wants zero-based indices.  We add BEGV back later.  */
+      if (! sys_setjmp (ctx.jmp))
+	early_abort = compareseq (0, size_a, 0, size_b, false, &ctx);
+      else
+	early_abort = true;
+    }
   else
     early_abort = true;
 
@@ -2141,8 +2158,7 @@ a buffer or a string.  But this is deprecated.  */)
 			       make_fixnum (BUF_BEGV (b)),
 			       make_fixnum (BUF_ZV (b)));
       replace_range (min_a, min_a + size_a, src, true, false, inh);
-      SAFE_FREE_UNBIND_TO (count, Qnil);
-      return Qnil;
+      return unbind_to (count, Qnil);
     }
 
   Fundo_boundary ();
@@ -2196,7 +2212,7 @@ a buffer or a string.  But this is deprecated.  */)
       --j;
     }
 
-  SAFE_FREE_UNBIND_TO (count, Qnil);
+  unbind_to (count, Qnil);
 
   if (modification_hooks_inhibited)
     {
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.