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