m4-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU M4 source repository branch, branch-1.6, updated. v1.5.89a-76-


From: Eric Blake
Subject: [SCM] GNU M4 source repository branch, branch-1.6, updated. v1.5.89a-76-g5559d1c
Date: Tue, 16 Dec 2008 13:53:14 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU M4 source repository".

http://git.sv.gnu.org/gitweb/?p=m4.git;a=commitdiff;h=5559d1ca4d1f516c2b7fb44ee92cb8f6bac94402

The branch, branch-1.6 has been updated
       via  5559d1ca4d1f516c2b7fb44ee92cb8f6bac94402 (commit)
       via  b1d5d313974f058276f3dfa16f466165a9cce1e3 (commit)
       via  3313c558181d60991791b2b59d2d2ea8fd14a3ec (commit)
       via  abeee5b25e13d906ef449845f20707f1cb0c20ff (commit)
       via  731f4eb7d939f6bdd95a27352019d5a418b956f8 (commit)
      from  715c42128d8d357e3e751ec605069137d693c757 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 5559d1ca4d1f516c2b7fb44ee92cb8f6bac94402
Author: Eric Blake <address@hidden>
Date:   Wed Dec 10 07:16:51 2008 -0700

    Double size of temp file cache.
    
    * src/output.c (tmp_file, tmp_file_owner): Split...
    (tmp_file1, tmp_file2, tmp_file1_owner, tmp_file2_owner): ...into
    two variables.
    (tmp_file2_recent): New variable.
    (m4_tmpopen, m4_tmpclose, m4_tmpremove, m4_tmprename)
    (output_exit): Adjust callers.
    
    Signed-off-by: Eric Blake <address@hidden>

commit b1d5d313974f058276f3dfa16f466165a9cce1e3
Author: Eric Blake <address@hidden>
Date:   Wed Dec 10 06:15:53 2008 -0700

    Use fewer seeks on cached files.
    
    * src/output.c (m4_tmpfile): Use write, not append mode.
    (m4_tmpopen): Add parameter to decide when to skip seeks.
    (m4_tmprename, make_diversion, insert_diversion_helper)
    (freeze_diversions): Adjust callers.
    
    Signed-off-by: Eric Blake <address@hidden>

commit 3313c558181d60991791b2b59d2d2ea8fd14a3ec
Author: Eric Blake <address@hidden>
Date:   Tue Dec 9 21:36:32 2008 -0700

    Cache most recently spilled diversion.
    
    * src/output.c (tmp_file, tmp_file_owner): New variables, for
    1-deep cache of spilled diversions.
    (m4_tmpfile): Open in append mode, since we might revisit
    diversion without closing it now.
    (m4_tmpopen): Check cache first.
    (m4_tmpclose): Update cache, rather than closing.  Add parameter.
    (m4_tmpremove): Close cache before removing.
    (m4_tmprename): Deal with open files when renaming.
    (output_exit): Close cache before exiting.
    (make_room_for, make_diversion, insert_diversion_helper): Adjust
    callers.
    * configure.ac (RENAME_OPEN_FILE_WORKS): New configure test.
    
    Signed-off-by: Eric Blake <address@hidden>

commit abeee5b25e13d906ef449845f20707f1cb0c20ff
Author: Eric Blake <address@hidden>
Date:   Tue Dec 9 21:23:44 2008 -0700

    Correctly track size of in-memory diversions.
    
    * src/output.c (insert_diversion_helper): Correctly track total
    in-memory diversion size after undivert.
    
    Signed-off-by: Eric Blake <address@hidden>

