pool.c 5.27 KB
Newer Older
1 2 3 4 5 6 7
/*
 * Copyright (C) the libgit2 contributors. All rights reserved.
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */

8
#include "pool.h"
9

10
#include "posix.h"
11 12 13 14 15 16
#ifndef GIT_WIN32
#include <unistd.h>
#endif

struct git_pool_page {
	git_pool_page *next;
17 18
	size_t size;
	size_t avail;
19
	GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8);
20 21
};

22
static void *pool_alloc_page(git_pool *pool, size_t size);
23

24
#ifndef GIT_DEBUG_POOL
25

26 27 28 29 30 31 32 33 34
static size_t system_page_size = 0;

int git_pool_global_init(void)
{
	if (git__page_size(&system_page_size) < 0)
		system_page_size = 4096;
	/* allow space for malloc overhead */
	system_page_size -= (2 * sizeof(void *)) + sizeof(git_pool_page);
	return 0;
35 36
}

37
int git_pool_init(git_pool *pool, size_t item_size)
38
{
Edward Thomson committed
39 40
	GIT_ASSERT_ARG(pool);
	GIT_ASSERT_ARG(item_size >= 1);
41 42 43

	memset(pool, 0, sizeof(git_pool));
	pool->item_size = item_size;
44
	pool->page_size = system_page_size;
45 46

	return 0;
47 48 49 50 51 52
}

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

53
	for (scan = pool->pages; scan != NULL; scan = next) {
54 55 56 57
		next = scan->next;
		git__free(scan);
	}

58
	pool->pages = NULL;
59 60
}

61
static void *pool_alloc_page(git_pool *pool, size_t size)
62 63
{
	git_pool_page *page;
64
	const size_t new_page_size = (size <= pool->page_size) ? pool->page_size : size;
65
	size_t alloc_size;
66

67
	if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) ||
68
		!(page = git__malloc(alloc_size)))
69
		return NULL;
70

71
	page->size = new_page_size;
72
	page->avail = new_page_size - size;
73
	page->next = pool->pages;
74

75
	pool->pages = page;
76

77
	return page->data;
78 79
}

80
static void *pool_alloc(git_pool *pool, size_t size)
81
{
82 83
	git_pool_page *page = pool->pages;
	void *ptr = NULL;
84

85
	if (!page || page->avail < size)
86 87
		return pool_alloc_page(pool, size);

88 89
	ptr = &page->data[page->size - page->avail];
	page->avail -= size;
90

91
	return ptr;
92 93
}

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
uint32_t git_pool__open_pages(git_pool *pool)
{
	uint32_t ct = 0;
	git_pool_page *scan;
	for (scan = pool->pages; 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->pages; scan != NULL; scan = scan->next)
		if ((void *)scan->data <= ptr &&
			(void *)(((char *)scan->data) + scan->size) > ptr)
			return true;
	return false;
}

#else

114 115 116 117 118
int git_pool_global_init(void)
{
	return 0;
}

119 120 121 122 123 124 125 126 127 128 129 130 131
static int git_pool__ptr_cmp(const void * a, const void * b)
{
	if(a > b) {
		return 1;
	}
	if(a < b) {
		return -1;
	}
	else {
		return 0;
	}
}

132
int git_pool_init(git_pool *pool, size_t item_size)
133
{
Edward Thomson committed
134 135
	GIT_ASSERT_ARG(pool);
	GIT_ASSERT_ARG(item_size >= 1);
136 137 138 139 140

	memset(pool, 0, sizeof(git_pool));
	pool->item_size = item_size;
	pool->page_size = git_pool__system_page_size();
	git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp);
141 142

	return 0;
143 144 145 146 147 148 149
}

void git_pool_clear(git_pool *pool)
{
	git_vector_free_deep(&pool->allocations);
}

150
static void *pool_alloc(git_pool *pool, size_t size) {
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	void *ptr = NULL;
	if((ptr = git__malloc(size)) == NULL) {
		return NULL;
	}
	git_vector_insert_sorted(&pool->allocations, ptr, NULL);
	return ptr;
}

bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
{
	size_t pos;
	return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND;
}
#endif

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));
}

178
static size_t alloc_size(git_pool *pool, size_t count)
179
{
180
	const size_t align = sizeof(void *) - 1;
181 182

	if (pool->item_size > 1) {
183
		const size_t item_size = (pool->item_size + align) & ~align;
184 185 186 187 188 189
		return item_size * count;
	}

	return (count + align) & ~align;
}

190
void *git_pool_malloc(git_pool *pool, size_t items)
191
{
192
	return pool_alloc(pool, alloc_size(pool, items));
193 194
}

195
void *git_pool_mallocz(git_pool *pool, size_t items)
196
{
197
	const size_t size = alloc_size(pool, items);
198 199 200 201 202 203
	void *ptr = pool_alloc(pool, size);
	if (ptr)
		memset(ptr, 0x0, size);
	return ptr;
}

204 205
char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
{
206
	char *ptr = NULL;
207

Edward Thomson committed
208 209 210
	GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
	GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
	GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
211

212
	if (n == SIZE_MAX)
213 214
		return NULL;

215
	if ((ptr = git_pool_malloc(pool, (n + 1))) != NULL) {
216
		memcpy(ptr, str, n);
217
		ptr[n] = '\0';
218
	}
219

220 221 222 223 224
	return ptr;
}

char *git_pool_strdup(git_pool *pool, const char *str)
{
Edward Thomson committed
225 226 227 228
	GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
	GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
	GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);

229 230 231
	return git_pool_strndup(pool, str, strlen(str));
}

232 233
char *git_pool_strdup_safe(git_pool *pool, const char *str)
{
234
	return str ? git_pool_strdup(pool, str) : NULL;
235 236
}

237 238 239
char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
{
	void *ptr;
240
	size_t len_a, len_b, total;
241

Edward Thomson committed
242 243
	GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
	GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
244 245 246 247

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

248 249 250 251 252
	if (GIT_ADD_SIZET_OVERFLOW(&total, len_a, len_b) ||
		GIT_ADD_SIZET_OVERFLOW(&total, total, 1))
		return NULL;

	if ((ptr = git_pool_malloc(pool, total)) != NULL) {
253 254 255 256 257 258 259
		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';
	}
	return ptr;
260
}