Git fork
at reftables-rust 220 lines 5.3 kB view raw
1/* 2 * Memory Pool implementation logic. 3 */ 4 5#define DISABLE_SIGN_COMPARE_WARNINGS 6 7#include "git-compat-util.h" 8#include "mem-pool.h" 9#include "gettext.h" 10 11#define BLOCK_GROWTH_SIZE (1024 * 1024 - sizeof(struct mp_block)) 12 13/* 14 * The inner union is an approximation for C11's max_align_t, and the 15 * struct + offsetof computes _Alignof. This can all just be replaced 16 * with _Alignof(max_align_t) if/when C11 is part of the baseline. 17 * Note that _Alignof(X) need not be the same as sizeof(X); it's only 18 * required to be a (possibly trivial) factor. They are the same for 19 * most architectures, but m68k for example has only 2-byte alignment 20 * for its 4-byte and 8-byte types, so using sizeof would waste space. 21 * 22 * Add more types to the union if the current set is insufficient. 23 */ 24struct git_max_alignment { 25 char unalign; 26 union { 27 uintmax_t max_align_uintmax; 28 void *max_align_pointer; 29 } aligned; 30}; 31#define GIT_MAX_ALIGNMENT offsetof(struct git_max_alignment, aligned) 32 33/* 34 * Allocate a new mp_block and insert it after the block specified in 35 * `insert_after`. If `insert_after` is NULL, then insert block at the 36 * head of the linked list. 37 */ 38static struct mp_block *mem_pool_alloc_block(struct mem_pool *pool, 39 size_t block_alloc, 40 struct mp_block *insert_after) 41{ 42 struct mp_block *p; 43 44 pool->pool_alloc += sizeof(struct mp_block) + block_alloc; 45 p = xmalloc(st_add(sizeof(struct mp_block), block_alloc)); 46 47 p->next_free = (char *)p->space; 48 p->end = p->next_free + block_alloc; 49 50 if (insert_after) { 51 p->next_block = insert_after->next_block; 52 insert_after->next_block = p; 53 } else { 54 p->next_block = pool->mp_block; 55 pool->mp_block = p; 56 } 57 58 return p; 59} 60 61void mem_pool_init(struct mem_pool *pool, size_t initial_size) 62{ 63 memset(pool, 0, sizeof(*pool)); 64 pool->block_alloc = BLOCK_GROWTH_SIZE; 65 66 if (initial_size > 0) 67 mem_pool_alloc_block(pool, initial_size, NULL); 68} 69 70void mem_pool_discard(struct mem_pool *pool, int invalidate_memory) 71{ 72 struct mp_block *block, *block_to_free; 73 74 block = pool->mp_block; 75 while (block) 76 { 77 block_to_free = block; 78 block = block->next_block; 79 80 if (invalidate_memory) 81 memset(block_to_free->space, 0xDD, ((char *)block_to_free->end) - ((char *)block_to_free->space)); 82 83 free(block_to_free); 84 } 85 86 pool->mp_block = NULL; 87 pool->pool_alloc = 0; 88} 89 90void *mem_pool_alloc(struct mem_pool *pool, size_t len) 91{ 92 struct mp_block *p = NULL; 93 void *r; 94 95 len = DIV_ROUND_UP(len, GIT_MAX_ALIGNMENT) * GIT_MAX_ALIGNMENT; 96 97 if (pool->mp_block && 98 pool->mp_block->end - pool->mp_block->next_free >= len) 99 p = pool->mp_block; 100 101 if (!p) { 102 if (len >= (pool->block_alloc / 2)) 103 p = mem_pool_alloc_block(pool, len, pool->mp_block); 104 else 105 p = mem_pool_alloc_block(pool, pool->block_alloc, NULL); 106 } 107 108 r = p->next_free; 109 p->next_free += len; 110 return r; 111} 112 113static char *mem_pool_strvfmt(struct mem_pool *pool, const char *fmt, 114 va_list ap) 115{ 116 struct mp_block *block = pool->mp_block; 117 char *next_free = block ? block->next_free : NULL; 118 size_t available = block ? block->end - block->next_free : 0; 119 va_list cp; 120 int len, len2; 121 size_t size; 122 char *ret; 123 124 va_copy(cp, ap); 125 len = vsnprintf(next_free, available, fmt, cp); 126 va_end(cp); 127 if (len < 0) 128 die(_("unable to format message: %s"), fmt); 129 130 size = st_add(len, 1); /* 1 for NUL */ 131 ret = mem_pool_alloc(pool, size); 132 133 /* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */ 134 if (ret == next_free) 135 return ret; 136 137 len2 = vsnprintf(ret, size, fmt, ap); 138 if (len2 != len) 139 BUG("your vsnprintf is broken (returns inconsistent lengths)"); 140 return ret; 141} 142 143char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...) 144{ 145 va_list ap; 146 char *ret; 147 148 va_start(ap, fmt); 149 ret = mem_pool_strvfmt(pool, fmt, ap); 150 va_end(ap); 151 return ret; 152} 153 154void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size) 155{ 156 size_t len = st_mult(count, size); 157 void *r = mem_pool_alloc(pool, len); 158 memset(r, 0, len); 159 return r; 160} 161 162char *mem_pool_strdup(struct mem_pool *pool, const char *str) 163{ 164 size_t len = strlen(str) + 1; 165 char *ret = mem_pool_alloc(pool, len); 166 167 return memcpy(ret, str, len); 168} 169 170char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len) 171{ 172 char *p = memchr(str, '\0', len); 173 size_t actual_len = (p ? p - str : len); 174 char *ret = mem_pool_alloc(pool, actual_len+1); 175 176 ret[actual_len] = '\0'; 177 return memcpy(ret, str, actual_len); 178} 179 180int mem_pool_contains(struct mem_pool *pool, void *mem) 181{ 182 struct mp_block *p; 183 184 /* Check if memory is allocated in a block */ 185 for (p = pool->mp_block; p; p = p->next_block) 186 if ((mem >= ((void *)p->space)) && 187 (mem < ((void *)p->end))) 188 return 1; 189 190 return 0; 191} 192 193void mem_pool_combine(struct mem_pool *dst, struct mem_pool *src) 194{ 195 struct mp_block *p; 196 197 /* Append the blocks from src to dst */ 198 if (dst->mp_block && src->mp_block) { 199 /* 200 * src and dst have blocks, append 201 * blocks from src to dst. 202 */ 203 p = dst->mp_block; 204 while (p->next_block) 205 p = p->next_block; 206 207 p->next_block = src->mp_block; 208 } else if (src->mp_block) { 209 /* 210 * src has blocks, dst is empty. 211 */ 212 dst->mp_block = src->mp_block; 213 } else { 214 /* src is empty, nothing to do. */ 215 } 216 217 dst->pool_alloc += src->pool_alloc; 218 src->pool_alloc = 0; 219 src->mp_block = NULL; 220}