1/* @title: Allocation Macros */
2#pragma once
3#include <compiler/core.h>
4#include <compiler/diagnostic.h>
5#include <compiler/intrinsic.h>
6
7#define alloc_params_with_defaults(_flags, _behavior, ...) \
8 cc_wno_override_init_expr( \
9 struct alloc_params, \
10 ((struct alloc_params) {.flags = (_flags), \
11 .behavior = (_behavior), \
12 .priority = ALLOC_PRIORITY_DEFAULT, \
13 ##__VA_ARGS__}))
14
15#define vfree_1(ptr) vfree_2((ptr), ALLOC_BEHAVIOR_DEFAULT)
16#define vfree_2(ptr, bh) vfree_full((ptr), (bh))
17
18#define vfree(...) PP_CALL(vfree, __VA_ARGS__)
19
20#define valloc(size, ...) \
21 valloc_full((size), alloc_params_with_defaults(ALLOC_FLAGS_DEFAULT, \
22 ALLOC_BEHAVIOR_DEFAULT, \
23 ##__VA_ARGS__))
24
25#define valloc_aligned(size, align, ...) \
26 valloc_aligned_full((size), (align), \
27 alloc_params_with_defaults(ALLOC_FLAGS_DEFAULT, \
28 ALLOC_BEHAVIOR_DEFAULT, \
29 ##__VA_ARGS__))
30
31/* struct foo *f = vnew(f, ALLOC_ZERO) */
32#define VNEW_PTR(p) typeof(&*(p))
33#define VNEW_CHECK(p) \
34 static_assert( \
35 __builtin_types_compatible_p(__typeof_unqual__(p), typeof(&*(p))), \
36 "vnew: target must be a pointer lvalue, not an array"); \
37 static_assert(!cc_generic((p), void * : 1, const void * : 1, \
38 volatile void * : 1, const volatile void * : 1, \
39 default : 0), \
40 "vnew: void pointer target, use valloc() with a size")
41
42#define vnew(p, ...) \
43 ({ \
44 VNEW_CHECK(p); \
45 (VNEW_PTR(p)) valloc(sizeof(*(p)), ##__VA_ARGS__); \
46 })
47
48/* struct foo *arr = vnews(arr, count, ALLOC_ZERO); */
49#define vnews(p, n, ...) \
50 ({ \
51 VNEW_CHECK(p); \
52 size_t _vn_bytes_; \
53 ci_mul_overflow((n), sizeof(*(p)), &_vn_bytes_) \
54 ? (VNEW_PTR(p)) NULL \
55 : (VNEW_PTR(p)) valloc(_vn_bytes_, ##__VA_ARGS__); \
56 })
57
58#if cn_has_builtin(__builtin_counted_by_ref)
59#define VNEW_FLEX_COUNTER(q, member) \
60 _Generic(__builtin_counted_by_ref((q)->member), \
61 void *: &(size_t) {0}, \
62 default: __builtin_counted_by_ref((q)->member))
63#else
64#define VNEW_FLEX_COUNTER(q, member) (&(size_t) {0})
65#endif
66
67/* struct pkt { uint32_t len; uint8_t data[] cc_counted_by(len); };
68 * struct pkt *pkt = vnew_flex(pkt, data, len, ALLOC_ZERO);
69 *
70 * pkt->len = len when data is counted_by or NULL if len doesn't fit it */
71#define vnew_flex(p, member, n, ...) \
72 ({ \
73 VNEW_CHECK(p); \
74 static_assert( \
75 !__builtin_types_compatible_p(__typeof_unqual__((p)->member), \
76 typeof(&(p)->member[0])), \
77 "vnew_flex: member must be an array, not a pointer"); \
78 size_t _vf_tail_, _vf_bytes_; \
79 __typeof_unqual__(*VNEW_FLEX_COUNTER(p, member)) _vf_count_; \
80 VNEW_PTR(p) _vf_q_ = NULL; \
81 if (!ci_add_overflow((n), 0, &_vf_count_) && \
82 !ci_mul_overflow((n), sizeof((p)->member[0]), &_vf_tail_) && \
83 !ci_add_overflow(sizeof(*(p)), _vf_tail_, &_vf_bytes_)) { \
84 _vf_q_ = (VNEW_PTR(p)) valloc(_vf_bytes_, ##__VA_ARGS__); \
85 if (_vf_q_) \
86 *VNEW_FLEX_COUNTER(_vf_q_, member) = _vf_count_; \
87 } \
88 _vf_q_; \
89 })
90
91#define vfree_aligned_1(ptr) vfree_aligned_2((ptr), ALLOC_BEHAVIOR_DEFAULT)
92#define vfree_aligned_2(ptr, bh) vfree_aligned_full((ptr), (bh))
93#define vfree_aligned(...) PP_CALL(vfree_aligned, __VA_ARGS__)
94
95#define vrealloc(ptr, size, ...) \
96 vrealloc_full((ptr), (size), \
97 alloc_params_with_defaults(ALLOC_FLAGS_DEFAULT, \
98 ALLOC_BEHAVIOR_DEFAULT, \
99 ##__VA_ARGS__))
100
101/* void **grown = vrenews(node->pages, needed); */
102#define vrenews(old, n, ...) \
103 ({ \
104 VNEW_CHECK(old); \
105 VNEW_PTR(old) _vr_old_ = (old); \
106 size_t _vr_bytes_; \
107 ci_mul_overflow((n), sizeof(*_vr_old_), &_vr_bytes_) \
108 ? (VNEW_PTR(old)) NULL \
109 : (VNEW_PTR(old)) vrealloc(_vr_old_, _vr_bytes_, ##__VA_ARGS__); \
110 })
111