1 /* A buffer that accumulates a string by piecewise concatenation.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation, either version 3 of the
7 License, or (at your option) any later version.
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2021. */
22 #include "string-buffer.h"
30 sb_init (struct string_buffer
*buffer
)
32 buffer
->data
= buffer
->space
;
34 buffer
->allocated
= sizeof (buffer
->space
);
35 buffer
->error
= false;
38 /* Ensures that INCREMENT bytes are available beyond the current used length
40 Returns 0, or -1 in case of out-of-memory error. */
42 sb_ensure_more_bytes (struct string_buffer
*buffer
, size_t increment
)
44 size_t incremented_length
= buffer
->length
+ increment
;
45 if (incremented_length
< increment
)
49 if (buffer
->allocated
< incremented_length
)
51 size_t new_allocated
= 2 * buffer
->allocated
;
52 if (new_allocated
< buffer
->allocated
)
55 if (new_allocated
< incremented_length
)
56 new_allocated
= incremented_length
;
59 if (buffer
->data
== buffer
->space
)
61 new_data
= (char *) malloc (new_allocated
);
65 memcpy (new_data
, buffer
->data
, buffer
->length
);
69 new_data
= (char *) realloc (buffer
->data
, new_allocated
);
74 buffer
->data
= new_data
;
75 buffer
->allocated
= new_allocated
;
81 sb_append (struct string_buffer
*buffer
, const char *str
)
83 size_t len
= strlen (str
);
84 if (sb_ensure_more_bytes (buffer
, len
) < 0)
89 memcpy (buffer
->data
+ buffer
->length
, str
, len
);
90 buffer
->length
+= len
;
95 sb_appendvf (struct string_buffer
*buffer
, const char *formatstring
,
100 /* Make a bit of room, so that the probability that the first vsnprintf() call
102 size_t room
= buffer
->allocated
- buffer
->length
;
105 if (sb_ensure_more_bytes (buffer
, 64) < 0)
107 buffer
->error
= true;
110 room
= buffer
->allocated
- buffer
->length
;
113 va_copy (list_copy
, list
);
115 /* First vsnprintf() call. */
116 int ret
= vsnprintf (buffer
->data
+ buffer
->length
, room
, formatstring
, list
);
120 buffer
->error
= true;
125 if ((size_t) ret
<= room
)
127 /* The result has fit into room bytes. */
128 buffer
->length
+= (size_t) ret
;
133 /* The result was truncated. Make more room, for a second vsnprintf()
135 if (sb_ensure_more_bytes (buffer
, (size_t) ret
) < 0)
137 buffer
->error
= true;
142 /* Second vsnprintf() call. */
143 room
= buffer
->allocated
- buffer
->length
;
144 ret
= vsnprintf (buffer
->data
+ buffer
->length
, room
,
145 formatstring
, list_copy
);
149 buffer
->error
= true;
154 if ((size_t) ret
<= room
)
156 /* The result has fit into room bytes. */
157 buffer
->length
+= (size_t) ret
;
161 /* The return values of the vsnprintf() calls are not
174 sb_appendf (struct string_buffer
*buffer
, const char *formatstring
, ...)
178 /* Make a bit of room, so that the probability that the first vsnprintf() call
180 size_t room
= buffer
->allocated
- buffer
->length
;
183 if (sb_ensure_more_bytes (buffer
, 64) < 0)
185 buffer
->error
= true;
188 room
= buffer
->allocated
- buffer
->length
;
191 va_start (args
, formatstring
);
193 /* First vsnprintf() call. */
194 int ret
= vsnprintf (buffer
->data
+ buffer
->length
, room
, formatstring
, args
);
198 buffer
->error
= true;
203 if ((size_t) ret
<= room
)
205 /* The result has fit into room bytes. */
206 buffer
->length
+= (size_t) ret
;
211 /* The result was truncated. Make more room, for a second vsnprintf()
213 if (sb_ensure_more_bytes (buffer
, (size_t) ret
) < 0)
215 buffer
->error
= true;
220 /* Second vsnprintf() call. */
221 room
= buffer
->allocated
- buffer
->length
;
223 va_start (args
, formatstring
);
224 ret
= vsnprintf (buffer
->data
+ buffer
->length
, room
,
229 buffer
->error
= true;
234 if ((size_t) ret
<= room
)
236 /* The result has fit into room bytes. */
237 buffer
->length
+= (size_t) ret
;
241 /* The return values of the vsnprintf() calls are not
254 sb_free (struct string_buffer
*buffer
)
256 if (buffer
->data
!= buffer
->space
)
260 /* Returns the contents of BUFFER, and frees all other memory held
261 by BUFFER. Returns NULL upon failure or if there was an error earlier. */
263 sb_dupfree (struct string_buffer
*buffer
)
268 if (sb_ensure_more_bytes (buffer
, 1) < 0)
270 buffer
->data
[buffer
->length
] = '\0';
273 if (buffer
->data
== buffer
->space
)
275 char *copy
= (char *) malloc (buffer
->length
);
278 memcpy (copy
, buffer
->data
, buffer
->length
);
283 /* Shrink the string before returning it. */
284 char *contents
= buffer
->data
;
285 if (buffer
->length
< buffer
->allocated
)
287 contents
= realloc (contents
, buffer
->length
);
288 if (contents
== NULL
)