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