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 #pragma once
19 
20 #include <stdarg.h>
21 #include <stdint.h>
22 #include <string.h>
23 
24 // Unlike standard malloc, `size` may be 0, in which case this returns an empty
25 // allocation which can still be used as a parent for other allocations.
26 void *pl_alloc(void *parent, size_t size);
27 void *pl_zalloc(void *parent, size_t size);
28 void *pl_realloc(void *parent, void *ptr, size_t size);
29 
pl_calloc(void * parent,size_t count,size_t size)30 static inline void *pl_calloc(void *parent, size_t count, size_t size)
31 {
32     return pl_zalloc(parent, count * size);
33 }
34 
35 #define pl_tmp(parent) pl_alloc(parent, 0)
36 
37 // Variants of the above which resolve to sizeof(*ptr)
38 #define pl_alloc_ptr(parent, ptr) \
39     (__typeof__(ptr)) pl_alloc(parent, sizeof(*(ptr)))
40 #define pl_zalloc_ptr(parent, ptr) \
41     (__typeof__(ptr)) pl_zalloc(parent, sizeof(*(ptr)))
42 #define pl_calloc_ptr(parent, num, ptr) \
43     (__typeof__(ptr)) pl_calloc(parent, num, sizeof(*(ptr)))
44 
45 // Helper function to allocate a struct and immediately assign it
46 #define pl_alloc_struct(parent, type, ...) \
47     (type *) pl_memdup(parent, &(type) __VA_ARGS__, sizeof(type))
48 
49 // Free an allocation and its children (recursively)
50 void pl_free(void *ptr);
51 void pl_free_children(void *ptr);
52 
53 #define pl_free_ptr(ptr)    \
54     do {                    \
55         pl_free(*(ptr));    \
56         *(ptr) = NULL;      \
57     } while (0)
58 
59 // Get the current size of an allocation.
60 size_t pl_get_size(void *ptr);
61 
62 #define pl_grow(parent, ptr, size)                      \
63     do {                                                \
64         size_t _size = (size);                          \
65         if (_size > pl_get_size(*(ptr)))                \
66             *(ptr) = pl_realloc(parent, *(ptr), _size); \
67     } while (0)
68 
69 // Reparent an allocation onto a new parent
70 void *pl_steal(void *parent, void *ptr);
71 
72 // Wrapper functions around common string utilities
73 void *pl_memdup(void *parent, const void *ptr, size_t size);
74 char *pl_str0dup0(void *parent, const char *str);
75 char *pl_strndup0(void *parent, const char *str, size_t size);
76 
77 #define pl_memdup_ptr(parent, ptr) \
78     (__typeof__(ptr)) pl_memdup(parent, ptr, sizeof(*(ptr)))
79 
80 // Helper functions for allocating public/private pairs, done by allocating
81 // `priv` at the address of `pub` + sizeof(pub), rounded up to the maximum
82 // alignment requirements.
83 
84 #define pl_max_align offsetof(struct { char c; intmax_t x; }, x)
85 #define PL_ALIGN_MEM(size) \
86     (((size) + pl_max_align - 1) & ~(pl_max_align - 1))
87 
88 #define PL_PRIV(pub) \
89     (void *) ((uintptr_t) (pub) + PL_ALIGN_MEM(sizeof(*(pub))))
90 
91 #define pl_alloc_obj(parent, ptr, priv) \
92     (__typeof__(ptr)) pl_alloc(parent, PL_ALIGN_MEM(sizeof(*(ptr))) + sizeof(priv))
93 
94 #define pl_zalloc_obj(parent, ptr, priv) \
95     (__typeof__(ptr)) pl_zalloc(parent, PL_ALIGN_MEM(sizeof(*(ptr))) + sizeof(priv))
96 
97 // Refcounting helper
98 
99 struct pl_ref;
100 
101 // pl_ref_deref will free the ref and all of its children as soon as the
102 // internal refcount reaches 0
103 struct pl_ref *pl_ref_new(void *parent);
104 struct pl_ref *pl_ref_dup(struct pl_ref *ref);
105 void pl_ref_deref(struct pl_ref **ref);
106 
107 // Helper functions for dealing with arrays
108 
109 #define PL_ARRAY(type) struct { type *elem; int num; }
110 
111 #define PL_ARRAY_RESIZE(parent, arr, len)                                       \
112     do {                                                                        \
113         size_t _new_size = (len) * sizeof((arr).elem[0]);                       \
114         (arr).elem = pl_realloc((void *) parent, (arr).elem, _new_size);        \
115     } while (0)
116 
117 #define PL_ARRAY_GROW(parent, arr)                                              \
118     do {                                                                        \
119         size_t _avail = pl_get_size((arr).elem) / sizeof((arr).elem[0]);        \
120         if (_avail < 10) {                                                      \
121             PL_ARRAY_RESIZE(parent, arr, 10);                                   \
122         } else if ((arr).num == _avail) {                                       \
123             PL_ARRAY_RESIZE(parent, arr, (arr).num * 1.5);                      \
124         } else {                                                                \
125             assert((arr).elem);                                                 \
126         }                                                                       \
127     } while (0)
128 
129 #define PL_ARRAY_APPEND(parent, arr, ...)                                       \
130     do {                                                                        \
131         PL_ARRAY_GROW(parent, arr);                                             \
132         (arr).elem[(arr).num++] = __VA_ARGS__;                                  \
133     } while (0)
134 
135 #define PL_ARRAY_CONCAT(parent, to, from)                                       \
136     do {                                                                        \
137         if ((from).num) {                                                       \
138             PL_ARRAY_RESIZE(parent, to, (to).num + (from).num);                 \
139             memmove(&(to).elem[(to).num], (from).elem,                          \
140                     (from).num * sizeof((from).elem[0]));                       \
141             (to).num += (from).num;                                             \
142         }                                                                       \
143     } while (0)
144 
145 #define PL_ARRAY_REMOVE_RANGE(arr, idx, count)                                  \
146     do {                                                                        \
147         size_t _idx = (idx);                                                    \
148         size_t _count = (count);                                                \
149         assert(_idx + _count <= (arr).num);                                     \
150         memmove(&(arr).elem[_idx], &(arr).elem[_idx + _count],                  \
151                 ((arr).num - _idx - _count) * sizeof((arr).elem[0]));           \
152         (arr).num -= _count;                                                    \
153     } while (0)
154 
155 #define PL_ARRAY_REMOVE_AT(arr, idx) PL_ARRAY_REMOVE_RANGE(arr, idx, 1)
156 
157 #define PL_ARRAY_INSERT_AT(parent, arr, idx, ...)                               \
158     do {                                                                        \
159         size_t _idx = (idx);                                                    \
160         assert(_idx < (arr).num);                                               \
161         PL_ARRAY_GROW(parent, arr);                                             \
162         memmove(&(arr).elem[_idx + 1], &(arr).elem[_idx],                       \
163                 ((arr).num++ - _idx) * sizeof((arr).elem[0]));                  \
164         (arr).elem[_idx] = __VA_ARGS__;                                         \
165     } while (0)
166 
167 // Returns whether or not there was any element to pop
168 #define PL_ARRAY_POP(arr, out)                                                  \
169     ((arr).num > 0                                                              \
170         ? (*(out) = (arr).elem[--(arr).num], true)                              \
171         : false                                                                 \
172     )
173 
174 // Wrapper for dealing with non-PL_ARRAY arrays
175 #define PL_ARRAY_APPEND_RAW(parent, arr, idxvar, ...)                           \
176     do {                                                                        \
177         PL_ARRAY(__typeof__((arr)[0])) _arr = { (arr), (idxvar) };              \
178         PL_ARRAY_APPEND(parent, _arr, __VA_ARGS__);                             \
179         (arr) = _arr.elem;                                                      \
180         (idxvar) = _arr.num;                                                    \
181     } while (0)
182