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 }