summaryrefslogtreecommitdiffstats
path: root/src/pl_alloc.h
blob: 78df08ae2a2360cc7ea254b0cb4813f8c098f221 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 * This file is part of libplacebo.
 *
 * libplacebo is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * libplacebo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdalign.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

// Unlike standard malloc, `size` may be 0, in which case this returns an empty
// allocation which can still be used as a parent for other allocations.
void *pl_alloc(void *parent, size_t size);
void *pl_zalloc(void *parent, size_t size);
void *pl_realloc(void *parent, void *ptr, size_t size);

static inline void *pl_calloc(void *parent, size_t count, size_t size)
{
    return pl_zalloc(parent, count * size);
}

#define pl_tmp(parent) pl_alloc(parent, 0)

// Variants of the above which resolve to sizeof(*ptr)
#define pl_alloc_ptr(parent, ptr) \
    (__typeof__(ptr)) pl_alloc(parent, sizeof(*(ptr)))
#define pl_zalloc_ptr(parent, ptr) \
    (__typeof__(ptr)) pl_zalloc(parent, sizeof(*(ptr)))
#define pl_calloc_ptr(parent, num, ptr) \
    (__typeof__(ptr)) pl_calloc(parent, num, sizeof(*(ptr)))

// Helper function to allocate a struct and immediately assign it
#define pl_alloc_struct(parent, type, ...) \
    (type *) pl_memdup(parent, &(type) __VA_ARGS__, sizeof(type))

// Free an allocation and its children (recursively)
void pl_free(void *ptr);
void pl_free_children(void *ptr);

#define pl_free_ptr(ptr)    \
    do {                    \
        pl_free(*(ptr));    \
        *(ptr) = NULL;      \
    } while (0)

// Get the current size of an allocation.
size_t pl_get_size(const void *ptr);

#define pl_grow(parent, ptr, size)                      \
    do {                                                \
        size_t _size = (size);                          \
        if (_size > pl_get_size(*(ptr)))                \
            *(ptr) = pl_realloc(parent, *(ptr), _size); \
    } while (0)

// Reparent an allocation onto a new parent
void *pl_steal(void *parent, void *ptr);

// Wrapper functions around common string utilities
void *pl_memdup(void *parent, const void *ptr, size_t size);
char *pl_str0dup0(void *parent, const char *str);
char *pl_strndup0(void *parent, const char *str, size_t size);

#define pl_memdup_ptr(parent, ptr) \
    (__typeof__(ptr)) pl_memdup(parent, ptr, sizeof(*(ptr)))

// Helper functions for allocating public/private pairs, done by allocating
// `priv` at the address of `pub` + sizeof(pub), rounded up to the maximum
// alignment requirements.

#define PL_ALIGN_MEM(size) PL_ALIGN2(size, alignof(max_align_t))

#define PL_PRIV(pub) \
    (void *) ((uintptr_t) (pub) + PL_ALIGN_MEM(sizeof(*(pub))))

#define pl_alloc_obj(parent, ptr, priv) \
    (__typeof__(ptr)) pl_alloc(parent, PL_ALIGN_MEM(sizeof(*(ptr))) + sizeof(priv))

#define pl_zalloc_obj(parent, ptr, priv) \
    (__typeof__(ptr)) pl_zalloc(parent, PL_ALIGN_MEM(sizeof(*(ptr))) + sizeof(priv))

// Helper functions for dealing with arrays

#define PL_ARRAY(type) struct { type *elem; int num; }

#define PL_ARRAY_REALLOC(parent, arr, len)                                      \
    do {                                                                        \
        size_t _new_size = (len) * sizeof((arr).elem[0]);                       \
        (arr).elem = pl_realloc((void *) parent, (arr).elem, _new_size);        \
    } while (0)

#define PL_ARRAY_RESIZE(parent, arr, len)                                       \
    do {                                                                        \
        size_t _avail = pl_get_size((arr).elem) / sizeof((arr).elem[0]);        \
        size_t _min_len = (len);                                                \
        if (_avail < _min_len)                                                  \
            PL_ARRAY_REALLOC(parent, arr, _min_len);                            \
    } while (0)