commit 731f4eb7d939f6bdd95a27352019d5a418b956f8
Author: Eric Blake <address@hidden>
Date:   Tue Dec 9 20:56:37 2008 -0700

    Avoid quadratic behavior for some cases of divert/undivert.
    
    * src/output.c (struct m4_diversion): Improve comments.
    (m4_tmpname, make_diversion): Strengthen preconditions.
    (m4_tmprename): New function.
    (output_init, output_exit): Move after internal functions.
    (make_room_for): Don't bother copying uninitialized bytes.
    (insert_diversion_helper): Transfer metadata, rather than copying
    contents, when undiverting into a previously unused diversion.
    * doc/m4.texinfo (Diversions): Add test.
    (Undivert): Enhance test.
    * NEWS: Document the speedup.
    
    Signed-off-by: Eric Blake <address@hidden>

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog      |   46 +++++++++
 NEWS           |    2 +-
 configure.ac   |   18 ++++
 doc/m4.texinfo |   29 ++++++
 src/output.c   |  299 ++++++++++++++++++++++++++++++++++++++++++++------------
 5 files changed, 330 insertions(+), 64 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e991a8c..63a0516 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,49 @@
+2008-12-10  Eric Blake  <address@hidden>
+
+       Double size of temp file cache.
+       * src/output.c (tmp_file, tmp_file_owner): Split...
+       (tmp_file1, tmp_file2, tmp_file1_owner, tmp_file2_owner): ...into
+       two variables.
+       (tmp_file2_recent): New variable.
+       (m4_tmpopen, m4_tmpclose, m4_tmpremove, m4_tmprename)
+       (output_exit): Adjust callers.
+
+       Use fewer seeks on cached files.
+       * src/output.c (m4_tmpfile): Use write, not append mode.
+       (m4_tmpopen): Add parameter to decide when to skip seeks.
+       (m4_tmprename, make_diversion, insert_diversion_helper)
+       (freeze_diversions): Adjust callers.
+
+       Cache most recently spilled diversion.
+       * src/output.c (tmp_file, tmp_file_owner): New variables, for
+       1-deep cache of spilled diversions.
+       (m4_tmpfile): Open in append mode, since we might revisit
+       diversion without closing it now.
+       (m4_tmpopen): Check cache first.
+       (m4_tmpclose): Update cache, rather than closing.  Add parameter.
+       (m4_tmpremove): Close cache before removing.
+       (m4_tmprename): Deal with open files when renaming.
+       (output_exit): Close cache before exiting.
+       (make_room_for, make_diversion, insert_diversion_helper): Adjust
+       callers.
+       * configure.ac (RENAME_OPEN_FILE_WORKS): New configure test.
+
+       Correctly track size of in-memory diversions.
+       * src/output.c (insert_diversion_helper): Correctly track total
+       in-memory diversion size after undivert.
+
+       Avoid quadratic behavior for some cases of divert/undivert.
+       * src/output.c (struct m4_diversion): Improve comments.
+       (m4_tmpname, make_diversion): Strengthen preconditions.
+       (m4_tmprename): New function.
+       (output_init, output_exit): Move after internal functions.
+       (make_room_for): Don't bother copying uninitialized bytes.
+       (insert_diversion_helper): Transfer metadata, rather than copying
+       contents, when undiverting into a previously unused diversion.
+       * doc/m4.texinfo (Diversions): Add test.
+       (Undivert): Enhance test.
+       * NEWS: Document the speedup.
+
 2008-12-03  Eric Blake  <address@hidden>
 
        Stage 27: Allow embedded NUL in text processing macros.
