1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
4  * This file is part of libgit2, distributed under the GNU GPL v2 with
5  * a Linking Exception. For full terms see the included COPYING file.
6  */
7 
8 #include "pool.h"
9 
10 #include "posix.h"
11 #ifndef GIT_WIN32
12 #include <unistd.h>
13 #endif
14 
15 struct git_pool_page {
16 	git_pool_page *next;
17 	size_t size;
18 	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 
pool_system_page_size(void)24 static size_t pool_system_page_size(void)
25 {
26 	static size_t size = 0;
27 
28 	if (!size) {
29 		size_t page_size;
30 		if (git__page_size(&page_size) < 0)
31 			page_size = 4096;
32 		/* allow space for malloc overhead */
33 		size = (page_size - (2 * sizeof(void *)) - sizeof(git_pool_page));
34 	}
35 
36 	return size;
37 }
38 
39 #ifndef GIT_DEBUG_POOL
git_pool_init(git_pool * pool,size_t item_size)40 int git_pool_init(git_pool *pool, size_t item_size)
41 {
42 	assert(pool);
43 	assert(item_size >= 1);
44 
45 	memset(pool, 0, sizeof(git_pool));
46 	pool->item_size = item_size;
47 	pool->page_size = pool_system_page_size();
48 
49 	return 0;
50 }
51 
git_pool_clear(git_pool * pool)52 void git_pool_clear(git_pool *pool)
53 {
54 	git_pool_page *scan, *next;
55 
56 	for (scan = pool->pages; scan != NULL; scan = next) {
57 		next = scan->next;
58 		git__free(scan);
59 	}
60 
61 	pool->pages = NULL;
62 }
63 
pool_alloc_page(git_pool * pool,size_t size)64 static void *pool_alloc_page(git_pool *pool, size_t size)
65 {
66 	git_pool_page *page;
67 	const size_t new_page_size = (size <= pool->page_size) ? pool->page_size : size;
68 	size_t alloc_size;
69 
70 	if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) ||
71 		!(page = git__malloc(alloc_size)))
72 		return NULL;
73 
74 	page->size = new_page_size;
75 	page->avail = new_page_size - size;
76 	page->next = pool->pages;
77 
78 	pool->pages = page;
79 
80 	return page->data;
81 }
82 
pool_alloc(git_pool * pool,size_t size)83 static void *pool_alloc(git_pool *pool, size_t size)
84 {
85 	git_pool_page *page = pool->pages;
86 	void *ptr = NULL;
87 
88 	if (!page || page->avail < size)
89 		return pool_alloc_page(pool, size);
90 
91 	ptr = &page->data[page->size - page->avail];
92 	page->avail -= size;
93 
94 	return ptr;
95 }
96 
git_pool__open_pages(git_pool * pool)97 uint32_t git_pool__open_pages(git_pool *pool)
98 {
99 	uint32_t ct = 0;
100 	git_pool_page *scan;
101 	for (scan = pool->pages; scan != NULL; scan = scan->next) ct++;
102 	return ct;
103 }
104 
git_pool__ptr_in_pool(git_pool * pool,void * ptr)105 bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
106 {
107 	git_pool_page *scan;
108 	for (scan = pool->pages; scan != NULL; scan = scan->next)
109 		if ((void *)scan->data <= ptr &&
110 			(void *)(((char *)scan->data) + scan->size) > ptr)
111 			return true;
112 	return false;
113 }
114 
115 #else
116 
git_pool__ptr_cmp(const void * a,const void * b)117 static int git_pool__ptr_cmp(const void * a, const void * b)
118 {
119 	if(a > b) {
120 		return 1;
121 	}
122 	if(a < b) {
123 		return -1;
124 	}
125 	else {
126 		return 0;
127 	}
128 }
129 
git_pool_init(git_pool * pool,size_t item_size)130 int git_pool_init(git_pool *pool, size_t item_size)
131 {
132 	assert(pool);
133 	assert(item_size >= 1);
134 
135 	memset(pool, 0, sizeof(git_pool));
136 	pool->item_size = item_size;
137 	pool->page_size = git_pool__system_page_size();
138 	git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp);
139 
140 	return 0;
141 }
142 
git_pool_clear(git_pool * pool)143 void git_pool_clear(git_pool *pool)
144 {
145 	git_vector_free_deep(&pool->allocations);
146 }
147 
pool_alloc(git_pool * pool,size_t size)148 static void *pool_alloc(git_pool *pool, size_t size) {
149 	void *ptr = NULL;
150 	if((ptr = git__malloc(size)) == NULL) {
151 		return NULL;
152 	}
153 	git_vector_insert_sorted(&pool->allocations, ptr, NULL);
154 	return ptr;
155 }
156 
git_pool__ptr_in_pool(git_pool * pool,void * ptr)157 bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
158 {
159 	size_t pos;
160 	return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND;
161 }
162 #endif
163 
git_pool_swap(git_pool * a,git_pool * b)164 void git_pool_swap(git_pool *a, git_pool *b)
165 {
166 	git_pool temp;
167 
168 	if (a == b)
169 		return;
170 
171 	memcpy(&temp, a, sizeof(temp));
172 	memcpy(a, b, sizeof(temp));
173 	memcpy(b, &temp, sizeof(temp));
174 }
175 
alloc_size(git_pool * pool,size_t count)176 static size_t alloc_size(git_pool *pool, size_t count)
177 {
178 	const size_t align = sizeof(void *) - 1;
179 
180 	if (pool->item_size > 1) {
181 		const size_t item_size = (pool->item_size + align) & ~align;
182 		return item_size * count;
183 	}
184 
185 	return (count + align) & ~align;
186 }
187 
git_pool_malloc(git_pool * pool,size_t items)188 void *git_pool_malloc(git_pool *pool, size_t items)
189 {
190 	return pool_alloc(pool, alloc_size(pool, items));
191 }
192 
git_pool_mallocz(git_pool * pool,size_t items)193 void *git_pool_mallocz(git_pool *pool, size_t items)
194 {
195 	const size_t size = alloc_size(pool, items);
196 	void *ptr = pool_alloc(pool, size);
197 	if (ptr)
198 		memset(ptr, 0x0, size);
199 	return ptr;
200 }
201 
git_pool_strndup(git_pool * pool,const char * str,size_t n)202 char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
203 {
204 	char *ptr = NULL;
205 
206 	assert(pool && str && pool->item_size == sizeof(char));
207 
208 	if (n == SIZE_MAX)
209 		return NULL;
210 
211 	if ((ptr = git_pool_malloc(pool, (n + 1))) != NULL) {
212 		memcpy(ptr, str, n);
213 		ptr[n] = '\0';
214 	}
215 
216 	return ptr;
217 }
218 
git_pool_strdup(git_pool * pool,const char * str)219 char *git_pool_strdup(git_pool *pool, const char *str)
220 {
221 	assert(pool && str && pool->item_size == sizeof(char));
222 	return git_pool_strndup(pool, str, strlen(str));
223 }
224 
git_pool_strdup_safe(git_pool * pool,const char * str)225 char *git_pool_strdup_safe(git_pool *pool, const char *str)
226 {
227 	return str ? git_pool_strdup(pool, str) : NULL;
228 }
229 
git_pool_strcat(git_pool * pool,const char * a,const char * b)230 char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
231 {
232 	void *ptr;
233 	size_t len_a, len_b, total;
234 
235 	assert(pool && pool->item_size == sizeof(char));
236 
237 	len_a = a ? strlen(a) : 0;
238 	len_b = b ? strlen(b) : 0;
239 
240 	if (GIT_ADD_SIZET_OVERFLOW(&total, len_a, len_b) ||
241 		GIT_ADD_SIZET_OVERFLOW(&total, total, 1))
242 		return NULL;
243 
244 	if ((ptr = git_pool_malloc(pool, total)) != NULL) {
245 		if (len_a)
246 			memcpy(ptr, a, len_a);
247 		if (len_b)
248 			memcpy(((char *)ptr) + len_a, b, len_b);
249 		*(((char *)ptr) + len_a + len_b) = '\0';
250 	}
251 	return ptr;
252 }
253