Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright © 2010 Intel Corporation
  3.  *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a
  5.  * copy of this software and associated documentation files (the "Software"),
  6.  * to deal in the Software without restriction, including without limitation
  7.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8.  * and/or sell copies of the Software, and to permit persons to whom the
  9.  * Software is furnished to do so, subject to the following conditions:
  10.  *
  11.  * The above copyright notice and this permission notice (including the next
  12.  * paragraph) shall be included in all copies or substantial portions of the
  13.  * Software.
  14.  *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20.  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21.  * DEALINGS IN THE SOFTWARE.
  22.  */
  23.  
  24. #include <assert.h>
  25. #include <stdlib.h>
  26. #include <stdarg.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <stdint.h>
  30.  
  31. /* Android defines SIZE_MAX in limits.h, instead of the standard stdint.h */
  32. #ifdef ANDROID
  33. #include <limits.h>
  34. #endif
  35.  
  36. /* Some versions of MinGW are missing _vscprintf's declaration, although they
  37.  * still provide the symbol in the import library. */
  38. #ifdef __MINGW32__
  39. _CRTIMP int _vscprintf(const char *format, va_list argptr);
  40. #endif
  41.  
  42. #include "ralloc.h"
  43.  
  44. #ifndef va_copy
  45. #ifdef __va_copy
  46. #define va_copy(dest, src) __va_copy((dest), (src))
  47. #else
  48. #define va_copy(dest, src) (dest) = (src)
  49. #endif
  50. #endif
  51.  
  52. #define CANARY 0x5A1106
  53.  
  54. struct ralloc_header
  55. {
  56.    /* A canary value used to determine whether a pointer is ralloc'd. */
  57.    unsigned canary;
  58.  
  59.    struct ralloc_header *parent;
  60.  
  61.    /* The first child (head of a linked list) */
  62.    struct ralloc_header *child;
  63.  
  64.    /* Linked list of siblings */
  65.    struct ralloc_header *prev;
  66.    struct ralloc_header *next;
  67.  
  68.    void (*destructor)(void *);
  69. };
  70.  
  71. typedef struct ralloc_header ralloc_header;
  72.  
  73. static void unlink_block(ralloc_header *info);
  74. static void unsafe_free(ralloc_header *info);
  75.  
  76. static ralloc_header *
  77. get_header(const void *ptr)
  78. {
  79.    ralloc_header *info = (ralloc_header *) (((char *) ptr) -
  80.                                             sizeof(ralloc_header));
  81.    assert(info->canary == CANARY);
  82.    return info;
  83. }
  84.  
  85. #define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header))
  86.  
  87. static void
  88. add_child(ralloc_header *parent, ralloc_header *info)
  89. {
  90.    if (parent != NULL) {
  91.       info->parent = parent;
  92.       info->next = parent->child;
  93.       parent->child = info;
  94.  
  95.       if (info->next != NULL)
  96.          info->next->prev = info;
  97.    }
  98. }
  99.  
  100. void *
  101. ralloc_context(const void *ctx)
  102. {
  103.    return ralloc_size(ctx, 0);
  104. }
  105.  
  106. void *
  107. ralloc_size(const void *ctx, size_t size)
  108. {
  109.    void *block = calloc(1, size + sizeof(ralloc_header));
  110.    ralloc_header *info;
  111.    ralloc_header *parent;
  112.  
  113.    if (unlikely(block == NULL))
  114.       return NULL;
  115.    info = (ralloc_header *) block;
  116.    parent = ctx != NULL ? get_header(ctx) : NULL;
  117.  
  118.    add_child(parent, info);
  119.  
  120.    info->canary = CANARY;
  121.  
  122.    return PTR_FROM_HEADER(info);
  123. }
  124.  
  125. void *
  126. rzalloc_size(const void *ctx, size_t size)
  127. {
  128.    void *ptr = ralloc_size(ctx, size);
  129.    if (likely(ptr != NULL))
  130.       memset(ptr, 0, size);
  131.    return ptr;
  132. }
  133.  
  134. /* helper function - assumes ptr != NULL */
  135. static void *
  136. resize(void *ptr, size_t size)
  137. {
  138.    ralloc_header *child, *old, *info;
  139.  
  140.    old = get_header(ptr);
  141.    info = realloc(old, size + sizeof(ralloc_header));
  142.  
  143.    if (info == NULL)
  144.       return NULL;
  145.  
  146.    /* Update parent and sibling's links to the reallocated node. */
  147.    if (info != old && info->parent != NULL) {
  148.       if (info->parent->child == old)
  149.          info->parent->child = info;
  150.  
  151.       if (info->prev != NULL)
  152.          info->prev->next = info;
  153.  
  154.       if (info->next != NULL)
  155.          info->next->prev = info;
  156.    }
  157.  
  158.    /* Update child->parent links for all children */
  159.    for (child = info->child; child != NULL; child = child->next)
  160.       child->parent = info;
  161.  
  162.    return PTR_FROM_HEADER(info);
  163. }
  164.  
  165. void *
  166. reralloc_size(const void *ctx, void *ptr, size_t size)
  167. {
  168.    if (unlikely(ptr == NULL))
  169.       return ralloc_size(ctx, size);
  170.  
  171.    assert(ralloc_parent(ptr) == ctx);
  172.    return resize(ptr, size);
  173. }
  174.  
  175. void *
  176. ralloc_array_size(const void *ctx, size_t size, unsigned count)
  177. {
  178.    if (count > SIZE_MAX/size)
  179.       return NULL;
  180.  
  181.    return ralloc_size(ctx, size * count);
  182. }
  183.  
  184. void *
  185. rzalloc_array_size(const void *ctx, size_t size, unsigned count)
  186. {
  187.    if (count > SIZE_MAX/size)
  188.       return NULL;
  189.  
  190.    return rzalloc_size(ctx, size * count);
  191. }
  192.  
  193. void *
  194. reralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count)
  195. {
  196.    if (count > SIZE_MAX/size)
  197.       return NULL;
  198.  
  199.    return reralloc_size(ctx, ptr, size * count);
  200. }
  201.  
  202. void
  203. ralloc_free(void *ptr)
  204. {
  205.    ralloc_header *info;
  206.  
  207.    if (ptr == NULL)
  208.       return;
  209.  
  210.    info = get_header(ptr);
  211.    unlink_block(info);
  212.    unsafe_free(info);
  213. }
  214.  
  215. static void
  216. unlink_block(ralloc_header *info)
  217. {
  218.    /* Unlink from parent & siblings */
  219.    if (info->parent != NULL) {
  220.       if (info->parent->child == info)
  221.          info->parent->child = info->next;
  222.  
  223.       if (info->prev != NULL)
  224.          info->prev->next = info->next;
  225.  
  226.       if (info->next != NULL)
  227.          info->next->prev = info->prev;
  228.    }
  229.    info->parent = NULL;
  230.    info->prev = NULL;
  231.    info->next = NULL;
  232. }
  233.  
  234. static void
  235. unsafe_free(ralloc_header *info)
  236. {
  237.    /* Recursively free any children...don't waste time unlinking them. */
  238.    ralloc_header *temp;
  239.    while (info->child != NULL) {
  240.       temp = info->child;
  241.       info->child = temp->next;
  242.       unsafe_free(temp);
  243.    }
  244.  
  245.    /* Free the block itself.  Call the destructor first, if any. */
  246.    if (info->destructor != NULL)
  247.       info->destructor(PTR_FROM_HEADER(info));
  248.  
  249.    free(info);
  250. }
  251.  
  252. void
  253. ralloc_steal(const void *new_ctx, void *ptr)
  254. {
  255.    ralloc_header *info, *parent;
  256.  
  257.    if (unlikely(ptr == NULL))
  258.       return;
  259.  
  260.    info = get_header(ptr);
  261.    parent = get_header(new_ctx);
  262.  
  263.    unlink_block(info);
  264.  
  265.    add_child(parent, info);
  266. }
  267.  
  268. void *
  269. ralloc_parent(const void *ptr)
  270. {
  271.    ralloc_header *info;
  272.  
  273.    if (unlikely(ptr == NULL))
  274.       return NULL;
  275.  
  276.    info = get_header(ptr);
  277.    return info->parent ? PTR_FROM_HEADER(info->parent) : NULL;
  278. }
  279.  
  280. static void *autofree_context = NULL;
  281.  
  282. static void
  283. autofree(void)
  284. {
  285.    ralloc_free(autofree_context);
  286. }
  287.  
  288. void *
  289. ralloc_autofree_context(void)
  290. {
  291.    if (unlikely(autofree_context == NULL)) {
  292.       autofree_context = ralloc_context(NULL);
  293.       atexit(autofree);
  294.    }
  295.    return autofree_context;
  296. }
  297.  
  298. void
  299. ralloc_set_destructor(const void *ptr, void(*destructor)(void *))
  300. {
  301.    ralloc_header *info = get_header(ptr);
  302.    info->destructor = destructor;
  303. }
  304.  
  305. char *
  306. ralloc_strdup(const void *ctx, const char *str)
  307. {
  308.    size_t n;
  309.    char *ptr;
  310.  
  311.    if (unlikely(str == NULL))
  312.       return NULL;
  313.  
  314.    n = strlen(str);
  315.    ptr = ralloc_array(ctx, char, n + 1);
  316.    memcpy(ptr, str, n);
  317.    ptr[n] = '\0';
  318.    return ptr;
  319. }
  320.  
  321. char *
  322. ralloc_strndup(const void *ctx, const char *str, size_t max)
  323. {
  324.    size_t n;
  325.    char *ptr;
  326.  
  327.    if (unlikely(str == NULL))
  328.       return NULL;
  329.  
  330.    n = strlen(str);
  331.    if (n > max)
  332.       n = max;
  333.  
  334.    ptr = ralloc_array(ctx, char, n + 1);
  335.    memcpy(ptr, str, n);
  336.    ptr[n] = '\0';
  337.    return ptr;
  338. }
  339.  
  340. /* helper routine for strcat/strncat - n is the exact amount to copy */
  341. static bool
  342. cat(char **dest, const char *str, size_t n)
  343. {
  344.    char *both;
  345.    size_t existing_length;
  346.    assert(dest != NULL && *dest != NULL);
  347.  
  348.    existing_length = strlen(*dest);
  349.    both = resize(*dest, existing_length + n + 1);
  350.    if (unlikely(both == NULL))
  351.       return false;
  352.  
  353.    memcpy(both + existing_length, str, n);
  354.    both[existing_length + n] = '\0';
  355.  
  356.    *dest = both;
  357.    return true;
  358. }
  359.  
  360.  
  361. bool
  362. ralloc_strcat(char **dest, const char *str)
  363. {
  364.    return cat(dest, str, strlen(str));
  365. }
  366.  
  367. bool
  368. ralloc_strncat(char **dest, const char *str, size_t n)
  369. {
  370.    /* Clamp n to the string length */
  371.    size_t str_length = strlen(str);
  372.    if (str_length < n)
  373.       n = str_length;
  374.  
  375.    return cat(dest, str, n);
  376. }
  377.  
  378. char *
  379. ralloc_asprintf(const void *ctx, const char *fmt, ...)
  380. {
  381.    char *ptr;
  382.    va_list args;
  383.    va_start(args, fmt);
  384.    ptr = ralloc_vasprintf(ctx, fmt, args);
  385.    va_end(args);
  386.    return ptr;
  387. }
  388.  
  389. /* Return the length of the string that would be generated by a printf-style
  390.  * format and argument list, not including the \0 byte.
  391.  */
  392. static size_t
  393. printf_length(const char *fmt, va_list untouched_args)
  394. {
  395.    int size;
  396.    char junk;
  397.  
  398.    /* Make a copy of the va_list so the original caller can still use it */
  399.    va_list args;
  400.    va_copy(args, untouched_args);
  401.  
  402. #ifdef _WIN32
  403.    /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
  404.     * if the number of characters to write is greater than count.
  405.     */
  406.    size = _vscprintf(fmt, args);
  407.    (void)junk;
  408. #else
  409.    size = vsnprintf(&junk, 1, fmt, args);
  410. #endif
  411.    assert(size >= 0);
  412.  
  413.    va_end(args);
  414.  
  415.    return size;
  416. }
  417.  
  418. char *
  419. ralloc_vasprintf(const void *ctx, const char *fmt, va_list args)
  420. {
  421.    size_t size = printf_length(fmt, args) + 1;
  422.  
  423.    char *ptr = ralloc_size(ctx, size);
  424.    if (ptr != NULL)
  425.       vsnprintf(ptr, size, fmt, args);
  426.  
  427.    return ptr;
  428. }
  429.  
  430. bool
  431. ralloc_asprintf_append(char **str, const char *fmt, ...)
  432. {
  433.    bool success;
  434.    va_list args;
  435.    va_start(args, fmt);
  436.    success = ralloc_vasprintf_append(str, fmt, args);
  437.    va_end(args);
  438.    return success;
  439. }
  440.  
  441. bool
  442. ralloc_vasprintf_append(char **str, const char *fmt, va_list args)
  443. {
  444.    size_t existing_length;
  445.    assert(str != NULL);
  446.    existing_length = *str ? strlen(*str) : 0;
  447.    return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args);
  448. }
  449.  
  450. bool
  451. ralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...)
  452. {
  453.    bool success;
  454.    va_list args;
  455.    va_start(args, fmt);
  456.    success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args);
  457.    va_end(args);
  458.    return success;
  459. }
  460.  
  461. bool
  462. ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt,
  463.                               va_list args)
  464. {
  465.    size_t new_length;
  466.    char *ptr;
  467.  
  468.    assert(str != NULL);
  469.  
  470.    if (unlikely(*str == NULL)) {
  471.       // Assuming a NULL context is probably bad, but it's expected behavior.
  472.       *str = ralloc_vasprintf(NULL, fmt, args);
  473.       return true;
  474.    }
  475.  
  476.    new_length = printf_length(fmt, args);
  477.  
  478.    ptr = resize(*str, *start + new_length + 1);
  479.    if (unlikely(ptr == NULL))
  480.       return false;
  481.  
  482.    vsnprintf(ptr + *start, new_length + 1, fmt, args);
  483.    *str = ptr;
  484.    *start += new_length;
  485.    return true;
  486. }
  487.