/src/libgit2/src/util/pool.c
Line | Count | Source (jump to first uncovered line) |
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 | | |
24 | | #ifndef GIT_DEBUG_POOL |
25 | | |
26 | | static size_t system_page_size = 0; |
27 | | |
28 | | int git_pool_global_init(void) |
29 | 2 | { |
30 | 2 | if (git__page_size(&system_page_size) < 0) |
31 | 0 | system_page_size = 4096; |
32 | | /* allow space for malloc overhead */ |
33 | 2 | system_page_size -= (2 * sizeof(void *)) + sizeof(git_pool_page); |
34 | 2 | return 0; |
35 | 2 | } |
36 | | |
37 | | int git_pool_init(git_pool *pool, size_t item_size) |
38 | 0 | { |
39 | 0 | GIT_ASSERT_ARG(pool); |
40 | 0 | GIT_ASSERT_ARG(item_size >= 1); |
41 | | |
42 | 0 | memset(pool, 0, sizeof(git_pool)); |
43 | 0 | pool->item_size = item_size; |
44 | 0 | pool->page_size = system_page_size; |
45 | |
|
46 | 0 | return 0; |
47 | 0 | } |
48 | | |
49 | | void git_pool_clear(git_pool *pool) |
50 | 0 | { |
51 | 0 | git_pool_page *scan, *next; |
52 | |
|
53 | 0 | for (scan = pool->pages; scan != NULL; scan = next) { |
54 | 0 | next = scan->next; |
55 | 0 | git__free(scan); |
56 | 0 | } |
57 | |
|
58 | 0 | pool->pages = NULL; |
59 | 0 | } |
60 | | |
61 | | static void *pool_alloc_page(git_pool *pool, size_t size) |
62 | 0 | { |
63 | 0 | git_pool_page *page; |
64 | 0 | const size_t new_page_size = (size <= pool->page_size) ? pool->page_size : size; |
65 | 0 | size_t alloc_size; |
66 | |
|
67 | 0 | if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) || |
68 | 0 | !(page = git__malloc(alloc_size))) |
69 | 0 | return NULL; |
70 | | |
71 | 0 | page->size = new_page_size; |
72 | 0 | page->avail = new_page_size - size; |
73 | 0 | page->next = pool->pages; |
74 | |
|
75 | 0 | pool->pages = page; |
76 | |
|
77 | 0 | return page->data; |
78 | 0 | } |
79 | | |
80 | | static void *pool_alloc(git_pool *pool, size_t size) |
81 | 0 | { |
82 | 0 | git_pool_page *page = pool->pages; |
83 | 0 | void *ptr = NULL; |
84 | |
|
85 | 0 | if (!page || page->avail < size) |
86 | 0 | return pool_alloc_page(pool, size); |
87 | | |
88 | 0 | ptr = &page->data[page->size - page->avail]; |
89 | 0 | page->avail -= size; |
90 | |
|
91 | 0 | return ptr; |
92 | 0 | } |
93 | | |
94 | | uint32_t git_pool__open_pages(git_pool *pool) |
95 | 0 | { |
96 | 0 | uint32_t ct = 0; |
97 | 0 | git_pool_page *scan; |
98 | 0 | for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; |
99 | 0 | return ct; |
100 | 0 | } |
101 | | |
102 | | bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) |
103 | 0 | { |
104 | 0 | git_pool_page *scan; |
105 | 0 | for (scan = pool->pages; scan != NULL; scan = scan->next) |
106 | 0 | if ((void *)scan->data <= ptr && |
107 | 0 | (void *)(((char *)scan->data) + scan->size) > ptr) |
108 | 0 | return true; |
109 | 0 | return false; |
110 | 0 | } |
111 | | |
112 | | #else |
113 | | |
114 | | int git_pool_global_init(void) |
115 | | { |
116 | | return 0; |
117 | | } |
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; |
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_dispose_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 | 0 | { |
168 | 0 | git_pool temp; |
169 | |
|
170 | 0 | if (a == b) |
171 | 0 | return; |
172 | | |
173 | 0 | memcpy(&temp, a, sizeof(temp)); |
174 | 0 | memcpy(a, b, sizeof(temp)); |
175 | 0 | memcpy(b, &temp, sizeof(temp)); |
176 | 0 | } |
177 | | |
178 | | static size_t alloc_size(git_pool *pool, size_t count) |
179 | 0 | { |
180 | 0 | const size_t align = sizeof(void *) - 1; |
181 | |
|
182 | 0 | if (pool->item_size > 1) { |
183 | 0 | const size_t item_size = (pool->item_size + align) & ~align; |
184 | 0 | return item_size * count; |
185 | 0 | } |
186 | | |
187 | 0 | return (count + align) & ~align; |
188 | 0 | } |
189 | | |
190 | | void *git_pool_malloc(git_pool *pool, size_t items) |
191 | 0 | { |
192 | 0 | return pool_alloc(pool, alloc_size(pool, items)); |
193 | 0 | } |
194 | | |
195 | | void *git_pool_mallocz(git_pool *pool, size_t items) |
196 | 0 | { |
197 | 0 | const size_t size = alloc_size(pool, items); |
198 | 0 | void *ptr = pool_alloc(pool, size); |
199 | 0 | if (ptr) |
200 | 0 | memset(ptr, 0x0, size); |
201 | 0 | return ptr; |
202 | 0 | } |
203 | | |
204 | | char *git_pool_strndup(git_pool *pool, const char *str, size_t n) |
205 | 0 | { |
206 | 0 | char *ptr = NULL; |
207 | |
|
208 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL); |
209 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(str, NULL); |
210 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL); |
211 | | |
212 | 0 | if (n == SIZE_MAX) |
213 | 0 | return NULL; |
214 | | |
215 | 0 | if ((ptr = git_pool_malloc(pool, (n + 1))) != NULL) { |
216 | 0 | memcpy(ptr, str, n); |
217 | 0 | ptr[n] = '\0'; |
218 | 0 | } |
219 | |
|
220 | 0 | return ptr; |
221 | 0 | } |
222 | | |
223 | | char *git_pool_strdup(git_pool *pool, const char *str) |
224 | 0 | { |
225 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL); |
226 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(str, NULL); |
227 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL); |
228 | | |
229 | 0 | return git_pool_strndup(pool, str, strlen(str)); |
230 | 0 | } |
231 | | |
232 | | char *git_pool_strdup_safe(git_pool *pool, const char *str) |
233 | 0 | { |
234 | 0 | return str ? git_pool_strdup(pool, str) : NULL; |
235 | 0 | } |
236 | | |
237 | | char *git_pool_strcat(git_pool *pool, const char *a, const char *b) |
238 | 0 | { |
239 | 0 | void *ptr; |
240 | 0 | size_t len_a, len_b, total; |
241 | |
|
242 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL); |
243 | 0 | GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL); |
244 | | |
245 | 0 | len_a = a ? strlen(a) : 0; |
246 | 0 | len_b = b ? strlen(b) : 0; |
247 | |
|
248 | 0 | if (GIT_ADD_SIZET_OVERFLOW(&total, len_a, len_b) || |
249 | 0 | GIT_ADD_SIZET_OVERFLOW(&total, total, 1)) |
250 | 0 | return NULL; |
251 | | |
252 | 0 | if ((ptr = git_pool_malloc(pool, total)) != NULL) { |
253 | 0 | if (len_a) |
254 | 0 | memcpy(ptr, a, len_a); |
255 | 0 | if (len_b) |
256 | 0 | memcpy(((char *)ptr) + len_a, b, len_b); |
257 | 0 | *(((char *)ptr) + len_a + len_b) = '\0'; |
258 | 0 | } |
259 | 0 | return ptr; |
260 | 0 | } |