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>
|
||||
|
||||
string-buffer: Enable resource leak warnings from clang.
|
||||
|
3
NEWS
3
NEWS
@@ -74,6 +74,9 @@ User visible incompatible 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"
|
||||
to <error.h>.
|
||||
|
||||
|
@@ -87,7 +87,33 @@ sb_ensure_more_bytes (struct string_buffer *buffer, size_t increment)
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -107,10 +133,57 @@ sb_free (struct string_buffer *buffer)
|
||||
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;
|
||||
@@ -120,21 +193,22 @@ sb_dupfree (struct string_buffer *buffer)
|
||||
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;
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "attribute.h"
|
||||
#include "string-desc.h"
|
||||
|
||||
typedef char * _GL_ATTRIBUTE_CAPABILITY_TYPE ("memory resource")
|
||||
sb_heap_allocated_pointer_t;
|
||||
@@ -51,9 +52,17 @@ extern "C" {
|
||||
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.
|
||||
@@ -89,10 +98,29 @@ extern int sb_appendf (struct string_buffer *buffer,
|
||||
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
|
||||
|
@@ -9,6 +9,7 @@ lib/string-buffer-printf.c
|
||||
Depends-on:
|
||||
stdbool
|
||||
attribute
|
||||
string-desc
|
||||
stdarg
|
||||
vsnzprintf-posix
|
||||
|
||||
|
@@ -44,12 +44,50 @@ char invalid_format_string_2[] = "%^";
|
||||
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);
|
||||
}
|
||||
@@ -58,23 +96,35 @@ main ()
|
||||
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);
|
||||
}
|
||||
@@ -84,10 +134,10 @@ main ()
|
||||
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);
|
||||
}
|
||||
@@ -102,7 +152,7 @@ main ()
|
||||
int ret;
|
||||
|
||||
sb_init (&buffer);
|
||||
sb_append (&buffer, "<");
|
||||
sb_append_c (&buffer, "<");
|
||||
|
||||
ret = sb_appendf (&buffer, "%lc", (wint_t) 0x76543210);
|
||||
#if !defined _AIX
|
||||
@@ -110,20 +160,20 @@ main ()
|
||||
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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user