ralloc.c revision d8407755
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{ 64d8407755Smaya#ifndef NDEBUG 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 assert(info->canary == CANARY); 92af69d88dSmrg return info; 93af69d88dSmrg} 94af69d88dSmrg 95af69d88dSmrg#define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header)) 96af69d88dSmrg 97af69d88dSmrgstatic void 98af69d88dSmrgadd_child(ralloc_header *parent, ralloc_header *info) 99af69d88dSmrg{ 100af69d88dSmrg if (parent != NULL) { 101af69d88dSmrg info->parent = parent; 102af69d88dSmrg info->next = parent->child; 103af69d88dSmrg parent->child = info; 104af69d88dSmrg 105af69d88dSmrg if (info->next != NULL) 106af69d88dSmrg info->next->prev = info; 107af69d88dSmrg } 108af69d88dSmrg} 109af69d88dSmrg 110af69d88dSmrgvoid * 111af69d88dSmrgralloc_context(const void *ctx) 112af69d88dSmrg{ 113af69d88dSmrg return ralloc_size(ctx, 0); 114af69d88dSmrg} 115af69d88dSmrg 116af69d88dSmrgvoid * 117af69d88dSmrgralloc_size(const void *ctx, size_t size) 118af69d88dSmrg{ 1197e995a2eSmrg void *block = malloc(size + sizeof(ralloc_header)); 120af69d88dSmrg ralloc_header *info; 121af69d88dSmrg ralloc_header *parent; 122af69d88dSmrg 123af69d88dSmrg if (unlikely(block == NULL)) 124af69d88dSmrg return NULL; 1257e995a2eSmrg 126af69d88dSmrg info = (ralloc_header *) block; 1277e995a2eSmrg /* measurements have shown that calloc is slower (because of 1287e995a2eSmrg * the multiplication overflow checking?), so clear things 1297e995a2eSmrg * manually 1307e995a2eSmrg */ 1317e995a2eSmrg info->parent = NULL; 1327e995a2eSmrg info->child = NULL; 1337e995a2eSmrg info->prev = NULL; 1347e995a2eSmrg info->next = NULL; 1357e995a2eSmrg info->destructor = NULL; 1367e995a2eSmrg 137af69d88dSmrg parent = ctx != NULL ? get_header(ctx) : NULL; 138af69d88dSmrg 139af69d88dSmrg add_child(parent, info); 140af69d88dSmrg 141d8407755Smaya#ifndef NDEBUG 142af69d88dSmrg info->canary = CANARY; 143af69d88dSmrg#endif 144af69d88dSmrg 145af69d88dSmrg return PTR_FROM_HEADER(info); 146af69d88dSmrg} 147af69d88dSmrg 148af69d88dSmrgvoid * 149af69d88dSmrgrzalloc_size(const void *ctx, size_t size) 150af69d88dSmrg{ 151af69d88dSmrg void *ptr = ralloc_size(ctx, size); 1527e995a2eSmrg 1537e995a2eSmrg if (likely(ptr)) 154af69d88dSmrg memset(ptr, 0, size); 1557e995a2eSmrg 156af69d88dSmrg return ptr; 157af69d88dSmrg} 158af69d88dSmrg 159af69d88dSmrg/* helper function - assumes ptr != NULL */ 160af69d88dSmrgstatic void * 161af69d88dSmrgresize(void *ptr, size_t size) 162af69d88dSmrg{ 163af69d88dSmrg ralloc_header *child, *old, *info; 164af69d88dSmrg 165af69d88dSmrg old = get_header(ptr); 166af69d88dSmrg info = realloc(old, size + sizeof(ralloc_header)); 167af69d88dSmrg 168af69d88dSmrg if (info == NULL) 169af69d88dSmrg return NULL; 170af69d88dSmrg 171af69d88dSmrg /* Update parent and sibling's links to the reallocated node. */ 172af69d88dSmrg if (info != old && info->parent != NULL) { 173af69d88dSmrg if (info->parent->child == old) 174af69d88dSmrg info->parent->child = info; 175af69d88dSmrg 176af69d88dSmrg if (info->prev != NULL) 177af69d88dSmrg info->prev->next = info; 178af69d88dSmrg 179af69d88dSmrg if (info->next != NULL) 180af69d88dSmrg info->next->prev = info; 181af69d88dSmrg } 182af69d88dSmrg 183af69d88dSmrg /* Update child->parent links for all children */ 184af69d88dSmrg for (child = info->child; child != NULL; child = child->next) 185af69d88dSmrg child->parent = info; 186af69d88dSmrg 187af69d88dSmrg return PTR_FROM_HEADER(info); 188af69d88dSmrg} 189af69d88dSmrg 190af69d88dSmrgvoid * 191af69d88dSmrgreralloc_size(const void *ctx, void *ptr, size_t size) 192af69d88dSmrg{ 193af69d88dSmrg if (unlikely(ptr == NULL)) 194af69d88dSmrg return ralloc_size(ctx, size); 195af69d88dSmrg 196af69d88dSmrg assert(ralloc_parent(ptr) == ctx); 197af69d88dSmrg return resize(ptr, size); 198af69d88dSmrg} 199af69d88dSmrg 200af69d88dSmrgvoid * 201af69d88dSmrgralloc_array_size(const void *ctx, size_t size, unsigned count) 202af69d88dSmrg{ 203af69d88dSmrg if (count > SIZE_MAX/size) 204af69d88dSmrg return NULL; 205af69d88dSmrg 206af69d88dSmrg return ralloc_size(ctx, size * count); 207af69d88dSmrg} 208af69d88dSmrg 209af69d88dSmrgvoid * 210af69d88dSmrgrzalloc_array_size(const void *ctx, size_t size, unsigned count) 211af69d88dSmrg{ 212af69d88dSmrg if (count > SIZE_MAX/size) 213af69d88dSmrg return NULL; 214af69d88dSmrg 215af69d88dSmrg return rzalloc_size(ctx, size * count); 216af69d88dSmrg} 217af69d88dSmrg 218af69d88dSmrgvoid * 219af69d88dSmrgreralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count) 220af69d88dSmrg{ 221af69d88dSmrg if (count > SIZE_MAX/size) 222af69d88dSmrg return NULL; 223af69d88dSmrg 224af69d88dSmrg return reralloc_size(ctx, ptr, size * count); 225af69d88dSmrg} 226af69d88dSmrg 227af69d88dSmrgvoid 228af69d88dSmrgralloc_free(void *ptr) 229af69d88dSmrg{ 230af69d88dSmrg ralloc_header *info; 231af69d88dSmrg 232af69d88dSmrg if (ptr == NULL) 233af69d88dSmrg return; 234af69d88dSmrg 235af69d88dSmrg info = get_header(ptr); 236af69d88dSmrg unlink_block(info); 237af69d88dSmrg unsafe_free(info); 238af69d88dSmrg} 239af69d88dSmrg 240af69d88dSmrgstatic void 241af69d88dSmrgunlink_block(ralloc_header *info) 242af69d88dSmrg{ 243af69d88dSmrg /* Unlink from parent & siblings */ 244af69d88dSmrg if (info->parent != NULL) { 245af69d88dSmrg if (info->parent->child == info) 246af69d88dSmrg info->parent->child = info->next; 247af69d88dSmrg 248af69d88dSmrg if (info->prev != NULL) 249af69d88dSmrg info->prev->next = info->next; 250af69d88dSmrg 251af69d88dSmrg if (info->next != NULL) 252af69d88dSmrg info->next->prev = info->prev; 253af69d88dSmrg } 254af69d88dSmrg info->parent = NULL; 255af69d88dSmrg info->prev = NULL; 256af69d88dSmrg info->next = NULL; 257af69d88dSmrg} 258af69d88dSmrg 259af69d88dSmrgstatic void 260af69d88dSmrgunsafe_free(ralloc_header *info) 261af69d88dSmrg{ 262af69d88dSmrg /* Recursively free any children...don't waste time unlinking them. */ 263af69d88dSmrg ralloc_header *temp; 264af69d88dSmrg while (info->child != NULL) { 265af69d88dSmrg temp = info->child; 266af69d88dSmrg info->child = temp->next; 267af69d88dSmrg unsafe_free(temp); 268af69d88dSmrg } 269af69d88dSmrg 270af69d88dSmrg /* Free the block itself. Call the destructor first, if any. */ 271af69d88dSmrg if (info->destructor != NULL) 272af69d88dSmrg info->destructor(PTR_FROM_HEADER(info)); 273af69d88dSmrg 274af69d88dSmrg free(info); 275af69d88dSmrg} 276af69d88dSmrg 277af69d88dSmrgvoid 278af69d88dSmrgralloc_steal(const void *new_ctx, void *ptr) 279af69d88dSmrg{ 280af69d88dSmrg ralloc_header *info, *parent; 281af69d88dSmrg 282af69d88dSmrg if (unlikely(ptr == NULL)) 283af69d88dSmrg return; 284af69d88dSmrg 285af69d88dSmrg info = get_header(ptr); 2867e995a2eSmrg parent = new_ctx ? get_header(new_ctx) : NULL; 287af69d88dSmrg 288af69d88dSmrg unlink_block(info); 289af69d88dSmrg 290af69d88dSmrg add_child(parent, info); 291af69d88dSmrg} 292af69d88dSmrg 2937e995a2eSmrgvoid 2947e995a2eSmrgralloc_adopt(const void *new_ctx, void *old_ctx) 2957e995a2eSmrg{ 2967e995a2eSmrg ralloc_header *new_info, *old_info, *child; 2977e995a2eSmrg 2987e995a2eSmrg if (unlikely(old_ctx == NULL)) 2997e995a2eSmrg return; 3007e995a2eSmrg 3017e995a2eSmrg old_info = get_header(old_ctx); 3027e995a2eSmrg new_info = get_header(new_ctx); 3037e995a2eSmrg 3047e995a2eSmrg /* If there are no children, bail. */ 3057e995a2eSmrg if (unlikely(old_info->child == NULL)) 3067e995a2eSmrg return; 3077e995a2eSmrg 3087e995a2eSmrg /* Set all the children's parent to new_ctx; get a pointer to the last child. */ 3097e995a2eSmrg for (child = old_info->child; child->next != NULL; child = child->next) { 3107e995a2eSmrg child->parent = new_info; 3117e995a2eSmrg } 3127e995a2eSmrg child->parent = new_info; 3137e995a2eSmrg 3147e995a2eSmrg /* Connect the two lists together; parent them to new_ctx; make old_ctx empty. */ 3157e995a2eSmrg child->next = new_info->child; 3167e995a2eSmrg if (child->next) 3177e995a2eSmrg child->next->prev = child; 3187e995a2eSmrg new_info->child = old_info->child; 3197e995a2eSmrg old_info->child = NULL; 3207e995a2eSmrg} 3217e995a2eSmrg 322af69d88dSmrgvoid * 323af69d88dSmrgralloc_parent(const void *ptr) 324af69d88dSmrg{ 325af69d88dSmrg ralloc_header *info; 326af69d88dSmrg 327af69d88dSmrg if (unlikely(ptr == NULL)) 328af69d88dSmrg return NULL; 329af69d88dSmrg 330af69d88dSmrg info = get_header(ptr); 331af69d88dSmrg return info->parent ? PTR_FROM_HEADER(info->parent) : NULL; 332af69d88dSmrg} 333af69d88dSmrg 334af69d88dSmrgvoid 335af69d88dSmrgralloc_set_destructor(const void *ptr, void(*destructor)(void *)) 336af69d88dSmrg{ 337af69d88dSmrg ralloc_header *info = get_header(ptr); 338af69d88dSmrg info->destructor = destructor; 339af69d88dSmrg} 340af69d88dSmrg 341af69d88dSmrgchar * 342af69d88dSmrgralloc_strdup(const void *ctx, const char *str) 343af69d88dSmrg{ 344af69d88dSmrg size_t n; 345af69d88dSmrg char *ptr; 346af69d88dSmrg 347af69d88dSmrg if (unlikely(str == NULL)) 348af69d88dSmrg return NULL; 349af69d88dSmrg 350af69d88dSmrg n = strlen(str); 351af69d88dSmrg ptr = ralloc_array(ctx, char, n + 1); 352af69d88dSmrg memcpy(ptr, str, n); 353af69d88dSmrg ptr[n] = '\0'; 354af69d88dSmrg return ptr; 355af69d88dSmrg} 356af69d88dSmrg 357af69d88dSmrgchar * 358af69d88dSmrgralloc_strndup(const void *ctx, const char *str, size_t max) 359af69d88dSmrg{ 360af69d88dSmrg size_t n; 361af69d88dSmrg char *ptr; 362af69d88dSmrg 363af69d88dSmrg if (unlikely(str == NULL)) 364af69d88dSmrg return NULL; 365af69d88dSmrg 3667e995a2eSmrg n = strnlen(str, max); 367af69d88dSmrg ptr = ralloc_array(ctx, char, n + 1); 368af69d88dSmrg memcpy(ptr, str, n); 369af69d88dSmrg ptr[n] = '\0'; 370af69d88dSmrg return ptr; 371af69d88dSmrg} 372af69d88dSmrg 373af69d88dSmrg/* helper routine for strcat/strncat - n is the exact amount to copy */ 374af69d88dSmrgstatic bool 375af69d88dSmrgcat(char **dest, const char *str, size_t n) 376af69d88dSmrg{ 377af69d88dSmrg char *both; 378af69d88dSmrg size_t existing_length; 379af69d88dSmrg assert(dest != NULL && *dest != NULL); 380af69d88dSmrg 381af69d88dSmrg existing_length = strlen(*dest); 382af69d88dSmrg both = resize(*dest, existing_length + n + 1); 383af69d88dSmrg if (unlikely(both == NULL)) 384af69d88dSmrg return false; 385af69d88dSmrg 386af69d88dSmrg memcpy(both + existing_length, str, n); 387af69d88dSmrg both[existing_length + n] = '\0'; 388af69d88dSmrg 389af69d88dSmrg *dest = both; 390af69d88dSmrg return true; 391af69d88dSmrg} 392af69d88dSmrg 393af69d88dSmrg 394af69d88dSmrgbool 395af69d88dSmrgralloc_strcat(char **dest, const char *str) 396af69d88dSmrg{ 397af69d88dSmrg return cat(dest, str, strlen(str)); 398af69d88dSmrg} 399af69d88dSmrg 400af69d88dSmrgbool 401af69d88dSmrgralloc_strncat(char **dest, const char *str, size_t n) 402af69d88dSmrg{ 4037e995a2eSmrg return cat(dest, str, strnlen(str, n)); 4047e995a2eSmrg} 4057e995a2eSmrg 4067e995a2eSmrgbool 4077e995a2eSmrgralloc_str_append(char **dest, const char *str, 4087e995a2eSmrg size_t existing_length, size_t str_size) 4097e995a2eSmrg{ 4107e995a2eSmrg char *both; 4117e995a2eSmrg assert(dest != NULL && *dest != NULL); 4127e995a2eSmrg 4137e995a2eSmrg both = resize(*dest, existing_length + str_size + 1); 4147e995a2eSmrg if (unlikely(both == NULL)) 4157e995a2eSmrg return false; 416af69d88dSmrg 4177e995a2eSmrg memcpy(both + existing_length, str, str_size); 4187e995a2eSmrg both[existing_length + str_size] = '\0'; 4197e995a2eSmrg 4207e995a2eSmrg *dest = both; 4217e995a2eSmrg 4227e995a2eSmrg return true; 423af69d88dSmrg} 424af69d88dSmrg 425af69d88dSmrgchar * 426af69d88dSmrgralloc_asprintf(const void *ctx, const char *fmt, ...) 427af69d88dSmrg{ 428af69d88dSmrg char *ptr; 429af69d88dSmrg va_list args; 430af69d88dSmrg va_start(args, fmt); 431af69d88dSmrg ptr = ralloc_vasprintf(ctx, fmt, args); 432af69d88dSmrg va_end(args); 433af69d88dSmrg return ptr; 434af69d88dSmrg} 435af69d88dSmrg 436af69d88dSmrg/* Return the length of the string that would be generated by a printf-style 437af69d88dSmrg * format and argument list, not including the \0 byte. 438af69d88dSmrg */ 439af69d88dSmrgstatic size_t 440af69d88dSmrgprintf_length(const char *fmt, va_list untouched_args) 441af69d88dSmrg{ 442af69d88dSmrg int size; 443af69d88dSmrg char junk; 444af69d88dSmrg 445af69d88dSmrg /* Make a copy of the va_list so the original caller can still use it */ 446af69d88dSmrg va_list args; 447af69d88dSmrg va_copy(args, untouched_args); 448af69d88dSmrg 449af69d88dSmrg#ifdef _WIN32 450af69d88dSmrg /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1 451af69d88dSmrg * if the number of characters to write is greater than count. 452af69d88dSmrg */ 453af69d88dSmrg size = _vscprintf(fmt, args); 454af69d88dSmrg (void)junk; 455af69d88dSmrg#else 456af69d88dSmrg size = vsnprintf(&junk, 1, fmt, args); 457af69d88dSmrg#endif 458af69d88dSmrg assert(size >= 0); 459af69d88dSmrg 460af69d88dSmrg va_end(args); 461af69d88dSmrg 462af69d88dSmrg return size; 463af69d88dSmrg} 464af69d88dSmrg 465af69d88dSmrgchar * 466af69d88dSmrgralloc_vasprintf(const void *ctx, const char *fmt, va_list args) 467af69d88dSmrg{ 468af69d88dSmrg size_t size = printf_length(fmt, args) + 1; 469af69d88dSmrg 470af69d88dSmrg char *ptr = ralloc_size(ctx, size); 471af69d88dSmrg if (ptr != NULL) 472af69d88dSmrg vsnprintf(ptr, size, fmt, args); 473af69d88dSmrg 474af69d88dSmrg return ptr; 475af69d88dSmrg} 476af69d88dSmrg 477af69d88dSmrgbool 478af69d88dSmrgralloc_asprintf_append(char **str, const char *fmt, ...) 479af69d88dSmrg{ 480af69d88dSmrg bool success; 481af69d88dSmrg va_list args; 482af69d88dSmrg va_start(args, fmt); 483af69d88dSmrg success = ralloc_vasprintf_append(str, fmt, args); 484af69d88dSmrg va_end(args); 485af69d88dSmrg return success; 486af69d88dSmrg} 487af69d88dSmrg 488af69d88dSmrgbool 489af69d88dSmrgralloc_vasprintf_append(char **str, const char *fmt, va_list args) 490af69d88dSmrg{ 491af69d88dSmrg size_t existing_length; 492af69d88dSmrg assert(str != NULL); 493af69d88dSmrg existing_length = *str ? strlen(*str) : 0; 494af69d88dSmrg return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args); 495af69d88dSmrg} 496af69d88dSmrg 497af69d88dSmrgbool 498af69d88dSmrgralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...) 499af69d88dSmrg{ 500af69d88dSmrg bool success; 501af69d88dSmrg va_list args; 502af69d88dSmrg va_start(args, fmt); 503af69d88dSmrg success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args); 504af69d88dSmrg va_end(args); 505af69d88dSmrg return success; 506af69d88dSmrg} 507af69d88dSmrg 508af69d88dSmrgbool 509af69d88dSmrgralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt, 510af69d88dSmrg va_list args) 511af69d88dSmrg{ 512af69d88dSmrg size_t new_length; 513af69d88dSmrg char *ptr; 514af69d88dSmrg 515af69d88dSmrg assert(str != NULL); 516af69d88dSmrg 517af69d88dSmrg if (unlikely(*str == NULL)) { 518af69d88dSmrg // Assuming a NULL context is probably bad, but it's expected behavior. 519af69d88dSmrg *str = ralloc_vasprintf(NULL, fmt, args); 5207e995a2eSmrg *start = strlen(*str); 521af69d88dSmrg return true; 522af69d88dSmrg } 523af69d88dSmrg 524af69d88dSmrg new_length = printf_length(fmt, args); 525af69d88dSmrg 526af69d88dSmrg ptr = resize(*str, *start + new_length + 1); 527af69d88dSmrg if (unlikely(ptr == NULL)) 528af69d88dSmrg return false; 529af69d88dSmrg 530af69d88dSmrg vsnprintf(ptr + *start, new_length + 1, fmt, args); 531af69d88dSmrg *str = ptr; 532af69d88dSmrg *start += new_length; 533af69d88dSmrg return true; 534af69d88dSmrg} 5357e995a2eSmrg 5367e995a2eSmrg/*************************************************************************** 5377e995a2eSmrg * Linear allocator for short-lived allocations. 5387e995a2eSmrg *************************************************************************** 5397e995a2eSmrg * 5407e995a2eSmrg * The allocator consists of a parent node (2K buffer), which requires 5417e995a2eSmrg * a ralloc parent, and child nodes (allocations). Child nodes can't be freed 5427e995a2eSmrg * directly, because the parent doesn't track them. You have to release 5437e995a2eSmrg * the parent node in order to release all its children. 5447e995a2eSmrg * 5457e995a2eSmrg * The allocator uses a fixed-sized buffer with a monotonically increasing 5467e995a2eSmrg * offset after each allocation. If the buffer is all used, another buffer 5477e995a2eSmrg * is allocated, sharing the same ralloc parent, so all buffers are at 5487e995a2eSmrg * the same level in the ralloc hierarchy. 5497e995a2eSmrg * 5507e995a2eSmrg * The linear parent node is always the first buffer and keeps track of all 5517e995a2eSmrg * other buffers. 5527e995a2eSmrg */ 5537e995a2eSmrg 5547e995a2eSmrg#define MIN_LINEAR_BUFSIZE 2048 5557e995a2eSmrg#define SUBALLOC_ALIGNMENT 8 5567e995a2eSmrg#define LMAGIC 0x87b9c7d3 5577e995a2eSmrg 5587e995a2eSmrgstruct 5597e995a2eSmrg#ifdef _MSC_VER 5607e995a2eSmrg __declspec(align(8)) 5617e995a2eSmrg#elif defined(__LP64__) 5627e995a2eSmrg __attribute__((aligned(16))) 5637e995a2eSmrg#else 5647e995a2eSmrg __attribute__((aligned(8))) 5657e995a2eSmrg#endif 5667e995a2eSmrg linear_header { 567d8407755Smaya#ifndef NDEBUG 5687e995a2eSmrg unsigned magic; /* for debugging */ 5697e995a2eSmrg#endif 5707e995a2eSmrg unsigned offset; /* points to the first unused byte in the buffer */ 5717e995a2eSmrg unsigned size; /* size of the buffer */ 5727e995a2eSmrg void *ralloc_parent; /* new buffers will use this */ 5737e995a2eSmrg struct linear_header *next; /* next buffer if we have more */ 5747e995a2eSmrg struct linear_header *latest; /* the only buffer that has free space */ 5757e995a2eSmrg 5767e995a2eSmrg /* After this structure, the buffer begins. 5777e995a2eSmrg * Each suballocation consists of linear_size_chunk as its header followed 5787e995a2eSmrg * by the suballocation, so it goes: 5797e995a2eSmrg * 5807e995a2eSmrg * - linear_size_chunk 5817e995a2eSmrg * - allocated space 5827e995a2eSmrg * - linear_size_chunk 5837e995a2eSmrg * - allocated space 5847e995a2eSmrg * etc. 5857e995a2eSmrg * 5867e995a2eSmrg * linear_size_chunk is only needed by linear_realloc. 5877e995a2eSmrg */ 5887e995a2eSmrg}; 5897e995a2eSmrg 5907e995a2eSmrgstruct linear_size_chunk { 5917e995a2eSmrg unsigned size; /* for realloc */ 5927e995a2eSmrg unsigned _padding; 5937e995a2eSmrg}; 5947e995a2eSmrg 5957e995a2eSmrgtypedef struct linear_header linear_header; 5967e995a2eSmrgtypedef struct linear_size_chunk linear_size_chunk; 5977e995a2eSmrg 5987e995a2eSmrg#define LINEAR_PARENT_TO_HEADER(parent) \ 5997e995a2eSmrg (linear_header*) \ 6007e995a2eSmrg ((char*)(parent) - sizeof(linear_size_chunk) - sizeof(linear_header)) 6017e995a2eSmrg 6027e995a2eSmrg/* Allocate the linear buffer with its header. */ 6037e995a2eSmrgstatic linear_header * 6047e995a2eSmrgcreate_linear_node(void *ralloc_ctx, unsigned min_size) 6057e995a2eSmrg{ 6067e995a2eSmrg linear_header *node; 6077e995a2eSmrg 6087e995a2eSmrg min_size += sizeof(linear_size_chunk); 6097e995a2eSmrg 6107e995a2eSmrg if (likely(min_size < MIN_LINEAR_BUFSIZE)) 6117e995a2eSmrg min_size = MIN_LINEAR_BUFSIZE; 6127e995a2eSmrg 6137e995a2eSmrg node = ralloc_size(ralloc_ctx, sizeof(linear_header) + min_size); 6147e995a2eSmrg if (unlikely(!node)) 6157e995a2eSmrg return NULL; 6167e995a2eSmrg 617d8407755Smaya#ifndef NDEBUG 6187e995a2eSmrg node->magic = LMAGIC; 6197e995a2eSmrg#endif 6207e995a2eSmrg node->offset = 0; 6217e995a2eSmrg node->size = min_size; 6227e995a2eSmrg node->ralloc_parent = ralloc_ctx; 6237e995a2eSmrg node->next = NULL; 6247e995a2eSmrg node->latest = node; 6257e995a2eSmrg return node; 6267e995a2eSmrg} 6277e995a2eSmrg 6287e995a2eSmrgvoid * 6297e995a2eSmrglinear_alloc_child(void *parent, unsigned size) 6307e995a2eSmrg{ 6317e995a2eSmrg linear_header *first = LINEAR_PARENT_TO_HEADER(parent); 6327e995a2eSmrg linear_header *latest = first->latest; 6337e995a2eSmrg linear_header *new_node; 6347e995a2eSmrg linear_size_chunk *ptr; 6357e995a2eSmrg unsigned full_size; 6367e995a2eSmrg 6377e995a2eSmrg assert(first->magic == LMAGIC); 6387e995a2eSmrg assert(!latest->next); 6397e995a2eSmrg 6407e995a2eSmrg size = ALIGN_POT(size, SUBALLOC_ALIGNMENT); 6417e995a2eSmrg full_size = sizeof(linear_size_chunk) + size; 6427e995a2eSmrg 6437e995a2eSmrg if (unlikely(latest->offset + full_size > latest->size)) { 6447e995a2eSmrg /* allocate a new node */ 6457e995a2eSmrg new_node = create_linear_node(latest->ralloc_parent, size); 6467e995a2eSmrg if (unlikely(!new_node)) 6477e995a2eSmrg return NULL; 6487e995a2eSmrg 6497e995a2eSmrg first->latest = new_node; 6507e995a2eSmrg latest->latest = new_node; 6517e995a2eSmrg latest->next = new_node; 6527e995a2eSmrg latest = new_node; 6537e995a2eSmrg } 6547e995a2eSmrg 6557e995a2eSmrg ptr = (linear_size_chunk *)((char*)&latest[1] + latest->offset); 6567e995a2eSmrg ptr->size = size; 6577e995a2eSmrg latest->offset += full_size; 6587e995a2eSmrg 6597e995a2eSmrg assert((uintptr_t)&ptr[1] % SUBALLOC_ALIGNMENT == 0); 6607e995a2eSmrg return &ptr[1]; 6617e995a2eSmrg} 6627e995a2eSmrg 6637e995a2eSmrgvoid * 6647e995a2eSmrglinear_alloc_parent(void *ralloc_ctx, unsigned size) 6657e995a2eSmrg{ 6667e995a2eSmrg linear_header *node; 6677e995a2eSmrg 6687e995a2eSmrg if (unlikely(!ralloc_ctx)) 6697e995a2eSmrg return NULL; 6707e995a2eSmrg 6717e995a2eSmrg size = ALIGN_POT(size, SUBALLOC_ALIGNMENT); 6727e995a2eSmrg 6737e995a2eSmrg node = create_linear_node(ralloc_ctx, size); 6747e995a2eSmrg if (unlikely(!node)) 6757e995a2eSmrg return NULL; 6767e995a2eSmrg 6777e995a2eSmrg return linear_alloc_child((char*)node + 6787e995a2eSmrg sizeof(linear_header) + 6797e995a2eSmrg sizeof(linear_size_chunk), size); 6807e995a2eSmrg} 6817e995a2eSmrg 6827e995a2eSmrgvoid * 6837e995a2eSmrglinear_zalloc_child(void *parent, unsigned size) 6847e995a2eSmrg{ 6857e995a2eSmrg void *ptr = linear_alloc_child(parent, size); 6867e995a2eSmrg 6877e995a2eSmrg if (likely(ptr)) 6887e995a2eSmrg memset(ptr, 0, size); 6897e995a2eSmrg return ptr; 6907e995a2eSmrg} 6917e995a2eSmrg 6927e995a2eSmrgvoid * 6937e995a2eSmrglinear_zalloc_parent(void *parent, unsigned size) 6947e995a2eSmrg{ 6957e995a2eSmrg void *ptr = linear_alloc_parent(parent, size); 6967e995a2eSmrg 6977e995a2eSmrg if (likely(ptr)) 6987e995a2eSmrg memset(ptr, 0, size); 6997e995a2eSmrg return ptr; 7007e995a2eSmrg} 7017e995a2eSmrg 7027e995a2eSmrgvoid 7037e995a2eSmrglinear_free_parent(void *ptr) 7047e995a2eSmrg{ 7057e995a2eSmrg linear_header *node; 7067e995a2eSmrg 7077e995a2eSmrg if (unlikely(!ptr)) 7087e995a2eSmrg return; 7097e995a2eSmrg 7107e995a2eSmrg node = LINEAR_PARENT_TO_HEADER(ptr); 7117e995a2eSmrg assert(node->magic == LMAGIC); 7127e995a2eSmrg 7137e995a2eSmrg while (node) { 7147e995a2eSmrg void *ptr = node; 7157e995a2eSmrg 7167e995a2eSmrg node = node->next; 7177e995a2eSmrg ralloc_free(ptr); 7187e995a2eSmrg } 7197e995a2eSmrg} 7207e995a2eSmrg 7217e995a2eSmrgvoid 7227e995a2eSmrgralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr) 7237e995a2eSmrg{ 7247e995a2eSmrg linear_header *node; 7257e995a2eSmrg 7267e995a2eSmrg if (unlikely(!ptr)) 7277e995a2eSmrg return; 7287e995a2eSmrg 7297e995a2eSmrg node = LINEAR_PARENT_TO_HEADER(ptr); 7307e995a2eSmrg assert(node->magic == LMAGIC); 7317e995a2eSmrg 7327e995a2eSmrg while (node) { 7337e995a2eSmrg ralloc_steal(new_ralloc_ctx, node); 7347e995a2eSmrg node->ralloc_parent = new_ralloc_ctx; 7357e995a2eSmrg node = node->next; 7367e995a2eSmrg } 7377e995a2eSmrg} 7387e995a2eSmrg 7397e995a2eSmrgvoid * 7407e995a2eSmrgralloc_parent_of_linear_parent(void *ptr) 7417e995a2eSmrg{ 7427e995a2eSmrg linear_header *node = LINEAR_PARENT_TO_HEADER(ptr); 7437e995a2eSmrg assert(node->magic == LMAGIC); 7447e995a2eSmrg return node->ralloc_parent; 7457e995a2eSmrg} 7467e995a2eSmrg 7477e995a2eSmrgvoid * 7487e995a2eSmrglinear_realloc(void *parent, void *old, unsigned new_size) 7497e995a2eSmrg{ 7507e995a2eSmrg unsigned old_size = 0; 7517e995a2eSmrg ralloc_header *new_ptr; 7527e995a2eSmrg 7537e995a2eSmrg new_ptr = linear_alloc_child(parent, new_size); 7547e995a2eSmrg 7557e995a2eSmrg if (unlikely(!old)) 7567e995a2eSmrg return new_ptr; 7577e995a2eSmrg 7587e995a2eSmrg old_size = ((linear_size_chunk*)old)[-1].size; 7597e995a2eSmrg 7607e995a2eSmrg if (likely(new_ptr && old_size)) 7617e995a2eSmrg memcpy(new_ptr, old, MIN2(old_size, new_size)); 7627e995a2eSmrg 7637e995a2eSmrg return new_ptr; 7647e995a2eSmrg} 7657e995a2eSmrg 7667e995a2eSmrg/* All code below is pretty much copied from ralloc and only the alloc 7677e995a2eSmrg * calls are different. 7687e995a2eSmrg */ 7697e995a2eSmrg 7707e995a2eSmrgchar * 7717e995a2eSmrglinear_strdup(void *parent, const char *str) 7727e995a2eSmrg{ 7737e995a2eSmrg unsigned n; 7747e995a2eSmrg char *ptr; 7757e995a2eSmrg 7767e995a2eSmrg if (unlikely(!str)) 7777e995a2eSmrg return NULL; 7787e995a2eSmrg 7797e995a2eSmrg n = strlen(str); 7807e995a2eSmrg ptr = linear_alloc_child(parent, n + 1); 7817e995a2eSmrg if (unlikely(!ptr)) 7827e995a2eSmrg return NULL; 7837e995a2eSmrg 7847e995a2eSmrg memcpy(ptr, str, n); 7857e995a2eSmrg ptr[n] = '\0'; 7867e995a2eSmrg return ptr; 7877e995a2eSmrg} 7887e995a2eSmrg 7897e995a2eSmrgchar * 7907e995a2eSmrglinear_asprintf(void *parent, const char *fmt, ...) 7917e995a2eSmrg{ 7927e995a2eSmrg char *ptr; 7937e995a2eSmrg va_list args; 7947e995a2eSmrg va_start(args, fmt); 7957e995a2eSmrg ptr = linear_vasprintf(parent, fmt, args); 7967e995a2eSmrg va_end(args); 7977e995a2eSmrg return ptr; 7987e995a2eSmrg} 7997e995a2eSmrg 8007e995a2eSmrgchar * 8017e995a2eSmrglinear_vasprintf(void *parent, const char *fmt, va_list args) 8027e995a2eSmrg{ 8037e995a2eSmrg unsigned size = printf_length(fmt, args) + 1; 8047e995a2eSmrg 8057e995a2eSmrg char *ptr = linear_alloc_child(parent, size); 8067e995a2eSmrg if (ptr != NULL) 8077e995a2eSmrg vsnprintf(ptr, size, fmt, args); 8087e995a2eSmrg 8097e995a2eSmrg return ptr; 8107e995a2eSmrg} 8117e995a2eSmrg 8127e995a2eSmrgbool 8137e995a2eSmrglinear_asprintf_append(void *parent, char **str, const char *fmt, ...) 8147e995a2eSmrg{ 8157e995a2eSmrg bool success; 8167e995a2eSmrg va_list args; 8177e995a2eSmrg va_start(args, fmt); 8187e995a2eSmrg success = linear_vasprintf_append(parent, str, fmt, args); 8197e995a2eSmrg va_end(args); 8207e995a2eSmrg return success; 8217e995a2eSmrg} 8227e995a2eSmrg 8237e995a2eSmrgbool 8247e995a2eSmrglinear_vasprintf_append(void *parent, char **str, const char *fmt, va_list args) 8257e995a2eSmrg{ 8267e995a2eSmrg size_t existing_length; 8277e995a2eSmrg assert(str != NULL); 8287e995a2eSmrg existing_length = *str ? strlen(*str) : 0; 8297e995a2eSmrg return linear_vasprintf_rewrite_tail(parent, str, &existing_length, fmt, args); 8307e995a2eSmrg} 8317e995a2eSmrg 8327e995a2eSmrgbool 8337e995a2eSmrglinear_asprintf_rewrite_tail(void *parent, char **str, size_t *start, 8347e995a2eSmrg const char *fmt, ...) 8357e995a2eSmrg{ 8367e995a2eSmrg bool success; 8377e995a2eSmrg va_list args; 8387e995a2eSmrg va_start(args, fmt); 8397e995a2eSmrg success = linear_vasprintf_rewrite_tail(parent, str, start, fmt, args); 8407e995a2eSmrg va_end(args); 8417e995a2eSmrg return success; 8427e995a2eSmrg} 8437e995a2eSmrg 8447e995a2eSmrgbool 8457e995a2eSmrglinear_vasprintf_rewrite_tail(void *parent, char **str, size_t *start, 8467e995a2eSmrg const char *fmt, va_list args) 8477e995a2eSmrg{ 8487e995a2eSmrg size_t new_length; 8497e995a2eSmrg char *ptr; 8507e995a2eSmrg 8517e995a2eSmrg assert(str != NULL); 8527e995a2eSmrg 8537e995a2eSmrg if (unlikely(*str == NULL)) { 8547e995a2eSmrg *str = linear_vasprintf(parent, fmt, args); 8557e995a2eSmrg *start = strlen(*str); 8567e995a2eSmrg return true; 8577e995a2eSmrg } 8587e995a2eSmrg 8597e995a2eSmrg new_length = printf_length(fmt, args); 8607e995a2eSmrg 8617e995a2eSmrg ptr = linear_realloc(parent, *str, *start + new_length + 1); 8627e995a2eSmrg if (unlikely(ptr == NULL)) 8637e995a2eSmrg return false; 8647e995a2eSmrg 8657e995a2eSmrg vsnprintf(ptr + *start, new_length + 1, fmt, args); 8667e995a2eSmrg *str = ptr; 8677e995a2eSmrg *start += new_length; 8687e995a2eSmrg return true; 8697e995a2eSmrg} 8707e995a2eSmrg 8717e995a2eSmrg/* helper routine for strcat/strncat - n is the exact amount to copy */ 8727e995a2eSmrgstatic bool 8737e995a2eSmrglinear_cat(void *parent, char **dest, const char *str, unsigned n) 8747e995a2eSmrg{ 8757e995a2eSmrg char *both; 8767e995a2eSmrg unsigned existing_length; 8777e995a2eSmrg assert(dest != NULL && *dest != NULL); 8787e995a2eSmrg 8797e995a2eSmrg existing_length = strlen(*dest); 8807e995a2eSmrg both = linear_realloc(parent, *dest, existing_length + n + 1); 8817e995a2eSmrg if (unlikely(both == NULL)) 8827e995a2eSmrg return false; 8837e995a2eSmrg 8847e995a2eSmrg memcpy(both + existing_length, str, n); 8857e995a2eSmrg both[existing_length + n] = '\0'; 8867e995a2eSmrg 8877e995a2eSmrg *dest = both; 8887e995a2eSmrg return true; 8897e995a2eSmrg} 8907e995a2eSmrg 8917e995a2eSmrgbool 8927e995a2eSmrglinear_strcat(void *parent, char **dest, const char *str) 8937e995a2eSmrg{ 8947e995a2eSmrg return linear_cat(parent, dest, str, strlen(str)); 8957e995a2eSmrg} 896