]> Savannah Git Hosting - gnulib.git/commitdiff
string-buffer: New module.
authorBruno Haible <bruno@clisp.org>
Sun, 21 Feb 2021 20:39:07 +0000 (21:39 +0100)
committerBruno Haible <bruno@clisp.org>
Sun, 21 Feb 2021 20:41:33 +0000 (21:41 +0100)
* lib/string-buffer.h: New file.
* lib/string-buffer.c: New file.
* modules/string-buffer: New file.
* doc/posix-functions/open_memstream.texi: Mention the new module.

ChangeLog
doc/posix-functions/open_memstream.texi
lib/string-buffer.c [new file with mode: 0644]
lib/string-buffer.h [new file with mode: 0644]
modules/string-buffer [new file with mode: 0644]

index e1c6466a26cb3907d063c7fff7df438d73fddf08..9c0e9b8a47fc652859eac694284e1324f69d4365 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2021-02-21  Bruno Haible  <bruno@clisp.org>
+
+       string-buffer: New module.
+       * lib/string-buffer.h: New file.
+       * lib/string-buffer.c: New file.
+       * modules/string-buffer: New file.
+       * doc/posix-functions/open_memstream.texi: Mention the new module.
+
 2021-02-21  Bruno Haible  <bruno@clisp.org>
 
        scratch_buffer: Document the exported API.
index e28766478f0cc0a25f15a4a453c7cfe172848614..479738e7123f06d979a8035f59713af8c3286311 100644 (file)
@@ -16,3 +16,6 @@ Portability problems not fixed by Gnulib:
 This function is missing on some platforms:
 Mac OS X 10.5, FreeBSD 6.0, NetBSD 7.1, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.3, Cygwin 1.5.x, mingw, MSVC 14, Android 5.1.
 @end itemize
