1 /*
2 * Copyright © 2010 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "util/macros.h"
32 #include "util/u_math.h"
33 #include "util/u_printf.h"
34
35 #include "ralloc.h"
36
37 #define CANARY 0x5A1106
38
39 /* Align the header's size so that ralloc() allocations will return with the
40 * same alignment as a libc malloc would have (8 on 32-bit GLIBC, 16 on
41 * 64-bit), avoiding performance penalities on x86 and alignment faults on
42 * ARM.
43 */
44 struct ralloc_header
45 {
46 #if defined(__LP64__) || defined(_WIN64)
47 alignas(16)
48 #else
49 alignas(8)
50 #endif
51
52 #ifndef NDEBUG
53 /* A canary value used to determine whether a pointer is ralloc'd. */
54 unsigned canary;
55 #endif
56
57 struct ralloc_header *parent;
58
59 /* The first child (head of a linked list) */
60 struct ralloc_header *child;
61
62 /* Linked list of siblings */
63 struct ralloc_header *prev;
64 struct ralloc_header *next;
65
66 void (*destructor)(void *);
67 };
68
69 typedef struct ralloc_header ralloc_header;
70
71 static void unlink_block(ralloc_header *info);
72 static void unsafe_free(ralloc_header *info);
73
74 static ralloc_header *
get_header(const void * ptr)75 get_header(const void *ptr)
76 {
77 ralloc_header *info = (ralloc_header *) (((char *) ptr) -
78 sizeof(ralloc_header));
79 assert(info->canary == CANARY);
80 return info;
81 }
82
83 #define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header))
84
85 static void
add_child(ralloc_header * parent,ralloc_header * info)86 add_child(ralloc_header *parent, ralloc_header *info)
87 {
88 if (parent != NULL) {
89 info->parent = parent;
90 info->next = parent->child;
91 parent->child = info;
92
93 if (info->next != NULL)
94 info->next->prev = info;
95 }
96 }
97
98 void *
ralloc_context(const void * ctx)99 ralloc_context(const void *ctx)
100 {
101 return ralloc_size(ctx, 0);
102 }
103
104 void *
ralloc_size(const void * ctx,size_t size)105 ralloc_size(const void *ctx, size_t size)
106 {
107 /* Some malloc allocation doesn't always align to 16 bytes even on 64 bits
108 * system, from Android bionic/tests/malloc_test.cpp:
109 * - Allocations of a size that rounds up to a multiple of 16 bytes
110 * must have at least 16 byte alignment.
111 * - Allocations of a size that rounds up to a multiple of 8 bytes and
112 * not 16 bytes, are only required to have at least 8 byte alignment.
113 */
114 void *block = malloc(align64(size + sizeof(ralloc_header),
115 alignof(ralloc_header)));
116 ralloc_header *info;
117 ralloc_header *parent;
118
119 if (unlikely(block == NULL))
120 return NULL;
121
122 info = (ralloc_header *) block;
123 /* measurements have shown that calloc is slower (because of
124 * the multiplication overflow checking?), so clear things
125 * manually
126 */
127 info->parent = NULL;
128 info->child = NULL;
129 info->prev = NULL;
130 info->next = NULL;
131 info->destructor = NULL;
132
133 parent = ctx != NULL ? get_header(ctx) : NULL;
134
135 add_child(parent, info);
136
137 #ifndef NDEBUG
138 info->canary = CANARY;
139 #endif
140
141 return PTR_FROM_HEADER(info);
142 }
143
144 void *
rzalloc_size(const void * ctx,size_t size)145 rzalloc_size(const void *ctx, size_t size)
146 {
147 void *ptr = ralloc_size(ctx, size);
148
149 if (likely(ptr))
150 memset(ptr, 0, size);
151
152 return ptr;
153 }
154
155 /* helper function - assumes ptr != NULL */
156 static void *
resize(void * ptr,size_t size)157 resize(void *ptr, size_t size)
158 {
159 ralloc_header *child, *old, *info;
160
161 old = get_header(ptr);
162 info = realloc(old, align64(size + sizeof(ralloc_header),
163 alignof(ralloc_header)));
164
165 if (info == NULL)
166 return NULL;
167
168 /* Update parent and sibling's links to the reallocated node. */
169 if (info != old && info->parent != NULL) {
170 if (info->parent->child == old)
171 info->parent->child = info;
172
173 if (info->prev != NULL)
174 info->prev->next = info;
175
176 if (info->next != NULL)
177 info->next->prev = info;
178 }
179
180 /* Update child->parent links for all children */
181 for (child = info->child; child != NULL; child = child->next)
182 child->parent = info;
183
184 return PTR_FROM_HEADER(info);
185 }
186
187 void *
reralloc_size(const void * ctx,void * ptr,size_t size)188 reralloc_size(const void *ctx, void *ptr, size_t size)
189 {
190 if (unlikely(ptr == NULL))
191 return ralloc_size(ctx, size);
192
193 assert(ralloc_parent(ptr) == ctx);
194 return resize(ptr, size);
195 }
196
197 void *
rerzalloc_size(const void * ctx,void * ptr,size_t old_size,size_t new_size)198 rerzalloc_size(const void *ctx, void *ptr, size_t old_size, size_t new_size)
199 {
200 if (unlikely(ptr == NULL))
201 return rzalloc_size(ctx, new_size);
202
203 assert(ralloc_parent(ptr) == ctx);
204 ptr = resize(ptr, new_size);
205
206 if (new_size > old_size)
207 memset((char *)ptr + old_size, 0, new_size - old_size);
208
209 return ptr;
210 }
211
212 void *
ralloc_array_size(const void * ctx,size_t size,unsigned count)213 ralloc_array_size(const void *ctx, size_t size, unsigned count)
214 {
215 if (count > SIZE_MAX/size)
216 return NULL;
217
218 return ralloc_size(ctx, size * count);
219 }
220
221 void *
rzalloc_array_size(const void * ctx,size_t size,unsigned count)222 rzalloc_array_size(const void *ctx, size_t size, unsigned count)
223 {
224 if (count > SIZE_MAX/size)
225 return NULL;
226
227 return rzalloc_size(ctx, size * count);
228 }
229
230 void *
reralloc_array_size(const void * ctx,void * ptr,size_t size,unsigned count)231 reralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count)
232 {
233 if (count > SIZE_MAX/size)
234 return NULL;
235
236 return reralloc_size(ctx, ptr, size * count);
237 }
238
239 void *
rerzalloc_array_size(const void * ctx,void * ptr,size_t size,unsigned old_count,unsigned new_count)240 rerzalloc_array_size(const void *ctx, void *ptr, size_t size,
241 unsigned old_count, unsigned new_count)
242 {
243 if (new_count > SIZE_MAX/size)
244 return NULL;
245
246 return rerzalloc_size(ctx, ptr, size * old_count, size * new_count);
247 }
248
249 void
ralloc_free(void * ptr)250 ralloc_free(void *ptr)
251 {
252 ralloc_header *info;
253
254 if (ptr == NULL)
255 return;
256
257 info = get_header(ptr);
258 unlink_block(info);
259 unsafe_free(info);
260 }
261
262 static void
unlink_block(ralloc_header * info)263 unlink_block(ralloc_header *info)
264 {
265 /* Unlink from parent & siblings */
266 if (info->parent != NULL) {
267 if (info->parent->child == info)
268 info->parent->child = info->next;
269
270 if (info->prev != NULL)
271 info->prev->next = info->next;
272
273 if (info->next != NULL)
274 info->next->prev = info->prev;
275 }
276 info->parent = NULL;
277 info->prev = NULL;
278 info->next = NULL;
279 }
280
281 static void
unsafe_free(ralloc_header * info)282 unsafe_free(ralloc_header *info)
283 {
284 /* Recursively free any children...don't waste time unlinking them. */
285 ralloc_header *temp;
286 while (info->child != NULL) {
287 temp = info->child;
288 info->child = temp->next;
289 unsafe_free(temp);
290 }
291
292 /* Free the block itself. Call the destructor first, if any. */
293 if (info->destructor != NULL)
294 info->destructor(PTR_FROM_HEADER(info));
295
296 free(info);
297 }
298
299 void
ralloc_steal(const void * new_ctx,void * ptr)300 ralloc_steal(const void *new_ctx, void *ptr)
301 {
302 ralloc_header *info, *parent;
303
304 if (unlikely(ptr == NULL))
305 return;
306
307 info = get_header(ptr);
308 parent = new_ctx ? get_header(new_ctx) : NULL;
309
310 unlink_block(info);
311
312 add_child(parent, info);
313 }
314
315 void
ralloc_adopt(const void * new_ctx,void * old_ctx)316 ralloc_adopt(const void *new_ctx, void *old_ctx)
317 {
318 ralloc_header *new_info, *old_info, *child;
319
320 if (unlikely(old_ctx == NULL))
321 return;
322
323 old_info = get_header(old_ctx);
324 new_info = get_header(new_ctx);
325
326 /* If there are no children, bail. */
327 if (unlikely(old_info->child == NULL))
328 return;
329
330 /* Set all the children's parent to new_ctx; get a pointer to the last child. */
331 for (child = old_info->child; child->next != NULL; child = child->next) {
332 child->parent = new_info;
333 }
334 child->parent = new_info;
335
336 /* Connect the two lists together; parent them to new_ctx; make old_ctx empty. */
337 child->next = new_info->child;
338 if (child->next)
339 child->next->prev = child;
340 new_info->child = old_info->child;
341 old_info->child = NULL;
342 }
343
344 void *
ralloc_parent(const void * ptr)345 ralloc_parent(const void *ptr)
346 {
347 ralloc_header *info;
348
349 if (unlikely(ptr == NULL))
350 return NULL;
351
352 info = get_header(ptr);
353 return info->parent ? PTR_FROM_HEADER(info->parent) : NULL;
354 }
355
356 void
ralloc_set_destructor(const void * ptr,void (* destructor)(void *))357 ralloc_set_destructor(const void *ptr, void(*destructor)(void *))
358 {
359 ralloc_header *info = get_header(ptr);
360 info->destructor = destructor;
361 }
362
363 char *
ralloc_strdup(const void * ctx,const char * str)364 ralloc_strdup(const void *ctx, const char *str)
365 {
366 size_t n;
367 char *ptr;
368
369 if (unlikely(str == NULL))
370 return NULL;
371
372 n = strlen(str);
373 ptr = ralloc_array(ctx, char, n + 1);
374 memcpy(ptr, str, n);
375 ptr[n] = '\0';
376 return ptr;
377 }
378
379 char *
ralloc_strndup(const void * ctx,const char * str,size_t max)380 ralloc_strndup(const void *ctx, const char *str, size_t max)
381 {
382 size_t n;
383 char *ptr;
384
385 if (unlikely(str == NULL))
386 return NULL;
387
388 n = strnlen(str, max);
389 ptr = ralloc_array(ctx, char, n + 1);
390 memcpy(ptr, str, n);
391 ptr[n] = '\0';
392 return ptr;
393 }
394
395 /* helper routine for strcat/strncat - n is the exact amount to copy */
396 static bool
cat(char ** dest,const char * str,size_t n)397 cat(char **dest, const char *str, size_t n)
398 {
399 char *both;
400 size_t existing_length;
401 assert(dest != NULL && *dest != NULL);
402
403 existing_length = strlen(*dest);
404 both = resize(*dest, existing_length + n + 1);
405 if (unlikely(both == NULL))
406 return false;
407
408 memcpy(both + existing_length, str, n);
409 both[existing_length + n] = '\0';
410
411 *dest = both;
412 return true;
413 }
414
415
416 bool
ralloc_strcat(char ** dest,const char * str)417 ralloc_strcat(char **dest, const char *str)
418 {
419 return cat(dest, str, strlen(str));
420 }
421
422 bool
ralloc_strncat(char ** dest,const char * str,size_t n)423 ralloc_strncat(char **dest, const char *str, size_t n)
424 {
425 return cat(dest, str, strnlen(str, n));
426 }
427
428 bool
ralloc_str_append(char ** dest,const char * str,size_t existing_length,size_t str_size)429 ralloc_str_append(char **dest, const char *str,
430 size_t existing_length, size_t str_size)
431 {
432 char *both;
433 assert(dest != NULL && *dest != NULL);
434
435 both = resize(*dest, existing_length + str_size + 1);
436 if (unlikely(both == NULL))
437 return false;
438
439 memcpy(both + existing_length, str, str_size);
440 both[existing_length + str_size] = '\0';
441
442 *dest = both;
443
444 return true;
445 }
446
447 char *
ralloc_asprintf(const void * ctx,const char * fmt,...)448 ralloc_asprintf(const void *ctx, const char *fmt, ...)
449 {
450 char *ptr;
451 va_list args;
452 va_start(args, fmt);
453 ptr = ralloc_vasprintf(ctx, fmt, args);
454 va_end(args);
455 return ptr;
456 }
457
458 char *
ralloc_vasprintf(const void * ctx,const char * fmt,va_list args)459 ralloc_vasprintf(const void *ctx, const char *fmt, va_list args)
460 {
461 size_t size = u_printf_length(fmt, args) + 1;
462
463 char *ptr = ralloc_size(ctx, size);
464 if (ptr != NULL)
465 vsnprintf(ptr, size, fmt, args);
466
467 return ptr;
468 }
469
470 bool
ralloc_asprintf_append(char ** str,const char * fmt,...)471 ralloc_asprintf_append(char **str, const char *fmt, ...)
472 {
473 bool success;
474 va_list args;
475 va_start(args, fmt);
476 success = ralloc_vasprintf_append(str, fmt, args);
477 va_end(args);
478 return success;
479 }
480
481 bool
ralloc_vasprintf_append(char ** str,const char * fmt,va_list args)482 ralloc_vasprintf_append(char **str, const char *fmt, va_list args)
483 {
484 size_t existing_length;
485 assert(str != NULL);
486 existing_length = *str ? strlen(*str) : 0;
487 return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args);
488 }
489
490 bool
ralloc_asprintf_rewrite_tail(char ** str,size_t * start,const char * fmt,...)491 ralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...)
492 {
493 bool success;
494 va_list args;
495 va_start(args, fmt);
496 success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args);
497 va_end(args);
498 return success;
499 }
500
501 bool
ralloc_vasprintf_rewrite_tail(char ** str,size_t * start,const char * fmt,va_list args)502 ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt,
503 va_list args)
504 {
505 size_t new_length;
506 char *ptr;
507
508 assert(str != NULL);
509
510 if (unlikely(*str == NULL)) {
511 // Assuming a NULL context is probably bad, but it's expected behavior.
512 *str = ralloc_vasprintf(NULL, fmt, args);
513 *start = strlen(*str);
514 return true;
515 }
516
517 new_length = u_printf_length(fmt, args);
518
519 ptr = resize(*str, *start + new_length + 1);
520 if (unlikely(ptr == NULL))
521 return false;
522
523 vsnprintf(ptr + *start, new_length + 1, fmt, args);
524 *str = ptr;
525 *start += new_length;
526 return true;
527 }
528
529 /***************************************************************************
530 * Linear allocator for short-lived allocations.
531 ***************************************************************************
532 *
533 * The allocator consists of a parent node (2K buffer), which requires
534 * a ralloc parent, and child nodes (allocations). Child nodes can't be freed
535 * directly, because the parent doesn't track them. You have to release
536 * the parent node in order to release all its children.
537 *
538 * The allocator uses a fixed-sized buffer with a monotonically increasing
539 * offset after each allocation. If the buffer is all used, another buffer
540 * is allocated, sharing the same ralloc parent, so all buffers are at
541 * the same level in the ralloc hierarchy.
542 *
543 * The linear parent node is always the first buffer and keeps track of all
544 * other buffers.
545 */
546
547 #define MIN_LINEAR_BUFSIZE 2048
548 #define SUBALLOC_ALIGNMENT 8
549 #define LMAGIC 0x87b9c7d3
550
551 struct linear_header {
552
553 /* align first member to align struct */
554 #if defined(__LP64__) || defined(_WIN64)
555 alignas(16)
556 #else
557 alignas(8)
558 #endif
559
560 #ifndef NDEBUG
561 unsigned magic; /* for debugging */
562 #endif
563 unsigned offset; /* points to the first unused byte in the buffer */
564 unsigned size; /* size of the buffer */
565 void *ralloc_parent; /* new buffers will use this */
566 struct linear_header *next; /* next buffer if we have more */
567 struct linear_header *latest; /* the only buffer that has free space */
568
569 /* After this structure, the buffer begins.
570 * Each suballocation consists of linear_size_chunk as its header followed
571 * by the suballocation, so it goes:
572 *
573 * - linear_size_chunk
574 * - allocated space
575 * - linear_size_chunk
576 * - allocated space
577 * etc.
578 *
579 * linear_size_chunk is only needed by linear_realloc.
580 */
581 };
582
583 struct linear_size_chunk {
584 unsigned size; /* for realloc */
585 unsigned _padding;
586 };
587
588 typedef struct linear_header linear_header;
589 typedef struct linear_size_chunk linear_size_chunk;
590
591 #define LINEAR_PARENT_TO_HEADER(parent) \
592 (linear_header*) \
593 ((char*)(parent) - sizeof(linear_size_chunk) - sizeof(linear_header))
594
595 /* Allocate the linear buffer with its header. */
596 static linear_header *
create_linear_node(void * ralloc_ctx,unsigned min_size)597 create_linear_node(void *ralloc_ctx, unsigned min_size)
598 {
599 linear_header *node;
600
601 min_size += sizeof(linear_size_chunk);
602
603 if (likely(min_size < MIN_LINEAR_BUFSIZE))
604 min_size = MIN_LINEAR_BUFSIZE;
605
606 node = ralloc_size(ralloc_ctx, sizeof(linear_header) + min_size);
607 if (unlikely(!node))
608 return NULL;
609
610 #ifndef NDEBUG
611 node->magic = LMAGIC;
612 #endif
613 node->offset = 0;
614 node->size = min_size;
615 node->ralloc_parent = ralloc_ctx;
616 node->next = NULL;
617 node->latest = node;
618 return node;
619 }
620
621 void *
linear_alloc_child(void * parent,unsigned size)622 linear_alloc_child(void *parent, unsigned size)
623 {
624 linear_header *first = LINEAR_PARENT_TO_HEADER(parent);
625 linear_header *latest = first->latest;
626 linear_header *new_node;
627 linear_size_chunk *ptr;
628 unsigned full_size;
629
630 assert(first->magic == LMAGIC);
631 assert(!latest->next);
632
633 size = ALIGN_POT(size, SUBALLOC_ALIGNMENT);
634 full_size = sizeof(linear_size_chunk) + size;
635
636 if (unlikely(latest->offset + full_size > latest->size)) {
637 /* allocate a new node */
638 new_node = create_linear_node(latest->ralloc_parent, size);
639 if (unlikely(!new_node))
640 return NULL;
641
642 first->latest = new_node;
643 latest->latest = new_node;
644 latest->next = new_node;
645 latest = new_node;
646 }
647
648 ptr = (linear_size_chunk *)((char*)&latest[1] + latest->offset);
649 ptr->size = size;
650 latest->offset += full_size;
651
652 assert((uintptr_t)&ptr[1] % SUBALLOC_ALIGNMENT == 0);
653 return &ptr[1];
654 }
655
656 void *
linear_alloc_parent(void * ralloc_ctx,unsigned size)657 linear_alloc_parent(void *ralloc_ctx, unsigned size)
658 {
659 linear_header *node;
660
661 if (unlikely(!ralloc_ctx))
662 return NULL;
663
664 size = ALIGN_POT(size, SUBALLOC_ALIGNMENT);
665
666 node = create_linear_node(ralloc_ctx, size);
667 if (unlikely(!node))
668 return NULL;
669
670 return linear_alloc_child((char*)node +
671 sizeof(linear_header) +
672 sizeof(linear_size_chunk), size);
673 }
674
675 void *
linear_zalloc_child(void * parent,unsigned size)676 linear_zalloc_child(void *parent, unsigned size)
677 {
678 void *ptr = linear_alloc_child(parent, size);
679
680 if (likely(ptr))
681 memset(ptr, 0, size);
682 return ptr;
683 }
684
685 void *
linear_zalloc_parent(void * parent,unsigned size)686 linear_zalloc_parent(void *parent, unsigned size)
687 {
688 void *ptr = linear_alloc_parent(parent, size);
689
690 if (likely(ptr))
691 memset(ptr, 0, size);
692 return ptr;
693 }
694
695 void
linear_free_parent(void * ptr)696 linear_free_parent(void *ptr)
697 {
698 linear_header *node;
699
700 if (unlikely(!ptr))
701 return;
702
703 node = LINEAR_PARENT_TO_HEADER(ptr);
704 assert(node->magic == LMAGIC);
705
706 while (node) {
707 void *ptr = node;
708
709 node = node->next;
710 ralloc_free(ptr);
711 }
712 }
713
714 void
ralloc_steal_linear_parent(void * new_ralloc_ctx,void * ptr)715 ralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr)
716 {
717 linear_header *node;
718
719 if (unlikely(!ptr))
720 return;
721
722 node = LINEAR_PARENT_TO_HEADER(ptr);
723 assert(node->magic == LMAGIC);
724
725 while (node) {
726 ralloc_steal(new_ralloc_ctx, node);
727 node->ralloc_parent = new_ralloc_ctx;
728 node = node->next;
729 }
730 }
731
732 void *
ralloc_parent_of_linear_parent(void * ptr)733 ralloc_parent_of_linear_parent(void *ptr)
734 {
735 linear_header *node = LINEAR_PARENT_TO_HEADER(ptr);
736 assert(node->magic == LMAGIC);
737 return node->ralloc_parent;
738 }
739
740 void *
linear_realloc(void * parent,void * old,unsigned new_size)741 linear_realloc(void *parent, void *old, unsigned new_size)
742 {
743 unsigned old_size = 0;
744 ralloc_header *new_ptr;
745
746 new_ptr = linear_alloc_child(parent, new_size);
747
748 if (unlikely(!old))
749 return new_ptr;
750
751 old_size = ((linear_size_chunk*)old)[-1].size;
752
753 if (likely(new_ptr && old_size))
754 memcpy(new_ptr, old, MIN2(old_size, new_size));
755
756 return new_ptr;
757 }
758
759 /* All code below is pretty much copied from ralloc and only the alloc
760 * calls are different.
761 */
762
763 char *
linear_strdup(void * parent,const char * str)764 linear_strdup(void *parent, const char *str)
765 {
766 unsigned n;
767 char *ptr;
768
769 if (unlikely(!str))
770 return NULL;
771
772 n = strlen(str);
773 ptr = linear_alloc_child(parent, n + 1);
774 if (unlikely(!ptr))
775 return NULL;
776
777 memcpy(ptr, str, n);
778 ptr[n] = '\0';
779 return ptr;
780 }
781
782 char *
linear_asprintf(void * parent,const char * fmt,...)783 linear_asprintf(void *parent, const char *fmt, ...)
784 {
785 char *ptr;
786 va_list args;
787 va_start(args, fmt);
788 ptr = linear_vasprintf(parent, fmt, args);
789 va_end(args);
790 return ptr;
791 }
792
793 char *
linear_vasprintf(void * parent,const char * fmt,va_list args)794 linear_vasprintf(void *parent, const char *fmt, va_list args)
795 {
796 unsigned size = u_printf_length(fmt, args) + 1;
797
798 char *ptr = linear_alloc_child(parent, size);
799 if (ptr != NULL)
800 vsnprintf(ptr, size, fmt, args);
801
802 return ptr;
803 }
804
805 bool
linear_asprintf_append(void * parent,char ** str,const char * fmt,...)806 linear_asprintf_append(void *parent, char **str, const char *fmt, ...)
807 {
808 bool success;
809 va_list args;
810 va_start(args, fmt);
811 success = linear_vasprintf_append(parent, str, fmt, args);
812 va_end(args);
813 return success;
814 }
815
816 bool
linear_vasprintf_append(void * parent,char ** str,const char * fmt,va_list args)817 linear_vasprintf_append(void *parent, char **str, const char *fmt, va_list args)
818 {
819 size_t existing_length;
820 assert(str != NULL);
821 existing_length = *str ? strlen(*str) : 0;
822 return linear_vasprintf_rewrite_tail(parent, str, &existing_length, fmt, args);
823 }
824
825 bool
linear_asprintf_rewrite_tail(void * parent,char ** str,size_t * start,const char * fmt,...)826 linear_asprintf_rewrite_tail(void *parent, char **str, size_t *start,
827 const char *fmt, ...)
828 {
829 bool success;
830 va_list args;
831 va_start(args, fmt);
832 success = linear_vasprintf_rewrite_tail(parent, str, start, fmt, args);
833 va_end(args);
834 return success;
835 }
836
837 bool
linear_vasprintf_rewrite_tail(void * parent,char ** str,size_t * start,const char * fmt,va_list args)838 linear_vasprintf_rewrite_tail(void *parent, char **str, size_t *start,
839 const char *fmt, va_list args)
840 {
841 size_t new_length;
842 char *ptr;
843
844 assert(str != NULL);
845
846 if (unlikely(*str == NULL)) {
847 *str = linear_vasprintf(parent, fmt, args);
848 *start = strlen(*str);
849 return true;
850 }
851
852 new_length = u_printf_length(fmt, args);
853
854 ptr = linear_realloc(parent, *str, *start + new_length + 1);
855 if (unlikely(ptr == NULL))
856 return false;
857
858 vsnprintf(ptr + *start, new_length + 1, fmt, args);
859 *str = ptr;
860 *start += new_length;
861 return true;
862 }
863
864 /* helper routine for strcat/strncat - n is the exact amount to copy */
865 static bool
linear_cat(void * parent,char ** dest,const char * str,unsigned n)866 linear_cat(void *parent, char **dest, const char *str, unsigned n)
867 {
868 char *both;
869 unsigned existing_length;
870 assert(dest != NULL && *dest != NULL);
871
872 existing_length = strlen(*dest);
873 both = linear_realloc(parent, *dest, existing_length + n + 1);
874 if (unlikely(both == NULL))
875 return false;
876
877 memcpy(both + existing_length, str, n);
878 both[existing_length + n] = '\0';
879
880 *dest = both;
881 return true;
882 }
883
884 bool
linear_strcat(void * parent,char ** dest,const char * str)885 linear_strcat(void *parent, char **dest, const char *str)
886 {
887 return linear_cat(parent, dest, str, strlen(str));
888 }
889