summaryrefslogtreecommitdiff
path: root/include/al/lib.h
blob: 8e16700b67714300a7a985fbafb21a2a0872fe5a (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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#ifndef _AL_LIB_H
#define _AL_LIB_H

#include "macros.h"
#include "types.h"

// Piggyback off c89atomic for pointer size macros.
#include <c89atomic.h>

#if defined C89ATOMIC_64BIT
#define AL_WE_64BIT
#elif defined C89ATOMIC_32BIT
#define AL_WE_32BIT
#endif

// https://forum.vcfed.org/index.php?threads/c-item-size-check-at-compile-time.1244920/
#define AL_ASSERT_TYPE_SIZE(type, size) \
    typedef char type##__size_assert[(!!(sizeof(type) == size)) * 2 - 1]

#define AL_EXPAND_CMP(a, cmp, b) a cmp b
#define AL_STATIC_ASSERT(what, ...) \
    typedef char what##__assert[(!!(AL_EXPAND_CMP(__VA_ARGS__))) * 2 - 1]

#define AL_IS_SIGNED(type) (((type)(-1)) < 0)
#define AL_TYPES_ARE_EQUAL(t1, t2) \
    AL_STATIC_ASSERT(t1_size_eq_t2, sizeof(t1), ==, sizeof(t2)); \
    AL_STATIC_ASSERT(t1_signedness_eq_t2, AL_IS_SIGNED(t1), ==, AL_IS_SIGNED(t2))

#define AL_CHARS_IF_LITERAL(s) ((char *)("" s "")) // This argument must be a static char array.

#define AL_USE_STDLIB
//#define AL_PARANOID_REALLOC
//#define AL_MEMORY_TRACKING
//#define AL_FORCE_DISABLE_OUTPUT

#if defined __clang__
#define AL_IGNORE_WARNING(warning) \
    _Pragma(XSTR(clang diagnostic push)) \
    _Pragma(XSTR(clang diagnostic ignored warning))
#define AL_IGNORE_WARNING_END _Pragma(XSTR(clang diagnostic pop))
#elif defined __GNUC__
#define AL_IGNORE_WARNING(warning) \
    _Pragma(XSTR(GCC diagnostic push)) \
    _Pragma(XSTR(GCC diagnostic ignored warning))
#define AL_IGNORE_WARNING_END _Pragma(XSTR(GCC diagnostic pop))
#else
#define AL_IGNORE_WARNING(warning)
#define AL_IGNORE_WARNING_END
#endif

#ifdef AL_USE_STDLIB
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#ifdef AL_MEMORY_TRACKING
void al_malloc_init(void);
void al_malloc_close(void);
void *al_malloc(size_t n);
void *al_calloc(size_t n, size_t size);
void *al_realloc(void *ptr, size_t n);
#ifdef AL_HAVE_POSIX_MEMALIGN
s32 al_posix_memalign(void **ptr, size_t alignment, size_t n);
#endif
void al_free(void *ptr);
void al_set_alloc(void *(*malloc_func)(size_t), void *(*calloc_func)(size_t, size_t),
    void *(*realloc_func)(void *, size_t),
#ifdef AL_HAVE_POSIX_MEMALIGN
    s32 (*posix_memalign_func)(void **, size_t, size_t),
#endif
    void (*free_func)(void *), void (*lock_func)(void), void (*unlock_func)(void));
void al_malloc_stats(size_t *current, size_t *peak, size_t *total, size_t *ops);
#else
#ifdef AL_USE_STDLIB
#define al_malloc malloc
#define al_calloc calloc
#ifdef AL_PARANOID_REALLOC
static inline void *al_realloc(void *ptr, size_t n) { return ptr ? realloc(ptr, n) : malloc(n); }
#else
#define al_realloc realloc
#endif
#ifdef AL_HAVE_POSIX_MEMALIGN
#define al_posix_memalign posix_memalign
#endif
#define al_free free
#else
#error "libalabaster configuration is malloc-less"
#endif
#endif

#define al_memcpy memcpy
#define al_memmove memmove
#define al_memchr memchr
#define al_memcmp memcmp
#define al_memset memset
#define al_bzero bzero
#define al_strlen strlen
#define al_strncmp strncmp
#define al_strscmp(s1, s2) strncmp(s1, AL_CHARS_IF_LITERAL(s2), sizeof(s2))
// strndup is _POSIX_C_SOURCE >= 200809L.
//#define al_strndup strndup
#define al_snprintf snprintf
#define al_vsnprintf vsnprintf
#define al_fflush fflush

#if (defined AL_DEBUG || defined AL_RELEASE_ASSERTS) && defined AL_ASSERT_FUNCTION
#include <assert.h>
// Directly define al_assert() as <assert.h> would if NDEBUG was not set.
#if defined AL_ASSERT_INCLUDE_FUNC
#define al_assert(expr) ((expr) ? (void)0 : AL_ASSERT_FUNCTION(#expr, __FILE__, __LINE__,  __func__))
#elif defined AL_ASSERT_EXPR_LAST
#define al_assert(expr) ((expr) ? (void)0 : AL_ASSERT_FUNCTION(__FILE__, __LINE__, #expr))
#else
#define al_assert(expr) ((expr) ? (void)0 : AL_ASSERT_FUNCTION(#expr, __FILE__, __LINE__))
#endif
// Loosely assert that <assert.h> behaves as we expect and redefines itself on every include.
#undef assert
#else
#define al_assert(expr) ((void)sizeof(expr)) // NOLINT(bugprone-sizeof-expression)
#endif
// Use __VA_ARGS__ to allow for an empty return.
#define al_assert_and_return(...) do { \
    al_assert(false); \
    return __VA_ARGS__; \
} while (0)

