1 /*
2 * Copyright (c) 2012 Tim Ruehsen
3 * Copyright (c) 2015-2021 Free Software Foundation, Inc.
4 *
5 * This file is part of libwget.
6 *
7 * Libwget is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Libwget is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with libwget. If not, see <https://www.gnu.org/licenses/>.
19 *
20 *
21 * Memory buffer data structure routines
22 *
23 * Changelog
24 * 22.08.2012 Tim Ruehsen created
25 *
26 */
27
28 #include <config.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 #include <wget.h>
36 #include "private.h"
37
38 /**
39 * \file
40 * \brief Buffer management functions
41 * \defgroup libwget-buffer Buffer management functions
42 * @{
43 *
44 * A buffer (represented with an opaque `wget_buffer`) is a managed memory area.
45 *
46 * Apart from a pointer to a raw chunk of memory (`char *`), it also has some metadata attached
47 * such as the length of the buffer and the actual occupied positions.
48 *
49 * Actually, when we talk about the **length** of the buffer, we refer to the actual number of bytes stored
50 * in it by the user. On the other hand, the **size** is the total number of slots in the buffer, either occupied
51 * or not.
52 *
53 * The stored data is always 0-terminated, so you safely use it with standard string functions.
54 *
55 * The functions here allow you to easily work with buffers, providing shortcuts to commonly used
56 * memory and string management operations and avoiding usual pitfalls, such as buffer overflows.
57 * They provide a higher-level abstraction to working with memory than the @link libwget-mem memory management functions@endlink.
58 *
59 * If your application uses memory allocation functions that may return %NULL values (e.g. like the standard libc functions),
60 * you have to check the `error` value before using the result. If set, it indicates that a memory allocation failure
61 * occurred during internal (re-)allocation.
62 *
63 * Example with wget_buffer on the stack (initial 16 bytes heap allocation)
64 * ```
65 * wget_buffer buf;
66 *
67 * wet_buffer_init(&buf, NULL, 16);
68 *
69 * wget_buffer_strcpy(&buf, "A");
70 * wget_buffer_strcat(&buf, "B");
71 * wget_buffer_memcat(&buf, "C", 1);
72 * wget_buffer_memset_append(&buf, 'D', 1);
73 * wget_buffer_printf_append(&buf, "%s", "E");
74 *
75 * // buf.data now contains the 0-terminated string "ABCDE"
76 * printf("buf.data = %s\n", buf.data);
77 *
78 * wget_buffer_deinit(&buf);
79 * ```
80 *
81 * Example with wget_buffer on the stack and 16 bytes initial stack buffer (no heap allocation is needed)
82 * ```
83 * wget_buffer buf;
84 * char sbuf[16];
85 *
86 * wet_buffer_init(&buf, sbuf, sizeof(sbuf));
87 *
88 * wget_buffer_strcpy(&buf, "A");
89 * wget_buffer_strcat(&buf, "B");
90 * wget_buffer_memcat(&buf, "C", 1);
91 * wget_buffer_memset_append(&buf, 'D', 1);
92 * wget_buffer_printf_append(&buf, "%s", "E");
93 *
94 * // buf.data now contains the 0-terminated string "ABCDE"
95 * printf("buf.data = %s\n", buf.data);
96 *
97 * wget_buffer_deinit(&buf);
98 * ```
99 *
100 * Example on how to check for memory failure and how to transfer buffer data
101 * ```
102 * wget_buffer buf;
103 *
104 * wet_buffer_init(&buf, NULL, 16);
105 *
106 * wget_buffer_strcpy(&buf, "A");
107 * wget_buffer_strcat(&buf, "B");
108 * wget_buffer_memcat(&buf, "C", 1);
109 * wget_buffer_memset_append(&buf, 'D', 1);
110 * wget_buffer_printf_append(&buf, "%s", "E");
111 *
112 * if (buf.error)
113 * panic("No memory !");
114 *
115 * // transfer ownership away from wget_buffer
116 * char *ret = buf.data;
117 * buf.data = NULL; // avoid double frees
118 *
119 * wget_buffer_deinit(&buf);
120 *
121 * return ret; // the caller must free() this value after use
122 * ```
123 */
124
125 /**
126 * \param[in] buf Pointer to the buffer that should become initialized.
127 * \param[in] data Initial contents of the buffer. Might be NULL.
128 * \param[in] size Initial length of the buffer. Might be zero (will default to 128 bytes).
129 * \return WGET_E_SUCCESS or WGET_E_MEMORY in case of a memory allocation failure.
130 *
131 * Create a new buffer.
132 *
133 * If \p data is NULL, the buffer will be empty, but it will be pre-allocated with \p size bytes.
134 * This will make future operations on the buffer faster since there will be less re-allocations needed.
135 *
136 * <b>If \p size is zero, the buffer will be pre-allocated with 128 bytes.</b>
137 *
138 * You may provide some \p data to fill the buffer with it. The contents of the \p data pointer
139 * are not copied, but rather the pointer itself is referenced directly within the buffer. If you modify the contents
140 * of \p data, those changes will be reflected in the buffer as they both point to the same memory area.
141 *
142 * Apart from that, there are other concerns you should keep in mind if you provide your own \p data here:
143 *
144 * - wget_buffer_deinit() _will not_ free that memory when you call it. So if you provide
145 * a \p data pointer, you must free it yourself before your program ends.
146 * - wget_buffer_realloc() will also not free that memory. It will allocate a new buffer and copy the contents
147 * there, but will not touch the old buffer. The new buffer _will_ be freed by these functions since it's been
148 * allocated by libwget internally and thus it knows it can be freed without harm.
149 *
150 * If an existing buffer is provided in \p buf, it will be initialized with the provided \p data and \p size
151 * according to the rules stated above.
152 */
wget_buffer_init(wget_buffer * buf,char * data,size_t size)153 int wget_buffer_init(wget_buffer *buf, char *data, size_t size)
154 {
155 if (data && likely(size)) {
156 buf->size = size - 1;
157 buf->data = data;
158 *buf->data = 0; // always 0 terminate data to allow string functions
159 buf->release_data = 0;
160 } else {
161 if (!size)
162 size = 127;
163 buf->size = size;
164 if (!(buf->data = wget_malloc(size + 1))) {
165 buf->error = 1;
166 return WGET_E_MEMORY;
167 }
168 *buf->data = 0; // always 0 terminate data to allow string functions
169 buf->release_data = 1;
170 }
171
172 buf->error = 0;
173 buf->release_buf = 0;
174 buf->length = 0;
175
176 return WGET_E_SUCCESS;
177 }
178
179 /**
180 * \param[in] size Initial length of the buffer.
181 * \return A new buffer.
182 *
183 * Allocates a new buffer of size \p size bytes.
184 *
185 * The buffer will be pre-allocated with that many bytes, all zeros.
186 *
187 * This is equivalent to wget_buffer_init(NULL, NULL, size).
188 */
wget_buffer_alloc(size_t size)189 wget_buffer *wget_buffer_alloc(size_t size)
190 {
191 wget_buffer *buf;
192
193 if (!(buf = wget_malloc(sizeof(wget_buffer))))
194 return NULL;
195
196 if (wget_buffer_init(buf, NULL, size) < 0) {
197 xfree(buf);
198 return NULL;
199 }
200
201 buf->release_buf = 1;
202
203 return buf;
204 }
205
buffer_realloc(wget_buffer * buf,size_t size)206 static int buffer_realloc(wget_buffer *buf, size_t size)
207 {
208 char *old_data = buf->data;
209
210 if (buf->release_data)
211 buf->data = wget_realloc(buf->data, size + 1);
212 else
213 buf->data = wget_malloc(size + 1);
214
215 if (!buf->data) {
216 buf->data = old_data;
217 buf->error = 1;
218 return WGET_E_MEMORY;
219 }
220
221 if (!buf->release_data) {
222 if (likely(old_data) && buf->length)
223 memcpy(buf->data, old_data, buf->length + 1);
224 else
225 *buf->data = 0; // always 0 terminate data to allow string functions
226
227 buf->release_data = 1;
228 }
229
230 buf->size = size;
231
232 return WGET_E_SUCCESS;
233 }
234
235 /**
236 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
237 * \param[in] size Total size (in bytes) required in the buffer
238 * \return WGET_E_SUCCESS on success, else WGET_E_MEMORY if the memory allocation failed
239 *
240 * Make sure the buffer \p buf has at least a **size** of \p size bytes.
241 *
242 * If the buffer's size is less than that, it will automatically enlarge it
243 * (with wget_buffer_realloc()) to make it at least as long.
244 */
wget_buffer_ensure_capacity(wget_buffer * buf,size_t size)245 int wget_buffer_ensure_capacity(wget_buffer *buf, size_t size)
246 {
247 if (likely(buf)) {
248 if (buf->size < size)
249 return buffer_realloc(buf, size);
250 }
251
252 return WGET_E_SUCCESS;
253 }
254
255 /**
256 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
257 *
258 * Free the buffer, and all its contents.
259 *
260 * If you provided your own data when calling wget_buffer_init() (you passed a non-NULL \p data pointer)
261 * then **that buffer will not be freed**. As stated in the description of wget_buffer_init() you
262 * must free that buffer yourself: this function will only free the `wget_buffer` structure.
263 *
264 * Similarly, if you provided your own buffer when calling wget_buffer_init() (\p buf was non-NULL)
265 * the buffer (the `wget_buffer` structure) **will not** be freed, and the data might or might not be freed
266 * depending on the above condition.
267 */
wget_buffer_deinit(wget_buffer * buf)268 void wget_buffer_deinit(wget_buffer *buf)
269 {
270 if (buf->release_data) {
271 xfree(buf->data);
272 buf->release_data = 0;
273 }
274
275 if (buf->release_buf)
276 wget_free(buf); // do not use xfree() since buf is NONNULL
277 }
278
279 /**
280 * \param[in] buf A double pointer to a buffer
281 *
282 * Free the buffer, and all its contents.
283 *
284 * It behaves like wget_buffer_deinit() but it also sets the \p buf pointer to NULL.
285 *
286 * This function is equivalent to:
287 *
288 * wget_buffer_deinit(*buf);
289 * *buf = NULL;
290 */
wget_buffer_free(wget_buffer ** buf)291 void wget_buffer_free(wget_buffer **buf)
292 {
293 if (likely(buf && *buf)) {
294 wget_buffer_deinit(*buf);
295 *buf = NULL;
296 }
297 }
298
299 /**
300 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
301 *
302 * Release the buffer's data, but keep the buffer itself (the `wget_buffer` structure).
303 *
304 * The **length** of the buffer will be maintained, but after this function succeeds, the **size**
305 * will obviously be zero.
306 *
307 * The same rules that apply to wget_buffer_deinit() also apply here: if you provided your own data
308 * when calling wget_buffer_init() (ie. \p data was non-NULL) then **that data will not be freed**, and this
309 * function will essentially be a no-op.
310 */
wget_buffer_free_data(wget_buffer * buf)311 void wget_buffer_free_data(wget_buffer *buf)
312 {
313 if (likely(buf)) {
314 if (buf->release_data) {
315 xfree(buf->data);
316 buf->release_data = 0;
317 buf->size = 0;
318 }
319 }
320 }
321
322 /**
323 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
324 *
325 * This function is lighter than wget_buffer_free_data(). It does not free the data buffer, it just
326 * sets its first byte to zero, as well as the length.
327 *
328 * This function is equivalent to:
329 *
330 * buf->length = 0;
331 * *buf->data = 0;
332 */
wget_buffer_reset(wget_buffer * buf)333 void wget_buffer_reset(wget_buffer *buf)
334 {
335 if (likely(buf)) {
336 buf->length = 0;
337 *buf->data = 0;
338 }
339 }
340
341 /**
342 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
343 * \param[in] data A pointer to the data to be copied
344 * \param[in] length How many bytes from \p data (starting at the beginning) should be copied
345 * \return The new length of the buffer after copying the data
346 *
347 * Copy the contents in the pointer \p data to the buffer \p buf,
348 * clobbering the previous contents.
349 *
350 * The first \p length bytes of \p data are written to \p buf.
351 * The content of \p buf is overwritten with the new \p data.
352 *
353 * If the buffer is not large enough to store that amount of data,
354 * it is enlarged automatically at least \p length bytes (with wget_buffer_realloc()).
355 */
wget_buffer_memcpy(wget_buffer * buf,const void * data,size_t length)356 size_t wget_buffer_memcpy(wget_buffer *buf, const void *data, size_t length)
357 {
358 if (unlikely(!buf))
359 return 0;
360
361 buf->length = 0;
362
363 return wget_buffer_memcat(buf, data, length);
364 }
365
366 /**
367 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
368 * \param[in] data A pointer to the data to be appended
369 * \param[in] length How many bytes of \p data should be written to \p buf
370 * \return The new length of the buffer after appending the data
371 *
372 * Append the provided \p data to the end of the buffer \p buf (preserving contents).
373 *
374 * If there's not enough space in \p buf, it is enlarged automatically
375 * (with wget_buffer_realloc()) at least \p length bytes, so that the whole
376 * data can be written.
377 */
wget_buffer_memcat(wget_buffer * buf,const void * data,size_t length)378 size_t wget_buffer_memcat(wget_buffer *buf, const void *data, size_t length)
379 {
380 if (unlikely(!buf))
381 return 0;
382
383 if (likely(length)) {
384 if (buf->size < buf->length + length)
385 if (buffer_realloc(buf, buf->size * 2 + length) != WGET_E_SUCCESS)
386 return buf->length;
387
388 if (likely(data))
389 memcpy(buf->data + buf->length, data, length);
390 else
391 memset(buf->data + buf->length, 0, length);
392 buf->length += length;
393 }
394 buf->data[buf->length] = 0; // always 0 terminate data to allow string functions
395
396 return buf->length;
397 }
398
399 /**
400 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
401 * \param[in] s A NULL-terminated string
402 * \return The new length of the buffer after copying the string
403 *
404 * Copy the NULL-terminated string \p s to the buffer \p buf,
405 * overwriting its original contents.
406 *
407 * If the buffer is not large enough it is enlarged automatically.
408 *
409 * This is essentially equivalent to:
410 *
411 * buf->length = 0;
412 * wget_buffer_memcat(buf, s, strlen(s));
413 */
wget_buffer_strcpy(wget_buffer * buf,const char * s)414 size_t wget_buffer_strcpy(wget_buffer *buf, const char *s)
415 {
416 if (likely(buf))
417 buf->length = 0;
418
419 return wget_buffer_memcat(buf, s, likely(s) ? strlen(s) : 0);
420 }
421
422 /**
423 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
424 * \param[in] s A NULL-terminated string
425 * \return The new length of the buffer after appending the string
426 *
427 * Append the NULL-terminated string \p s to the end of the buffer \p buf
428 * (preserving its contents).
429 *
430 * If the buffer is not large enough it is enlarged automatically.
431 *
432 * This is essentially equivalent to calling wget_buffer_memcat() with length equal to `strlen(s)`:
433 *
434 * wget_buffer_memcat(buf, s, strlen(s));
435 */
wget_buffer_strcat(wget_buffer * buf,const char * s)436 size_t wget_buffer_strcat(wget_buffer *buf, const char *s)
437 {
438 return wget_buffer_memcat(buf, s, likely(s) ? strlen(s) : 0);
439 }
440
441 /**
442 * \param[in] buf The destination buffer
443 * \param[in] src The source buffer
444 * \return The new length of the destination buffer \p buf after copying the contents of \p src
445 *
446 * Copy the contents of the buffer \p src in the buffer \p buf,
447 * clobbering its previous contents.
448 *
449 * If the buffer \p buf is not large enough it is enlarged automatically.
450 *
451 * This is equivalent to:
452 *
453 * wget_buffer_memcpy(buf, src->data, src->length);
454 */
wget_buffer_bufcpy(wget_buffer * buf,wget_buffer * src)455 size_t wget_buffer_bufcpy(wget_buffer *buf, wget_buffer *src)
456 {
457 if (likely(src))
458 return wget_buffer_memcpy(buf, src->data, src->length);
459 else
460 return wget_buffer_memcpy(buf, NULL, 0);
461 }
462
463 /**
464 * \param[in] buf The destination buffer
465 * \param[in] src The source buffer
466 * \return The new length of the destination buffer \p buf after appending the contents of \p src
467 *
468 * Append the contents of the buffer \p src to the end of the buffer \p buf.
469 *
470 * If the buffer \p buf is not large enough it is enlarged automatically.
471 *
472 * This is equivalent to:
473 *
474 * wget_buffer_memcat(buf, src->data, src->length);
475 */
wget_buffer_bufcat(wget_buffer * buf,wget_buffer * src)476 size_t wget_buffer_bufcat(wget_buffer *buf, wget_buffer *src)
477 {
478 if (likely(src))
479 return wget_buffer_memcat(buf, src->data, src->length);
480 else
481 return wget_buffer_memcat(buf, NULL, 0);
482 }
483
484 /**
485 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
486 * \param[in] c The byte to be copied at the end of the buffer
487 * \param[in] length How many times will the byte \p c be copied.
488 * \return The new length of the buffer \p buf.
489 *
490 * Copy the byte \p c repeatedly \p length times **starting at the beginning of the buffer**,
491 * so the first \p length bytes of the buffer are overwritten.
492 *
493 * If there's not enough space in \p buf, it is enlarged automatically
494 * (with wget_buffer_realloc()) at least \p length bytes.
495 */
wget_buffer_memset(wget_buffer * buf,char c,size_t length)496 size_t wget_buffer_memset(wget_buffer *buf, char c, size_t length)
497 {
498 if (likely(buf))
499 buf->length = 0;
500
501 return wget_buffer_memset_append(buf, c, length);
502 }
503
504 /**
505 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
506 * \param[in] c The byte to be copied at the end of the buffer
507 * \param[in] length How many times will the byte \p c be copied.
508 * \return The new length of the buffer \p buf.
509 *
510 * Copy the byte \p c at the end of the buffer \p buf repeatedly \p length times.
511 *
512 * If there's not enough space in \p buf, it is enlarged automatically
513 * (with wget_buffer_realloc()) at least \p length bytes.
514 */
wget_buffer_memset_append(wget_buffer * buf,char c,size_t length)515 size_t wget_buffer_memset_append(wget_buffer *buf, char c, size_t length)
516 {
517 if (unlikely(!buf))
518 return 0;
519
520 if (likely(length)) {
521 if (unlikely(buf->size < buf->length + length))
522 if (buffer_realloc(buf, buf->size * 2 + length) != WGET_E_SUCCESS)
523 return buf->length;
524
525 memset(buf->data + buf->length, c, length);
526 buf->length += length;
527 }
528 buf->data[buf->length] = 0; // always 0 terminate data to allow string functions
529
530 return buf->length;
531 }
532
533 /**
534 * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
535 * \return The buffer's new contents
536 *
537 * Remove all leading and trailing whitespace from the buffer \p buf.
538 *
539 * The transformation is done in-place, that is, the buffer's original content is overwritten
540 * with the new trimmed content.
541 */
wget_buffer_trim(wget_buffer * buf)542 char *wget_buffer_trim(wget_buffer *buf)
543 {
544 if (unlikely(!buf))
545 return NULL;
546
547 if (buf->length) {
548 char *start = buf->data;
549 char *end = start + buf->length - 1;
550
551 if (isspace(*end)) {
552 /* Skip trailing spaces */
553 for (; isspace(*end) && end >= start; end--)
554 ;
555 end[1] = 0;
556 buf->length = (size_t) (end - start + 1);
557 }
558
559 if (isspace(*start)) {
560 /* Skip leading spaces */
561 for (; isspace(*start) && end >= start; start++)
562 ;
563 buf->length = (size_t) (end - start + 1);
564 /* Include trailing 0 */
565 memmove(buf->data, start, buf->length + 1);
566 }
567 }
568
569 return buf->data;
570 }
571 /** @} */
572