ralloc.c revision 7e995a2e
1af69d88dSmrg/* 2af69d88dSmrg * Copyright © 2010 Intel Corporation 3af69d88dSmrg * 4af69d88dSmrg * Permission is hereby granted, free of charge, to any person obtaining a 5af69d88dSmrg * copy of this software and associated documentation files (the "Software"), 6af69d88dSmrg * to deal in the Software without restriction, including without limitation 7af69d88dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8af69d88dSmrg * and/or sell copies of the Software, and to permit persons to whom the 9af69d88dSmrg * Software is furnished to do so, subject to the following conditions: 10af69d88dSmrg * 11af69d88dSmrg * The above copyright notice and this permission notice (including the next 12af69d88dSmrg * paragraph) shall be included in all copies or substantial portions of the 13af69d88dSmrg * Software. 14af69d88dSmrg * 15af69d88dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16af69d88dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17af69d88dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19af69d88dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20af69d88dSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21af69d88dSmrg * DEALINGS IN THE SOFTWARE. 22af69d88dSmrg */ 23af69d88dSmrg 24af69d88dSmrg#include <assert.h> 25af69d88dSmrg#include <stdlib.h> 26af69d88dSmrg#include <stdarg.h> 27af69d88dSmrg#include <stdio.h> 28af69d88dSmrg#include <string.h> 29af69d88dSmrg#include <stdint.h> 30af69d88dSmrg 31af69d88dSmrg/* Some versions of MinGW are missing _vscprintf's declaration, although they 32af69d88dSmrg * still provide the symbol in the import library. */ 33af69d88dSmrg#ifdef __MINGW32__ 34af69d88dSmrg_CRTIMP int _vscprintf(const char *format, va_list argptr); 35af69d88dSmrg#endif 36af69d88dSmrg 37af69d88dSmrg#include "ralloc.h" 38af69d88dSmrg 39af69d88dSmrg#ifndef va_copy 40af69d88dSmrg#ifdef __va_copy 41af69d88dSmrg#define va_copy(dest, src) __va_copy((dest), (src)) 42af69d88dSmrg#else 43af69d88dSmrg#define va_copy(dest, src) (dest) = (src) 44af69d88dSmrg#endif 45af69d88dSmrg#endif 46af69d88dSmrg 47af69d88dSmrg#define CANARY 0x5A1106 48af69d88dSmrg 497e995a2eSmrg/* Align the header's size so that ralloc() allocations will return with the 507e995a2eSmrg * same alignment as a libc malloc would have (8 on 32-bit GLIBC, 16 on 517e995a2eSmrg * 64-bit), avoiding performance penalities on x86 and alignment faults on 527e995a2eSmrg * ARM. 537e995a2eSmrg */ 547e995a2eSmrgstruct 557e995a2eSmrg#ifdef _MSC_VER 567e995a2eSmrg __declspec(align(8)) 577e995a2eSmrg#elif defined(__LP64__) 587e995a2eSmrg __attribute__((aligned(16))) 597e995a2eSmrg#else 607e995a2eSmrg __attribute__((aligned(8))) 617e995a2eSmrg#endif 627e995a2eSmrg ralloc_header 63af69d88dSmrg{ 64af69d88dSmrg#ifdef DEBUG 65af69d88dSmrg /* A canary value used to determine whether a pointer is ralloc'd. */ 66af69d88dSmrg unsigned canary; 67af69d88dSmrg#endif 68af69d88dSmrg 69af69d88dSmrg struct ralloc_header *parent; 70af69d88dSmrg 71af69d88dSmrg /* The first child (head of a linked list) */ 72af69d88dSmrg struct ralloc_header *child; 73af69d88dSmrg 74af69d88dSmrg /* Linked list of siblings */ 75af69d88dSmrg struct ralloc_header *prev; 76af69d88dSmrg struct ralloc_header *next; 77af69d88dSmrg 78af69d88dSmrg void (*destructor)(void *); 79af69d88dSmrg}; 80af69d88dSmrg 81af69d88dSmrgtypedef struct ralloc_header ralloc_header; 82af69d88dSmrg 83af69d88dSmrgstatic void unlink_block(ralloc_header *info); 84af69d88dSmrgstatic void unsafe_free(ralloc_header *info); 85af69d88dSmrg 86af69d88dSmrgstatic ralloc_header * 87af69d88dSmrgget_header(const void *ptr) 88af69d88dSmrg{ 89af69d88dSmrg ralloc_header *info = (ralloc_header *) (((char *) ptr) - 90af69d88dSmrg sizeof(ralloc_header)); 91af69d88dSmrg#ifdef DEBUG 92af69d88dSmrg assert(info->canary == CANARY); 93af69d88dSmrg#endif 94af69d88dSmrg return info; 95af69d88dSmrg} 96af69d88dSmrg 97af69d88dSmrg#define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header)) 98af69d88dSmrg 99af69d88dSmrgstatic void 100af69d88dSmrgadd_child(ralloc_header *parent, ralloc_header *info) 101af69d88dSmrg{ 102af69d88dSmrg if (parent != NULL) { 103af69d88dSmrg info->parent = parent; 104af69d88dSmrg info->next = parent->child; 105af69d88dSmrg parent->child = info; 106af69d88dSmrg 107af69d88dSmrg if (info->next != NULL) 108af69d88dSmrg info->next->prev = info; 109af69d88dSmrg } 110af69d88dSmrg} 111af69d88dSmrg 112af69d88dSmrgvoid * 113af69d88dSmrgralloc_context(const void *ctx) 114af69d88dSmrg{ 115af69d88dSmrg return ralloc_size(ctx, 0); 116af69d88dSmrg} 117af69d88dSmrg 118af69d88dSmrgvoid * 119af69d88dSmrgralloc_size(const void *ctx, size_t size) 120af69d88dSmrg{ 1217e995a2eSmrg void *block = malloc(size + sizeof(ralloc_header)); 122af69d88dSmrg ralloc_header *info; 123af69d88dSmrg ralloc_header *parent; 124af69d88dSmrg 125af69d88dSmrg if (unlikely(block == NULL)) 126af69d88dSmrg return NULL; 1277e995a2eSmrg 128af69d88dSmrg info = (ralloc_header *) block; 1297e995a2eSmrg /* measurements have shown that calloc is slower (because of 1307e995a2eSmrg * the multiplication overflow checking?), so clear things 1317e995a2eSmrg * manually 1327e995a2eSmrg */ 1337e995a2eSmrg info->parent = NULL; 1347e995a2eSmrg info->child = NULL; 1357e995a2eSmrg info->prev = NULL; 1367e995a2eSmrg info->next = NULL; 1377e995a2eSmrg info->destructor = NULL; 1387e995a2eSmrg 139af69d88dSmrg parent = ctx != NULL ? get_header(ctx) : NULL; 140af69d88dSmrg 141af69d88dSmrg add_child(parent, info); 142af69d88dSmrg 143af69d88dSmrg#ifdef DEBUG 144af69d88dSmrg info->canary = CANARY; 145af69d88dSmrg#endif 146af69d88dSmrg 147af69d88dSmrg return PTR_FROM_HEADER(info); 148af69d88dSmrg} 149af69d88dSmrg 150af69d88dSmrgvoid * 151af69d88dSmrgrzalloc_size(const void *ctx, size_t size) 152af69d88dSmrg{ 153af69d88dSmrg void *ptr = ralloc_size(ctx, size); 1547e995a2eSmrg 1557e995a2eSmrg if (likely(ptr)) 156af69d88dSmrg memset(ptr, 0, size); 1577e995a2eSmrg 158af69d88dSmrg return ptr; 159af69d88dSmrg} 160af69d88dSmrg 161af69d88dSmrg/* helper function - assumes ptr != NULL */ 162af69d88dSmrgstatic void * 163af69d88dSmrgresize(void *ptr, size_t size) 164af69d88dSmrg{ 165af69d88dSmrg ralloc_header *child, *old, *info; 166af69d88dSmrg 167af69d88dSmrg old = get_header(ptr); 168af69d88dSmrg info = realloc(old, size + sizeof(ralloc_header)); 169af69d88dSmrg 170af69d88dSmrg if (info == NULL) 171af69d88dSmrg return NULL; 172af69d88dSmrg 173af69d88dSmrg /* Update parent and sibling's links to the reallocated node. */ 174af69d88dSmrg if (info != old && info->parent != NULL) { 175af69d88dSmrg if (info->parent->child == old) 176af69d88dSmrg info->parent->child = info; 177af69d88dSmrg 178af69d88dSmrg if (info->prev != NULL) 179af69d88dSmrg info->prev->next = info; 180af69d88dSmrg 181af69d88dSmrg if (info->next != NULL) 182af69d88dSmrg info->next->prev = info; 183af69d88dSmrg } 184af69d88dSmrg 185af69d88dSmrg /* Update child->parent links for all children */ 186af69d88dSmrg for (child = info->child; child != NULL; child = child->next) 187af69d88dSmrg child->parent = info; 188af69d88dSmrg 189af69d88dSmrg return PTR_FROM_HEADER(info); 190af69d88dSmrg} 191af69d88dSmrg 192af69d88dSmrgvoid * 193af69d88dSmrgreralloc_size(const void *ctx, void *ptr, size_t size) 194af69d88dSmrg{ 195af69d88dSmrg if (unlikely(ptr == NULL)) 196af69d88dSmrg return ralloc_size(ctx, size); 197af69d88dSmrg 198af69d88dSmrg assert(ralloc_parent(ptr) == ctx); 199af69d88dSmrg return resize(ptr, size); 200af69d88dSmrg} 201af69d88dSmrg 202af69d88dSmrgvoid * 203af69d88dSmrgralloc_array_size(const void *ctx, size_t size, unsigned count) 204af69d88dSmrg{ 205af69d88dSmrg if (count > SIZE_MAX/size) 206af69d88dSmrg return NULL; 207af69d88dSmrg 208af69d88dSmrg return ralloc_size(ctx, size * count); 209af69d88dSmrg} 210af69d88dSmrg 211af69d88dSmrgvoid * 212af69d88dSmrgrzalloc_array_size(const void *ctx, size_t size, unsigned count) 213af69d88dSmrg{ 214af69d88dSmrg if (count > SIZE_MAX/size) 215af69d88dSmrg return NULL; 216af69d88dSmrg 217af69d88dSmrg return rzalloc_size(ctx, size * count); 218af69d88dSmrg} 219af69d88dSmrg 220af69d88dSmrgvoid * 221af69d88dSmrgreralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count) 222af69d88dSmrg{ 223af69d88dSmrg if (count > SIZE_MAX/size) 224af69d88dSmrg return NULL; 225af69d88dSmrg 226af69d88dSmrg return reralloc_size(ctx, ptr, size * count); 227af69d88dSmrg} 228af69d88dSmrg 229af69d88dSmrgvoid 230af69d88dSmrgralloc_free(void *ptr) 231af69d88dSmrg{ 232af69d88dSmrg ralloc_header *info; 233af69d88dSmrg 234af69d88dSmrg if (ptr == NULL) 235af69d88dSmrg return; 236af69d88dSmrg 237af69d88dSmrg info = get_header(ptr); 238af69d88dSmrg unlink_block(info); 239af69d88dSmrg unsafe_free(info); 240af69d88dSmrg} 241af69d88dSmrg 242af69d88dSmrgstatic void 243af69d88dSmrgunlink_block(ralloc_header *info) 244af69d88dSmrg{ 245af69d88dSmrg /* Unlink from parent & siblings */ 246af69d88dSmrg if (info->parent != NULL) { 247af69d88dSmrg if (info->parent->child == info) 248af69d88dSmrg info->parent->child = info->next; 249af69d88dSmrg 250af69d88dSmrg if (info->prev != NULL) 251af69d88dSmrg info->prev->next = info->next; 252af69d88dSmrg 253af69d88dSmrg if (info->next != NULL) 254af69d88dSmrg info->next->prev = info->prev; 255af69d88dSmrg } 256af69d88dSmrg info->parent = NULL; 257af69d88dSmrg info->prev = NULL; 258af69d88dSmrg info->next = NULL; 259af69d88dSmrg} 260af69d88dSmrg 261af69d88dSmrgstatic void 262af69d88dSmrgunsafe_free(ralloc_header *info) 263af69d88dSmrg{ 264af69d88dSmrg /* Recursively free any children...don't waste time unlinking them. */ 265af69d88dSmrg ralloc_header *temp; 266af69d88dSmrg while (info->child != NULL) { 267af69d88dSmrg temp = info->child; 268af69d88dSmrg info->child = temp->next; 269af69d88dSmrg unsafe_free(temp); 270af69d88dSmrg } 271af69d88dSmrg 272af69d88dSmrg /* Free the block itself. Call the destructor first, if any. */ 273af69d88dSmrg if (info->destructor != NULL) 274af69d88dSmrg info->destructor(PTR_FROM_HEADER(info)); 275af69d88dSmrg 276af69d88dSmrg free(info); 277af69d88dSmrg} 278af69d88dSmrg 279af69d88dSmrgvoid 280af69d88dSmrgralloc_steal(const void *new_ctx, void *ptr) 281af69d88dSmrg{ 282af69d88dSmrg ralloc_header *info, *parent; 283af69d88dSmrg 284af69d88dSmrg if (unlikely(ptr == NULL)) 285af69d88dSmrg return; 286af69d88dSmrg 287af69d88dSmrg info = get_header(ptr); 2887e995a2eSmrg parent = new_ctx ? get_header(new_ctx) : NULL; 289af69d88dSmrg 290af69d88dSmrg unlink_block(info); 291af69d88dSmrg 292af69d88dSmrg add_child(parent, info); 293af69d88dSmrg} 294af69d88dSmrg 2957e995a2eSmrgvoid 2967e995a2eSmrgralloc_adopt(const void *new_ctx, void *old_ctx) 2977e995a2eSmrg{ 2987e995a2eSmrg ralloc_header *new_info, *old_info, *child; 2997e995a2eSmrg 3007e995a2eSmrg if (unlikely(old_ctx == NULL)) 3017e995a2eSmrg return; 3027e995a2eSmrg 3037e995a2eSmrg old_info = get_header(old_ctx); 3047e995a2eSmrg new_info = get_header(new_ctx); 3057e995a2eSmrg 3067e995a2eSmrg /* If there are no children, bail. */ 3077e995a2eSmrg if (unlikely(old_info->child == NULL)) 3087e995a2eSmrg return; 3097e995a2eSmrg 3107e995a2eSmrg /* Set all the children's parent to new_ctx; get a pointer to the last child. */ 3117e995a2eSmrg for (child = old_info->child; child->next != NULL; child = child->next) { 3127e995a2eSmrg child->parent = new_info; 3137e995a2eSmrg } 3147e995a2eSmrg child->parent = new_info; 3157e995a2eSmrg 3167e995a2eSmrg /* Connect the two lists together; parent them to new_ctx; make old_ctx empty. */ 3177e995a2eSmrg child->next = new_info->child; 3187e995a2eSmrg if (child->next) 3197e995a2eSmrg child->next->prev = child; 3207e995a2eSmrg new_info->child = old_info->child; 3217e995a2eSmrg old_info->child = NULL; 3227e995a2eSmrg} 3237e995a2eSmrg 324af69d88dSmrgvoid * 325af69d88dSmrgralloc_parent(const void *ptr) 326af69d88dSmrg{ 327af69d88dSmrg ralloc_header *info; 328af69d88dSmrg 329af69d88dSmrg if (unlikely(ptr == NULL)) 330af69d88dSmrg return NULL; 331af69d88dSmrg 332af69d88dSmrg info = get_header(ptr); 333af69d88dSmrg return info->parent ? PTR_FROM_HEADER(info->parent) : NULL; 334af69d88dSmrg} 335af69d88dSmrg 336af69d88dSmrgvoid 337af69d88dSmrgralloc_set_destructor(const void *ptr, void(*destructor)(void *)) 338af69d88dSmrg{ 339af69d88dSmrg ralloc_header *info = get_header(ptr); 340af69d88dSmrg info->destructor = destructor; 341af69d88dSmrg} 342af69d88dSmrg 343af69d88dSmrgchar * 344af69d88dSmrgralloc_strdup(const void *ctx, const char *str) 345af69d88dSmrg{ 346af69d88dSmrg size_t n; 347af69d88dSmrg char *ptr; 348af69d88dSmrg 349af69d88dSmrg if (unlikely(str == NULL)) 350af69d88dSmrg return NULL; 351af69d88dSmrg 352af69d88dSmrg n = strlen(str); 353af69d88dSmrg ptr = ralloc_array(ctx, char, n + 1); 354af69d88dSmrg memcpy(ptr, str, n); 355af69d88dSmrg ptr[n] = '\0'; 356af69d88dSmrg return ptr; 357af69d88dSmrg} 358af69d88dSmrg 359af69d88dSmrgchar * 360af69d88dSmrgralloc_strndup(const void *ctx, const char *str, size_t max) 361af69d88dSmrg{ 362af69d88dSmrg size_t n; 363af69d88dSmrg char *ptr; 364af69d88dSmrg 365af69d88dSmrg if (unlikely(str == NULL)) 366af69d88dSmrg return NULL; 367af69d88dSmrg 3687e995a2eSmrg n = strnlen(str, max); 369af69d88dSmrg ptr = ralloc_array(ctx, char, n + 1); 370af69d88dSmrg memcpy(ptr, str, n); 371af69d88dSmrg ptr[n] = '\0'; 372af69d88dSmrg return ptr; 373af69d88dSmrg} 374af69d88dSmrg 375af69d88dSmrg/* helper routine for strcat/strncat - n is the exact amount to copy */ 376af69d88dSmrgstatic bool 377af69d88dSmrgcat(char **dest, const char *str, size_t n) 378af69d88dSmrg{ 379af69d88dSmrg char *both; 380af69d88dSmrg size_t existing_length; 381af69d88dSmrg assert(dest != NULL && *dest != NULL); 382af69d88dSmrg 383af69d88dSmrg existing_length = strlen(*dest); 384af69d88dSmrg both = resize(*dest, existing_length + n + 1); 385af69d88dSmrg if (unlikely(both == NULL)) 386af69d88dSmrg return false; 387af69d88dSmrg 388af69d88dSmrg memcpy(both + existing_length, str, n); 389af69d88dSmrg both[existing_length + n] = '\0'; 390af69d88dSmrg 391af69d88dSmrg *dest = both; 392af69d88dSmrg return true; 393af69d88dSmrg} 394af69d88dSmrg 395af69d88dSmrg 396af69d88dSmrgbool 397af69d88dSmrgralloc_strcat(char **dest, const char *str) 398af69d88dSmrg{ 399af69d88dSmrg return cat(dest, str, strlen(str)); 400af69d88dSmrg} 401af69d88dSmrg 402af69d88dSmrgbool 403af69d88dSmrgralloc_strncat(char **dest, const char *str, size_t n) 404af69d88dSmrg{ 4057e995a2eSmrg return cat(dest, str, strnlen(str, n)); 4067e995a2eSmrg} 4077e995a2eSmrg 4087e995a2eSmrgbool 4097e995a2eSmrgralloc_str_append(char **dest, const char *str, 4107e995a2eSmrg size_t existing_length, size_t str_size) 4117e995a2eSmrg{ 4127e995a2eSmrg char *both; 4137e995a2eSmrg assert(dest != NULL && *dest != NULL); 4147e995a2eSmrg 4157e995a2eSmrg both = resize(*dest, existing_length + str_size + 1); 4167e995a2eSmrg if (unlikely(both == NULL)) 4177e995a2eSmrg return false; 418af69d88dSmrg 4197e995a2eSmrg memcpy(both + existing_length, str, str_size); 4207e995a2eSmrg both[existing_length + str_size] = '\0'; 4217e995a2eSmrg 4227e995a2eSmrg *dest = both; 4237e995a2eSmrg 4247e995a2eSmrg return true; 425af69d88dSmrg} 426af69d88dSmrg 427af69d88dSmrgchar * 428af69d88dSmrgralloc_asprintf(const void *ctx, const char *fmt, ...) 429af69d88dSmrg{ 430af69d88dSmrg char *ptr; 431af69d88dSmrg va_list args; 432af69d88dSmrg va_start(args, fmt); 433af69d88dSmrg ptr = ralloc_vasprintf(ctx, fmt, args); 434af69d88dSmrg va_end(args); 435af69d88dSmrg return ptr; 436af69d88dSmrg} 437af69d88dSmrg 438af69d88dSmrg/* Return the length of the string that would be generated by a printf-style 439af69d88dSmrg * format and argument list, not including the \0 byte. 440af69d88dSmrg */ 441af69d88dSmrgstatic size_t 442af69d88dSmrgprintf_length(const char *fmt, va_list untouched_args) 443af69d88dSmrg{ 444af69d88dSmrg int size; 445af69d88dSmrg char junk; 446af69d88dSmrg 447af69d88dSmrg /* Make a copy of the va_list so the original caller can still use it */ 448af69d88dSmrg va_list args; 449af69d88dSmrg va_copy(args, untouched_args); 450af69d88dSmrg 451af69d88dSmrg#ifdef _WIN32 452af69d88dSmrg /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1 453af69d88dSmrg * if the number of characters to write is greater than count. 454af69d88dSmrg */ 455af69d88dSmrg size = _vscprintf(fmt, args); 456af69d88dSmrg (void)junk; 457af69d88dSmrg#else 458af69d88dSmrg size = vsnprintf(&junk, 1, fmt, args); 459af69d88dSmrg#endif 460af69d88dSmrg assert(size >= 0); 461af69d88dSmrg 462af69d88dSmrg va_end(args); 463af69d88dSmrg 464af69d88dSmrg return size; 465af69d88dSmrg} 466af69d88dSmrg 467af69d88dSmrgchar * 468af69d88dSmrgralloc_vasprintf(const void *ctx, const char *fmt, va_list args) 469af69d88dSmrg{ 470af69d88dSmrg size_t size = printf_length(fmt, args) + 1; 471af69d88dSmrg 472af69d88dSmrg char *ptr = ralloc_size(ctx, size); 473af69d88dSmrg if (ptr != NULL) 474af69d88dSmrg vsnprintf(ptr, size, fmt, args); 475af69d88dSmrg 476af69d88dSmrg return ptr; 477af69d88dSmrg} 478af69d88dSmrg 479af69d88dSmrgbool 480af69d88dSmrgralloc_asprintf_append(char **str, const char *fmt, ...) 481af69d88dSmrg{ 482af69d88dSmrg bool success; 483af69d88dSmrg va_list args; 484af69d88dSmrg va_start(args, fmt); 485af69d88dSmrg success = ralloc_vasprintf_append(str, fmt, args); 486af69d88dSmrg va_end(args); 487af69d88dSmrg return success; 488af69d88dSmrg} 489af69d88dSmrg 490af69d88dSmrgbool 491af69d88dSmrgralloc_vasprintf_append(char **str, const char *fmt, va_list args) 492af69d88dSmrg{ 493af69d88dSmrg size_t existing_length; 494af69d88dSmrg assert(str != NULL); 495af69d88dSmrg existing_length = *str ? strlen(*str) : 0; 496af69d88dSmrg return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args); 497af69d88dSmrg} 498af69d88dSmrg 499af69d88dSmrgbool 500af69d88dSmrgralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...) 501af69d88dSmrg{ 502af69d88dSmrg bool success; 503af69d88dSmrg va_list args; 504af69d88dSmrg va_start(args, fmt); 505af69d88dSmrg success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args); 506af69d88dSmrg va_end(args); 507af69d88dSmrg return success; 508af69d88dSmrg} 509af69d88dSmrg 510af69d88dSmrgbool 511af69d88dSmrgralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt, 512af69d88dSmrg va_list args) 513af69d88dSmrg{ 514af69d88dSmrg size_t new_length; 515af69d88dSmrg char *ptr; 516af69d88dSmrg 517af69d88dSmrg assert(str != NULL); 518af69d88dSmrg 519af69d88dSmrg if (unlikely(*str == NULL)) { 520af69d88dSmrg // Assuming a NULL context is probably bad, but it's expected behavior. 521af69d88dSmrg *str = ralloc_vasprintf(NULL, fmt, args); 5227e995a2eSmrg *start = strlen(*str); 523af69d88dSmrg return true; 524af69d88dSmrg } 525af69d88dSmrg 526af69d88dSmrg new_length = printf_length(fmt, args); 527af69d88dSmrg 528af69d88dSmrg ptr = resize(*str, *start + new_length + 1); 529af69d88dSmrg if (unlikely(ptr == NULL)) 530af69d88dSmrg return false; 531af69d88dSmrg 532af69d88dSmrg vsnprintf(ptr + *start, new_length + 1, fmt, args); 533af69d88dSmrg *str = ptr; 534af69d88dSmrg *start += new_length; 535af69d88dSmrg return true; 536af69d88dSmrg} 5377e995a2eSmrg 5387e995a2eSmrg/*************************************************************************** 5397e995a2eSmrg * Linear allocator for short-lived allocations. 5407e995a2eSmrg *************************************************************************** 5417e995a2eSmrg * 5427e995a2eSmrg * The allocator consists of a parent node (2K buffer), which requires 5437e995a2eSmrg * a ralloc parent, and child nodes (allocations). Child nodes can't be freed 5447e995a2eSmrg * directly, because the parent doesn't track them. You have to release 5457e995a2eSmrg * the parent node in order to release all its children. 5467e995a2eSmrg * 5477e995a2eSmrg * The allocator uses a fixed-sized buffer with a monotonically increasing 5487e995a2eSmrg * offset after each allocation. If the buffer is all used, another buffer 5497e995a2eSmrg * is allocated, sharing the same ralloc parent, so all buffers are at 5507e995a2eSmrg * the same level in the ralloc hierarchy. 5517e995a2eSmrg * 5527e995a2eSmrg * The linear parent node is always the first buffer and keeps track of all 5537e995a2eSmrg * other buffers. 5547e995a2eSmrg */ 5557e995a2eSmrg 5567e995a2eSmrg#define MIN_LINEAR_BUFSIZE 2048 5577e995a2eSmrg#define SUBALLOC_ALIGNMENT 8 5587e995a2eSmrg#define LMAGIC 0x87b9c7d3 5597e995a2eSmrg 5607e995a2eSmrgstruct 5617e995a2eSmrg#ifdef _MSC_VER 5627e995a2eSmrg __declspec(align(8)) 5637e995a2eSmrg#elif defined(__LP64__) 5647e995a2eSmrg __attribute__((aligned(16))) 5657e995a2eSmrg#else 5667e995a2eSmrg __attribute__((aligned(8))) 5677e995a2eSmrg#endif 5687e995a2eSmrg linear_header { 5697e995a2eSmrg#ifdef DEBUG 5707e995a2eSmrg unsigned magic; /* for debugging */ 5717e995a2eSmrg#endif 5727e995a2eSmrg unsigned offset; /* points to the first unused byte in the buffer */ 5737e995a2eSmrg unsigned size; /* size of the buffer */ 5747e995a2eSmrg void *ralloc_parent; /* new buffers will use this */ 5757e995a2eSmrg struct linear_header *next; /* next buffer if we have more */ 5767e995a2eSmrg struct linear_header *latest; /* the only buffer that has free space */ 5777e995a2eSmrg 5787e995a2eSmrg /* After this structure, the buffer begins. 5797e995a2eSmrg * Each suballocation consists of linear_size_chunk as its header followed 5807e995a2eSmrg * by the suballocation, so it goes: 5817e995a2eSmrg * 5827e995a2eSmrg * - linear_size_chunk 5837e995a2eSmrg * - allocated space 5847e995a2eSmrg * - linear_size_chunk 5857e995a2eSmrg * - allocated space 5867e995a2eSmrg * etc. 5877e995a2eSmrg * 5887e995a2eSmrg * linear_size_chunk is only needed by linear_realloc. 5897e995a2eSmrg */ 5907e995a2eSmrg}; 5917e995a2eSmrg 5927e995a2eSmrgstruct linear_size_chunk { 5937e995a2eSmrg unsigned size; /* for realloc */ 5947e995a2eSmrg unsigned _padding; 5957e995a2eSmrg}; 5967e995a2eSmrg 5977e995a2eSmrgtypedef struct linear_header linear_header; 5987e995a2eSmrgtypedef struct linear_size_chunk linear_size_chunk; 5997e995a2eSmrg 6007e995a2eSmrg#define LINEAR_PARENT_TO_HEADER(parent) \ 6017e995a2eSmrg (linear_header*) \ 6027e995a2eSmrg ((char*)(parent) - sizeof(linear_size_chunk) - sizeof(linear_header)) 6037e995a2eSmrg 6047e995a2eSmrg/* Allocate the linear buffer with its header. */ 6057e995a2eSmrgstatic linear_header * 6067e995a2eSmrgcreate_linear_node(void *ralloc_ctx, unsigned min_size) 6077e995a2eSmrg{ 6087e995a2eSmrg linear_header *node; 6097e995a2eSmrg 6107e995a2eSmrg min_size += sizeof(linear_size_chunk); 6117e995a2eSmrg 6127e995a2eSmrg if (likely(min_size < MIN_LINEAR_BUFSIZE)) 6137e995a2eSmrg min_size = MIN_LINEAR_BUFSIZE; 6147e995a2eSmrg 6157e995a2eSmrg node = ralloc_size(ralloc_ctx, sizeof(linear_header) + min_size); 6167e995a2eSmrg if (unlikely(!node)) 6177e995a2eSmrg return NULL; 6187e995a2eSmrg 6197e995a2eSmrg#ifdef DEBUG 6207e995a2eSmrg node->magic = LMAGIC; 6217e995a2eSmrg#endif 6227e995a2eSmrg node->offset = 0; 6237e995a2eSmrg node->size = min_size; 6247e995a2eSmrg node->ralloc_parent = ralloc_ctx; 6257e995a2eSmrg node->next = NULL; 6267e995a2eSmrg node->latest = node; 6277e995a2eSmrg return node; 6287e995a2eSmrg} 6297e995a2eSmrg 6307e995a2eSmrgvoid * 6317e995a2eSmrglinear_alloc_child(void *parent, unsigned size) 6327e995a2eSmrg{ 6337e995a2eSmrg linear_header *first = LINEAR_PARENT_TO_HEADER(parent); 6347e995a2eSmrg linear_header *latest = first->latest; 6357e995a2eSmrg linear_header *new_node; 6367e995a2eSmrg linear_size_chunk *ptr; 6377e995a2eSmrg unsigned full_size; 6387e995a2eSmrg 6397e995a2eSmrg#ifdef DEBUG 6407e995a2eSmrg assert(first->magic == LMAGIC); 6417e995a2eSmrg#endif 6427e995a2eSmrg assert(!latest->next); 6437e995a2eSmrg 6447e995a2eSmrg size = ALIGN_POT(size, SUBALLOC_ALIGNMENT); 6457e995a2eSmrg full_size = sizeof(linear_size_chunk) + size; 6467e995a2eSmrg 6477e995a2eSmrg if (unlikely(latest->offset + full_size > latest->size)) { 6487e995a2eSmrg /* allocate a new node */ 6497e995a2eSmrg new_node = create_linear_node(latest->ralloc_parent, size); 6507e995a2eSmrg if (unlikely(!new_node)) 6517e995a2eSmrg return NULL; 6527e995a2eSmrg 6537e995a2eSmrg first->latest = new_node; 6547e995a2eSmrg latest->latest = new_node; 6557e995a2eSmrg latest->next = new_node; 6567e995a2eSmrg latest = new_node; 6577e995a2eSmrg } 6587e995a2eSmrg 6597e995a2eSmrg ptr = (linear_size_chunk *)((char*)&latest[1] + latest->offset); 6607e995a2eSmrg ptr->size = size; 6617e995a2eSmrg latest->offset += full_size; 6627e995a2eSmrg 6637e995a2eSmrg assert((uintptr_t)&ptr[1] % SUBALLOC_ALIGNMENT == 0); 6647e995a2eSmrg return &ptr[1]; 6657e995a2eSmrg} 6667e995a2eSmrg 6677e995a2eSmrgvoid * 6687e995a2eSmrglinear_alloc_parent(void *ralloc_ctx, unsigned size) 6697e995a2eSmrg{ 6707e995a2eSmrg linear_header *node; 6717e995a2eSmrg 6727e995a2eSmrg if (unlikely(!ralloc_ctx)) 6737e995a2eSmrg return NULL; 6747e995a2eSmrg 6757e995a2eSmrg size = ALIGN_POT(size, SUBALLOC_ALIGNMENT); 6767e995a2eSmrg 6777e995a2eSmrg node = create_linear_node(ralloc_ctx, size); 6787e995a2eSmrg if (unlikely(!node)) 6797e995a2eSmrg return NULL; 6807e995a2eSmrg 6817e995a2eSmrg return linear_alloc_child((char*)node + 6827e995a2eSmrg sizeof(linear_header) + 6837e995a2eSmrg sizeof(linear_size_chunk), size); 6847e995a2eSmrg} 6857e995a2eSmrg 6867e995a2eSmrgvoid * 6877e995a2eSmrglinear_zalloc_child(void *parent, unsigned size) 6887e995a2eSmrg{ 6897e995a2eSmrg void *ptr = linear_alloc_child(parent, size); 6907e995a2eSmrg 6917e995a2eSmrg if (likely(ptr)) 6927e995a2eSmrg memset(ptr, 0, size); 6937e995a2eSmrg return ptr; 6947e995a2eSmrg} 6957e995a2eSmrg 6967e995a2eSmrgvoid * 6977e995a2eSmrglinear_zalloc_parent(void *parent, unsigned size) 6987e995a2eSmrg{ 6997e995a2eSmrg void *ptr = linear_alloc_parent(parent, size); 7007e995a2eSmrg 7017e995a2eSmrg if (likely(ptr)) 7027e995a2eSmrg memset(ptr, 0, size); 7037e995a2eSmrg return ptr; 7047e995a2eSmrg} 7057e995a2eSmrg 7067e995a2eSmrgvoid 7077e995a2eSmrglinear_free_parent(void *ptr) 7087e995a2eSmrg{ 7097e995a2eSmrg linear_header *node; 7107e995a2eSmrg 7117e995a2eSmrg if (unlikely(!ptr)) 7127e995a2eSmrg return; 7137e995a2eSmrg 7147e995a2eSmrg node = LINEAR_PARENT_TO_HEADER(ptr); 7157e995a2eSmrg#ifdef DEBUG 7167e995a2eSmrg assert(node->magic == LMAGIC); 7177e995a2eSmrg#endif 7187e995a2eSmrg 7197e995a2eSmrg while (node) { 7207e995a2eSmrg void *ptr = node; 7217e995a2eSmrg 7227e995a2eSmrg node = node->next; 7237e995a2eSmrg ralloc_free(ptr); 7247e995a2eSmrg } 7257e995a2eSmrg} 7267e995a2eSmrg 7277e995a2eSmrgvoid 7287e995a2eSmrgralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr) 7297e995a2eSmrg{ 7307e995a2eSmrg linear_header *node; 7317e995a2eSmrg 7327e995a2eSmrg if (unlikely(!ptr)) 7337e995a2eSmrg return; 7347e995a2eSmrg 7357e995a2eSmrg node = LINEAR_PARENT_TO_HEADER(ptr); 7367e995a2eSmrg#ifdef DEBUG 7377e995a2eSmrg assert(node->magic == LMAGIC); 7387e995a2eSmrg#endif 7397e995a2eSmrg 7407e995a2eSmrg while (node) { 7417e995a2eSmrg ralloc_steal(new_ralloc_ctx, node); 7427e995a2eSmrg node->ralloc_parent = new_ralloc_ctx; 7437e995a2eSmrg node = node->next; 7447e995a2eSmrg } 7457e995a2eSmrg} 7467e995a2eSmrg 7477e995a2eSmrgvoid * 7487e995a2eSmrgralloc_parent_of_linear_parent(void *ptr) 7497e995a2eSmrg{ 7507e995a2eSmrg linear_header *node = LINEAR_PARENT_TO_HEADER(ptr); 7517e995a2eSmrg#ifdef DEBUG 7527e995a2eSmrg assert(node->magic == LMAGIC); 7537e995a2eSmrg#endif 7547e995a2eSmrg return node->ralloc_parent; 7557e995a2eSmrg} 7567e995a2eSmrg 7577e995a2eSmrgvoid * 7587e995a2eSmrglinear_realloc(void *parent, void *old, unsigned new_size) 7597e995a2eSmrg{ 7607e995a2eSmrg unsigned old_size = 0; 7617e995a2eSmrg ralloc_header *new_ptr; 7627e995a2eSmrg 7637e995a2eSmrg new_ptr = linear_alloc_child(parent, new_size); 7647e995a2eSmrg 7657e995a2eSmrg if (unlikely(!old)) 7667e995a2eSmrg return new_ptr; 7677e995a2eSmrg 7687e995a2eSmrg old_size = ((linear_size_chunk*)old)[-1].size; 7697e995a2eSmrg 7707e995a2eSmrg if (likely(new_ptr && old_size)) 7717e995a2eSmrg memcpy(new_ptr, old, MIN2(old_size, new_size)); 7727e995a2eSmrg 7737e995a2eSmrg return new_ptr; 7747e995a2eSmrg} 7757e995a2eSmrg 7767e995a2eSmrg/* All code below is pretty much copied from ralloc and only the alloc 7777e995a2eSmrg * calls are different. 7787e995a2eSmrg */ 7797e995a2eSmrg 7807e995a2eSmrgchar * 7817e995a2eSmrglinear_strdup(void *parent, const char *str) 7827e995a2eSmrg{ 7837e995a2eSmrg unsigned n; 7847e995a2eSmrg char *ptr; 7857e995a2eSmrg 7867e995a2eSmrg if (unlikely(!str)) 7877e995a2eSmrg return NULL; 7887e995a2eSmrg 7897e995a2eSmrg n = strlen(str); 7907e995a2eSmrg ptr = linear_alloc_child(parent, n + 1); 7917e995a2eSmrg if (unlikely(!ptr)) 7927e995a2eSmrg return NULL; 7937e995a2eSmrg 7947e995a2eSmrg memcpy(ptr, str, n); 7957e995a2eSmrg ptr[n] = '\0'; 7967e995a2eSmrg return ptr; 7977e995a2eSmrg} 7987e995a2eSmrg 7997e995a2eSmrgchar * 8007e995a2eSmrglinear_asprintf(void *parent, const char *fmt, ...) 8017e995a2eSmrg{ 8027e995a2eSmrg char *ptr; 8037e995a2eSmrg va_list args; 8047e995a2eSmrg va_start(args, fmt); 8057e995a2eSmrg ptr = linear_vasprintf(parent, fmt, args); 8067e995a2eSmrg va_end(args); 8077e995a2eSmrg return ptr; 8087e995a2eSmrg} 8097e995a2eSmrg 8107e995a2eSmrgchar * 8117e995a2eSmrglinear_vasprintf(void *parent, const char *fmt, va_list args) 8127e995a2eSmrg{ 8137e995a2eSmrg unsigned size = printf_length(fmt, args) + 1; 8147e995a2eSmrg 8157e995a2eSmrg char *ptr = linear_alloc_child(parent, size); 8167e995a2eSmrg if (ptr != NULL) 8177e995a2eSmrg vsnprintf(ptr, size, fmt, args); 8187e995a2eSmrg 8197e995a2eSmrg return ptr; 8207e995a2eSmrg} 8217e995a2eSmrg 8227e995a2eSmrgbool 8237e995a2eSmrglinear_asprintf_append(void *parent, char **str, const char *fmt, ...) 8247e995a2eSmrg{ 8257e995a2eSmrg bool success; 8267e995a2eSmrg va_list args; 8277e995a2eSmrg va_start(args, fmt); 8287e995a2eSmrg success = linear_vasprintf_append(parent, str, fmt, args); 8297e995a2eSmrg va_end(args); 8307e995a2eSmrg return success; 8317e995a2eSmrg} 8327e995a2eSmrg 8337e995a2eSmrgbool 8347e995a2eSmrglinear_vasprintf_append(void *parent, char **str, const char *fmt, va_list args) 8357e995a2eSmrg{ 8367e995a2eSmrg size_t existing_length; 8377e995a2eSmrg assert(str != NULL); 8387e995a2eSmrg existing_length = *str ? strlen(*str) : 0; 8397e995a2eSmrg return linear_vasprintf_rewrite_tail(parent, str, &existing_length, fmt, args); 8407e995a2eSmrg} 8417e995a2eSmrg 8427e995a2eSmrgbool 8437e995a2eSmrglinear_asprintf_rewrite_tail(void *parent, char **str, size_t *start, 8447e995a2eSmrg const char *fmt, ...) 8457e995a2eSmrg{ 8467e995a2eSmrg bool success; 8477e995a2eSmrg va_list args; 8487e995a2eSmrg va_start(args, fmt); 8497e995a2eSmrg success = linear_vasprintf_rewrite_tail(parent, str, start, fmt, args); 8507e995a2eSmrg va_end(args); 8517e995a2eSmrg return success; 8527e995a2eSmrg} 8537e995a2eSmrg 8547e995a2eSmrgbool 8557e995a2eSmrglinear_vasprintf_rewrite_tail(void *parent, char **str, size_t *start, 8567e995a2eSmrg const char *fmt, va_list args) 8577e995a2eSmrg{ 8587e995a2eSmrg size_t new_length; 8597e995a2eSmrg char *ptr; 8607e995a2eSmrg 8617e995a2eSmrg assert(str != NULL); 8627e995a2eSmrg 8637e995a2eSmrg if (unlikely(*str == NULL)) { 8647e995a2eSmrg *str = linear_vasprintf(parent, fmt, args); 8657e995a2eSmrg *start = strlen(*str); 8667e995a2eSmrg return true; 8677e995a2eSmrg } 8687e995a2eSmrg 8697e995a2eSmrg new_length = printf_length(fmt, args); 8707e995a2eSmrg 8717e995a2eSmrg ptr = linear_realloc(parent, *str, *start + new_length + 1); 8727e995a2eSmrg if (unlikely(ptr == NULL)) 8737e995a2eSmrg return false; 8747e995a2eSmrg 8757e995a2eSmrg vsnprintf(ptr + *start, new_length + 1, fmt, args); 8767e995a2eSmrg *str = ptr; 8777e995a2eSmrg *start += new_length; 8787e995a2eSmrg return true; 8797e995a2eSmrg} 8807e995a2eSmrg 8817e995a2eSmrg/* helper routine for strcat/strncat - n is the exact amount to copy */ 8827e995a2eSmrgstatic bool 8837e995a2eSmrglinear_cat(void *parent, char **dest, const char *str, unsigned n) 8847e995a2eSmrg{ 8857e995a2eSmrg char *both; 8867e995a2eSmrg unsigned existing_length; 8877e995a2eSmrg assert(dest != NULL && *dest != NULL); 8887e995a2eSmrg 8897e995a2eSmrg existing_length = strlen(*dest); 8907e995a2eSmrg both = linear_realloc(parent, *dest, existing_length + n + 1); 8917e995a2eSmrg if (unlikely(both == NULL)) 8927e995a2eSmrg return false; 8937e995a2eSmrg 8947e995a2eSmrg memcpy(both + existing_length, str, n); 8957e995a2eSmrg both[existing_length + n] = '\0'; 8967e995a2eSmrg 8977e995a2eSmrg *dest = both; 8987e995a2eSmrg return true; 8997e995a2eSmrg} 9007e995a2eSmrg 9017e995a2eSmrgbool 9027e995a2eSmrglinear_strcat(void *parent, char **dest, const char *str) 9037e995a2eSmrg{ 9047e995a2eSmrg return linear_cat(parent, dest, str, strlen(str)); 9057e995a2eSmrg} 906