* lib/string-buffer.h: Include string-desc.h.
(sb_append1, sb_append_desc): New declarations.
(sb_append_c): Renamed from sb_append.
(sb_contents, sb_contents_c, sb_dupfree): New declarations.
(sb_dupfree_c): Renamed from sb_dupfree.
* lib/string-buffer.c (sb_append1, sb_append_desc): New functions.
(sb_append_c): Renamed from sb_append.
(sb_contents, sb_contents_c, sb_dupfree): New functions.
(sb_dupfree_c): Renamed from sb_dupfree. Optimize.
* modules/string-buffer (Depends-on): Add string-desc.
* tests/test-string-buffer.c (main): Use sb_append_c instead of
sb_append. Use sb_dupfree_c instead of sb_dupfree. Test also sb_append1,
sb_append_desc, sb_contents.
* NEWS: Mention the changes.
+2024-09-25 Bruno Haible <bruno@clisp.org>
+
+ string-buffer: Add more API.
+ * lib/string-buffer.h: Include string-desc.h.
+ (sb_append1, sb_append_desc): New declarations.
+ (sb_append_c): Renamed from sb_append.
+ (sb_contents, sb_contents_c, sb_dupfree): New declarations.
+ (sb_dupfree_c): Renamed from sb_dupfree.
+ * lib/string-buffer.c (sb_append1, sb_append_desc): New functions.
+ (sb_append_c): Renamed from sb_append.
+ (sb_contents, sb_contents_c, sb_dupfree): New functions.
+ (sb_dupfree_c): Renamed from sb_dupfree. Optimize.
+ * modules/string-buffer (Depends-on): Add string-desc.
+ * tests/test-string-buffer.c (main): Use sb_append_c instead of
+ sb_append. Use sb_dupfree_c instead of sb_dupfree. Test also sb_append1,
+ sb_append_desc, sb_contents.
+ * NEWS: Mention the changes.
+
2024-09-25 Bruno Haible <bruno@clisp.org>
string-buffer: Enable resource leak warnings from clang.
Date Modules Changes
+2024-09-25 string-buffer The function sb_append is renamed to sb_append_c.
+ The function sb_dupfree is renamed to sb_dupfree_c.
+
2024-08-14 verror The include file is changed from "verror.h"
to <error.h>.
}
int
-sb_append (struct string_buffer *buffer, const char *str)
+sb_append1 (struct string_buffer *buffer, char c)
+{
+ if (sb_ensure_more_bytes (buffer, 1) < 0)
+ {
+ buffer->error = true;
+ return -1;
+ }
+ buffer->data[buffer->length++] = c;
+ return 0;
+}
+
+int
+sb_append_desc (struct string_buffer *buffer, string_desc_t s)
+{
+ size_t len = string_desc_length (s);
+ if (sb_ensure_more_bytes (buffer, len) < 0)
+ {
+ buffer->error = true;
+ return -1;
+ }
+ memcpy (buffer->data + buffer->length, string_desc_data (s), len);
+ buffer->length += len;
+ return 0;
+}
+
+int
+sb_append_c (struct string_buffer *buffer, const char *str)
{
size_t len = strlen (str);
if (sb_ensure_more_bytes (buffer, len) < 0)
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 *
+string_desc_t
+sb_contents (struct string_buffer *buffer)
+{
+ return string_desc_new_addr (buffer->length, buffer->data);
+}
+
+const char *
+sb_contents_c (struct string_buffer *buffer)
+{
+ if (sb_ensure_more_bytes (buffer, 1) < 0)
+ return NULL;
+ buffer->data[buffer->length] = '\0';
+
+ return buffer->data;
+}
+
+string_desc_t
sb_dupfree (struct string_buffer *buffer)
+{
+ if (buffer->error)
+ goto fail;
+
+ size_t length = buffer->length;
+ if (buffer->data == buffer->space)
+ {
+ char *copy = (char *) malloc (length > 0 ? length : 1);
+ if (copy == NULL)
+ goto fail;
+ memcpy (copy, buffer->data, length);
+ return string_desc_new_addr (length, copy);
+ }
+ else
+ {
+ /* Shrink the string before returning it. */
+ char *contents = buffer->data;
+ if (length < buffer->allocated)
+ {
+ contents = realloc (contents, length > 0 ? length : 1);
+ if (contents == NULL)
+ goto fail;
+ }
+ return string_desc_new_addr (length, contents);
+ }
+
+ fail:
+ sb_free (buffer);
+ return string_desc_new_addr (0, NULL);
+}
+
+char *
+sb_dupfree_c (struct string_buffer *buffer)
{
if (buffer->error)
goto fail;
buffer->data[buffer->length] = '\0';
buffer->length++;
+ size_t length = buffer->length;
if (buffer->data == buffer->space)
{
- char *copy = (char *) malloc (buffer->length);
+ char *copy = (char *) malloc (length);
if (copy == NULL)
goto fail;
- memcpy (copy, buffer->data, buffer->length);
+ memcpy (copy, buffer->data, length);
return copy;
}
else
{
/* Shrink the string before returning it. */
char *contents = buffer->data;
- if (buffer->length < buffer->allocated)
+ if (length < buffer->allocated)
{
- contents = realloc (contents, buffer->length);
+ contents = realloc (contents, length);
if (contents == NULL)
goto fail;
}
#include <stdlib.h>
#include "attribute.h"
+#include "string-desc.h"
typedef char * _GL_ATTRIBUTE_CAPABILITY_TYPE ("memory resource")
sb_heap_allocated_pointer_t;
extern void sb_init (struct string_buffer *buffer)
_GL_ATTRIBUTE_ACQUIRE_CAPABILITY (buffer->data);
-/* Appends the contents of STR to BUFFER.
+/* Appends the character C to BUFFER.
Returns 0, or -1 in case of out-of-memory error. */
-extern int sb_append (struct string_buffer *buffer, const char *str);
+extern int sb_append1 (struct string_buffer *buffer, char c);
+
+/* Appends the contents of the memory area STR to BUFFER.
+ Returns 0, or -1 in case of out-of-memory error. */
+extern int sb_append_desc (struct string_buffer *buffer, string_desc_t s);
+
+/* Appends the contents of the C string STR to BUFFER.
+ Returns 0, or -1 in case of out-of-memory error. */
+extern int sb_append_c (struct string_buffer *buffer, const char *str);
/* Appends the result of the printf-compatible FORMATSTRING with the argument
list LIST to BUFFER.
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 sb_appendf (struct string_buffer *buffer,
- const char *formatstring, ...)
+ const char *formatstring, ...)
#if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__
ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 3))
#else
extern void sb_free (struct string_buffer *buffer)
_GL_ATTRIBUTE_RELEASE_CAPABILITY (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.
+/* 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 sb_contents (struct string_buffer *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.
+ Returns NULL upon out-of-memory error.
+ The result is only valid until the next operation on BUFFER. */
+extern const char * sb_contents_c (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 string_desc_free() the result. */
+extern string_desc_t sb_dupfree (struct string_buffer *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 * sb_dupfree (struct string_buffer *buffer)
+extern char * sb_dupfree_c (struct string_buffer *buffer)
_GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
#ifdef __cplusplus
Depends-on:
stdbool
attribute
+string-desc
stdarg
vsnzprintf-posix
int
main ()
{
+ /* Test accumulation. */
+ {
+ struct string_buffer buffer;
+
+ sb_init (&buffer);
+ sb_append1 (&buffer, 'x');
+ sb_append1 (&buffer, '\377');
+ char *s = sb_dupfree_c (&buffer);
+ ASSERT (s != NULL && strcmp (s, "x\377") == 0);
+ free (s);
+ }
+ {
+ struct string_buffer buffer;
+
+ sb_init (&buffer);
+ sb_append1 (&buffer, 'x');
+ sb_append1 (&buffer, '\377');
+ {
+ string_desc_t sd = sb_contents (&buffer);
+ ASSERT (string_desc_length (sd) == 2);
+ ASSERT (string_desc_char_at (sd, 0) == 'x');
+ ASSERT (string_desc_char_at (sd, 1) == '\377');
+ }
+ sb_append1 (&buffer, '\0');
+ sb_append1 (&buffer, 'z');
+ {
+ string_desc_t sd = sb_contents (&buffer);
+ ASSERT (string_desc_length (sd) == 4);
+ ASSERT (string_desc_char_at (sd, 0) == 'x');
+ ASSERT (string_desc_char_at (sd, 1) == '\377');
+ ASSERT (string_desc_char_at (sd, 2) == '\0');
+ ASSERT (string_desc_char_at (sd, 3) == 'z');
+ }
+ char *s = sb_dupfree_c (&buffer);
+ ASSERT (s != NULL && memcmp (s, "x\377\0z\0", 5) == 0);
+ free (s);
+ }
+
/* Test simple string concatenation. */
{
struct string_buffer buffer;
sb_init (&buffer);
- char *s = sb_dupfree (&buffer);
+ char *s = sb_dupfree_c (&buffer);
ASSERT (s != NULL && strcmp (s, "") == 0);
free (s);
}
struct string_buffer buffer;
sb_init (&buffer);
- sb_append (&buffer, "abc");
- sb_append (&buffer, "");
- sb_append (&buffer, "defg");
- char *s = sb_dupfree (&buffer);
+ sb_append_c (&buffer, "abc");
+ sb_append_c (&buffer, "");
+ sb_append_c (&buffer, "defg");
+ char *s = sb_dupfree_c (&buffer);
ASSERT (s != NULL && strcmp (s, "abcdefg") == 0);
free (s);
}
+ {
+ struct string_buffer buffer;
+
+ sb_init (&buffer);
+ sb_append_c (&buffer, "abc");
+ sb_append_desc (&buffer, string_desc_new_addr (5, "de\0fg"));
+ sb_append_c (&buffer, "hij");
+ char *s = sb_dupfree_c (&buffer);
+ ASSERT (s != NULL && memcmp (s, "abcde\0fghij", 12) == 0);
+ free (s);
+ }
+
/* Test printf-like formatting. */
{
struct string_buffer buffer;
sb_init (&buffer);
- sb_append (&buffer, "<");
+ sb_append_c (&buffer, "<");
sb_appendf (&buffer, "%x", 3735928559U);
- sb_append (&buffer, ">");
- char *s = sb_dupfree (&buffer);
+ sb_append_c (&buffer, ">");
+ char *s = sb_dupfree_c (&buffer);
ASSERT (s != NULL && strcmp (s, "<deadbeef>") == 0);
free (s);
}
struct string_buffer buffer;
sb_init (&buffer);
- sb_append (&buffer, "<");
+ sb_append_c (&buffer, "<");
my_appendf (&buffer, "%x", 3735928559U);
- sb_append (&buffer, ">");
- char *s = sb_dupfree (&buffer);
+ sb_append_c (&buffer, ">");
+ char *s = sb_dupfree_c (&buffer);
ASSERT (s != NULL && strcmp (s, "<deadbeef>") == 0);
free (s);
}
int ret;
sb_init (&buffer);
- sb_append (&buffer, "<");
+ sb_append_c (&buffer, "<");
ret = sb_appendf (&buffer, "%lc", (wint_t) 0x76543210);
#if !defined _AIX
ASSERT (errno == EILSEQ);
#endif
- sb_append (&buffer, "|");
+ sb_append_c (&buffer, "|");
ret = sb_appendf (&buffer, invalid_format_string_1, 1);
ASSERT (ret < 0);
ASSERT (errno == EINVAL);
- sb_append (&buffer, "|");
+ sb_append_c (&buffer, "|");
ret = sb_appendf (&buffer, invalid_format_string_2, 2);
ASSERT (ret < 0);
ASSERT (errno == EINVAL);
- sb_append (&buffer, ">");
- char *s = sb_dupfree (&buffer);
+ sb_append_c (&buffer, ">");
+ char *s = sb_dupfree_c (&buffer);
ASSERT (s == NULL);
}