rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

mem_proxy_allocator.c (8476B)


      1 /* Copyright (C) 2013-2023, 2025 Vincent Forest (vaplv@free.fr)
      2  *
      3  * The RSys library is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published
      5  * by the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * The RSys library is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200112L /* snprintf support */
     17 
     18 #include "math.h"
     19 #include "mem_allocator.h"
     20 #include "mutex.h"
     21 
     22 #include <string.h>
     23 
     24 struct proxy_data {
     25   struct mem_allocator* allocator;
     26   struct mutex* mutex;
     27   struct mem_node* node_list;
     28 };
     29 
     30 struct mem_node {
     31   struct mem_node* next;
     32   struct mem_node* prev;
     33   size_t size;
     34   const char* filename;
     35   unsigned int fileline;
     36   char reserved[2];
     37 };
     38 
     39 #define PROXY_DEFAULT_ALIGNMENT 8
     40 
     41 /*******************************************************************************
     42  * Helper functions
     43  ******************************************************************************/
     44 static void*
     45 proxy_alloc_aligned
     46   (void* data,
     47    const size_t size,
     48    const size_t align,
     49    const char* filename,
     50    const unsigned int fileline)
     51 {
     52   struct proxy_data* proxy_data = NULL;
     53   char* mem = NULL;
     54   size_t node_header_size = 0;
     55   size_t node_size = 0;
     56   size_t align_adjusted = 0;
     57   struct mem_node* node = NULL;
     58 
     59   ASSERT(data);
     60   proxy_data = data;
     61 
     62   if((IS_POW2(align) == 0) || align > 32768)
     63     return NULL;
     64   align_adjusted = align < PROXY_DEFAULT_ALIGNMENT
     65     ? PROXY_DEFAULT_ALIGNMENT : align;
     66 
     67   node_header_size = ALIGN_SIZE(sizeof(struct mem_node), align_adjusted);
     68   node_size = node_header_size + size;
     69   node = MEM_ALLOC_ALIGNED(proxy_data->allocator, node_size, align_adjusted);
     70   if(!node)
     71     return NULL;
     72 
     73   mem = (char*)((uintptr_t)node + (uintptr_t)node_header_size);
     74   mem[-1] = (char)(align_adjusted & 0xFF);
     75   mem[-2] = (char)((align_adjusted >> 8) & 0xFF);
     76   node->prev = NULL;
     77   node->filename = filename;
     78   node->fileline = fileline;
     79   node->size = size;
     80 
     81   mutex_lock(proxy_data->mutex);
     82   node->next = proxy_data->node_list;
     83   if(proxy_data->node_list)
     84     proxy_data->node_list->prev = node;
     85   proxy_data->node_list = node;
     86   mutex_unlock(proxy_data->mutex);
     87   return mem;
     88 }
     89 
     90 static void*
     91 proxy_alloc
     92   (void* data,
     93    const size_t size,
     94    const char* filename,
     95    const unsigned int fileline)
     96 {
     97   return proxy_alloc_aligned
     98     (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline);
     99 }
    100 
    101 static void*
    102 proxy_calloc
    103   (void* data,
    104    const size_t nbelmts,
    105    const size_t size,
    106    const char* filename,
    107    const unsigned int fileline)
    108 {
    109   size_t allocation_size = nbelmts * size;
    110   void* mem = proxy_alloc_aligned
    111     (data, allocation_size, PROXY_DEFAULT_ALIGNMENT, filename, fileline);
    112   if(mem)
    113     mem = memset(mem, 0, allocation_size);
    114   return mem;
    115 }
    116 
    117 static void
    118 proxy_free(void* data, void* mem)
    119 {
    120   if(mem) {
    121     struct proxy_data* proxy_data = NULL;
    122     struct mem_node* node = NULL;
    123     uintptr_t alignment = 0;
    124 
    125     ASSERT(data);
    126     proxy_data = data;
    127 
    128     alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8));
    129     node =
    130       (void*)((uintptr_t)mem - ALIGN_SIZE(sizeof(struct mem_node), alignment));
    131 
    132     mutex_lock(proxy_data->mutex);
    133     if(node->prev) {
    134       node->prev->next = node->next;
    135     }
    136     if(node->next) {
    137       node->next->prev = node->prev;
    138     }
    139     if(node->prev == NULL) {
    140       proxy_data->node_list = node->next;
    141     }
    142     mutex_unlock(proxy_data->mutex);
    143     MEM_RM(proxy_data->allocator, node);
    144   }
    145 }
    146 
    147 static void*
    148 proxy_realloc
    149   (void* data,
    150    void* mem,
    151    const size_t size,
    152    const char* filename,
    153    const unsigned int fileline)
    154 {
    155   if(size == 0) {
    156     proxy_free(data, mem);
    157     return NULL;
    158   } else if(mem == NULL) {
    159     return proxy_alloc_aligned
    160       (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline);
    161   } else {
    162     struct mem_node* node = NULL;
    163     uintptr_t node_header_size = 0;
    164     uintptr_t alignment = 0;
    165 
    166     alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8));
    167     node_header_size = ALIGN_SIZE(sizeof(struct mem_node), alignment);
    168     node = (void*)((uintptr_t)mem - node_header_size);
    169 
    170     if(node->size == size) {
    171       return mem;
    172     } else {
    173       void* dst = proxy_alloc_aligned
    174         (data, size, alignment, filename, fileline);
    175       if(!dst) {
    176         proxy_free(data, mem);
    177         return NULL;
    178       } else {
    179         dst = memcpy(dst, mem, size < node->size ? size : node->size);
    180         proxy_free(data, mem);
    181         return dst;
    182       }
    183     }
    184   }
    185 }
    186 
    187 static size_t
    188 proxy_mem_size(void* data, void* mem)
    189 {
    190   const uintptr_t alignment = (uintptr_t)
    191     (((char*)mem)[-1] | (((char*)mem)[-2] << 8));
    192   struct mem_node* node = (struct mem_node*)
    193     ((uintptr_t)mem - ALIGN_SIZE(sizeof(struct mem_node), alignment));
    194   struct proxy_data* proxy_data = (struct proxy_data*)data;
    195   ASSERT(data);
    196   return MEM_SIZE(proxy_data->allocator, node);
    197 }
    198 
    199 static size_t
    200 proxy_allocated_size(const void* data)
    201 {
    202   const struct proxy_data* proxy_data = NULL;
    203   struct mem_node* node = NULL;
    204   size_t allocated_size = 0;
    205 
    206   ASSERT(data);
    207   proxy_data = data;
    208   mutex_lock(proxy_data->mutex);
    209   for(node = proxy_data->node_list; node != NULL; node = node->next) {
    210     allocated_size += MEM_SIZE(proxy_data->allocator, node);
    211   }
    212   mutex_unlock(proxy_data->mutex);
    213   return allocated_size;
    214 }
    215 
    216 static size_t
    217 proxy_dump
    218   (const void* data,
    219    char* dump,
    220    const size_t max_dump_len)
    221 {
    222   const struct proxy_data* proxy_data = NULL;
    223   struct mem_node* node = NULL;
    224   size_t dump_len = 0;
    225   size_t avaible_dump_space = max_dump_len ? max_dump_len - 1 /*NULL char*/ : 0;
    226 
    227   ASSERT(data && (!max_dump_len || dump));
    228   proxy_data = data;
    229 
    230   mutex_lock(proxy_data->mutex);
    231   for(node = proxy_data->node_list; node != NULL; node = node->next) {
    232     if(dump) {
    233       const int len = snprintf
    234         (dump,
    235          avaible_dump_space,
    236          "%lu bytes allocated at %s:%u%s",
    237          (long unsigned)MEM_SIZE(proxy_data->allocator, node),
    238          node->filename ? node->filename : "none",
    239          node->fileline,
    240          node->next ? ".\n" : ".");
    241       ASSERT(len >= 0);
    242       dump_len += (size_t)len;
    243 
    244       if((size_t)len < avaible_dump_space) {
    245         dump += len;
    246         avaible_dump_space -= (size_t)len;
    247       } else if(dump) {
    248         dump[avaible_dump_space] = '\0';
    249         avaible_dump_space = 0;
    250         dump = NULL;
    251       }
    252     }
    253   }
    254   mutex_unlock(proxy_data->mutex);
    255   return dump_len;
    256 }
    257 
    258 /*******************************************************************************
    259  * Exported functions
    260  ******************************************************************************/
    261 res_T
    262 mem_init_proxy_allocator
    263   (struct mem_allocator* proxy_allocator,
    264    struct mem_allocator* allocator)
    265 {
    266   struct proxy_data* proxy_data = NULL;
    267   res_T res = RES_OK;
    268 
    269   if(!allocator || !proxy_allocator) {
    270     res = RES_BAD_ARG;
    271     goto error;
    272   }
    273   memset(proxy_allocator, 0, sizeof(struct mem_allocator));
    274   proxy_data = MEM_CALLOC(allocator, 1, sizeof(struct proxy_data));
    275   if(!proxy_data) {
    276     res = RES_MEM_ERR;
    277     goto error;
    278   }
    279   proxy_allocator->data = (void*)proxy_data;
    280 
    281   proxy_data->allocator = allocator;
    282   proxy_data->mutex = mutex_create();
    283   if(!proxy_data->mutex) {
    284     res = RES_MEM_ERR;
    285     goto error;
    286   }
    287   proxy_allocator->alloc = proxy_alloc;
    288   proxy_allocator->calloc = proxy_calloc;
    289   proxy_allocator->realloc = proxy_realloc;
    290   proxy_allocator->mem_size = proxy_mem_size;
    291   proxy_allocator->alloc_aligned = proxy_alloc_aligned;
    292   proxy_allocator->rm = proxy_free;
    293   proxy_allocator->allocated_size = proxy_allocated_size;
    294   proxy_allocator->dump = proxy_dump;
    295 
    296 exit:
    297   return res;
    298 error:
    299   if(proxy_allocator)
    300     mem_shutdown_proxy_allocator(proxy_allocator);
    301   goto exit;
    302 }
    303 
    304 void
    305 mem_shutdown_proxy_allocator(struct mem_allocator* proxy)
    306 {
    307   struct proxy_data* proxy_data = NULL;
    308 
    309   ASSERT(proxy);
    310   proxy_data = proxy->data;
    311   if(proxy_data) {
    312     ASSERT(proxy_data->node_list == NULL);
    313     if(proxy_data->mutex) mutex_destroy(proxy_data->mutex);
    314     MEM_RM(proxy_data->allocator, proxy_data);
    315   }
    316   memset(proxy, 0, sizeof(struct mem_allocator));
    317 }