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 
git_pathspec_prefix(const git_strarray * pathspec)22 static void *pool_alloc_page(git_pool *pool, size_t size);
23 
24 #ifndef GIT_DEBUG_POOL
25 
26 static size_t system_page_size = 0;
27 
28 int git_pool_global_init(void)
29 {
30 	if (git__page_size(&system_page_size) < 0)
31 		system_page_size = 4096;
32 	/* allow space for malloc overhead */
33 	system_page_size -= (2 * sizeof(void *)) + sizeof(git_pool_page);
34 	return 0;
35 }
36 
37 int git_pool_init(git_pool *pool, size_t item_size)
38 {
39 	GIT_ASSERT_ARG(pool);
40 	GIT_ASSERT_ARG(item_size >= 1);
41 
42 	memset(pool, 0, sizeof(git_pool));
43 	pool->item_size = item_size;
44 	pool->page_size = system_page_size;
45 
46 	return 0;
47 }
48 
49 void git_pool_clear(git_pool *pool)
git_pathspec_is_empty(const git_strarray * pathspec)50 {
51 	git_pool_page *scan, *next;
52 
53 	for (scan = pool->pages; scan != NULL; scan = next) {
54 		next = scan->next;
55 		git__free(scan);
56 	}
57 
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)) ||
git_pathspec__vinit(git_vector * vspec,const git_strarray * strspec,git_pool * strpool)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 	git_pool_page *page = pool->pages;
83 	void *ptr = NULL;
84 
85 	if (!page || page->avail < size)
86 		return pool_alloc_page(pool, size);
87 
88 	ptr = &page->data[page->size - page->avail];
89 	page->avail -= size;
90 
91 	return ptr;
92 }
93 
94 uint32_t git_pool__open_pages(git_pool *pool)
95 {
96 	uint32_t ct = 0;
97 	git_pool_page *scan;
98 	for (scan = pool->pages; scan != NULL; scan = scan->next) ct++;
99 	return ct;
100 }
101 
102 bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
103 {
104 	git_pool_page *scan;
105 	for (scan = pool->pages; scan != NULL; scan = scan->next)
106 		if ((void *)scan->data <= ptr &&
git_pathspec__vfree(git_vector * vspec)107 			(void *)(((char *)scan->data) + scan->size) > ptr)
108 			return true;
109 	return false;
110 }
111 
112 #else
113 
114 int git_pool_global_init(void)
115 {
116 	return 0;
117 }
pathspec_match_context_init(struct pathspec_match_context * ctxt,bool disable_fnmatch,bool casefold)118 
119 static int git_pool__ptr_cmp(const void * a, const void * b)
120 {
121 	if(a > b) {
122 		return 1;
123 	}
124 	if(a < b) {
125 		return -1;
126 	}
127 	else {
128 		return 0;
129 	}
130 }
131 
132 int git_pool_init(git_pool *pool, size_t item_size)
133 {
134 	GIT_ASSERT_ARG(pool);
135 	GIT_ASSERT_ARG(item_size >= 1);
136 
137 	memset(pool, 0, sizeof(git_pool));
138 	pool->item_size = item_size;
pathspec_match_one(const git_attr_fnmatch * match,struct pathspec_match_context * ctxt,const char * path)139 	pool->page_size = git_pool__system_page_size();
140 	git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp);
141 
142 	return 0;
143 }
144 
145 void git_pool_clear(git_pool *pool)
146 {
147 	git_vector_free_deep(&pool->allocations);
148 }
149 
150 static void *pool_alloc(git_pool *pool, size_t size) {
151 	void *ptr = NULL;
152 	if((ptr = git__malloc(size)) == NULL) {
153 		return NULL;
154 	}
155 	git_vector_insert_sorted(&pool->allocations, ptr, NULL);
156 	return ptr;
157 }
158 
159 bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
160 {
161 	size_t pos;
162 	return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND;
163 }
164 #endif
165 
166 void git_pool_swap(git_pool *a, git_pool *b)
167 {
168 	git_pool temp;
169 
170 	if (a == b)
171 		return;
172 
173 	memcpy(&temp, a, sizeof(temp));
git_pathspec__match_at(size_t * matched_at,const git_vector * vspec,struct pathspec_match_context * ctxt,const char * path0,const char * path1)174 	memcpy(a, b, sizeof(temp));
175 	memcpy(b, &temp, sizeof(temp));
176 }
177 
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 		return item_size * count;
185 	}
186 
187 	return (count + align) & ~align;
188 }
189 
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 {
git_pathspec__match(const git_vector * vspec,const char * path,bool disable_fnmatch,bool casefold,const char ** matched_pathspec,size_t * matched_at)197 	const size_t size = alloc_size(pool, items);
198 	void *ptr = pool_alloc(pool, size);
199 	if (ptr)
200 		memset(ptr, 0x0, size);
201 	return ptr;
202 }
203 
204 char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
205 {
206 	char *ptr = NULL;
207 
208 	GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
209 	GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
210 	GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
211 
212 	if (n == SIZE_MAX)
213 		return NULL;
214 
215 	if ((ptr = git_pool_malloc(pool, (n + 1))) != NULL) {
216 		memcpy(ptr, str, n);
217 		ptr[n] = '\0';
218 	}
219 
220 	return ptr;
221 }
222 
223 char *git_pool_strdup(git_pool *pool, const char *str)
224 {
225 	GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
226 	GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
227 	GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
228 
229 	return git_pool_strndup(pool, str, strlen(str));
230 }
231 
232 char *git_pool_strdup_safe(git_pool *pool, const char *str)
233 {
git_pathspec__init(git_pathspec * ps,const git_strarray * paths)234 	return str ? git_pool_strdup(pool, str) : NULL;
235 }
236 
237 char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
238 {
239 	void *ptr;
240 	size_t len_a, len_b, total;
241 
242 	GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
243 	GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
244 
245 	len_a = a ? strlen(a) : 0;
246 	len_b = b ? strlen(b) : 0;
247 
248 	if (GIT_ADD_SIZET_OVERFLOW(&total, len_a, len_b) ||
git_pathspec__clear(git_pathspec * ps)249 		GIT_ADD_SIZET_OVERFLOW(&total, total, 1))
250 		return NULL;
251 
252 	if ((ptr = git_pool_malloc(pool, total)) != NULL) {
253 		if (len_a)
254 			memcpy(ptr, a, len_a);
255 		if (len_b)
256 			memcpy(((char *)ptr) + len_a, b, len_b);
git_pathspec_new(git_pathspec ** out,const git_strarray * pathspec)257 		*(((char *)ptr) + len_a + len_b) = '\0';
258 	}
259 	return ptr;
260 }
261