Git fork
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}