pool.c 7.04 KB
Newer Older
1
#include "pool.h"
2
#include "posix.h"
3 4 5 6 7 8 9 10
#ifndef GIT_WIN32
#include <unistd.h>
#endif

struct git_pool_page {
	git_pool_page *next;
	uint32_t size;
	uint32_t avail;
11
	GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8);
12 13
};

14 15
struct pool_freelist {
	struct pool_freelist *next;
16 17
};

18 19 20
#define GIT_POOL_MIN_USABLE	4
#define GIT_POOL_MIN_PAGESZ	2 * sizeof(void*)

21
static void *pool_alloc_page(git_pool *pool, uint32_t size);
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
static void pool_insert_page(git_pool *pool, git_pool_page *page);

int git_pool_init(
	git_pool *pool, uint32_t item_size, uint32_t items_per_page)
{
	assert(pool);

	if (!item_size)
		item_size = 1;
	/* round up item_size for decent object alignment */
	if (item_size > 4)
		item_size = (item_size + 7) & ~7;
	else if (item_size == 3)
		item_size = 4;

37 38
	if (!items_per_page)
		items_per_page = git_pool__suggest_items_per_page(item_size);
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
	if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ)
		items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size;

	memset(pool, 0, sizeof(git_pool));
	pool->item_size = item_size;
	pool->page_size = item_size * items_per_page;

	return 0;
}

void git_pool_clear(git_pool *pool)
{
	git_pool_page *scan, *next;

	for (scan = pool->open; scan != NULL; scan = next) {
		next = scan->next;
		git__free(scan);
	}
	pool->open = NULL;

	for (scan = pool->full; scan != NULL; scan = next) {
		next = scan->next;
		git__free(scan);
	}
	pool->full = NULL;

	pool->free_list = NULL;

67 68
	pool->items = 0;

69 70 71 72 73
	pool->has_string_alloc     = 0;
	pool->has_multi_item_alloc = 0;
	pool->has_large_page_alloc = 0;
}

74 75 76 77 78 79 80 81 82 83 84 85
void git_pool_swap(git_pool *a, git_pool *b)
{
	git_pool temp;

	if (a == b)
		return;

	memcpy(&temp, a, sizeof(temp));
	memcpy(a, b, sizeof(temp));
	memcpy(b, &temp, sizeof(temp));
}

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
static void pool_insert_page(git_pool *pool, git_pool_page *page)
{
	git_pool_page *scan;

	/* If there are no open pages or this page has the most open space,
	 * insert it at the beginning of the list.  This is the common case.
	 */
	if (pool->open == NULL || pool->open->avail < page->avail) {
		page->next = pool->open;
		pool->open = page;
		return;
	}

	/* Otherwise insert into sorted position. */
	for (scan = pool->open;
		 scan->next && scan->next->avail > page->avail;
		 scan = scan->next);
	page->next = scan->next;
	scan->next = page;
}

107
static void *pool_alloc_page(git_pool *pool, uint32_t size)
108 109 110 111 112 113 114 115 116 117 118 119 120
{
	git_pool_page *page;
	uint32_t alloc_size;

	if (size <= pool->page_size)
		alloc_size = pool->page_size;
	else {
		alloc_size = size;
		pool->has_large_page_alloc = 1;
	}

	page = git__calloc(1, alloc_size + sizeof(git_pool_page));
	if (!page)
121
		return NULL;
122 123 124 125 126 127 128 129 130 131 132

	page->size  = alloc_size;
	page->avail = alloc_size - size;

	if (page->avail > 0)
		pool_insert_page(pool, page);
	else {
		page->next = pool->full;
		pool->full = page;
	}

133
	pool->items++;
134

135
	return page->data;
136 137 138 139 140 141 142 143 144 145 146
}

GIT_INLINE(void) pool_remove_page(
	git_pool *pool, git_pool_page *page, git_pool_page *prev)
{
	if (prev == NULL)
		pool->open = page->next;
	else
		prev->next = page->next;
}

147
void *git_pool_malloc(git_pool *pool, uint32_t items)
148 149
{
	git_pool_page *scan = pool->open, *prev;
150
	uint32_t size = ((items * pool->item_size) + 7) & ~7;
151
	void *ptr = NULL;
152 153 154 155 156

	pool->has_string_alloc = 0;
	if (items > 1)
		pool->has_multi_item_alloc = 1;
	else if (pool->free_list != NULL) {
157
		ptr = pool->free_list;
158
		pool->free_list = ((struct pool_freelist *)pool->free_list)->next;
159
		return ptr;
160 161 162 163
	}

	/* just add a block if there is no open one to accomodate this */
	if (size >= pool->page_size || !scan || scan->avail < size)
164 165 166
		return pool_alloc_page(pool, size);

	pool->items++;
167 168 169 170 171 172 173

	/* find smallest block in free list with space */
	for (scan = pool->open, prev = NULL;
		 scan->next && scan->next->avail >= size;
		 prev = scan, scan = scan->next);

	/* allocate space from the block */
174
	ptr = &scan->data[scan->size - scan->avail];
175 176 177 178 179 180 181 182 183 184 185 186 187 188
	scan->avail -= size;

	/* move to full list if there is almost no space left */
	if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) {
		pool_remove_page(pool, scan, prev);
		scan->next = pool->full;
		pool->full = scan;
	}
	/* reorder list if block is now smaller than the one after it */
	else if (scan->next != NULL && scan->next->avail > scan->avail) {
		pool_remove_page(pool, scan, prev);
		pool_insert_page(pool, scan);
	}

