From: Bruno Haible Date: Wed, 5 Feb 2025 19:57:16 +0000 (+0100) Subject: string-buffer-reversed: New module. X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=17a3c9deeed0b45115a4f1ce430ef1a50e200b1a;p=gnulib.git string-buffer-reversed: New module. * lib/string-buffer-reversed.h: New file, based on lib/string-buffer.h. * lib/string-buffer-reversed.c: New file, based on lib/string-buffer.c. * lib/string-buffer-reversed-printf.c: New file, based on lib/string-buffer-printf.c. * modules/string-buffer-reversed: New file. --- diff --git a/ChangeLog b/ChangeLog index 5db0072a97..5e01a5dfb4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2025-02-05 Bruno Haible + + string-buffer-reversed: New module. + * lib/string-buffer-reversed.h: New file, based on lib/string-buffer.h. + * lib/string-buffer-reversed.c: New file, based on lib/string-buffer.c. + * lib/string-buffer-reversed-printf.c: New file, based on + lib/string-buffer-printf.c. + * modules/string-buffer-reversed: New file. + 2025-02-05 Bruno Haible string-buffer: Improve comments. diff --git a/lib/string-buffer-reversed-printf.c b/lib/string-buffer-reversed-printf.c new file mode 100644 index 0000000000..76db571cf3 --- /dev/null +++ b/lib/string-buffer-reversed-printf.c @@ -0,0 +1,207 @@ +/* A buffer that accumulates a string by piecewise concatenation, from the end + to the start. + Copyright (C) 2021-2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This file 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2025. */ + +#include + +/* Specification. */ +#include "string-buffer-reversed.h" + +/* Undocumented. */ +extern int sbr_ensure_more_bytes (struct string_buffer_reversed *buffer, + size_t increment); + +#include +#include +#include +#include +#include + +int +sbr_prependvf (struct string_buffer_reversed *buffer, const char *formatstring, + va_list list) +{ + va_list list_copy; + + /* Make a bit of room, so that the probability that the first vsnzprintf() + call succeeds is high. */ + size_t room = buffer->allocated - buffer->length; + if (room < 64) + { + if (sbr_ensure_more_bytes (buffer, 64) < 0) + { + buffer->error = true; + errno = ENOMEM; + return -1; + } + room = buffer->allocated - buffer->length; + } + + va_copy (list_copy, list); + + /* First vsnzprintf() call. */ + ptrdiff_t ret = vsnzprintf (buffer->data, room, formatstring, list); + if (ret < 0) + { + /* Failed. errno is set. */ + buffer->error = true; + ret = -1; + } + else + { + if (ret <= room) + { + /* The result has fit into room bytes. */ + memmove (buffer->data + buffer->allocated - buffer->length - (size_t) ret, + buffer->data, + (size_t) ret); + buffer->length += (size_t) ret; + ret = 0; + } + else + { + /* The result was truncated. Make more room, for a second + vsnzprintf() call. */ + if (sbr_ensure_more_bytes (buffer, (size_t) ret) < 0) + { + buffer->error = true; + errno = ENOMEM; + ret = -1; + } + else + { + /* Second vsnzprintf() call. */ + room = buffer->allocated - buffer->length; + ret = vsnzprintf (buffer->data, room, formatstring, list_copy); + if (ret < 0) + { + /* Failed. errno is set. */ + buffer->error = true; + ret = -1; + } + else + { + if (ret <= room) + { + /* The result has fit into room bytes. */ + memmove (buffer->data + buffer->allocated - buffer->length - (size_t) ret, + buffer->data, + (size_t) ret); + buffer->length += (size_t) ret; + ret = 0; + } + else + /* The return values of the vsnzprintf() calls are not + consistent. */ + abort (); + } + } + } + } + + va_end (list_copy); + return ret; +} + +int +sbr_prependf (struct string_buffer_reversed *buffer, const char *formatstring, + ...) +{ + va_list args; + + /* Make a bit of room, so that the probability that the first vsnzprintf() + call succeeds is high. */ + size_t room = buffer->allocated - buffer->length; + if (room < 64) + { + if (sbr_ensure_more_bytes (buffer, 64) < 0) + { + buffer->error = true; + errno = ENOMEM; + return -1; + } + room = buffer->allocated - buffer->length; + } + + va_start (args, formatstring); + + /* First vsnzprintf() call. */ + ptrdiff_t ret = vsnzprintf (buffer->data, room, formatstring, args); + if (ret < 0) + { + /* Failed. errno is set. */ + buffer->error = true; + ret = -1; + } + else + { + if (ret <= room) + { + /* The result has fit into room bytes. */ + memmove (buffer->data + buffer->allocated - buffer->length - (size_t) ret, + buffer->data, + (size_t) ret); + buffer->length += (size_t) ret; + ret = 0; + } + else + { + /* The result was truncated. Make more room, for a second + vsnzprintf() call. */ + if (sbr_ensure_more_bytes (buffer, (size_t) ret) < 0) + { + buffer->error = true; + errno = ENOMEM; + ret = -1; + } + else + { + /* Second vsnzprintf() call. */ + room = buffer->allocated - buffer->length; + va_end (args); + va_start (args, formatstring); + ret = vsnzprintf (buffer->data, room, formatstring, args); + if (ret < 0) + { + /* Failed. errno is set. */ + buffer->error = true; + ret = -1; + } + else + { + if (ret <= room) + { + /* The result has fit into room bytes. */ + memmove (buffer->data + buffer->allocated - buffer->length - (size_t) ret, + buffer->data, + (size_t) ret); + buffer->length += (size_t) ret; + ret = 0; + } + else + /* The return values of the vsnzprintf() calls are not + consistent. */ + abort (); + } + } + } + } + + va_end (args); + return ret; +} diff --git a/lib/string-buffer-reversed.c b/lib/string-buffer-reversed.c new file mode 100644 index 0000000000..a2bce83b9c --- /dev/null +++ b/lib/string-buffer-reversed.c @@ -0,0 +1,222 @@ +/* A buffer that accumulates a string by piecewise concatenation, from the end + to the start. + Copyright (C) 2021-2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This file 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2025. */ + +#include + +/* Specification. */ +#include "string-buffer-reversed.h" + +/* Undocumented. */ +extern int sbr_ensure_more_bytes (struct string_buffer_reversed *buffer, + size_t increment); + +#include +#include +#include + +/* The warnings about memory resource 'buffer->data' in this file are not + relevant. Silence them. */ +#if __clang_major__ >= 3 +# pragma clang diagnostic ignored "-Wthread-safety" +#endif + +void +sbr_init (struct string_buffer_reversed *buffer) +{ + buffer->data = buffer->space; + /* Pre-allocate a trailing NUL. This makes it easy to implement + sbr_contents_c(). */ + buffer->data[sizeof (buffer->space) - 1] = '\0'; + buffer->length = 1; + 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. */ +int +sbr_ensure_more_bytes (struct string_buffer_reversed *buffer, size_t increment) +{ + size_t incremented_length = increment + buffer->length; + 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 + new_allocated - buffer->length, + buffer->data + buffer->allocated - buffer->length, + buffer->length); + } + else + { + new_data = (char *) realloc (buffer->data, new_allocated); + if (new_data == NULL) + /* Out-of-memory. */ + return -1; + memmove (new_data + new_allocated - buffer->length, + new_data + buffer->allocated - buffer->length, + buffer->length); + } + buffer->data = new_data; + buffer->allocated = new_allocated; + } + return 0; +} + +int +sbr_prepend1 (struct string_buffer_reversed *buffer, char c) +{ + if (sbr_ensure_more_bytes (buffer, 1) < 0) + { + buffer->error = true; + return -1; + } + buffer->data[buffer->allocated - buffer->length - 1] = c; + buffer->length++; + return 0; +} + +int +sbr_prepend_desc (struct string_buffer_reversed *buffer, string_desc_t s) +{ + size_t len = sd_length (s); + if (sbr_ensure_more_bytes (buffer, len) < 0) + { + buffer->error = true; + return -1; + } + memcpy (buffer->data + buffer->allocated - buffer->length - len, sd_data (s), len); + buffer->length += len; + return 0; +} + +int +sbr_prepend_c (struct string_buffer_reversed *buffer, const char *str) +{ + size_t len = strlen (str); + if (sbr_ensure_more_bytes (buffer, len) < 0) + { + buffer->error = true; + return -1; + } + memcpy (buffer->data + buffer->allocated - buffer->length - len, str, len); + buffer->length += len; + return 0; +} + +void +sbr_free (struct string_buffer_reversed *buffer) +{ + if (buffer->data != buffer->space) + free (buffer->data); +} + +string_desc_t +sbr_contents (struct string_buffer_reversed *buffer) +{ + return sd_new_addr (buffer->length - 1, + buffer->data + buffer->allocated - buffer->length); +} + +const char * +sbr_contents_c (struct string_buffer_reversed *buffer) +{ + return buffer->data + buffer->allocated - buffer->length; +} + +string_desc_t +sbr_dupfree (struct string_buffer_reversed *buffer) +{ + if (buffer->error) + goto fail; + + size_t length = buffer->length; + if (buffer->data == buffer->space) + { + char *copy = (char *) malloc (length > 1 ? length - 1 : 1); + if (copy == NULL) + goto fail; + memcpy (copy, buffer->data + buffer->allocated - length, length - 1); + return sd_new_addr (length - 1, copy); + } + else + { + /* Shrink the string before returning it. */ + char *contents = buffer->data; + memmove (contents, contents + buffer->allocated - length, length - 1); + contents = realloc (contents, length > 1 ? length - 1 : 1); + if (contents == NULL) + goto fail; + return sd_new_addr (length - 1, contents); + } + + fail: + sbr_free (buffer); + return sd_new_addr (0, NULL); +} + +char * +sbr_dupfree_c (struct string_buffer_reversed *buffer) +{ + if (buffer->error) + goto fail; + + size_t length = buffer->length; + if (buffer->data == buffer->space) + { + char *copy = (char *) malloc (length); + if (copy == NULL) + goto fail; + memcpy (copy, buffer->data + buffer->allocated - length, length); + return copy; + } + else + { + /* Shrink the string before returning it. */ + char *contents = buffer->data; + if (length < buffer->allocated) + { + memmove (contents, contents + buffer->allocated - length, length); + contents = realloc (contents, length); + if (contents == NULL) + goto fail; + } + return contents; + } + + fail: + sbr_free (buffer); + return NULL; +} diff --git a/lib/string-buffer-reversed.h b/lib/string-buffer-reversed.h new file mode 100644 index 0000000000..7fe05e4869 --- /dev/null +++ b/lib/string-buffer-reversed.h @@ -0,0 +1,202 @@ +/* A buffer that accumulates a string by piecewise concatenation, from the end + to the start. + Copyright (C) 2021-2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This file 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2025. */ + +#ifndef _STRING_BUFFER_REVERSED_H +#define _STRING_BUFFER_REVERSED_H + +/* This file uses _GL_ATTRIBUTE_MALLOC, _GL_ATTRIBUTE_RETURNS_NONNULL, + _GL_ATTRIBUTE_CAPABILITY_TYPE, _GL_ATTRIBUTE_ACQUIRE_CAPABILITY, + _GL_ATTRIBUTE_RELEASE_CAPABILITY. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + +#include +#include + +#include "attribute.h" +#include "string-desc.h" + +typedef char * _GL_ATTRIBUTE_CAPABILITY_TYPE ("memory resource") + sbr_heap_allocated_pointer_t; + +/* A string buffer type. */ +struct string_buffer_reversed +{ + /* data[allocated-length .. allocated-1] are used. */ + sbr_heap_allocated_pointer_t 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 + +/* ================== Functions in module 'string-buffer' ================== */ + +/* Initializes BUFFER to the empty string. */ +extern void sbr_init (struct string_buffer_reversed *buffer) + _GL_ATTRIBUTE_ACQUIRE_CAPABILITY (buffer->data); + +/* Prepends the character C to BUFFER. + Returns 0, or -1 in case of out-of-memory error. */ +extern int sbr_prepend1 (struct string_buffer_reversed *buffer, char c); + +/* Prepends the contents of the memory area S to BUFFER. + Returns 0, or -1 in case of out-of-memory error. */ +extern int sbr_prepend_desc (struct string_buffer_reversed *buffer, + string_desc_t s); + +/* Prepends the contents of the C string STR to BUFFER. + Returns 0, or -1 in case of out-of-memory error. */ +extern int sbr_prepend_c (struct string_buffer_reversed *buffer, + const char *str); + +/* Prepends the result of the printf-compatible FORMATSTRING with the argument + list LIST to BUFFER. + Returns 0, or -1 with errno set in case of error. + Error code EOVERFLOW can only occur when a width > INT_MAX is used. + Therefore, if the format string is valid and does not use %ls/%lc + directives nor widths, the only possible error code is ENOMEM. */ +extern int sbr_prependvf (struct string_buffer_reversed *buffer, + const char *formatstring, va_list list) + #if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__ + ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 0)) + #else + ATTRIBUTE_FORMAT ((__printf__, 2, 0)) + #endif + ; + +/* Prepends the result of the printf-compatible FORMATSTRING with the following + arguments to BUFFER. + Returns 0, or -1 with errno set in case of error. + Error code EOVERFLOW can only occur when a width > INT_MAX is used. + Therefore, if the format string is valid and does not use %ls/%lc + directives nor widths, the only possible error code is ENOMEM. */ +extern int sbr_prependf (struct string_buffer_reversed *buffer, + const char *formatstring, ...) + #if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__ + ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 3)) + #else + ATTRIBUTE_FORMAT ((__printf__, 2, 3)) + #endif + ; + +/* Frees the memory held by BUFFER. */ +extern void sbr_free (struct string_buffer_reversed *buffer) + _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data); + +/* Returns a read-only view of the current contents of BUFFER. + The result is only valid until the next operation on BUFFER. */ +extern string_desc_t sbr_contents (struct string_buffer_reversed *buffer); + +/* Ensures the contents of BUFFER is followed by a NUL byte (without + incrementing the length of the contents). + Then returns a read-only view of the current contents of BUFFER, + that is, the current contents of BUFFER as a C string. + The result is only valid until the next operation on BUFFER. */ +extern const char * sbr_contents_c (struct string_buffer_reversed *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 sd_free() the result. */ +extern string_desc_t sbr_dupfree (struct string_buffer_reversed *buffer) + _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data); + +/* Returns the contents of BUFFER (with an added trailing NUL, that is, + as a C string), 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 * sbr_dupfree_c (struct string_buffer_reversed *buffer) + _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data); + +/* ================== Functions in module 'xstring-buffer' ================== */ + +#if GNULIB_XSTRING_BUFFER_REVERSED + +/* The following functions invoke xalloc_die () in case of out-of-memory + error. */ + +/* Prepends the character C to BUFFER. */ +extern void sbr_xprepend1 (struct string_buffer_reversed *buffer, char c); + +/* Prepends the contents of the memory area S to BUFFER. */ +extern void sbr_xprepend_desc (struct string_buffer_reversed *buffer, + string_desc_t s); + +/* Prepends the contents of the C string STR to BUFFER. */ +extern void sbr_xprepend_c (struct string_buffer_reversed *buffer, + const char *str); + +/* Prepends the result of the printf-compatible FORMATSTRING with the argument + list LIST to BUFFER. + Returns 0, or -1 in case of error other than out-of-memory error. + Error code EOVERFLOW can only occur when a width > INT_MAX is used. + Therefore, if the format string is valid and does not use %ls/%lc + directives nor widths, no error is possible. */ +extern int sbr_xprependvf (struct string_buffer_reversed *buffer, + const char *formatstring, va_list list) + #if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__ + ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 0)) + #else + ATTRIBUTE_FORMAT ((__printf__, 2, 0)) + #endif + ; + +/* Prepends the result of the printf-compatible FORMATSTRING with the following + arguments to BUFFER. + Returns 0, or -1 in case of error other than out-of-memory error. + Error code EOVERFLOW can only occur when a width > INT_MAX is used. + Therefore, if the format string is valid and does not use %ls/%lc + directives nor widths, no error is possible. */ +extern int sbr_xprependf (struct string_buffer_reversed *buffer, + const char *formatstring, ...) + #if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__ + ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 3)) + #else + ATTRIBUTE_FORMAT ((__printf__, 2, 3)) + #endif + ; + +/* Returns the contents of BUFFER and frees all other memory held by BUFFER. + Returns (0, NULL) if there was an error earlier. + It is the responsibility of the caller to sd_free() the result. */ +extern string_desc_t sbr_xdupfree (struct string_buffer_reversed *buffer) + _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data); + +/* Returns the contents of BUFFER (with an added trailing NUL, that is, + as a C string), and frees all other memory held by BUFFER. + Returns NULL if there was an error earlier. + It is the responsibility of the caller to free() the result. */ +extern char * sbr_xdupfree_c (struct string_buffer_reversed *buffer) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE + _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data); + +#endif /* GNULIB_XSTRING_BUFFER_REVERSED */ + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif + +#endif /* _STRING_BUFFER_REVERSED_H */ diff --git a/modules/string-buffer-reversed b/modules/string-buffer-reversed new file mode 100644 index 0000000000..a4ac8d5d86 --- /dev/null +++ b/modules/string-buffer-reversed @@ -0,0 +1,29 @@ +Description: +A buffer that accumulates a string by piecewise concatenation, +from the end to the start. + +Files: +lib/string-buffer-reversed.h +lib/string-buffer-reversed.c +lib/string-buffer-reversed-printf.c + +Depends-on: +bool +attribute +string-desc +stdarg-h +vsnzprintf-posix + +configure.ac: + +Makefile.am: +lib_SOURCES += string-buffer-reversed.c string-buffer-reversed-printf.c + +Include: +"string-buffer-reversed.h" + +License: +LGPL + +Maintainer: +all