2 * Memory Pool implementation logic.
5 #include "git-compat-util.h"
9 #define BLOCK_GROWTH_SIZE (1024 * 1024 - sizeof(struct mp_block))
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.
20 * Add more types to the union if the current set is insufficient.
22 struct git_max_alignment
{
25 uintmax_t max_align_uintmax
;
26 void *max_align_pointer
;
29 #define GIT_MAX_ALIGNMENT offsetof(struct git_max_alignment, aligned)
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.
36 static struct mp_block
*mem_pool_alloc_block(struct mem_pool
*pool
,
38 struct mp_block
*insert_after
)
42 pool
->pool_alloc
+= sizeof(struct mp_block
) + block_alloc
;
43 p
= xmalloc(st_add(sizeof(struct mp_block
), block_alloc
));
45 p
->next_free
= (char *)p
->space
;
46 p
->end
= p
->next_free
+ block_alloc
;
49 p
->next_block
= insert_after
->next_block
;
50 insert_after
->next_block
= p
;
52 p
->next_block
= pool
->mp_block
;
59 void mem_pool_init(struct mem_pool
*pool
, size_t initial_size
)
61 memset(pool
, 0, sizeof(*pool
));
62 pool
->block_alloc
= BLOCK_GROWTH_SIZE
;
65 mem_pool_alloc_block(pool
, initial_size
, NULL
);
68 void mem_pool_discard(struct mem_pool
*pool
, int invalidate_memory
)
70 struct mp_block
*block
, *block_to_free
;
72 block
= pool
->mp_block
;
75 block_to_free
= block
;
76 block
= block
->next_block
;
78 if (invalidate_memory
)
79 memset(block_to_free
->space
, 0xDD, ((char *)block_to_free
->end
) - ((char *)block_to_free
->space
));
84 pool
->mp_block
= NULL
;
88 void *mem_pool_alloc(struct mem_pool
*pool
, size_t len
)
90 struct mp_block
*p
= NULL
;
93 len
= DIV_ROUND_UP(len
, GIT_MAX_ALIGNMENT
) * GIT_MAX_ALIGNMENT
;
96 pool
->mp_block
->end
- pool
->mp_block
->next_free
>= len
)
100 if (len
>= (pool
->block_alloc
/ 2))
101 p
= mem_pool_alloc_block(pool
, len
, pool
->mp_block
);
103 p
= mem_pool_alloc_block(pool
, pool
->block_alloc
, NULL
);
111 static char *mem_pool_strvfmt(struct mem_pool
*pool
, const char *fmt
,
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;
123 len
= vsnprintf(next_free
, available
, fmt
, cp
);
126 die(_("unable to format message: %s"), fmt
);
128 size
= st_add(len
, 1); /* 1 for NUL */
129 ret
= mem_pool_alloc(pool
, size
);
131 /* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */
132 if (ret
== next_free
)
135 len2
= vsnprintf(ret
, size
, fmt
, ap
);
137 BUG("your vsnprintf is broken (returns inconsistent lengths)");
141 char *mem_pool_strfmt(struct mem_pool
*pool
, const char *fmt
, ...)
147 ret
= mem_pool_strvfmt(pool
, fmt
, ap
);
152 void *mem_pool_calloc(struct mem_pool
*pool
, size_t count
, size_t size
)
154 size_t len
= st_mult(count
, size
);
155 void *r
= mem_pool_alloc(pool
, len
);
160 char *mem_pool_strdup(struct mem_pool
*pool
, const char *str
)
162 size_t len
= strlen(str
) + 1;
163 char *ret
= mem_pool_alloc(pool
, len
);
165 return memcpy(ret
, str
, len
);
168 char *mem_pool_strndup(struct mem_pool
*pool
, const char *str
, size_t len
)
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);
174 ret
[actual_len
] = '\0';
175 return memcpy(ret
, str
, actual_len
);
178 int mem_pool_contains(struct mem_pool
*pool
, void *mem
)
182 /* Check if memory is allocated in a block */
183 for (p
= pool
->mp_block
; p
; p
= p
->next_block
)
184 if ((mem
>= ((void *)p
->space
)) &&
185 (mem
< ((void *)p
->end
)))
191 void mem_pool_combine(struct mem_pool
*dst
, struct mem_pool
*src
)
195 /* Append the blocks from src to dst */
196 if (dst
->mp_block
&& src
->mp_block
) {
198 * src and dst have blocks, append
199 * blocks from src to dst.
202 while (p
->next_block
)
205 p
->next_block
= src
->mp_block
;
206 } else if (src
->mp_block
) {
208 * src has blocks, dst is empty.
210 dst
->mp_block
= src
->mp_block
;
212 /* src is empty, nothing to do. */
215 dst
->pool_alloc
+= src
->pool_alloc
;
217 src
->mp_block
= NULL
;