diff --git a/NEWS b/NEWS
index 3fb4d90..df960e7 100644
--- a/NEWS
+++ b/NEWS
@@ -45,7 +45,7 @@ Foundation, Inc.
 ** The `divert' builtin now accepts an optional second argument of text
    that is immediately placed in the new diversion, regardless of whether
    the current expansion is nested within argument collection of another
-   macro.
+   macro.  It has also been optimized for faster performance.
 
 ** The `-d'/`--debug' command-line option now understands `-' and `+'
    modifiers, the way the builtin `debugmode' has always done; this allows
diff --git a/configure.ac b/configure.ac
index ea4e130..59b7311 100644
--- a/configure.ac
+++ b/configure.ac
@@ -138,6 +138,24 @@ if test "$M4_cv_func_system_consistent" = no ; then
     [Define to 1 if the return value of system() disagrees with pclose().])
 fi
 
+AC_CACHE_CHECK([whether an open file can be renamed],
+  [M4_cv_func_rename_open_file_works],
+  [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+      [FILE *f = fopen ("conftest.1", "w+");
+       int result = rename ("conftest.1", "conftest.2");
+       fclose (f); remove ("conftest.1"); remove ("conftest.2");
+       return result;])],
+    [M4_cv_func_rename_open_file_works=yes],
+    [M4_cv_func_rename_open_file_works=no],
+    [M4_cv_func_rename_open_file_works='guessing no'])])
+if test "$M4_cv_func_rename_open_file_works" = yes ; then
+  M4_rename_open_works=1
+else
+  M4_rename_open_works=0
+fi
+AC_DEFINE_UNQUOTED([RENAME_OPEN_FILE_WORKS], [$M4_rename_open_works],
+  [Define to 1 if a file can be renamed while open, or to 0 if not.])
+
 dnl Don't let changeword get in our way, if bootstrapping with a version of
 dnl m4 that already turned the feature on.
 m4_ifdef([changeword], [m4_undefine([changeword])])dnl
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 2fb676d..7c6bf56 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -5530,7 +5530,12 @@ len(f)
 @result{}1048576
 divert(`1')
 f
