ralloc.c revision af69d88d
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/* Android defines SIZE_MAX in limits.h, instead of the standard stdint.h */ 32af69d88dSmrg#ifdef ANDROID 33af69d88dSmrg#include <limits.h> 34af69d88dSmrg#endif 35af69d88dSmrg 36af69d88dSmrg/* Some versions of MinGW are missing _vscprintf's declaration, although they 37af69d88dSmrg * still provide the symbol in the import library. */ 38af69d88dSmrg#ifdef __MINGW32__ 39af69d88dSmrg_CRTIMP int _vscprintf(const char *format, va_list argptr); 40af69d88dSmrg#endif 41af69d88dSmrg 42af69d88dSmrg#include "ralloc.h" 43af69d88dSmrg 44af69d88dSmrg#ifndef va_copy 45af69d88dSmrg#ifdef __va_copy 46af69d88dSmrg#define va_copy(dest, src) __va_copy((dest), (src)) 47af69d88dSmrg#else 48af69d88dSmrg#define va_copy(dest, src) (dest) = (src) 49af69d88dSmrg#endif 50af69d88dSmrg#endif 51af69d88dSmrg 52af69d88dSmrg#define CANARY 0x5A1106 53af69d88dSmrg 54af69d88dSmrgstruct ralloc_header 55af69d88dSmrg{ 56af69d88dSmrg#ifdef DEBUG 57af69d88dSmrg /* A canary value used to determine whether a pointer is ralloc'd. */ 58af69d88dSmrg unsigned canary; 59af69d88dSmrg#endif 60af69d88dSmrg 61af69d88dSmrg struct ralloc_header *parent; 62af69d88dSmrg 63af69d88dSmrg /* The first child (head of a linked list) */ 64af69d88dSmrg struct ralloc_header *child; 65af69d88dSmrg 66af69d88dSmrg /* Linked list of siblings */ 67af69d88dSmrg struct ralloc_header *prev; 68af69d88dSmrg struct ralloc_header *next; 69af69d88dSmrg 70af69d88dSmrg void (*destructor)(void *); 71af69d88dSmrg}; 72af69d88dSmrg 73af69d88dSmrgtypedef struct ralloc_header ralloc_header; 74af69d88dSmrg 75af69d88dSmrgstatic void unlink_block(ralloc_header *info); 76af69d88dSmrgstatic void unsafe_free(ralloc_header *info); 77af69d88dSmrg 78af69d88dSmrgstatic ralloc_header * 79af69d88dSmrgget_header(const void *ptr) 80af69d88dSmrg{ 81af69d88dSmrg ralloc_header *info = (ralloc_header *) (((char *) ptr) - 82af69d88dSmrg sizeof(ralloc_header)); 83af69d88dSmrg#ifdef DEBUG 84af69d88dSmrg assert(info->canary == CANARY); 85af69d88dSmrg#endif 86af69d88dSmrg return info; 87af69d88dSmrg} 88af69d88dSmrg 89af69d88dSmrg#define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header)) 90af69d88dSmrg 91af69d88dSmrgstatic void 92af69d88dSmrgadd_child(ralloc_header *parent, ralloc_header *info) 93af69d88dSmrg{ 94af69d88dSmrg if (parent != NULL) { 95af69d88dSmrg info->parent = parent; 96af69d88dSmrg info->next = parent->child; 97af69d88dSmrg parent->child = info; 98af69d88dSmrg 99af69d88dSmrg if (info->next != NULL) 100af69d88dSmrg info->next->prev = info; 101af69d88dSmrg } 102af69d88dSmrg} 103af69d88dSmrg 104af69d88dSmrgvoid * 105af69d88dSmrgralloc_context(const void *ctx) 106af69d88dSmrg{ 107af69d88dSmrg return ralloc_size(ctx, 0); 108af69d88dSmrg} 109af69d88dSmrg 110af69d88dSmrgvoid * 111af69d88dSmrgralloc_size(const void *ctx, size_t size) 112af69d88dSmrg{ 113af69d88dSmrg void *block = calloc(1, size + sizeof(ralloc_header)); 114af69d88dSmrg ralloc_header *info; 115af69d88dSmrg ralloc_header *parent; 116af69d88dSmrg 117af69d88dSmrg if (unlikely(block == NULL)) 118af69d88dSmrg return NULL; 119af69d88dSmrg info = (ralloc_header *) block; 120af69d88dSmrg parent = ctx != NULL ? get_header(ctx) : NULL; 121af69d88dSmrg 122af69d88dSmrg add_child(parent, info); 123af69d88dSmrg 124af69d88dSmrg#ifdef DEBUG 125af69d88dSmrg info->canary = CANARY; 126af69d88dSmrg#endif 127af69d88dSmrg 128af69d88dSmrg return PTR_FROM_HEADER(info); 129af69d88dSmrg} 130af69d88dSmrg 131af69d88dSmrgvoid * 132af69d88dSmrgrzalloc_size(const void *ctx, size_t size) 133af69d88dSmrg{ 134af69d88dSmrg void *ptr = ralloc_size(ctx, size); 135af69d88dSmrg if (likely(ptr != NULL)) 136af69d88dSmrg memset(ptr, 0, size); 137af69d88dSmrg return ptr; 138af69d88dSmrg} 139af69d88dSmrg 140af69d88dSmrg/* helper function - assumes ptr != NULL */ 141af69d88dSmrgstatic void * 142af69d88dSmrgresize(void *ptr, size_t size) 143af69d88dSmrg{ 144af69d88dSmrg ralloc_header *child, *old, *info; 145af69d88dSmrg 146af69d88dSmrg old = get_header(ptr); 147af69d88dSmrg info = realloc(old, size + sizeof(ralloc_header)); 148af69d88dSmrg 149af69d88dSmrg if (info == NULL) 150af69d88dSmrg return NULL; 151af69d88dSmrg 152af69d88dSmrg /* Update parent and sibling's links to the reallocated node. */ 153af69d88dSmrg if (info != old && info->parent != NULL) { 154af69d88dSmrg if (info->parent->child == old) 155af69d88dSmrg info->parent->child = info; 156af69d88dSmrg 157af69d88dSmrg if (info->prev != NULL) 158af69d88dSmrg info->prev->next = info; 159af69d88dSmrg 160af69d88dSmrg if (info->next != NULL) 161af69d88dSmrg info->next->prev = info; 162af69d88dSmrg } 163af69d88dSmrg 164af69d88dSmrg /* Update child->parent links for all children */ 165af69d88dSmrg for (child = info->child; child != NULL; child = child->next) 166af69d88dSmrg child->parent = info; 167af69d88dSmrg 168af69d88dSmrg return PTR_FROM_HEADER(info); 169af69d88dSmrg} 170af69d88dSmrg 171af69d88dSmrgvoid * 172af69d88dSmrgreralloc_size(const void *ctx, void *ptr, size_t size) 173af69d88dSmrg{ 174af69d88dSmrg if (unlikely(ptr == NULL)) 175af69d88dSmrg return ralloc_size(ctx, size); 176af69d88dSmrg 177af69d88dSmrg assert(ralloc_parent(ptr) == ctx); 178af69d88dSmrg return resize(ptr, size); 179af69d88dSmrg} 180af69d88dSmrg 181af69d88dSmrgvoid * 182af69d88dSmrgralloc_array_size(const void *ctx, size_t size, unsigned count) 183af69d88dSmrg{ 184af69d88dSmrg if (count > SIZE_MAX/size) 185af69d88dSmrg return NULL; 186af69d88dSmrg 187af69d88dSmrg return ralloc_size(ctx, size * count); 188af69d88dSmrg} 189af69d88dSmrg 190af69d88dSmrgvoid * 191af69d88dSmrgrzalloc_array_size(const void *ctx, size_t size, unsigned count) 192af69d88dSmrg{ 193af69d88dSmrg if (count > SIZE_MAX/size) 194af69d88dSmrg return NULL; 195af69d88dSmrg 196af69d88dSmrg return rzalloc_size(ctx, size * count); 197af69d88dSmrg} 198af69d88dSmrg 199af69d88dSmrgvoid * 200af69d88dSmrgreralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count) 201af69d88dSmrg{ 202af69d88dSmrg if (count > SIZE_MAX/size) 203af69d88dSmrg return NULL; 204af69d88dSmrg 205af69d88dSmrg return reralloc_size(ctx, ptr, size * count); 206af69d88dSmrg} 207af69d88dSmrg 208af69d88dSmrgvoid 209af69d88dSmrgralloc_free(void *ptr) 210af69d88dSmrg{ 211af69d88dSmrg ralloc_header *info; 212af69d88dSmrg 213af69d88dSmrg if (ptr == NULL) 214af69d88dSmrg return; 215af69d88dSmrg 216af69d88dSmrg info = get_header(ptr); 217af69d88dSmrg unlink_block(info); 218af69d88dSmrg unsafe_free(info); 219af69d88dSmrg} 220af69d88dSmrg 221af69d88dSmrgstatic void 222af69d88dSmrgunlink_block(ralloc_header *info) 223af69d88dSmrg{ 224af69d88dSmrg /* Unlink from parent & siblings */ 225af69d88dSmrg if (info->parent != NULL) { 226af69d88dSmrg if (info->parent->child == info) 227af69d88dSmrg info->parent->child = info->next; 228af69d88dSmrg 229af69d88dSmrg if (info->prev != NULL) 230af69d88dSmrg info->prev->next = info->next; 231af69d88dSmrg 232af69d88dSmrg if (info->next != NULL) 233af69d88dSmrg info->next->prev = info->prev; 234af69d88dSmrg } 235af69d88dSmrg info->parent = NULL; 236af69d88dSmrg info->prev = NULL; 237af69d88dSmrg info->next = NULL; 238af69d88dSmrg} 239af69d88dSmrg 240af69d88dSmrgstatic void 241af69d88dSmrgunsafe_free(ralloc_header *info) 242af69d88dSmrg{ 243af69d88dSmrg /* Recursively free any children...don't waste time unlinking them. */ 244af69d88dSmrg ralloc_header *temp; 245af69d88dSmrg while (info->child != NULL) { 246af69d88dSmrg temp = info->child; 247af69d88dSmrg info->child = temp->next; 248af69d88dSmrg unsafe_free(temp); 249af69d88dSmrg } 250af69d88dSmrg 251af69d88dSmrg /* Free the block itself. Call the destructor first, if any. */ 252af69d88dSmrg if (info->destructor != NULL) 253af69d88dSmrg info->destructor(PTR_FROM_HEADER(info)); 254af69d88dSmrg 255af69d88dSmrg free(info); 256af69d88dSmrg} 257af69d88dSmrg 258af69d88dSmrgvoid 259af69d88dSmrgralloc_steal(const void *new_ctx, void *ptr) 260af69d88dSmrg{ 261af69d88dSmrg ralloc_header *info, *parent; 262af69d88dSmrg 263af69d88dSmrg if (unlikely(ptr == NULL)) 264af69d88dSmrg return; 265af69d88dSmrg 266af69d88dSmrg info = get_header(ptr); 267af69d88dSmrg parent = get_header(new_ctx); 268af69d88dSmrg 269af69d88dSmrg unlink_block(info); 270af69d88dSmrg 271af69d88dSmrg add_child(parent, info); 272af69d88dSmrg} 273af69d88dSmrg 274af69d88dSmrgvoid * 275af69d88dSmrgralloc_parent(const void *ptr) 276af69d88dSmrg{ 277af69d88dSmrg ralloc_header *info; 278af69d88dSmrg 279af69d88dSmrg if (unlikely(ptr == NULL)) 280af69d88dSmrg return NULL; 281af69d88dSmrg 282af69d88dSmrg info = get_header(ptr); 283af69d88dSmrg return info->parent ? PTR_FROM_HEADER(info->parent) : NULL; 284af69d88dSmrg} 285af69d88dSmrg 286af69d88dSmrgstatic void *autofree_context = NULL; 287af69d88dSmrg 288af69d88dSmrgstatic void 289af69d88dSmrgautofree(void) 290af69d88dSmrg{ 291af69d88dSmrg ralloc_free(autofree_context); 292af69d88dSmrg} 293af69d88dSmrg 294af69d88dSmrgvoid * 295af69d88dSmrgralloc_autofree_context(void) 296af69d88dSmrg{ 297af69d88dSmrg if (unlikely(autofree_context == NULL)) { 298af69d88dSmrg autofree_context = ralloc_context(NULL); 299af69d88dSmrg atexit(autofree); 300af69d88dSmrg } 301af69d88dSmrg return autofree_context; 302af69d88dSmrg} 303af69d88dSmrg 304af69d88dSmrgvoid 305af69d88dSmrgralloc_set_destructor(const void *ptr, void(*destructor)(void *)) 306af69d88dSmrg{ 307af69d88dSmrg ralloc_header *info = get_header(ptr); 308af69d88dSmrg info->destructor = destructor; 309af69d88dSmrg} 310af69d88dSmrg 311af69d88dSmrgchar * 312af69d88dSmrgralloc_strdup(const void *ctx, const char *str) 313af69d88dSmrg{ 314af69d88dSmrg size_t n; 315af69d88dSmrg char *ptr; 316af69d88dSmrg 317af69d88dSmrg if (unlikely(str == NULL)) 318af69d88dSmrg return NULL; 319af69d88dSmrg 320af69d88dSmrg n = strlen(str); 321af69d88dSmrg ptr = ralloc_array(ctx, char, n + 1); 322af69d88dSmrg memcpy(ptr, str, n); 323af69d88dSmrg ptr[n] = '\0'; 324af69d88dSmrg return ptr; 325af69d88dSmrg} 326af69d88dSmrg 327af69d88dSmrgchar * 328af69d88dSmrgralloc_strndup(const void *ctx, const char *str, size_t max) 329af69d88dSmrg{ 330af69d88dSmrg size_t n; 331af69d88dSmrg char *ptr; 332af69d88dSmrg 333af69d88dSmrg if (unlikely(str == NULL)) 334af69d88dSmrg return NULL; 335af69d88dSmrg 336af69d88dSmrg n = strlen(str); 337af69d88dSmrg if (n > max) 338af69d88dSmrg n = max; 339af69d88dSmrg 340af69d88dSmrg ptr = ralloc_array(ctx, char, n + 1); 341af69d88dSmrg memcpy(ptr, str, n); 342af69d88dSmrg ptr[n] = '\0'; 343af69d88dSmrg return ptr; 344af69d88dSmrg} 345af69d88dSmrg 346af69d88dSmrg/* helper routine for strcat/strncat - n is the exact amount to copy */ 347af69d88dSmrgstatic bool 348af69d88dSmrgcat(char **dest, const char *str, size_t n) 349af69d88dSmrg{ 350af69d88dSmrg char *both; 351af69d88dSmrg size_t existing_length; 352af69d88dSmrg assert(dest != NULL && *dest != NULL); 353af69d88dSmrg 354af69d88dSmrg existing_length = strlen(*dest); 355af69d88dSmrg both = resize(*dest, existing_length + n + 1); 356af69d88dSmrg if (unlikely(both == NULL)) 357af69d88dSmrg return false; 358af69d88dSmrg 359af69d88dSmrg memcpy(both + existing_length, str, n); 360af69d88dSmrg both[existing_length + n] = '\0'; 361af69d88dSmrg 362af69d88dSmrg *dest = both; 363af69d88dSmrg return true; 364af69d88dSmrg} 365af69d88dSmrg 366af69d88dSmrg 367af69d88dSmrgbool 368af69d88dSmrgralloc_strcat(char **dest, const char *str) 369af69d88dSmrg{ 370af69d88dSmrg return cat(dest, str, strlen(str)); 371af69d88dSmrg} 372af69d88dSmrg 373af69d88dSmrgbool 374af69d88dSmrgralloc_strncat(char **dest, const char *str, size_t n) 375af69d88dSmrg{ 376af69d88dSmrg /* Clamp n to the string length */ 377af69d88dSmrg size_t str_length = strlen(str); 378af69d88dSmrg if (str_length < n) 379af69d88dSmrg n = str_length; 380af69d88dSmrg 381af69d88dSmrg return cat(dest, str, n); 382af69d88dSmrg} 383af69d88dSmrg 384af69d88dSmrgchar * 385af69d88dSmrgralloc_asprintf(const void *ctx, const char *fmt, ...) 386af69d88dSmrg{ 387af69d88dSmrg char *ptr; 388af69d88dSmrg va_list args; 389af69d88dSmrg va_start(args, fmt); 390af69d88dSmrg ptr = ralloc_vasprintf(ctx, fmt, args); 391af69d88dSmrg va_end(args); 392af69d88dSmrg return ptr; 393af69d88dSmrg} 394af69d88dSmrg 395af69d88dSmrg/* Return the length of the string that would be generated by a printf-style 396af69d88dSmrg * format and argument list, not including the \0 byte. 397af69d88dSmrg */ 398af69d88dSmrgstatic size_t 399af69d88dSmrgprintf_length(const char *fmt, va_list untouched_args) 400af69d88dSmrg{ 401af69d88dSmrg int size; 402af69d88dSmrg char junk; 403af69d88dSmrg 404af69d88dSmrg /* Make a copy of the va_list so the original caller can still use it */ 405af69d88dSmrg va_list args; 406af69d88dSmrg va_copy(args, untouched_args); 407af69d88dSmrg 408af69d88dSmrg#ifdef _WIN32 409af69d88dSmrg /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1 410af69d88dSmrg * if the number of characters to write is greater than count. 411af69d88dSmrg */ 412af69d88dSmrg size = _vscprintf(fmt, args); 413af69d88dSmrg (void)junk; 414af69d88dSmrg#else 415af69d88dSmrg size = vsnprintf(&junk, 1, fmt, args); 416af69d88dSmrg#endif 417af69d88dSmrg assert(size >= 0); 418af69d88dSmrg 419af69d88dSmrg va_end(args); 420af69d88dSmrg 421af69d88dSmrg return size; 422af69d88dSmrg} 423af69d88dSmrg 424af69d88dSmrgchar * 425af69d88dSmrgralloc_vasprintf(const void *ctx, const char *fmt, va_list args) 426af69d88dSmrg{ 427af69d88dSmrg size_t size = printf_length(fmt, args) + 1; 428af69d88dSmrg 429af69d88dSmrg char *ptr = ralloc_size(ctx, size); 430af69d88dSmrg if (ptr != NULL) 431af69d88dSmrg vsnprintf(ptr, size, fmt, args); 432af69d88dSmrg 433af69d88dSmrg return ptr; 434af69d88dSmrg} 435af69d88dSmrg 436af69d88dSmrgbool 437af69d88dSmrgralloc_asprintf_append(char **str, const char *fmt, ...) 438af69d88dSmrg{ 439af69d88dSmrg bool success; 440af69d88dSmrg va_list args; 441af69d88dSmrg va_start(args, fmt); 442af69d88dSmrg success = ralloc_vasprintf_append(str, fmt, args); 443af69d88dSmrg va_end(args); 444af69d88dSmrg return success; 445af69d88dSmrg} 446af69d88dSmrg 447af69d88dSmrgbool 448af69d88dSmrgralloc_vasprintf_append(char **str, const char *fmt, va_list args) 449af69d88dSmrg{ 450af69d88dSmrg size_t existing_length; 451af69d88dSmrg assert(str != NULL); 452af69d88dSmrg existing_length = *str ? strlen(*str) : 0; 453af69d88dSmrg return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args); 454af69d88dSmrg} 455af69d88dSmrg 456af69d88dSmrgbool 457af69d88dSmrgralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...) 458af69d88dSmrg{ 459af69d88dSmrg bool success; 460af69d88dSmrg va_list args; 461af69d88dSmrg va_start(args, fmt); 462af69d88dSmrg success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args); 463af69d88dSmrg va_end(args); 464af69d88dSmrg return success; 465af69d88dSmrg} 466af69d88dSmrg 467af69d88dSmrgbool 468af69d88dSmrgralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt, 469af69d88dSmrg va_list args) 470af69d88dSmrg{ 471af69d88dSmrg size_t new_length; 472af69d88dSmrg char *ptr; 473af69d88dSmrg 474af69d88dSmrg assert(str != NULL); 475af69d88dSmrg 476af69d88dSmrg if (unlikely(*str == NULL)) { 477af69d88dSmrg // Assuming a NULL context is probably bad, but it's expected behavior. 478af69d88dSmrg *str = ralloc_vasprintf(NULL, fmt, args); 479af69d88dSmrg return true; 480af69d88dSmrg } 481af69d88dSmrg 482af69d88dSmrg new_length = printf_length(fmt, args); 483af69d88dSmrg 484af69d88dSmrg ptr = resize(*str, *start + new_length + 1); 485af69d88dSmrg if (unlikely(ptr == NULL)) 486af69d88dSmrg return false; 487af69d88dSmrg 488af69d88dSmrg vsnprintf(ptr + *start, new_length + 1, fmt, args); 489af69d88dSmrg *str = ptr; 490af69d88dSmrg *start += new_length; 491af69d88dSmrg return true; 492af69d88dSmrg} 493