mirror of
https://git.savannah.gnu.org/git/gnulib.git
synced 2025-08-08 17:22:05 +03:00
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.
This commit is contained in:
18
ChangeLog
18
ChangeLog
@@ -1,3 +1,21 @@
|
|||||||
|
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>
|
2024-09-25 Bruno Haible <bruno@clisp.org>
|
||||||
|
|
||||||
string-buffer: Enable resource leak warnings from clang.
|
string-buffer: Enable resource leak warnings from clang.
|
||||||
|
3
NEWS
3
NEWS
@@ -74,6 +74,9 @@ User visible incompatible changes
|
|||||||
|
|
||||||
Date Modules Changes
|
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"
|
2024-08-14 verror The include file is changed from "verror.h"
|
||||||
to <error.h>.
|
to <error.h>.
|
||||||
|
|
||||||
|
@@ -87,7 +87,33 @@ sb_ensure_more_bytes (struct string_buffer *buffer, size_t increment)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
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);
|
size_t len = strlen (str);
|
||||||
if (sb_ensure_more_bytes (buffer, len) < 0)
|
if (sb_ensure_more_bytes (buffer, len) < 0)
|
||||||
@@ -107,10 +133,57 @@ sb_free (struct string_buffer *buffer)
|
|||||||
free (buffer->data);
|
free (buffer->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the contents of BUFFER, and frees all other memory held
|
string_desc_t
|
||||||
by BUFFER. Returns NULL upon failure or if there was an error earlier. */
|
sb_contents (struct string_buffer *buffer)
|
||||||
char *
|
{
|
||||||
|
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)
|
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)
|
if (buffer->error)
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -120,21 +193,22 @@ sb_dupfree (struct string_buffer *buffer)
|
|||||||
buffer->data[buffer->length] = '\0';
|
buffer->data[buffer->length] = '\0';
|
||||||
buffer->length++;
|
buffer->length++;
|
||||||
|
|
||||||
|
size_t length = buffer->length;
|
||||||
if (buffer->data == buffer->space)
|
if (buffer->data == buffer->space)
|
||||||
{
|
{
|
||||||
char *copy = (char *) malloc (buffer->length);
|
char *copy = (char *) malloc (length);
|
||||||
if (copy == NULL)
|
if (copy == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
memcpy (copy, buffer->data, buffer->length);
|
memcpy (copy, buffer->data, length);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Shrink the string before returning it. */
|
/* Shrink the string before returning it. */
|
||||||
char *contents = buffer->data;
|
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)
|
if (contents == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "attribute.h"
|
#include "attribute.h"
|
||||||
|
#include "string-desc.h"
|
||||||
|
|
||||||
typedef char * _GL_ATTRIBUTE_CAPABILITY_TYPE ("memory resource")
|
typedef char * _GL_ATTRIBUTE_CAPABILITY_TYPE ("memory resource")
|
||||||
sb_heap_allocated_pointer_t;
|
sb_heap_allocated_pointer_t;
|
||||||
@@ -51,9 +52,17 @@ extern "C" {
|
|||||||
extern void sb_init (struct string_buffer *buffer)
|
extern void sb_init (struct string_buffer *buffer)
|
||||||
_GL_ATTRIBUTE_ACQUIRE_CAPABILITY (buffer->data);
|
_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. */
|
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
|
/* Appends the result of the printf-compatible FORMATSTRING with the argument
|
||||||
list LIST to BUFFER.
|
list LIST to BUFFER.
|
||||||
@@ -77,7 +86,7 @@ extern int sb_appendvf (struct string_buffer *buffer,
|
|||||||
Therefore, if the format string is valid and does not use %ls/%lc
|
Therefore, if the format string is valid and does not use %ls/%lc
|
||||||
directives nor widths, the only possible error code is ENOMEM. */
|
directives nor widths, the only possible error code is ENOMEM. */
|
||||||
extern int sb_appendf (struct string_buffer *buffer,
|
extern int sb_appendf (struct string_buffer *buffer,
|
||||||
const char *formatstring, ...)
|
const char *formatstring, ...)
|
||||||
#if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__
|
#if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__
|
||||||
ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 3))
|
ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 3))
|
||||||
#else
|
#else
|
||||||
@@ -89,10 +98,29 @@ extern int sb_appendf (struct string_buffer *buffer,
|
|||||||
extern void sb_free (struct string_buffer *buffer)
|
extern void sb_free (struct string_buffer *buffer)
|
||||||
_GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
|
_GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
|
||||||
|
|
||||||
/* Returns the contents of BUFFER, and frees all other memory held
|
/* Returns a read-only view of the current contents of BUFFER.
|
||||||
by BUFFER. Returns NULL upon failure or if there was an error earlier.
|
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. */
|
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);
|
_GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@@ -9,6 +9,7 @@ lib/string-buffer-printf.c
|
|||||||
Depends-on:
|
Depends-on:
|
||||||
stdbool
|
stdbool
|
||||||
attribute
|
attribute
|
||||||
|
string-desc
|
||||||
stdarg
|
stdarg
|
||||||
vsnzprintf-posix
|
vsnzprintf-posix
|
||||||
|
|
||||||
|
@@ -44,12 +44,50 @@ char invalid_format_string_2[] = "%^";
|
|||||||
int
|
int
|
||||||
main ()
|
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. */
|
/* Test simple string concatenation. */
|
||||||
{
|
{
|
||||||
struct string_buffer buffer;
|
struct string_buffer buffer;
|
||||||
|
|
||||||
sb_init (&buffer);
|
sb_init (&buffer);
|
||||||
char *s = sb_dupfree (&buffer);
|
char *s = sb_dupfree_c (&buffer);
|
||||||
ASSERT (s != NULL && strcmp (s, "") == 0);
|
ASSERT (s != NULL && strcmp (s, "") == 0);
|
||||||
free (s);
|
free (s);
|
||||||
}
|
}
|
||||||
@@ -58,23 +96,35 @@ main ()
|
|||||||
struct string_buffer buffer;
|
struct string_buffer buffer;
|
||||||
|
|
||||||
sb_init (&buffer);
|
sb_init (&buffer);
|
||||||
sb_append (&buffer, "abc");
|
sb_append_c (&buffer, "abc");
|
||||||
sb_append (&buffer, "");
|
sb_append_c (&buffer, "");
|
||||||
sb_append (&buffer, "defg");
|
sb_append_c (&buffer, "defg");
|
||||||
char *s = sb_dupfree (&buffer);
|
char *s = sb_dupfree_c (&buffer);
|
||||||
ASSERT (s != NULL && strcmp (s, "abcdefg") == 0);
|
ASSERT (s != NULL && strcmp (s, "abcdefg") == 0);
|
||||||
free (s);
|
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. */
|
/* Test printf-like formatting. */
|
||||||
{
|
{
|
||||||
struct string_buffer buffer;
|
struct string_buffer buffer;
|
||||||
|
|
||||||
sb_init (&buffer);
|
sb_init (&buffer);
|
||||||
sb_append (&buffer, "<");
|
sb_append_c (&buffer, "<");
|
||||||
sb_appendf (&buffer, "%x", 3735928559U);
|
sb_appendf (&buffer, "%x", 3735928559U);
|
||||||
sb_append (&buffer, ">");
|
sb_append_c (&buffer, ">");
|
||||||
char *s = sb_dupfree (&buffer);
|
char *s = sb_dupfree_c (&buffer);
|
||||||
ASSERT (s != NULL && strcmp (s, "<deadbeef>") == 0);
|
ASSERT (s != NULL && strcmp (s, "<deadbeef>") == 0);
|
||||||
free (s);
|
free (s);
|
||||||
}
|
}
|
||||||
@@ -84,10 +134,10 @@ main ()
|
|||||||
struct string_buffer buffer;
|
struct string_buffer buffer;
|
||||||
|
|
||||||
sb_init (&buffer);
|
sb_init (&buffer);
|
||||||
sb_append (&buffer, "<");
|
sb_append_c (&buffer, "<");
|
||||||
my_appendf (&buffer, "%x", 3735928559U);
|
my_appendf (&buffer, "%x", 3735928559U);
|
||||||
sb_append (&buffer, ">");
|
sb_append_c (&buffer, ">");
|
||||||
char *s = sb_dupfree (&buffer);
|
char *s = sb_dupfree_c (&buffer);
|
||||||
ASSERT (s != NULL && strcmp (s, "<deadbeef>") == 0);
|
ASSERT (s != NULL && strcmp (s, "<deadbeef>") == 0);
|
||||||
free (s);
|
free (s);
|
||||||
}
|
}
|
||||||
@@ -102,7 +152,7 @@ main ()
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sb_init (&buffer);
|
sb_init (&buffer);
|
||||||
sb_append (&buffer, "<");
|
sb_append_c (&buffer, "<");
|
||||||
|
|
||||||
ret = sb_appendf (&buffer, "%lc", (wint_t) 0x76543210);
|
ret = sb_appendf (&buffer, "%lc", (wint_t) 0x76543210);
|
||||||
#if !defined _AIX
|
#if !defined _AIX
|
||||||
@@ -110,20 +160,20 @@ main ()
|
|||||||
ASSERT (errno == EILSEQ);
|
ASSERT (errno == EILSEQ);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sb_append (&buffer, "|");
|
sb_append_c (&buffer, "|");
|
||||||
|
|
||||||
ret = sb_appendf (&buffer, invalid_format_string_1, 1);
|
ret = sb_appendf (&buffer, invalid_format_string_1, 1);
|
||||||
ASSERT (ret < 0);
|
ASSERT (ret < 0);
|
||||||
ASSERT (errno == EINVAL);
|
ASSERT (errno == EINVAL);
|
||||||
|
|
||||||
sb_append (&buffer, "|");
|
sb_append_c (&buffer, "|");
|
||||||
|
|
||||||
ret = sb_appendf (&buffer, invalid_format_string_2, 2);
|
ret = sb_appendf (&buffer, invalid_format_string_2, 2);
|
||||||
ASSERT (ret < 0);
|
ASSERT (ret < 0);
|
||||||
ASSERT (errno == EINVAL);
|
ASSERT (errno == EINVAL);
|
||||||
|
|
||||||
sb_append (&buffer, ">");
|
sb_append_c (&buffer, ">");
|
||||||
char *s = sb_dupfree (&buffer);
|
char *s = sb_dupfree_c (&buffer);
|
||||||
ASSERT (s == NULL);
|
ASSERT (s == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user