+
+An alternative to the @code{open_memstream} function is the Gnulib module
+@code{string-buffer}.
diff --git a/lib/string-buffer.c b/lib/string-buffer.c
new file mode 100644 (file)
index 0000000..b5a4009
--- /dev/null
@@ -0,0 +1,297 @@
+/* A buffer that accumulates a string by piecewise concatenation.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2021.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "string-buffer.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void
+sb_init (struct string_buffer *buffer)
+{
+  buffer->data = buffer->space;
+  buffer->length = 0;
+  buffer->allocated = sizeof (buffer->space);
+  buffer->error = false;
+}
+
+/* Ensures that INCREMENT bytes are available beyond the current used length
+   of BUFFER.
+   Returns 0, or -1 in case of out-of-memory error.  */
+static int
+sb_ensure_more_bytes (struct string_buffer *buffer, size_t increment)
+{
+  size_t incremented_length = buffer->length + increment;
+  if (incremented_length < increment)
+    /* Overflow.  */
+    return -1;
+
+  if (buffer->allocated < incremented_length)
+    {
+      size_t new_allocated = 2 * buffer->allocated;
+      if (new_allocated < buffer->allocated)
+        /* Overflow.  */
+        return -1;
+      if (new_allocated < incremented_length)
+        new_allocated = incremented_length;
+
+      char *new_data;
+      if (buffer->data == buffer->space)
+        {
+          new_data = (char *) malloc (new_allocated);
+          if (new_data == NULL)
+            /* Out-of-memory.  */
+            return -1;
+          memcpy (new_data, buffer->data, buffer->length);
+        }
+      else
+        {
+          new_data = (char *) realloc (buffer->data, new_allocated);
+          if (new_data == NULL)
+            /* Out-of-memory.  */
+            return -1;
+        }
+      buffer->data = new_data;
+      buffer->allocated = new_allocated;
+    }
+  return 0;
+}
+
+int
+sb_append (struct string_buffer *buffer, const char *str)
+{
+  size_t len = strlen (str);
+  if (sb_ensure_more_bytes (buffer, len) < 0)
+    {
+      buffer->error = true;
+      return -1;
+    }
+  memcpy (buffer->data + buffer->length, str, len);
+  buffer->length += len;
+  return 0;
+}
+
+int
+sb_appendvf (struct string_buffer *buffer, const char *formatstring,
+             va_list list)
+{
+  va_list list_copy;
+
+  /* Make a bit of room, so that the probability that the first vsnprintf() call
+     succeeds is high.  */
+  size_t room = buffer->allocated - buffer->length;
+  if (room < 64)
+    {
+      if (sb_ensure_more_bytes (buffer, 64) < 0)
+        {
+          buffer->error = true;
+          return -1;
+        }
+      room = buffer->allocated - buffer->length;
+    }
+
+  va_copy (list_copy, list);
+
+  /* First vsnprintf() call.  */
+  int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, list);
+  if (ret < 0)
+    {
+      /* Failed.  */
+      buffer->error = true;
+      ret = -1;
+    }
+  else
+    {
+      if ((size_t) ret <= room)
+        {
+          /* The result has fit into room bytes.  */
+          buffer->length += (size_t) ret;
+          ret = 0;
+        }
+      else
+        {
+          /* The result was truncated.  Make more room, for a second vsnprintf()
+             call.  */
+          if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
+            {
+              buffer->error = true;
+              ret = -1;
+            }
+          else
+            {
+              /* Second vsnprintf() call.  */
+              room = buffer->allocated - buffer->length;
+              ret = vsnprintf (buffer->data + buffer->length, room,
+                               formatstring, list_copy);
+              if (ret < 0)
+                {
+                  /* Failed.  */
+                  buffer->error = true;
+                  ret = -1;
+                }
+              else
+                {
+                  if ((size_t) ret <= room)
+                    {
+                      /* The result has fit into room bytes.  */
+                      buffer->length += (size_t) ret;
+                      ret = 0;
+                    }
+                  else
+                    /* The return values of the vsnprintf() calls are not
+                       consistent.  */
+                    abort ();
+                }
+            }
+        }
+    }
+
+  va_end (list_copy);
+  return ret;
+}
+
+int
+sb_appendf (struct string_buffer *buffer, const char *formatstring, ...)
+{
+  va_list args;
+
+  /* Make a bit of room, so that the probability that the first vsnprintf() call
+     succeeds is high.  */
+  size_t room = buffer->allocated - buffer->length;
+  if (room < 64)
+    {
+      if (sb_ensure_more_bytes (buffer, 64) < 0)
+        {
+          buffer->error = true;
+          return -1;
+        }
+      room = buffer->allocated - buffer->length;
+    }
+
+  va_start (args, formatstring);
+
+  /* First vsnprintf() call.  */
+  int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, args);
+  if (ret < 0)
+    {
+      /* Failed.  */
+      buffer->error = true;
+      ret = -1;
+    }
+  else
+    {
+      if ((size_t) ret <= room)
+        {
+          /* The result has fit into room bytes.  */
+          buffer->length += (size_t) ret;
+          ret = 0;
+        }
+      else
+        {
+          /* The result was truncated.  Make more room, for a second vsnprintf()
+             call.  */
+          if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
+            {
+              buffer->error = true;
+              ret = -1;
+            }
+          else
+            {
+              /* Second vsnprintf() call.  */
+              room = buffer->allocated - buffer->length;
+              va_end (args);
+              va_start (args, formatstring);
+              ret = vsnprintf (buffer->data + buffer->length, room,
+                               formatstring, args);
+              if (ret < 0)
+                {
+                  /* Failed.  */
+                  buffer->error = true;
+                  ret = -1;
+                }
+              else
+                {
+                  if ((size_t) ret <= room)
+                    {
+                      /* The result has fit into room bytes.  */
+                      buffer->length += (size_t) ret;
+                      ret = 0;
+                    }
+                  else
+                    /* The return values of the vsnprintf() calls are not
+                       consistent.  */
+                    abort ();
+                }
+            }
+        }
+    }
+
+  va_end (args);
+  return ret;
+}
+
+void
+sb_free (struct string_buffer *buffer)
+{
+  if (buffer->data != buffer->space)
+    free (buffer->data);
+}
+
+/* Returns the contents of BUFFER, and frees all other memory held
+   by BUFFER.  Returns NULL upon failure or if there was an error earlier.  */
+char *
+sb_dupfree (struct string_buffer *buffer)
+{
+  if (buffer->error)
+    goto fail;
+
+  if (sb_ensure_more_bytes (buffer, 1) < 0)
+    goto fail;
+  buffer->data[buffer->length] = '\0';
+  buffer->length++;
+
+  if (buffer->data == buffer->space)
+    {
+      char *copy = (char *) malloc (buffer->length);
+      if (copy == NULL)
+        goto fail;
+      memcpy (copy, buffer->data, buffer->length);
+      return copy;
+    }
+  else
+    {
+      /* Shrink the string before returning it.  */
+      char *contents = buffer->data;
+      if (buffer->length < buffer->allocated)
+        {
+          contents = realloc (contents, buffer->length);
+          if (contents == NULL)
+            goto fail;
+        }
+      return contents;
+    }
+
+ fail:
+  sb_free (buffer);
+  return NULL;
+}
diff --git a/lib/string-buffer.h b/lib/string-buffer.h
new file mode 100644 (file)
index 0000000..6beaff5
--- /dev/null
@@ -0,0 +1,85 @@
+/* A buffer that accumulates a string by piecewise concatenation.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2021.  */
+
+#ifndef _STRING_BUFFER_H
+#define _STRING_BUFFER_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "attribute.h"
+
+/* A string buffer type.  */
+struct string_buffer
+{
+  char *data;
+  size_t length;     /* used bytes, <= allocated */
+  size_t allocated;  /* allocated bytes */
+  bool error;        /* true if there was an error */
+  char space[1024];  /* stack allocated space */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initializes BUFFER to the empty string.  */
+extern void sb_init (struct string_buffer *buffer);
+
+/* Appends the contents of STR to BUFFER.
+   Returns 0, or -1 in case of out-of-memory error.  */
+extern int sb_append (struct string_buffer *buffer, const char *str);
+
+/* Appends the result of the printf-compatible FORMATSTRING with the argument
+   list LIST to BUFFER.
+   Returns 0, or -1 in case of error.  */
+extern int sb_appendvf (struct string_buffer *buffer,
+                        const char *formatstring, va_list list)
+  #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
+  ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 0))
+  #else
+  ATTRIBUTE_FORMAT ((__printf__, 2, 0))
+  #endif
+  ;
+
+/* Appends the result of the printf-compatible FORMATSTRING with the following
+   arguments to BUFFER.
+   Returns 0, or -1 in case of error.  */
+extern int sb_appendf (struct string_buffer *buffer,
+                        const char *formatstring, ...)
+  #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
+  ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 3))
+  #else
+  ATTRIBUTE_FORMAT ((__printf__, 2, 3))
+  #endif
+  ;
+
+/* Frees the memory held by BUFFER.  */
+extern void sb_free (struct string_buffer *buffer);
+
+/* Returns the contents of BUFFER, and frees all other memory held
+   by BUFFER.  Returns NULL upon failure or if there was an error earlier.
+   It is the responsibility of the caller to free() the result.  */
+extern char * sb_dupfree (struct string_buffer *buffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STRING_BUFFER_H */
diff --git a/modules/string-buffer b/modules/string-buffer
new file mode 100644 (file)
index 0000000..4e7827c
--- /dev/null
@@ -0,0 +1,26 @@
+Description:
+A buffer that accumulates a string by piecewise concatenation.
+
+Files:
+lib/string-buffer.h
+lib/string-buffer.c
+
+Depends-on:
+stdbool
+attribute
+stdarg
+vsnprintf-posix
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += string-buffer.c
+
+Include:
+"string-buffer.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all