#define PL_ARRAY_MEMDUP(parent, arr, ptr, len)                                  \
    do {                                                                        \
        size_t _len = (len);                                                    \
        PL_ARRAY_RESIZE(parent, arr, _len);                                     \
        memcpy((arr).elem, ptr, _len * sizeof((arr).elem[0]));                  \
        (arr).num = _len;                                                       \
    } while (0)

#define PL_ARRAY_GROW(parent, arr)                                              \
    do {                                                                        \
        size_t _avail = pl_get_size((arr).elem) / sizeof((arr).elem[0]);        \
        if (_avail < 10) {                                                      \
            PL_ARRAY_REALLOC(parent, arr, 10);                                  \
        } else if ((arr).num == _avail) {                                       \
            PL_ARRAY_REALLOC(parent, arr, (arr).num * 1.5);                     \
        } else {                                                                \
            assert((arr).elem);                                                 \
        }                                                                       \
    } while (0)

#define PL_ARRAY_APPEND(parent, arr, ...)                                       \
    do {                                                                        \
        PL_ARRAY_GROW(parent, arr);                                             \
        (arr).elem[(arr).num++] = __VA_ARGS__;                                  \
    } while (0)

#define PL_ARRAY_CONCAT(parent, to, from)                                       \
    do {                                                                        \
        if ((from).num) {                                                       \
            PL_ARRAY_RESIZE(parent, to, (to).num + (from).num);                 \
            memmove(&(to).elem[(to).num], (from).elem,                          \
                    (from).num * sizeof((from).elem[0]));                       \
            (to).num += (from).num;                                             \
        }                                                                       \
    } while (0)

#define PL_ARRAY_REMOVE_RANGE(arr, idx, count)                                  \
    do {                                                                        \
        ptrdiff_t _idx = (idx);                                                 \
        if (_idx < 0)                                                           \
            _idx += (arr).num;                                                  \
        size_t _count = (count);                                                \
        assert(_idx >= 0 && _idx + _count <= (arr).num);                        \
        memmove(&(arr).elem[_idx], &(arr).elem[_idx + _count],                  \
                ((arr).num - _idx - _count) * sizeof((arr).elem[0]));           \
        (arr).num -= _count;                                                    \
    } while (0)

#define PL_ARRAY_REMOVE_AT(arr, idx) PL_ARRAY_REMOVE_RANGE(arr, idx, 1)

#define PL_ARRAY_INSERT_AT(parent, arr, idx, ...)                               \
    do {                                                                        \
        ptrdiff_t _idx = (idx);                                                 \
        if (_idx < 0)                                                           \
            _idx += (arr).num + 1;                                              \
        assert(_idx >= 0 && _idx <= (arr).num);                                 \
        PL_ARRAY_GROW(parent, arr);                                             \
        memmove(&(arr).elem[_idx + 1], &(arr).elem[_idx],                       \
                ((arr).num++ - _idx) * sizeof((arr).elem[0]));                  \
        (arr).elem[_idx] = __VA_ARGS__;                                         \
    } while (0)

// Returns whether or not there was any element to pop
#define PL_ARRAY_POP(arr, out)                                                  \
    ((arr).num > 0                                                              \
        ? (*(out) = (arr).elem[--(arr).num], true)                              \
        : false                                                                 \
    )

// Wrapper for dealing with non-PL_ARRAY arrays
#define PL_ARRAY_APPEND_RAW(parent, arr, idxvar, ...)                           \
    do {                                                                        \
        PL_ARRAY(__typeof__((arr)[0])) _arr = { (arr), (idxvar) };              \
        PL_ARRAY_APPEND(parent, _arr, __VA_ARGS__);                             \
        (arr) = _arr.elem;                                                      \
        (idxvar) = _arr.num;                                                    \
    } while (0)