+divert(`2')
+f
 divert(`-1')undivert
+divert(`1')bye
+^D
address@hidden
 @end example
 
 @comment Another test of spilled diversions.
@@ -5578,6 +5583,22 @@ format(%1000000d, 1)' | ]__program__[ | sed -n 1p])dnl
 sysval
 @result{}0
 @end example
+
address@hidden Avoid quadratic copying time when transferring diversions;
address@hidden test both in-memory and spilled to file.
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+include(`forloop2.m4')dnl
+divert(`1')format(`%10000s', `')dnl
+forloop(`i', `1', `10000',
+  `divert(incr(i))undivert(i)')dnl
+divert(`9001')format(`%1000000s', `')dnl
+forloop(`i', `9001', `10000',
+  `divert(incr(i))undivert(i)')dnl
+divert(`-1')undivert
address@hidden example
 @end ignore
 
 Diversions make it possible to generate output in a different order than
@@ -5805,6 +5826,14 @@ undivert(`0')
 undivert
 @result{}diverted text
 @result{}
+divert(`1')more
+divert(`2')undivert(`1')diverted text`'divert
address@hidden
+undivert(`1')
address@hidden
+undivert(`2')
address@hidden
address@hidden text
 @end example
 
 When a diversion has been undiverted, the diverted text is discarded,
diff --git a/src/output.c b/src/output.c
index 6a02b80..1add1a9 100644
--- a/src/output.c
+++ b/src/output.c
@@ -57,13 +57,13 @@ struct m4_diversion
   {
     union
       {
-       FILE *file;             /* diversion file on disk */
-       char *buffer;           /* in-memory diversion buffer */
-       m4_diversion *next;     /* free-list pointer */
+       FILE *file;             /* Diversion file on disk.  */
+       char *buffer;           /* Malloc'd diversion buffer.  */
+       m4_diversion *next;     /* Free-list pointer */
       } u;
-    int divnum;                        /* which diversion this represents */
-    int size;                  /* usable size before reallocation */
-    int used;                  /* used length in characters */
+    int divnum;                        /* Which diversion this represents.  */
+    int size;                  /* Usable size before reallocation.  */
+    int used;                  /* Used buffer length, or tmp file exists.  */
   };
 
 /* Table of diversions 1 through INT_MAX.  */
@@ -85,13 +85,25 @@ static int total_buffer_size;
    maintained for the `divnum' builtin function.  */
 int current_diversion;
 
-/* Current output diversion, NULL if output is being currently discarded.  */
+/* Current output diversion, NULL if output is being currently
+   discarded.  output_diversion->u is guaranteed non-NULL except when
+   the diversion has never been used; use size to determine if it is a
+   malloc'd buffer or a FILE.  output_diversion->used is 0 if u.file
+   is stdout, and non-zero if this is a malloc'd buffer or a temporary
+   diversion file.  */
 static m4_diversion *output_diversion;
 
-/* Values of some output_diversion fields, cached out for speed.  */
-static FILE *output_file;      /* current value of (file) */
-static char *output_cursor;    /* current value of (buffer + used) */
-static int output_unused;      /* current value of (size - used) */
+/* Cache of output_diversion->u.file, only valid when
+   output_diversion->size is 0.  */
+static FILE *output_file;
+
+/* Cache of output_diversion->u.buffer + output_diversion->used, only
+   valid when output_diversion->size is non-zero.  */
+static char *output_cursor;
+
+/* Cache of output_diversion->size - output_diversion->used, only
+   valid when output_diversion->size is non-zero.  */
+static int output_unused;
 
 /* Number of input line we are generating output for.  */
 int output_current_line;
@@ -99,11 +111,19 @@ int output_current_line;
 /* Temporary directory holding all spilled diversion files.  */
 static m4_temp_dir *output_temp_dir;
 
+/* Cache of most recently used spilled diversion files.  */
+static FILE *tmp_file1;
+static FILE *tmp_file2;
+
+/* Diversions that own tmp_file, or 0.  */
+static int tmp_file1_owner;
+static int tmp_file2_owner;
+
+/* True if tmp_file2 is more recently used.  */
+static bool tmp_file2_recent;
 
 
-/*------------------------.
-| Output initialization.  |
-`------------------------*/
+/* Internal routines.  */
 
 /* Callback for comparing list elements ELT1 and ELT2 for order in
    diversion_table.  */
@@ -127,28 +147,6 @@ threshold_diversion_CB (const void *elt, const void 
*threshold)
   return diversion->divnum >= *(const int *) threshold;
 }
 
-void
-output_init (void)
-{
-  diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB,
-                                         NULL);
-  div0.u.file = stdout;
-  output_diversion = &div0;
-  output_file = stdout;
-  obstack_init (&diversion_storage);
-}
-
-void
-output_exit (void)
-{
-  /* Order is important, since we may have registered cleanup_tmpfile
-     as an atexit handler, and it must not traverse stale memory.  */
-  gl_oset_t table = diversion_table;
-  diversion_table = NULL;
-  gl_oset_free (table);
-  obstack_free (&diversion_storage, NULL);
-}
-
 /* Clean up any temporary directory.  Designed for use as an atexit
    handler, where it is not safe to call exit() recursively; so this
    calls _exit if a problem is encountered.  */
@@ -196,6 +194,7 @@ m4_tmpname (int divnum)
       buffer = (char *) obstack_alloc (&diversion_storage,
                                       INT_BUFSIZE_BOUND (divnum));
     }
+  assert (0 < divnum);
   if (snprintf (&buffer[offset], INT_BUFSIZE_BOUND (divnum), "%d", divnum) < 0)
     m4_error (EXIT_FAILURE, errno, NULL,
              _("cannot create temporary file for diversion"));
@@ -205,9 +204,10 @@ m4_tmpname (int divnum)
 /* Create a temporary file for diversion DIVNUM open for reading and
    writing in a secure temp directory.  The file will be automatically
    closed and deleted on a fatal signal.  The file can be closed and
-   reopened with m4_tmpclose and m4_tmpopen; when finally done with
-   the file, close it with m4_tmpremove.  Exits on failure, so the
-   return value is always an open file.  */
+   reopened with m4_tmpclose and m4_tmpopen, or moved with
+   m4_tmprename; when finally done with the file, close it with
+   m4_tmpremove.  Exits on failure, so the return value is always an
+   open file.  */
 static FILE *
 m4_tmpfile (int divnum)
 {
@@ -237,42 +237,175 @@ m4_tmpfile (int divnum)
 }
 
 /* Reopen a temporary file for diversion DIVNUM for reading and
-   writing in a secure temp directory.  Exits on failure, so the
-   return value is always an open file.  */
+   writing in a secure temp directory.  If REREAD, the file is
+   positioned at offset 0, otherwise the file is positioned at the
+   end.  Exits on failure, so the return value is always an open
+   file.  */
 static FILE *
-m4_tmpopen (int divnum)
+m4_tmpopen (int divnum, bool reread)
 {
-  const char *name = m4_tmpname (divnum);
+  const char *name;
   FILE *file;
 
-  file = fopen_temp (name, O_BINARY ? "ab+" : "a+");
+  if (tmp_file1_owner == divnum)
+    {
+      if (reread && fseeko (tmp_file1, 0, SEEK_SET) != 0)
+       m4_error (EXIT_FAILURE, errno, NULL,
+                 _("cannot seek within diversion"));
+      tmp_file2_recent = false;
+      return tmp_file1;
+    }
+  else if (tmp_file2_owner == divnum)
+    {
+      if (reread && fseeko (tmp_file2, 0, SEEK_SET) != 0)
+       m4_error (EXIT_FAILURE, errno, NULL,
+                 _("cannot seek within diversion"));
+      tmp_file2_recent = true;
+      return tmp_file2;
+    }
+  name = m4_tmpname (divnum);
+  /* We need update mode, to avoid truncation.  */
+  file = fopen_temp (name, O_BINARY ? "rb+" : "r+");
   if (file == NULL)
     m4_error (EXIT_FAILURE, errno, NULL,
              _("cannot create temporary file for diversion"));
   else if (set_cloexec_flag (fileno (file), true) != 0)
     m4_warn (errno, NULL, _("cannot protect diversion across forks"));
-  /* POSIX states that it is undefined whether an append stream starts
-     at offset 0 or at the end.  We want the beginning.  */
-  else if (fseeko (file, 0, SEEK_SET) != 0)
+  /* Update mode starts at the beginning of the stream, but sometimes
+     we want the end.  */
+  else if (!reread && fseeko (file, 0, SEEK_END) != 0)
     m4_error (EXIT_FAILURE, errno, NULL,
-             _("cannot seek to beginning of diversion"));
+             _("cannot seek within diversion"));
   return file;
 }
 
-/* Close, but don't delete, a temporary FILE.  */
+/* Close, but don't delete, a temporary FILE for diversion DIVNUM.  To
+   reduce the I/O overhead of repeatedly opening and closing the same
+   file, this implementation caches the most recent spilled diversion.
+   On the other hand, keeping every spilled diversion open would run
+   into EMFILE limits.  */
 static int
-m4_tmpclose (FILE *file)
+m4_tmpclose (FILE *file, int divnum)
 {
-  return close_stream_temp (file);
+  int result = 0;
+  if (divnum != tmp_file1_owner && divnum != tmp_file2_owner)
+    {
+      if (tmp_file2_recent)
+       {
+         if (tmp_file1_owner)
+           result = close_stream_temp (tmp_file1);
+         tmp_file1 = file;
+         tmp_file1_owner = divnum;
+       }
+      else
+       {
+         if (tmp_file2_owner)
+           result = close_stream_temp (tmp_file2);
+         tmp_file2 = file;
+         tmp_file2_owner = divnum;
+       }
+    }
+  return result;
 }
 
 /* Delete a closed temporary FILE for diversion DIVNUM.  */
 static int
 m4_tmpremove (int divnum)
 {
+  if (divnum == tmp_file1_owner)
+    {
+      int result = close_stream_temp (tmp_file1);
+      if (result)
+       return result;
+      tmp_file1_owner = 0;
+    }
+  else if (divnum == tmp_file2_owner)
+    {
+      int result = close_stream_temp (tmp_file2);
+      if (result)
+       return result;
+      tmp_file2_owner = 0;
+    }
   return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum));
 }
 
