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