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