+/* Transfer the temporary file for diversion OLDNUM to the previously
+   unused diversion NEWNUM.  Return an open stream visiting the new
+   temporary file, positioned at the end, or exit on failure.  */
+static FILE*
+m4_tmprename (int oldnum, int newnum)
+{
+  /* m4_tmpname reuses its return buffer.  */
+  char *oldname = xstrdup (m4_tmpname (oldnum));
+  const char *newname = m4_tmpname (newnum);
+  register_temp_file (output_temp_dir, newname);
+  if (oldnum == tmp_file1_owner)
+    {
+      /* Be careful of mingw, which can't rename an open file.  */
+      if (RENAME_OPEN_FILE_WORKS)
+       tmp_file1_owner = newnum;
+      else
+       {
+         if (close_stream_temp (tmp_file1))
+           m4_error (EXIT_FAILURE, errno, NULL,
+                     _("cannot close temporary file for diversion"));
+         tmp_file1_owner = 0;
+       }
+    }
+  else if (oldnum == tmp_file2_owner)
+    {
+      /* Be careful of mingw, which can't rename an open file.  */
+      if (RENAME_OPEN_FILE_WORKS)
+       tmp_file2_owner = newnum;
+      else
+       {
+         if (close_stream_temp (tmp_file2))
+           m4_error (EXIT_FAILURE, errno, NULL,
+                     _("cannot close temporary file for diversion"));
+         tmp_file2_owner = 0;
+       }
+    }
+  /* Either it is safe to rename an open file, or no one should have
+     oldname open at this point.  */
+  if (rename (oldname, newname))
+    m4_error (EXIT_FAILURE, errno, NULL,
+             _("cannot create temporary file for diversion"));
+  unregister_temp_file (output_temp_dir, oldname);
+  free (oldname);
+  return m4_tmpopen (newnum, false);
+}
+
+
+/*------------------------.
+| Output initialization.  |
+`------------------------*/
+
+void
+output_init (void)
+{
+  diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB,
+                                         NULL);
+  div0.u.file = stdout;
+  output_diversion = &div0;
+  output_file = stdout;
+  obstack_init (&diversion_storage);
+}
+
+void
+output_exit (void)
+{
+  /* Order is important, since we may have registered cleanup_tmpfile
+     as an atexit handler, and it must not traverse stale memory.  */
+  gl_oset_t table = diversion_table;
+  if (tmp_file1_owner)
+    m4_tmpremove (tmp_file1_owner);
+  if (tmp_file2_owner)
+    m4_tmpremove (tmp_file2_owner);
+  diversion_table = NULL;
+  gl_oset_free (table);
+  obstack_free (&diversion_storage, NULL);
+}
+
 /*-----------------------------------------------------------------------.
 | Reorganize in-memory diversion buffers so the current diversion can   |
 | accomodate LENGTH more characters without further reorganization.  The |
@@ -375,14 +508,18 @@ make_room_for (int length)
        {
          FILE *file = selected_diversion->u.file;
          selected_diversion->u.file = NULL;
-         if (m4_tmpclose (file) != 0)
+         if (m4_tmpclose (file, selected_diversion->divnum) != 0)
            m4_warn (errno, NULL,
                     _("cannot close temporary file for diversion"));
        }
 
       /* The current buffer may be safely reallocated.  */
