Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "common.h"
19 : :
20 : : struct header {
21 : : #ifndef NDEBUG
22 : : #define MAGIC 0x20210119LU
23 : : uint32_t magic;
24 : : #endif
25 : : size_t size;
26 : : struct header *parent;
27 : : struct ext *ext;
28 : :
29 : : // Pointer to actual data, for alignment purposes
30 : : max_align_t data[];
31 : : };
32 : :
33 : : // Lazily allocated, to save space for leaf allocations and allocations which
34 : : // don't need fancy requirements
35 : : struct ext {
36 : : size_t num_children;
37 : : size_t children_size; // total allocated size of `children`
38 : : struct header *children[];
39 : : };
40 : :
41 : : #define PTR_OFFSET offsetof(struct header, data)
42 : : #define MAX_ALLOC (SIZE_MAX - PTR_OFFSET)
43 : : #define MINIMUM_CHILDREN 4
44 : :
45 : 1049272 : static inline struct header *get_header(void *ptr)
46 : : {
47 [ + + ]: 1049272 : if (!ptr)
48 : : return NULL;
49 : :
50 : 1032877 : struct header *hdr = (struct header *) ((uintptr_t) ptr - PTR_OFFSET);
51 : : #ifndef NDEBUG
52 [ - + ]: 1032877 : assert(hdr->magic == MAGIC);
53 : : #endif
54 : :
55 : : return hdr;
56 : : }
57 : :
58 : 0 : static inline void *oom(void)
59 : : {
60 : 0 : fprintf(stderr, "out of memory\n");
61 : 0 : abort();
62 : : }
63 : :
64 : 33157 : static inline struct ext *alloc_ext(struct header *h)
65 : : {
66 [ + - ]: 33157 : if (!h)
67 : : return NULL;
68 : :
69 [ + + ]: 33157 : if (!h->ext) {
70 : 5347 : h->ext = malloc(sizeof(struct ext) + MINIMUM_CHILDREN * sizeof(void *));
71 [ - + ]: 5347 : if (!h->ext)
72 : 0 : oom();
73 : 5347 : h->ext->num_children = 0;
74 : 5347 : h->ext->children_size = MINIMUM_CHILDREN;
75 : : }
76 : :
77 : 33157 : return h->ext;
78 : : }
79 : :
80 : 42226 : static inline void attach_child(struct header *parent, struct header *child)
81 : : {
82 : 42226 : child->parent = parent;
83 [ + + ]: 42226 : if (!parent)
84 : : return;
85 : :
86 : :
87 : 33157 : struct ext *ext = alloc_ext(parent);
88 [ + + ]: 33157 : if (ext->num_children == ext->children_size) {
89 : 1795 : size_t new_size = ext->children_size * 2;
90 : 1795 : ext = realloc(ext, sizeof(struct ext) + new_size * sizeof(void *));
91 [ - + ]: 1795 : if (!ext)
92 : 0 : oom();
93 : 1795 : ext->children_size = new_size;
94 : 1795 : parent->ext = ext;
95 : : }
96 : :
97 : 33157 : ext->children[ext->num_children++] = child;
98 : : }
99 : :
100 : 42226 : static inline void unlink_child(struct header *parent, struct header *child)
101 : : {
102 : 42226 : child->parent = NULL;
103 [ + + ]: 42226 : if (!parent)
104 : : return;
105 : :
106 : 5481 : struct ext *ext = parent->ext;
107 [ + - ]: 136739 : for (size_t i = 0; i < ext->num_children; i++) {
108 [ + + ]: 136739 : if (ext->children[i] == child) {
109 : 5481 : memmove(&ext->children[i], &ext->children[i + 1],
110 : 5481 : (--ext->num_children - i) * sizeof(ext->children[0]));
111 : 5481 : return;
112 : : }
113 : : }
114 : :
115 : 0 : assert(!"unlinking orphaned child?");
116 : : }
117 : :
118 : 24556 : void *pl_alloc(void *parent, size_t size)
119 : : {
120 [ - + ]: 24556 : if (size >= MAX_ALLOC)
121 : 0 : return oom();
122 : :
123 : 24556 : struct header *h = malloc(PTR_OFFSET + size);
124 [ - + ]: 24556 : if (!h)
125 : 0 : return oom();
126 : :
127 : : #ifndef NDEBUG
128 : 24556 : h->magic = MAGIC;
129 : : #endif
130 : 24556 : h->size = size;
131 : 24556 : h->ext = NULL;
132 : :
133 : 24556 : attach_child(get_header(parent), h);
134 : 24556 : return h->data;
135 : : }
136 : :
137 : 14517 : void *pl_zalloc(void *parent, size_t size)
138 : : {
139 [ - + ]: 14517 : if (size >= MAX_ALLOC)
140 : 0 : return oom();
141 : :
142 : 14517 : struct header *h = calloc(1, PTR_OFFSET + size);
143 [ - + ]: 14517 : if (!h)
144 : 0 : return oom();
145 : :
146 : : #ifndef NDEBUG
147 : 14517 : h->magic = MAGIC;
148 : : #endif
149 : 14517 : h->size = size;
150 : :
151 : 14517 : attach_child(get_header(parent), h);
152 : 14517 : return h->data;
153 : : }
154 : :
155 : 6583 : void *pl_realloc(void *parent, void *ptr, size_t size)
156 : : {
157 [ - + ]: 6583 : if (size >= MAX_ALLOC)
158 : 0 : return oom();
159 [ + + ]: 6583 : if (!ptr)
160 : 4692 : return pl_alloc(parent, size);
161 : :
162 : 1891 : struct header *h = get_header(ptr);
163 [ - + ]: 1891 : assert(get_header(parent) == h->parent);
164 [ + - ]: 1891 : if (h->size == size)
165 : : return ptr;
166 : :
167 : : struct header *old_h = h;
168 : 1891 : h = realloc(h, PTR_OFFSET + size);
169 [ - + ]: 1891 : if (!h)
170 : 0 : return oom();
171 : :
172 : 1891 : h->size = size;
173 : :
174 [ + + ]: 1891 : if (h != old_h) {
175 [ + - ]: 1409 : if (h->parent) {
176 : 1409 : struct ext *ext = h->parent->ext;
177 [ + - ]: 3048 : for (size_t i = 0; i < ext->num_children; i++) {
178 [ + + ]: 3048 : if (ext->children[i] == old_h) {
179 : 1409 : ext->children[i] = h;
180 : 1409 : goto done_reparenting;
181 : : }
182 : : }
183 : 0 : assert(!"reallocating orphaned child?");
184 : : }
185 : 0 : done_reparenting:
186 : :
187 [ - + ]: 1409 : if (h->ext) {
188 [ # # ]: 0 : for (size_t i = 0; i < h->ext->num_children; i++)
189 : 0 : h->ext->children[i]->parent = h;
190 : : }
191 : : }
192 : :
193 : 1891 : return h->data;
194 : : }
195 : :
196 : 40349 : void pl_free(void *ptr)
197 : : {
198 : 40349 : struct header *h = get_header(ptr);
199 [ + + ]: 40349 : if (!h)
200 : : return;
201 : :
202 : 39073 : pl_free_children(ptr);
203 : 39073 : unlink_child(h->parent, h);
204 : :
205 : 39073 : free(h->ext);
206 : 39073 : free(h);
207 : : }
208 : :
209 : 47400 : void pl_free_children(void *ptr)
210 : : {
211 : 47400 : struct header *h = get_header(ptr);
212 [ + - + + ]: 47400 : if (!h || !h->ext)
213 : : return;
214 : :
215 : : #ifndef NDEBUG
216 : : // this detects recursive hierarchies
217 : 11062 : h->magic = 0;
218 : : #endif
219 : :
220 [ + + ]: 38738 : for (size_t i = 0; i < h->ext->num_children; i++) {
221 : 27676 : h->ext->children[i]->parent = NULL; // prevent recursive access
222 : 27676 : pl_free(h->ext->children[i]->data);
223 : : }
224 : 11062 : h->ext->num_children = 0;
225 : :
226 : : #ifndef NDEBUG
227 : 11062 : h->magic = MAGIC;
228 : : #endif
229 : : }
230 : :
231 : 910439 : size_t pl_get_size(const void *ptr)
232 : : {
233 : 910439 : const struct header *h = get_header((void *) ptr);
234 [ + + ]: 910439 : return h ? h->size : 0;
235 : : }
236 : :
237 : 5074 : void *pl_steal(void *parent, void *ptr)
238 : : {
239 : 5074 : struct header *h = get_header(ptr);
240 [ + + ]: 5074 : if (!h)
241 : : return NULL;
242 : :
243 : 3155 : struct header *new_par = get_header(parent);
244 [ + + ]: 3155 : if (new_par != h->parent) {
245 : 3153 : unlink_child(h->parent, h);
246 : 3153 : attach_child(new_par, h);
247 : : }
248 : :
249 : 3155 : return h->data;
250 : : }
251 : :
252 : 6343 : void *pl_memdup(void *parent, const void *ptr, size_t size)
253 : : {
254 [ + + ]: 6343 : if (!size)
255 : : return NULL;
256 : :
257 : 5624 : void *new = pl_alloc(parent, size);
258 [ - + ]: 5624 : if (!new)
259 : 0 : return oom();
260 : :
261 [ - + ]: 5624 : assert(ptr);
262 : : memcpy(new, ptr, size);
263 : 5624 : return new;
264 : : }
265 : :
266 : 3407 : char *pl_str0dup0(void *parent, const char *str)
267 : : {
268 [ + + ]: 3407 : if (!str)
269 : : return NULL;
270 : :
271 : 3319 : return pl_memdup(parent, str, strlen(str) + 1);
272 : : }
273 : :
274 : 55 : char *pl_strndup0(void *parent, const char *str, size_t size)
275 : : {
276 [ + - ]: 55 : if (!str)
277 : : return NULL;
278 : :
279 : 55 : size_t str_size = strnlen(str, size);
280 : 55 : char *new = pl_alloc(parent, str_size + 1);
281 [ - + ]: 55 : if (!new)
282 : 0 : return oom();
283 : : memcpy(new, str, str_size);
284 : 55 : new[str_size] = '\0';
285 : 55 : return new;
286 : : }
287 : :
288 : 2604 : char *pl_asprintf(void *parent, const char *fmt, ...)
289 : : {
290 : : char *str;
291 : : va_list ap;
292 : 2604 : va_start(ap, fmt);
293 : 2604 : str = pl_vasprintf(parent, fmt, ap);
294 : 2604 : va_end(ap);
295 : 2604 : return str;
296 : : }
297 : :
298 : 3723 : char *pl_vasprintf(void *parent, const char *fmt, va_list ap)
299 : : {
300 : : // First, we need to determine the size that will be required for
301 : : // printing the entire string. Do this by making a copy of the va_list
302 : : // and printing it to a null buffer.
303 : : va_list copy;
304 [ + - ]: 3723 : va_copy(copy, ap);
305 : : int size = vsnprintf(NULL, 0, fmt, copy);
306 : 3723 : va_end(copy);
307 [ + - ]: 3723 : if (size < 0)
308 : : return NULL;
309 : :
310 : 3723 : char *str = pl_alloc(parent, size + 1);
311 : : vsnprintf(str, size + 1, fmt, ap);
312 : 3723 : return str;
313 : : }
|