#ifndef _AL_LIB_H #define _AL_LIB_H #include "macros.h" #include "types.h" // Piggyback off c89atomic for pointer size macros. #include #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 #include #include #include #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 // Directly define al_assert() as 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 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