-      output_diversion->u.buffer = xrealloc (output_diversion->u.buffer,
-                                            (size_t) wanted_size);
+      {
+       char *buffer = output_diversion->u.buffer;
+       output_diversion->u.buffer = xcharalloc ((size_t) wanted_size);
+       memcpy (output_diversion->u.buffer, buffer, output_diversion->used);
+       free (buffer);
+      }
 
       total_buffer_size += wanted_size - output_diversion->size;
       output_diversion->size = wanted_size;
@@ -613,10 +750,10 @@ make_diversion (int divnum)
     {
       if (!output_diversion->size && !output_diversion->u.file)
        {
+         assert (!output_diversion->used);
          if (!gl_oset_remove (diversion_table, output_diversion))
            assert (false);
          output_diversion->u.next = free_list;
-         output_diversion->used = 0;
          free_list = output_diversion;
        }
       else if (output_diversion->size)
@@ -625,7 +762,7 @@ make_diversion (int divnum)
        {
          FILE *file = output_diversion->u.file;
          output_diversion->u.file = NULL;
-         if (m4_tmpclose (file) != 0)
+         if (m4_tmpclose (file, output_diversion->divnum) != 0)
            m4_warn (errno, NULL,
                     _("cannot close temporary file for diversion"));
        }
@@ -682,7 +819,8 @@ make_diversion (int divnum)
   else
     {
       if (!output_diversion->u.file && output_diversion->used)
-       output_diversion->u.file = m4_tmpopen (output_diversion->divnum);
+       output_diversion->u.file = m4_tmpopen (output_diversion->divnum,
+                                              false);
       output_file = output_diversion->u.file;
     }
   output_current_line = -1;
@@ -732,11 +870,45 @@ insert_diversion_helper (m4_diversion *diversion)
   if (output_diversion)
     {
       if (diversion->size)
-       output_text (diversion->u.buffer, diversion->used);
+       {
+         if (!output_diversion->u.file)
+           {
+             /* Transferring diversion metadata is faster than
+                copying contents.  */
+             assert (!output_diversion->used && output_diversion != &div0
+                     && !output_file);
+             output_diversion->u.buffer = diversion->u.buffer;
+             output_diversion->size = diversion->size;
+             output_cursor = diversion->u.buffer + diversion->used;
+             output_unused = diversion->size - diversion->used;
+             diversion->u.buffer = NULL;
+           }
+         else
+           {
+             /* Avoid double-charging the total in-memory size when
+                transferring from one in-memory diversion to
+                another.  */
+             total_buffer_size -= diversion->size;
+             output_text (diversion->u.buffer, diversion->used);
+           }
+       }
+      else if (!output_diversion->u.file)
+       {
+         /* Transferring diversion metadata is faster than copying
+            contents.  */
+         assert (!output_diversion->used && output_diversion != &div0
+                 && !output_file);
+         output_diversion->u.file = m4_tmprename (diversion->divnum,
+                                                  output_diversion->divnum);
+         output_diversion->used = 1;
+         output_file = output_diversion->u.file;
+         diversion->u.file = NULL;
+         diversion->size = 1;
+       }
       else
        {
          if (!diversion->u.file)
-           diversion->u.file = m4_tmpopen (diversion->divnum);
+           diversion->u.file = m4_tmpopen (diversion->divnum, true);
          insert_file (diversion->u.file);
        }
 
@@ -746,9 +918,10 @@ insert_diversion_helper (m4_diversion *diversion)
   /* Return all space used by the diversion.  */
   if (diversion->size)
     {
+      if (!output_diversion)
+       total_buffer_size -= diversion->size;
       free (diversion->u.buffer);
       diversion->size = 0;
-      diversion->used = 0;
     }
   else
     {
@@ -756,14 +929,14 @@ insert_diversion_helper (m4_diversion *diversion)
        {
          FILE *file = diversion->u.file;
          diversion->u.file = NULL;
-         diversion->used = 0;
-         if (m4_tmpclose (file) != 0)
+         if (m4_tmpclose (file, diversion->divnum) != 0)
            m4_warn (errno, NULL,
                     _("cannot clean temporary file for diversion"));
        }
       if (m4_tmpremove (diversion->divnum) != 0)
        m4_warn (errno, NULL, _("cannot clean temporary file for diversion"));
     }
+  diversion->used = 0;
   gl_oset_remove (diversion_table, diversion);
   diversion->u.next = free_list;
   free_list = diversion;
@@ -840,7 +1013,7 @@ freeze_diversions (FILE *file)
          else
            {
              struct stat file_stat;
-             diversion->u.file = m4_tmpopen (diversion->divnum);
+             diversion->u.file = m4_tmpopen (diversion->divnum, true);
              if (fstat (fileno (diversion->u.file), &file_stat) < 0)
                m4_error (EXIT_FAILURE, errno, NULL,
                          _("cannot stat diversion"));


hooks/post-receive
--
GNU M4 source repository




reply via email to

[Prev in Thread] Current Thread [Next in Thread]