189
	return ptr;
190 191 192 193
}

char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
{
194
	char *ptr = NULL;
195 196 197

	assert(pool && str && pool->item_size == sizeof(char));

198 199 200
	if ((uint32_t)(n + 1) < n)
		return NULL;

Russell Belfer committed
201
	if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) {
202
		memcpy(ptr, str, n);
203
		ptr[n] = '\0';
204
	}
205

206 207 208 209 210 211 212 213 214
	pool->has_string_alloc = 1;

	return ptr;
}

char *git_pool_strdup(git_pool *pool, const char *str)
{
	assert(pool && str && pool->item_size == sizeof(char));

215 216 217
	return git_pool_strndup(pool, str, strlen(str));
}

218 219
char *git_pool_strdup_safe(git_pool *pool, const char *str)
{
220
	return str ? git_pool_strdup(pool, str) : NULL;
221 222
}

223 224 225 226 227 228 229 230 231 232
char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
{
	void *ptr;
	size_t len_a, len_b;

	assert(pool && a && b && pool->item_size == sizeof(char));

	len_a = a ? strlen(a) : 0;
	len_b = b ? strlen(b) : 0;

Russell Belfer committed
233
	if ((ptr = git_pool_malloc(pool, (uint32_t)(len_a + len_b + 1))) != NULL) {
234 235 236 237 238 239 240 241 242
		if (len_a)
			memcpy(ptr, a, len_a);
		if (len_b)
			memcpy(((char *)ptr) + len_a, b, len_b);
		*(((char *)ptr) + len_a + len_b) = '\0';
	}
	pool->has_string_alloc = 1;

	return ptr;
243 244 245 246
}

void git_pool_free(git_pool *pool, void *ptr)
{
247
	struct pool_freelist *item = ptr;
248

249
	assert(pool && pool->item_size >= sizeof(void*));
250

251 252 253
	if (item) {
		item->next = pool->free_list;
		pool->free_list = item;
254 255 256 257 258
	}
}

void git_pool_free_array(git_pool *pool, size_t count, void **ptrs)
{
259
	struct pool_freelist **items = (struct pool_freelist **)ptrs;
260 261 262 263 264 265 266 267
	size_t i;

	assert(pool && ptrs && pool->item_size >= sizeof(void*));

	if (!count)
		return;

	for (i = count - 1; i > 0; --i)
268
		items[i]->next = items[i - 1];
269

270 271
	items[i]->next  = pool->free_list;
	pool->free_list = items[count - 1];
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
}

uint32_t git_pool__open_pages(git_pool *pool)
{
	uint32_t ct = 0;
	git_pool_page *scan;
	for (scan = pool->open; scan != NULL; scan = scan->next) ct++;
	return ct;
}

uint32_t git_pool__full_pages(git_pool *pool)
{
	uint32_t ct = 0;
	git_pool_page *scan;
	for (scan = pool->full; scan != NULL; scan = scan->next) ct++;
	return ct;
}

bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
{
	git_pool_page *scan;
	for (scan = pool->open; scan != NULL; scan = scan->next)
Russell Belfer committed
294 295
		if ((void *)scan->data <= ptr &&
			(void *)(((char *)scan->data) + scan->size) > ptr)
296 297
			return true;
	for (scan = pool->full; scan != NULL; scan = scan->next)
Russell Belfer committed
298 299
		if ((void *)scan->data <= ptr &&
			(void *)(((char *)scan->data) + scan->size) > ptr)
300 301 302 303 304 305 306 307 308
			return true;
	return false;
}

uint32_t git_pool__system_page_size(void)
{
	static uint32_t size = 0;

	if (!size) {
309 310 311 312
		size_t page_size;
		if (git__page_size(&page_size) < 0)
			page_size = 4096;
		size = page_size - 2 * sizeof(void *); /* allow space for malloc overhead */
313 314 315 316 317
	}

	return size;
}

318 319 320 321 322 323 324
uint32_t git_pool__suggest_items_per_page(uint32_t item_size)
{
	uint32_t page_bytes =
		git_pool__system_page_size() - sizeof(git_pool_page);
	return page_bytes / item_size;
}