• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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