#define al_alloc_object(type) (type *)al_calloc(1, sizeof(type))

// https://stackoverflow.com/a/66346831
static inline size_t al_strnlen(const char *s, size_t n)
{
    const char *p = (const char *)memchr(s, '\0', n);
    return p ? (size_t)(p - s) : n;
}

static inline char *al_strndup(const char *s, size_t n)
{
    size_t len = al_strnlen(s, n);
    char *ret = (char *)al_malloc(len + 1);
    al_memcpy(ret, s, len);
    ret[len] = '\0';
    return ret;
}

static inline s32 al_printf(const char *fmt, ...)
{
#ifdef AL_FORCE_DISABLE_OUTPUT
    (void)fmt;
    return 0;
#else
    va_list args;
    va_start(args, fmt);
    s32 ret = vprintf(fmt, args);
    va_end(args);
    return ret;
#endif
}

static inline s32 al_vprintf(const char *fmt, va_list args)
{
#ifdef AL_FORCE_DISABLE_OUTPUT
    (void)fmt;
    (void)args;
    return 0;
#else
    s32 ret = vprintf(fmt, args);
    return ret;
#endif
}

static inline void *al_memrchr(const void *s, s32 c, size_t n)
{
    const u8 *src = (const u8 *)s + n - 1;
    u8 d = (u8)c;
    while (n--) {
        if (*src == d) {
            return (void *)src;
        }
        src--;
    }
    return NULL;
}

static inline char *al_memdup0(const char *s, size_t n)
{
    char *buf = (char *)al_malloc(n + 1);
    al_memcpy(buf, s, n);
    buf[n] = '\0';
    return buf;
}
#endif

static inline u32 al_next_power_of_two(u32 n)
{
    n--;
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8;
    n |= n >> 16;
    return ++n;
}

static inline bool al_isspace(char c)
{
    return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v');
}

// ASCII-only.
static inline bool al_isupper(char c)
{
    return c >= 'A' && c <= 'Z';
}

static inline bool al_islower(char c)
{
    return c >= 'a' && c <= 'z';
}

static inline char al_tolower(char c)
{
    return al_isupper(c) ? c - 'A' + 'a' : c;
}

static inline char al_toupper(char c)
{
    return al_islower(c) ? c - 'a' + 'A' : c;
}

static inline u32 al_u32_inc_wrap(u32 v)
{
    return (v == UINT32_MAX) ? 0 : v + 1;
}

static inline u32 al_u32_dec_wrap(u32 v)
{
    return (v == 0) ? UINT32_MAX : v - 1;
}

static inline u32 al_u32_add_wrap(u32 v, u32 add, u32 max)
{
    return (v > max - add) ? (add - (max - v)) : v + add;
}

static inline u16 al_u16_inc_wrap(u16 v)
{
    return (v == UINT16_MAX) ? 0 : v + 1;
}

static inline u16 al_u16_dec_wrap(u16 v)
{
    return (v == 0) ? UINT16_MAX : v - 1;
}

static inline u16 al_u16_add_wrap(u16 v, u16 add, u16 max)
{
    return (v > max - add) ? (u16)(add - (max - v)) : v + add;
}

#if defined AL_WE_64BIT
#define MAX_ALLOC_32 UINT32_MAX
#elif defined AL_WE_32BIT
#define MAX_ALLOC_32 ((INT32_MAX - 1) / 4)
#endif

// Once a growing allocation expands past this number, only grow in multiples of it.
extern u32 al_grow_step;
u32 al_growing_allocation(void **ptr, u32 prev_size, u32 size, void *(*realloc_func)(void *, size_t));

// Defaults to 4KB.
extern size_t al_page_size;
void al_set_page_size(size_t size);

#endif // _AL_LIB_H