From 209310c561d89c9ed2040aa7053b320d32cc393c Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Fri, 17 Jan 2025 11:20:17 -0500 Subject: all: Consider types and numerical limits - Vastly improve tests. - Change array/str/wstr APIs. - Use ptrdiff_t instead of size_t in ring buffer. - Make random seed thread-local. - Log now uses a static thread-local buffer instead of a malloc'd buffer. Signed-off-by: Andrew Opalach --- include/al/array_sort.h | 37 ++++++++++---- include/al/array_typed.h | 116 ++++++++++++++++++++---------------------- include/al/atomic.h | 6 ++- include/al/lib.h | 37 ++++++++++++-- include/al/log.h | 4 +- include/al/macros.h | 30 +++++++---- include/al/random.h | 15 ++++-- include/al/ring_buffer.h | 22 ++++---- include/al/str.h | 130 +++++++++++++++++++++-------------------------- include/al/test.h | 49 ++++++++++++++++-- include/al/types.h | 3 ++ include/al/wstr.h | 119 ++++++++++++++++++++++++++----------------- 12 files changed, 338 insertions(+), 230 deletions(-) (limited to 'include/al') diff --git a/include/al/array_sort.h b/include/al/array_sort.h index b6d7468..f10766f 100644 --- a/include/al/array_sort.h +++ b/include/al/array_sort.h @@ -3,24 +3,39 @@ #include "lib.h" +/* https://en.wikipedia.org/wiki/Insertion_sort +i ← 1 +while i < length(A) + x ← A[i] + j ← i + while j > 0 and A[j-1] > x + A[j] ← A[j-1] + j ← j - 1 + end while + A[j] ← x + i ← i + 1 +end while +*/ + #define AL_INSERSION_SORT(data, type, count, cmp) \ AL_MACRO_WRAP \ -({ \ - s32 i = 1; \ - while (i < (s32)count) { \ - s32 j = i - 1; \ - type x = data[i]; \ - while (j >= 0 && cmp(&data[j], &x) > 0) { \ - data[j + 1] = data[j]; \ - j--; \ +{ \ + u32 i = 1, k; \ + type x; \ + while (i < count) { \ + k = i; \ + x = (data)[i]; \ + while (k > 0 && cmp(&(data)[k - 1], &x) > 0) { \ + (data)[k] = (data)[k - 1]; \ + k--; \ } \ - data[j + 1] = x; \ + (data)[k] = x; \ i++; \ } \ -}) +} AL_MACRO_END #ifdef AL_USE_STDLIB -#define AL_STDLIB_QSORT(data, type, count, cmp) qsort(data, count, sizeof(type), cmp) +#define AL_STDLIB_QSORT(data, type, count, cmp) qsort(data, (size_t)count, sizeof(type), cmp) #endif #endif // _AL_ARRAY_SORT_H diff --git a/include/al/array_typed.h b/include/al/array_typed.h index 75b9d83..e7a4f80 100644 --- a/include/al/array_typed.h +++ b/include/al/array_typed.h @@ -39,118 +39,110 @@ #define al_array_free(...) AL_EMPTY_DEFAULT(_al_array_free, __VA_ARGS__) #define AL_ARRAY_DEFINE_FUNCTIONS(N, T, ...) \ - static inline T *al_array_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 i) \ + static inline T *al_array_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, u32 i) \ { \ - return &al_array_data(N, __VA_ARGS__)(a)[i]; \ + return &al_array_data(N, __VA_ARGS__)(array)[i]; \ } \ - static inline T *al_array_last(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a) \ + static inline T *al_array_last(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array) \ { \ - return al_array_at(N, __VA_ARGS__)(a, a->size - 1); \ + return al_array_at(N, __VA_ARGS__)(array, array->count - 1); \ } \ - static void al_array_reserve(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 size); \ - static inline void al_array_push(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, T *value) \ + static void al_array_reserve(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, u32 count); \ + static inline void al_array_push(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, T value) \ { \ - al_array_reserve(N, __VA_ARGS__)(a, a->size + 1); \ - *al_array_at(N, __VA_ARGS__)(a, a->size++) = *value; \ + al_array_reserve(N, __VA_ARGS__)(array, array->count + 1); \ + *al_array_at(N, __VA_ARGS__)(array, array->count++) = value; \ } \ - static inline void al_array_pop(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, T *ret) \ + static inline void al_array_insert(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, u32 i, T item) \ { \ - *ret = *al_array_at(N, __VA_ARGS__)(a, --a->size); \ - } \ - static inline void al_array_remove_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 i) \ - { \ - if (UNLIKELY(i == a->size - 1)) { \ - a->size--; \ - } else { \ - al_memmove(al_array_at(N, __VA_ARGS__)(a, i), al_array_at(N, __VA_ARGS__)(a, i + 1), \ - sizeof(T) * (--a->size - i)); \ + al_array_reserve(N, __VA_ARGS__)(array, array->count + 1); \ + if (i != array->count - 1) { \ + al_memmove(al_array_at(N, __VA_ARGS__)(array, i + 1), al_array_at(N, __VA_ARGS__)(array, i), \ + (array->count - i) * sizeof(T)); \ } \ + array->count++; \ + *al_array_at(N, __VA_ARGS__)(array, i) = item; \ } \ - static inline void al_array_pop_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 i, T *ret) \ + static inline void al_array_pop(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, T *ret) \ { \ - *ret = *al_array_at(N, __VA_ARGS__)(a, i); \ - al_array_remove_at(N, __VA_ARGS__)(a, i); \ + *ret = *al_array_at(N, __VA_ARGS__)(array, --array->count); \ } \ - static inline void al_array_insert(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 i, T *item) \ + static inline void al_array_remove_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, u32 i) \ { \ - if (a->size > i) { \ - al_array_reserve(N, __VA_ARGS__)(a, a->size + 1); \ - al_memmove(al_array_at(N, __VA_ARGS__)(a, i + 1), al_array_at(N, __VA_ARGS__)(a, i), \ - sizeof(T) * (a->size - i)); \ - a->size++; \ + if (UNLIKELY(i == array->count - 1)) { \ + array->count--; \ } else { \ - al_array_reserve(N, __VA_ARGS__)(a, i + 1); \ - a->size = i + 1; \ + al_memmove(al_array_at(N, __VA_ARGS__)(array, i), al_array_at(N, __VA_ARGS__)(array, i + 1), \ + (--array->count - i) * sizeof(T)); \ } \ - *al_array_at(N, __VA_ARGS__)(a, i) = *item; \ } \ - static inline void _al_array_reserve_internal(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a, u32 size) \ + static inline T al_array_pop_at(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, u32 i) \ { \ - if (a->alloc >= size) { \ - return; \ - } \ - size = size < al_grow_step ? al_next_power_of_two(size) : \ - (size + al_grow_step) & ~al_grow_step; \ - a->data = (T *)(!a->data ? al_malloc(sizeof(T) * size) : al_realloc(a->data, sizeof(T) * size)); \ - a->alloc = size; \ + T ret = *al_array_at(N, __VA_ARGS__)(array, i); \ + al_array_remove_at(N, __VA_ARGS__)(array, i); \ + return ret; \ + } \ + static inline void _al_array_reserve_internal(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array, u32 size) \ + { \ + array->alloc = al_default_growing_allocation((void **)&array->data, array->alloc, size, al_malloc, al_realloc); \ } \ - static inline void al_array_free(N, __VA_ARGS__)(array(N, __VA_ARGS__) *a) \ + static inline void al_array_free(N, __VA_ARGS__)(array(N, __VA_ARGS__) *array) \ { \ - if (a->data != NULL) al_free(a->data); \ + if (array->data != NULL) al_free(array->data); \ } #define AL_ARRAY_DEFINE(N, T) \ typedef struct array(N) array(N); \ struct array(N) { \ - u32 size; \ + u32 count; \ u32 alloc; \ T *data; \ }; \ - static inline void al_array_init(N)(array(N) *a) \ + static inline void al_array_init(N)(array(N) *array) \ { \ - a->size = 0; \ - a->alloc = 0; \ - a->data = NULL; \ + array->count = 0; \ + array->alloc = 0; \ + array->data = NULL; \ } \ - static inline T *al_array_data(N)(array(N) *a) \ + static inline T *al_array_data(N)(array(N) *array) \ { \ - return a->data; \ + return array->data; \ } \ AL_ARRAY_DEFINE_FUNCTIONS(N, T,) \ - static inline void al_array_reserve(N)(array(N) *a, u32 size) \ + static inline void al_array_reserve(N)(array(N) *array, u32 count) \ { \ - _al_array_reserve_internal(N)(a, size); \ + _al_array_reserve_internal(N)(array, count * sizeof(T)); \ } #define AL_ARRAY_INLINE_STORAGE_DEFINE(N, T, S) \ typedef struct array(N, S) array(N, S); \ struct array(N, S) { \ - u32 size; \ + u32 count; \ u32 alloc; \ T *data; \ T inline_data[S]; \ }; \ - static inline void al_array_init(N, S)(array(N, S) *a) \ + static inline void al_array_init(N, S)(array(N, S) *array) \ { \ - a->size = 0; \ - a->alloc = S; \ - a->data = NULL; \ + array->count = 0; \ + array->alloc = S * sizeof(T); \ + array->data = NULL; \ } \ - static inline T *al_array_data(N, S)(array(N, S) *a) \ + static inline T *al_array_data(N, S)(array(N, S) *array) \ { \ - if (a->data) return a->data; \ - else return a->inline_data; \ + if (array->data) return array->data; \ + else return array->inline_data; \ } \ AL_ARRAY_DEFINE_FUNCTIONS(N, T, S) \ - static inline void al_array_reserve(N, S)(array(N, S) *a, u32 size) \ + static inline void al_array_reserve(N, S)(array(N, S) *array, u32 count) \ { \ - if (S >= size) { \ + if (S >= count) { \ return; \ } \ - bool copy_inline = a->data == NULL; \ - _al_array_reserve_internal(N, S)(a, size); \ + bool copy_inline = array->data == NULL; \ + _al_array_reserve_internal(N, S)(array, count * sizeof(T)); \ if (UNLIKELY(copy_inline)) { \ - al_memcpy(a->data, a->inline_data, sizeof(T) * a->size); \ + al_memcpy(array->data, array->inline_data, array->count * sizeof(T)); \ } \ } diff --git a/include/al/atomic.h b/include/al/atomic.h index adb25c0..5c4ccc4 100644 --- a/include/al/atomic.h +++ b/include/al/atomic.h @@ -1,10 +1,10 @@ #ifndef _AL_ATOMIC_H #define _AL_ATOMIC_H -#include - #include "lib.h" +#include + #define AL_ATOMIC_RELAXED c89atomic_memory_order_relaxed #define AL_ATOMIC_CONSUME c89atomic_memory_order_consume #define AL_ATOMIC_ACQUIRE c89atomic_memory_order_acquire @@ -58,8 +58,10 @@ typedef f32 c89atomic_f32; #if defined C89ATOMIC_64BIT AL_ATOMIC_DEFINE(size_t, uint64, 64) +AL_ATOMIC_DEFINE(ptrdiff_t, int64, i64) #elif defined C89ATOMIC_32BIT AL_ATOMIC_DEFINE(size_t, uint32, 32) +AL_ATOMIC_DEFINE(ptrdiff_t, int32, i32) #endif AL_ATOMIC_DEFINE(u64, uint64, 64); AL_ATOMIC_DEFINE_EXT(u64, uint64, 64); diff --git a/include/al/lib.h b/include/al/lib.h index 30f54b0..4f03235 100644 --- a/include/al/lib.h +++ b/include/al/lib.h @@ -4,6 +4,9 @@ #include "types.h" #include "macros.h" +// Piggyback off c89atomic for pointer size macros. +#include + #define AL_USE_STDLIB //#define AL_MEMORY_TRACKING //#define AL_FORCE_DISABLE_OUTPUT @@ -72,8 +75,10 @@ void al_malloc_stats(size_t *current, size_t *peak, size_t *total); #define al_memset memset #define al_bzero bzero #define al_strlen strlen +#define al_strndup strndup #define al_snprintf snprintf #define al_vsnprintf vsnprintf +#define al_fflush fflush #ifdef _DEBUG_ #define al_assert assert #else @@ -82,15 +87,22 @@ void al_malloc_stats(size_t *current, size_t *peak, size_t *total); #define al_assert_and_return(...) \ AL_MACRO_WRAP \ -({ \ +{ \ al_assert(false); \ return __VA_ARGS__; \ -}) +} AL_MACRO_END #define al_alloc_object(type) (type *)al_calloc(1, sizeof(type)) AL_UNUSED_FUNCTION_PUSH +// 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 s32 al_printf(const char *fmt, ...) { #ifdef AL_FORCE_DISABLE_OUTPUT @@ -184,11 +196,21 @@ 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 u16 al_u16_inc_wrap(u16 v) { return (v == UINT16_MAX) ? 0 : v + 1; } +static inline u32 al_add_wrap(u32 v, u32 add, u32 max) +{ + return (v > max - add) ? (add - (max - v)) : v + add; +} + static inline u32 al_u32_add_wrap(u32 v, u32 add) { return (v > UINT32_MAX - add) ? (add - (UINT32_MAX - v)) : v + add; @@ -196,15 +218,24 @@ static inline u32 al_u32_add_wrap(u32 v, u32 add) static inline u16 al_u16_add_wrap(u16 v, u16 add) { - return (v > UINT16_MAX - add) ? (add - (UINT16_MAX - v)) : v + add; + return (v > UINT16_MAX - add) ? (u16)(add - (UINT16_MAX - v)) : v + add; } AL_UNUSED_FUNCTION_POP +#if defined C89ATOMIC_64BIT +#define MAX_ALLOC_32 UINT32_MAX +#elif defined C89ATOMIC_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_default_growing_allocation(void **ptr, u32 prev_size, u32 size, + void *(*malloc_func)(size_t), void *(*realloc_func)(void *, size_t)); + // Defaults to 4KB. extern size_t al_page_size; void al_set_page_size(size_t size); diff --git a/include/al/log.h b/include/al/log.h index 53ffbea..fbd5e48 100644 --- a/include/al/log.h +++ b/include/al/log.h @@ -1,12 +1,14 @@ #ifndef _AL_LOG_H #define _AL_LOG_H +// https://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html + #include #include #define __LOCATION__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__), __LINE__, __FUNCTION__ -#define AL_LOG_MESSAGE_SIZE 256 +#define AL_LOG_MESSAGE_SIZE 256u enum { AL_LOG_INFO = 0, diff --git a/include/al/macros.h b/include/al/macros.h index 81fb581..715718a 100644 --- a/include/al/macros.h +++ b/include/al/macros.h @@ -1,7 +1,8 @@ #ifndef _AL_MACROS_H #define _AL_MACROS_H -#define AL_MACRO_WRAP(c) do { c } while(0) +#define AL_MACRO_WRAP do { +#define AL_MACRO_END } while(0) #define AL_PASTER_EVALUATOR(X, Y) X##_##Y #define AL_PASTER(X, Y) AL_PASTER_EVALUATOR(X, Y) @@ -42,22 +43,24 @@ _a > _b ? _a : _b; \ }) #define CLAMP(n, lo, hi) MIN(hi, MAX(lo, n)) -#endif - -/* +#else // Compatible/Pedantic min/max. +/* +#error "MIN/MAX/CLAMP should not be used in this mode as they\ + differ in behavior without support for statement expressions" +*/ #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define CLAMP(n, lo, hi) MIN(hi, MAX(lo, n)) -*/ +#endif #define SWAP(a, b) \ AL_MACRO_WRAP \ -({ \ +{ \ __typeof__(a) tmp = a; \ a = b; \ b = tmp; \ -}) +} AL_MACRO_END #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -76,19 +79,26 @@ AL_MACRO_WRAP \ #error "Conflicting definitions for common attributes" #endif -#if __has_attribute(__counted_by__) +// https://github.com/google/double-conversion/pull/131/files +#ifdef __has_attribute +#define AL_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +#define AL_HAS_ATTRIBUTE(x) 0 +#endif + +#if AL_HAS_ATTRIBUTE(__counted_by__) #define __counted_by(member) __attribute__((__counted_by__(member))) #else #define __counted_by(member) #endif -#if __has_attribute(__sized_by__) +#if AL_HAS_ATTRIBUTE(__sized_by__) #define __sized_by(member) __attribute__((__sized_by__(member))) #else #define __sized_by(member) #endif -#ifdef _MSVC_ +#ifdef _MSC_VER #define __thread __declspec(thread) #endif diff --git a/include/al/random.h b/include/al/random.h index 358a915..3d0d7cb 100644 --- a/include/al/random.h +++ b/include/al/random.h @@ -2,20 +2,27 @@ #define _AL_RANDOM_H #include "lib.h" - -#include +#include "macros.h" #define AL_RAND_MAX RAND_MAX AL_UNUSED_FUNCTION_PUSH -static void al_rand_init(void) +extern u32 al_rand_seed; +extern __thread bool al_rand_set; + +static void al_rand_init(u32 seed) { - srand((u32)time(NULL)); + al_rand_seed = seed; } static s32 al_rand(void) { + if (UNLIKELY(!al_rand_set)) { + // srand() is thread-local on Windows CRT. + srand(al_rand_seed); + al_rand_set = true; + } return rand(); } diff --git a/include/al/ring_buffer.h b/include/al/ring_buffer.h index acfe4b0..32292e9 100644 --- a/include/al/ring_buffer.h +++ b/include/al/ring_buffer.h @@ -10,20 +10,20 @@ struct al_ring_buffer { u8 *write; }; -void al_ring_buffer_init(struct al_ring_buffer *buf, u8 *data, size_t length); +void al_ring_buffer_init(struct al_ring_buffer *buf, u8 *data, ptrdiff_t length); -size_t al_ring_buffer_space(struct al_ring_buffer *buf); -u8 *al_ring_buffer_write_chunk(struct al_ring_buffer *buf, size_t *size); -void al_ring_buffer_append(struct al_ring_buffer *buf, u8 *ptr, size_t n); +ptrdiff_t al_ring_buffer_space(struct al_ring_buffer *buf); +u8 *al_ring_buffer_write_chunk(struct al_ring_buffer *buf, ptrdiff_t *size); +void al_ring_buffer_append(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t n); -size_t al_ring_buffer_occupied(struct al_ring_buffer *buf); -u8 *al_ring_buffer_read_chunk(struct al_ring_buffer *buf, size_t *size); -void al_ring_buffer_consume(struct al_ring_buffer *buf, u8 *ptr, size_t n); +ptrdiff_t al_ring_buffer_occupied(struct al_ring_buffer *buf); +u8 *al_ring_buffer_read_chunk(struct al_ring_buffer *buf, ptrdiff_t *size); +void al_ring_buffer_consume(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t n); -size_t al_ring_buffer_write(struct al_ring_buffer *buf, u8 *data, size_t n); -size_t al_ring_buffer_read(struct al_ring_buffer *buf, u8 *ptr, size_t n); -size_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, size_t offset, size_t n); -size_t al_ring_buffer_discard(struct al_ring_buffer *buf, size_t n); +ptrdiff_t al_ring_buffer_write(struct al_ring_buffer *buf, u8 *data, ptrdiff_t n); +ptrdiff_t al_ring_buffer_read(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t n); +ptrdiff_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t offset, ptrdiff_t n); +ptrdiff_t al_ring_buffer_discard(struct al_ring_buffer *buf, ptrdiff_t n); void al_ring_buffer_reset(struct al_ring_buffer *buf); diff --git a/include/al/str.h b/include/al/str.h index 74cf922..5ee51d0 100644 --- a/include/al/str.h +++ b/include/al/str.h @@ -5,31 +5,33 @@ #include "macros.h" typedef struct { - u32 len; + u32 length; u32 alloc; char *__sized_by(alloc) data; } str; -#define al_str_zero() (str){ 0, 0, NULL } +#define al_str_null() ((str){ 0, 0, NULL }) +#define al_str_npos ((u32)-1) #define al_str_at(s, i) (s)->data[i] -#define al_str_atr(s, i) (s)->data[(s)->len + i] +#define al_str_atr(s, i) (s)->data[(s)->length - (i)] -#define al_string_if_literal(s) ((char *)(s "")) // use al_str_cr for runtime. -#define al_str_c(s) (&((str){ (u32)((sizeof(s) / sizeof(char)) - 1), 0, al_string_if_literal(s) })) -#define al_str_cr(s) (&((str){ (u32)al_strlen(s), 0, ((char *)(s)) })) // runtime -#define al_str_w(s, i, n) (&((str){ ((u32)(n)), 0, &(s)[i] })) +#define al_chars_if_literal(s) ((char *)(s "")) // use al_str_cs/cr for non-static arrays. +#define al_str_c(c) ((str){ (u32)(sizeof(c) - 1), 0, al_chars_if_literal(c) }) +#define al_str_cs(c) ((str){ (u32)al_strnlen(c, sizeof(c)), 0, ((char *)(c)) }) // (s)ized +#define al_str_cr(c) ((str){ (u32)al_strnlen(c, MAX_ALLOC_32), 0, ((char *)(c)) }) // (r)untime + +#define al_str_w(c, i, n) ((str){ ((u32)(n)), 0, &(c)[i] }) #define al_str_substr(s, start, end) al_str_w((s)->data, start, ((end) - (start))) -#define AL_STR_FMT "%.*s" -#define AL_STR_PRINTF(s) (s)->len, (s)->data -#define AL_STR_PRINTF_MAXLEN(s, l) MIN((s)->len, (l)), (s)->data +#define al_str_fmt(s) (s)->length, (s)->data +#define al_str_fmt_maxlen(s, l) MIN((s)->length, (l)), (s)->data AL_UNUSED_FUNCTION_PUSH static inline bool al_str_is_empty(str *s) { - return s->len == 0; + return s->length == 0; } static inline void al_str_free(str *s) @@ -39,43 +41,33 @@ static inline void al_str_free(str *s) static inline u32 al_str_reserve(str *s, u32 size) { - if (size <= s->alloc) { - return s->alloc; - } - - size = size < al_grow_step ? al_next_power_of_two(size) : - (size + al_grow_step) & ~al_grow_step; - - s->data = (char *)(!s->data ? al_malloc(size) : al_realloc(s->data, size)); - al_assert(s->data); - - return size; + return al_default_growing_allocation((void **)&s->data, s->alloc, size, al_malloc, al_realloc); } static inline void al_str_sized(str *s, u32 size) { - *s = al_str_zero(); + *s = al_str_null(); s->alloc = al_str_reserve(s, size); } static inline void al_str_clone(str *dest, str *src) { - al_str_sized(dest, src->len); - al_memcpy(dest->data, src->data, src->len); - dest->len = src->len; + al_str_sized(dest, src->length); + al_memcpy(dest->data, src->data, src->length); + dest->length = src->length; } static inline void al_str_from(str *s, const char *c_str) { - size_t len = al_strlen(c_str); - al_str_sized(s, (u32)len); - al_memcpy(s->data, c_str, len); - s->len = (u32)len; + size_t length = al_strlen(c_str); + al_str_sized(s, (u32)length); + al_memcpy(s->data, c_str, length); + s->length = (u32)length; } static inline void al_str_to_lower(str *s) { - for (u32 i = 0; i < s->len; i++) { + for (u32 i = 0; i < s->length; i++) { al_str_at(s, i) = al_tolower(al_str_at(s, i)); } } @@ -83,26 +75,26 @@ static inline void al_str_to_lower(str *s) // Returned c_str must be free'd by the user. static inline char *al_str_to_c_str(str *s) { - char *c = (char *)al_malloc(s->len + 1); - al_memcpy(c, s->data, s->len); - c[s->len] = '\0'; + char *c = (char *)al_malloc(s->length + 1); + al_memcpy(c, s->data, s->length); + c[s->length] = '\0'; return c; } static inline void al_str_cat(str *s, str *c) { - s->alloc = al_str_reserve(s, s->len + c->len); - al_memcpy(s->data + s->len, c->data, c->len); - s->len += c->len; + s->alloc = al_str_reserve(s, s->length + c->length); + al_memcpy(s->data + s->length, c->data, c->length); + s->length += c->length; } static inline void al_str_insert(str *s, u32 i, str *c) { - s->alloc = al_str_reserve(s, s->len + c->len); + s->alloc = al_str_reserve(s, s->length + c->length); char *data = s->data + i; - al_memmove(data + c->len, data, s->len - i); - al_memcpy(data, c->data, c->len); - s->len += c->len; + al_memmove(data + c->length, data, s->length - i); + al_memcpy(data, c->data, c->length); + s->length += c->length; } static inline void al_str_prepend(str *s, str *c) @@ -110,65 +102,57 @@ static inline void al_str_prepend(str *s, str *c) al_str_insert(s, 0, c); } -static inline s32 al_str_cmp(str *s, str *c, u32 i, u32 len) +static inline s32 al_str_cmp(str *s, str *c, u32 i, u32 length) { - if (i + len > s->len || len > c->len) { - return -1; - } - return al_memcmp(&al_str_at(s, i), c->data, len); + if (length > c->length) return 1; + else if (length + i > s->length) return -1; + return al_memcmp(&al_str_at(s, i), c->data, length); } static inline bool al_str_eq(str *s, str *c) { - return al_str_cmp(s, c, 0, MAX(s->len, c->len)) == 0; + return al_str_cmp(s, c, 0, MAX(s->length, c->length)) == 0; } -static inline s32 al_str_find(str *s, char c) +static inline u32 al_str_find(str *s, char c) { - char *loc = (char *)al_memchr(s->data, c, s->len); - if (!loc) { - return -1; - } - return (s32)(loc - s->data); + char *loc = (char *)al_memchr(s->data, c, s->length); + return loc ? (u32)(loc - s->data) : al_str_npos; } -static inline s32 al_str_rfind(str *s, char c) +static inline u32 al_str_rfind(str *s, char c) { - char *loc = (char *)al_memrchr(s->data, c, s->len); - if (!loc) { - return -1; - } - return (s32)(loc - s->data); + char *loc = (char *)al_memrchr(s->data, c, s->length); + return loc ? (u32)(loc - s->data) : al_str_npos; } // extremely slow. -static inline s32 al_str_find_str(str *s, str *c) -{ - if (c->len > s->len) { - return -1; - } - - for (u32 i = 0; i < s->len - c->len; i++) { - if (al_str_at(s, i) == al_str_at(c, 0) && al_str_cmp(s, c, i, c->len) == 0) { - return i; +static inline u32 al_str_find_str(str *s, str *c) +{ + if (c->length <= s->length) { + for (u32 i = 0; i < s->length - c->length; i++) { + if (al_str_at(s, i) == al_str_at(c, 0) && + (al_str_cmp(s, c, i, c->length) == 0)) { + return i; + } } } - - return -1; + return al_str_npos; } static inline u32 al_str_hash(str *s) { u32 hash = 5381; - for (u32 i = 0; i < s->len; i++) { - hash = ((hash << 5) + hash) + al_str_at(s, i); /* hash * 33 + c */ + for (u32 i = 0; i < s->length; i++) { + // hash * 33 + c + hash = ((hash << 5) + hash) + (uchar)al_str_at(s, i); } return hash; } AL_UNUSED_FUNCTION_POP -s64 al_str_to_long(str *s, s32 base); +s64 al_str_to_long(str *s, u32 base, bool *error); bool al_str_get_line(str *buffer, char sep, str *line); #endif // _AL_STR_H diff --git a/include/al/test.h b/include/al/test.h index da4f024..8fc37e3 100644 --- a/include/al/test.h +++ b/include/al/test.h @@ -14,7 +14,11 @@ if (!test()) return false; #define AL_TEST_START(name) \ - al_printf(" "name"...") +AL_MACRO_WRAP \ +{ \ + al_printf(" "name"..."); \ + al_fflush(stdout); \ +} AL_MACRO_END #define AL_TEST_END() \ al_printf("OK\n"); \ @@ -22,7 +26,7 @@ #define AL_TEST_DEF(a, b, T, cmp, explain) \ AL_MACRO_WRAP \ -({ \ +{ \ AL_TEST_TYPE_##T _a = a; \ AL_TEST_TYPE_##T _b = b; \ if (cmp) { \ @@ -30,7 +34,7 @@ AL_MACRO_WRAP \ AL_TEST_PRINTF_##T(_a), AL_TEST_PRINTF_##T(_b)); \ return false; \ } \ -}) +} AL_MACRO_END #define AL_TEST_EQ(a, b, T) \ AL_TEST_DEF(a, b, T, (AL_TEST_CMP_##T(_a, _b) != 0), \ @@ -64,6 +68,7 @@ AL_MACRO_WRAP \ AL_TEST_DEF(a, false, bool, (AL_TEST_CMP_bool(_a, _b) != 0), \ "is not") +// bool static inline s32 al_test_bool_cmp(bool a, bool b) { return (a == b) ? 0 : 1; @@ -75,6 +80,21 @@ static inline s32 al_test_bool_cmp(bool a, bool b) #define AL_TEST_FMT_bool "%s" #define AL_TEST_PRINTF_bool(b) BOOLSTR(b) +// u8 +static inline s32 al_test_u8_cmp(u8 a, u8 b) +{ + if (a == b) return 0; + else if (a < b) return -1; + else return 1; +} + +#define AL_TEST_CMP_u8 al_test_u8_cmp +#define AL_TEST_TYPE_u8 u8 + +#define AL_TEST_FMT_u8 "%hhu" +#define AL_TEST_PRINTF_u8(i) i + +// u16 static inline s32 al_test_u16_cmp(u16 a, u16 b) { if (a == b) return 0; @@ -88,6 +108,7 @@ static inline s32 al_test_u16_cmp(u16 a, u16 b) #define AL_TEST_FMT_u16 "%hu" #define AL_TEST_PRINTF_u16(i) i +// u32 static inline s32 al_test_u32_cmp(u32 a, u32 b) { if (a == b) return 0; @@ -101,6 +122,7 @@ static inline s32 al_test_u32_cmp(u32 a, u32 b) #define AL_TEST_FMT_u32 "%u" #define AL_TEST_PRINTF_u32(i) i +// s32 static inline s32 al_test_s32_cmp(s32 a, s32 b) { if (a == b) return 0; @@ -114,7 +136,8 @@ static inline s32 al_test_s32_cmp(s32 a, s32 b) #define AL_TEST_FMT_s32 "%d" #define AL_TEST_PRINTF_s32(i) i -static inline s32 al_test_size_t_cmp(size_t a, size_t b) +// s64 +static inline s32 al_test_s64_cmp(s64 a, s64 b) { if (a == b) return 0; else if (a < b) return -1; @@ -127,7 +150,8 @@ static inline s32 al_test_size_t_cmp(size_t a, size_t b) #define AL_TEST_FMT_s64 "%ld" #define AL_TEST_PRINTF_s64(i) i -static inline s32 al_test_s64_cmp(s64 a, s64 b) +// size_t +static inline s32 al_test_size_t_cmp(size_t a, size_t b) { if (a == b) return 0; else if (a < b) return -1; @@ -140,6 +164,21 @@ static inline s32 al_test_s64_cmp(s64 a, s64 b) #define AL_TEST_FMT_size_t "%zu" #define AL_TEST_PRINTF_size_t(i) i +// ptrdiff_t +static inline s32 al_test_ptrdiff_t_cmp(ptrdiff_t a, ptrdiff_t b) +{ + if (a == b) return 0; + else if (a < b) return -1; + else return 1; +} + +#define AL_TEST_CMP_ptrdiff_t al_test_ptrdiff_t_cmp +#define AL_TEST_TYPE_ptrdiff_t ptrdiff_t + +#define AL_TEST_FMT_ptrdiff_t "%zd" +#define AL_TEST_PRINTF_ptrdiff_t(i) i + +// pointer static inline s32 al_test_ptr_cmp(void *a, void *b) { if (a == b) return 0; diff --git a/include/al/types.h b/include/al/types.h index 3398700..fc88077 100644 --- a/include/al/types.h +++ b/include/al/types.h @@ -1,6 +1,8 @@ #ifndef _AL_TYPES_H #define _AL_TYPES_H +// https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Integer-Const-Type.html + #ifdef _POSIX_C_SOURCE #if _POSIX_C_SOURCE < 200112L #error "libalabaster requires _POSIX_C_SOURCE >= 200112L" @@ -39,6 +41,7 @@ typedef double f64; // long can be a distinctly different size based on the toolchain we are using. // Use long and ulong, if needed, for API compatibility. typedef unsigned long ulong; +typedef unsigned char uchar; #ifdef _MSC_VER #include diff --git a/include/al/wstr.h b/include/al/wstr.h index 561e236..c5734ba 100644 --- a/include/al/wstr.h +++ b/include/al/wstr.h @@ -7,27 +7,34 @@ #include typedef struct { - u32 len; + u32 length; u32 alloc; wchar_t *__sized_by(alloc) data; } wstr; -#define al_wstr_zero() (wstr){ 0, 0, NULL } +#define al_wstr_null() ((wstr){ 0, 0, NULL }) +#define al_wstr_npos ((u32)-1) #define al_wstr_at(w, i) (w)->data[i] -#define al_wstr_atr(w, i) (w)->data[(w)->len + i] +#define al_wstr_atr(w, i) (w)->data[(w)->length - (i)] -#define al_wide_string_if_literal(w) ((wchar_t *)(L"" w)) // use al_wstr_cr for runtime. -#define al_wstr_c(w) (&((wstr){ (u32)((sizeof(w) / sizeof(wchar_t)) - 1), 0, al_wide_string_if_literal(w) })) -#define al_wstr_cr(w) (&((wstr){ (u32)wcslen(w), 0, ((wchar_t *)(w)) })) // runtime -#define al_wstr_w(w, i, n) (&((wstr){ ((u32)(n)), 0, &al_wstr_at(w, i) })) -#define al_wstr_substr(w, start, end) al_wstr_w(w, start, ((end) - (start))) +#define al_wide_chars_if_literal(wc) ((wchar_t *)(L"" wc)) // use al_wstr_cs/r for non-static arrays. +#define al_wstr_c(wc) ((wstr){ (u32)((sizeof(wc) / sizeof(wchar_t)) - 1), 0, al_wide_chars_if_literal(wc) }) +#define al_wstr_cs(wc) ((wstr){ (u32)wcsnlen(wc, sizeof(wc)), 0, ((wchar_t *)(wc)) }) // (s)ized +#define al_wstr_cr(wc) ((wstr){ (u32)wcsnlen(wc, MAX_ALLOC_32), 0, ((wchar_t *)(wc)) }) // (r)untime -#define AL_WSTR_FMT "%ls" -#define AL_WSTR_PRINTF(w) (w)->data +#define al_wstr_w(wc, i, n) ((wstr){ ((u32)(n)), 0, &(wc)[i] }) +#define al_wstr_substr(w, start, end) al_wstr_w((w)->data, start, ((end) - (start))) + +#define al_wstr_fmt(w) (w)->data AL_UNUSED_FUNCTION_PUSH +static inline bool al_wstr_is_empty(wstr *w) +{ + return w->length == 0; +} + static inline s32 al_wchar_width(wchar_t wc) { #ifdef AL_HAVE_WIDE_STRING_WIDTH @@ -45,45 +52,37 @@ static inline void al_wstr_free(wstr *w) static inline u32 al_wstr_reserve(wstr *w, u32 size) { - if (size <= w->alloc) { - return w->alloc; - } - - size = size < al_grow_step ? al_next_power_of_two(size) : - (size + al_grow_step) & ~al_grow_step; - - w->data = (wchar_t *)(!w->data ? al_malloc(size) : al_realloc(w->data, size)); - al_assert(w->data); - - return size; + return al_default_growing_allocation((void **)&w->data, w->alloc, size, al_malloc, al_realloc); } static inline void al_wstr_sized(wstr *w, u32 size) { - *w = al_wstr_zero(); + *w = al_wstr_null(); w->alloc = al_wstr_reserve(w, size * sizeof(wchar_t)); } static inline void al_wstr_clone(wstr *dest, wstr *src) { - u32 len = src->len + 1; - al_wstr_sized(dest, len); - al_memcpy(dest->data, src->data, len * sizeof(wchar_t)); - dest->len = src->len; + u32 length = src->length; + al_wstr_sized(dest, length + 1); + al_memcpy(dest->data, src->data, length * sizeof(wchar_t)); + dest->data[length] = L'\0'; + dest->length = src->length; } static inline bool al_wstr_from_cstr(wstr *w, char *c) { #ifdef AL_HAVE_WIDE_STRING mbstate_t mbstate = { 0 }; - size_t len = mbsrtowcs(NULL, (const char **)&c, 0, &mbstate); - if (len == (size_t)-1) { + size_t length; + length = mbsrtowcs(NULL, (const char **)&c, 0, &mbstate); + if (length == (size_t)-1) { return false; } - al_wstr_sized(w, (u32)(len + 1)); - w->len = (u32)mbsrtowcs(w->data, (const char **)&c, len, &mbstate); - al_assert(w->len == (u32)len); - w->data[w->len] = L'\0'; + al_wstr_sized(w, (u32)(length + 1)); + w->length = (u32)mbsrtowcs(w->data, (const char **)&c, length, &mbstate); + al_assert(w->length == (u32)length); + w->data[w->length] = L'\0'; return true; #else (void)c; @@ -99,21 +98,26 @@ static inline void al_wstr_from_str(wstr *w, str *s) al_free(c_str); } +// This is horrible. static inline bool al_wstr_to_str(wstr *w, str *s) { // This is broken on windows. locale issue? #ifdef AL_HAVE_WIDE_STRING - wchar_t *data = w->data; + wstr copy; + // We have to make a copy here incase w->length is less than the + // full string pointed to by w->data. + al_wstr_clone(©, w); + wchar_t *copy_data = copy.data; mbstate_t mbstate = { 0 }; - size_t len = wcsrtombs(NULL, (const wchar_t **)&w->data, 0, &mbstate); - if (len == (size_t)-1) { + size_t length = wcsrtombs(NULL, (const wchar_t **)©_data, 0, &mbstate); + if (length == (size_t)-1) { return false; } - al_str_sized(s, (u32)len); + al_str_sized(s, (u32)length); // Excludes the null terminator. - s->len = (u32)wcsrtombs(s->data, (const wchar_t **)&w->data, len, &mbstate); - al_assert(s->len == (u32)len); - w->data = data; + s->length = (u32)wcsrtombs(s->data, (const wchar_t **)©_data, length, &mbstate); + al_assert(s->length == (u32)length); + al_wstr_free(©); return true; #else (void)s; @@ -125,17 +129,17 @@ static inline bool al_wstr_to_str(wstr *w, str *s) static inline s32 al_wstr_width(wstr *w) { #ifdef AL_HAVE_WIDE_STRING_WIDTH - return wcswidth(w->data, w->len); + return wcswidth(w->data, w->length); #else (void)w; al_assert_and_return(0); #endif } -static inline s32 al_wstr_cmp(wstr *w, wstr *c, u32 start, u32 len) +static inline s32 al_wstr_cmp(wstr *w, wstr *c, u32 start, u32 length) { #ifdef AL_HAVE_WIDE_STRING - return wcsncmp(&al_wstr_at(w, start), c->data, len); + return wcsncmp(&al_wstr_at(w, start), c->data, length); #else (void)w; (void)c; @@ -145,24 +149,43 @@ static inline s32 al_wstr_cmp(wstr *w, wstr *c, u32 start, u32 len) #endif } -static inline s32 al_wstr_find(wstr *w, wchar_t c) +static inline u32 al_wstr_find(wstr *w, wchar_t c) { - for (s32 i = 0; i < (s32)w->len; i++) { + for (u32 i = 0; i < w->length; i++) { if (al_wstr_at(w, i) == c) { return i; } } - return -1; + return al_wstr_npos; } -static inline s32 al_wstr_rfind(wstr *w, wchar_t c) +static inline u32 al_wstr_rfind(wstr *w, wchar_t c) { - for (s32 i = (s32)w->len - 1; i >= 0; i--) { + for (u32 i = w->length; --i > 0;) { if (al_wstr_at(w, i) == c) { return i; } } - return -1; + return al_wstr_npos; +} + +static inline bool al_wstr_tok(wstr *result, wchar_t delim) +{ + u32 index = al_wstr_find(result, delim); + if (index == al_str_npos) { + return false; + } + *result = al_wstr_substr(result, index + 1, result->length); + return true; +} + +static inline s64 al_wstr_to_long(wstr *w, u32 base, bool *error) +{ + str s; + al_wstr_to_str(w, &s); + s64 num = al_str_to_long(&s, base, error); + al_str_free(&s); + return num; } AL_UNUSED_FUNCTION_POP -- cgit v1.2.3-101-g0448