diff options
| -rw-r--r-- | include/al/array_sort.h | 37 | ||||
| -rw-r--r-- | include/al/array_typed.h | 116 | ||||
| -rw-r--r-- | include/al/atomic.h | 6 | ||||
| -rw-r--r-- | include/al/lib.h | 37 | ||||
| -rw-r--r-- | include/al/log.h | 4 | ||||
| -rw-r--r-- | include/al/macros.h | 30 | ||||
| -rw-r--r-- | include/al/random.h | 15 | ||||
| -rw-r--r-- | include/al/ring_buffer.h | 22 | ||||
| -rw-r--r-- | include/al/str.h | 128 | ||||
| -rw-r--r-- | include/al/test.h | 49 | ||||
| -rw-r--r-- | include/al/types.h | 3 | ||||
| -rw-r--r-- | include/al/wstr.h | 119 | ||||
| -rw-r--r-- | meson.build | 6 | ||||
| -rwxr-xr-x | scripts/line_count.sh | 3 | ||||
| -rw-r--r-- | src/array.c | 244 | ||||
| -rw-r--r-- | src/lib.c | 19 | ||||
| -rw-r--r-- | src/log.c | 45 | ||||
| -rw-r--r-- | src/random.c | 4 | ||||
| -rw-r--r-- | src/ring_buffer.c | 68 | ||||
| -rw-r--r-- | src/str.c | 47 | ||||
| -rw-r--r-- | tests/array.c | 427 | ||||
| -rw-r--r-- | tests/array.h | 2 | ||||
| -rw-r--r-- | tests/array_typed.c | 223 | ||||
| -rw-r--r-- | tests/array_typed.h | 2 | ||||
| -rw-r--r-- | tests/common.h | 12 | ||||
| -rw-r--r-- | tests/lib.c | 132 | ||||
| -rw-r--r-- | tests/main.c | 27 | ||||
| -rw-r--r-- | tests/ring_buffer.c | 177 | ||||
| -rw-r--r-- | tests/str.c | 358 | ||||
| -rw-r--r-- | tests/str.h | 2 |
30 files changed, 1366 insertions, 998 deletions
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 <c89atomic.h> - #include "lib.h" +#include <c89atomic.h> + #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 <c89atomic.h> + #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 <al/types.h> #include <al/lib.h> #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 <time.h> +#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) +static inline u32 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; + 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 <BaseTsd.h> 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 <wchar.h> 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 diff --git a/meson.build b/meson.build index 88247f8..75ee373 100644 --- a/meson.build +++ b/meson.build @@ -9,7 +9,10 @@ is_msvc_specifically = compiler.cmd_array()[0] == 'cl' supports_needed_gnu_extensions = not is_msvc_specifically have_builtin_expect = compiler.has_function('__builtin_expect') -have_typeof = compiler.has_function('__typeof__') +have_typeof = compiler.compiles('__typeof__(1) j;') +if not have_typeof + error('Compiler support for \'__typeof__()\' is required.') +endif have_posix_memalign = compiler.has_function( 'posix_memalign', prefix: '#define _POSIX_C_SOURCE 200112L\n#include <stdlib.h>') have_wide_string = compiler.has_function('wcsrtombs') @@ -49,6 +52,7 @@ c89atomic = subproject('c89atomic').get_variable('c89atomic') alabaster_src = [ 'src/lib.c', + 'src/random.c', 'src/str.c', 'src/log.c', 'src/ring_buffer.c' diff --git a/scripts/line_count.sh b/scripts/line_count.sh index 9b46c9e..a6ac787 100755 --- a/scripts/line_count.sh +++ b/scripts/line_count.sh @@ -1,2 +1,3 @@ #! /usr/bin/env sh -find ./src ./include -type f -exec wc -l {} + +export LC_ALL="C" +find ./src ./include -type f -exec sloccount {} + diff --git a/src/array.c b/src/array.c index 3236f67..6021a85 100644 --- a/src/array.c +++ b/src/array.c @@ -7,171 +7,159 @@ #define array(type) \ struct { \ - u32 size; \ + u32 count; \ u32 alloc; \ type *__sized_by(alloc) data; \ } -#define al_array_init(arr) ((arr).size = 0, (arr).alloc = 0, (arr).data = NULL) -#define al_array_item_size(arr) sizeof(*(arr).data) // NOLINT(bugprone-sizeof-expression) +#define al_array_init(array) ((array).count = 0, (array).alloc = 0, (array).data = NULL) -#define al_array_is_empty(arr) ((arr).size == 0) +#define al_array_offset(array, n) ((array).data + (n)) +#define al_array_at(array, i) ((array).data[i]) +#define al_array_last(array) al_array_at(array, (array).count - 1) -#define al_array_offset(arr, i) ((arr).data + (i)) - -#define al_array_at(arr, i) ((arr).data[i]) -#define al_array_last(arr) al_array_at(arr, (arr).size - 1) - -#define al_array_reserve(arr, size) \ - ((arr).alloc = _al_array_reserve((void *)&(arr).data, al_array_item_size(arr), (arr).alloc, size)) +#define al_array_item_size(array) sizeof(*(array).data) // NOLINT(bugprone-sizeof-expression) +#define al_array_reserve(array, count) \ + ((array).alloc = _al_array_reserve((void **)&(array).data, (array).alloc, ((count) * al_array_item_size(array)))) #define al_array_clone(dest, src) \ AL_MACRO_WRAP \ -({ \ +{ \ al_array_init(dest); \ - al_array_reserve(dest, (src).size); \ - (dest).size = (src).size; \ + al_array_reserve(dest, (src).count); \ + (dest).count = (src).count; \ al_memcpy(al_array_offset(dest, 0), al_array_offset(src, 0), \ - al_array_item_size(dest) * (dest).size); \ -}) + al_array_item_size(dest) * (dest).count); \ +} AL_MACRO_END #define al_array_copy(dest, src) \ AL_MACRO_WRAP \ -({ \ - al_array_reserve(dest, (src).size); \ - (dest).size = (src).size; \ +{ \ + al_array_reserve(dest, (src).count); \ + (dest).count = (src).count; \ al_memcpy(al_array_offset(dest, 0), al_array_offset(src, 0), \ - al_array_item_size(dest) * (dest).size); \ -}) + al_array_item_size(dest) * (dest).count); \ +} AL_MACRO_END -#define al_array_push(arr, item) \ -AL_MACRO_WRAP \ -({ \ - al_array_reserve(arr, (arr).size + 1); \ - al_array_at(arr, (arr).size++) = item; \ -}) +#define al_array_push(array, item) \ +AL_MACRO_WRAP \ +{ \ + al_array_reserve(array, (array).count + 1); \ + __typeof__(item) v = item; \ + (void)((array).data == &v); \ + al_array_at(array, (array).count++) = v; \ +} AL_MACRO_END -#define al_array_insert(arr, i, item) \ -AL_MACRO_WRAP \ -({ \ - if ((arr).size > i) { \ - al_array_reserve(arr, (arr).size + 1); \ - al_memmove(al_array_offset(arr, i + 1), al_array_offset(arr, i), \ - al_array_item_size(arr) * ((arr).size - i)); \ - (arr).size++; \ - } else { \ - al_array_reserve(arr, i + 1); \ - (arr).size = i + 1; \ - } \ - al_array_at(arr, i) = item; \ -}) +#define al_array_insert(array, i, item) \ +AL_MACRO_WRAP \ +{ \ + al_array_reserve(array, (array).count + 1); \ + if (i != (array).count - 1) { \ + al_memmove(al_array_offset(array, i + 1), al_array_offset(array, i), \ + al_array_item_size(array) * ((array).count - i)); \ + } \ + (array).count++; \ + __typeof__(item) v = item; \ + (void)((array).data == &v); \ + al_array_at(array, i) = v; \ +} AL_MACRO_END -#define al_array_pop(arr) al_array_at(arr, --(arr).size) -#define al_array_pop_at(arr, i, r) \ -AL_MACRO_WRAP \ -({ \ - r = al_array_at(arr, i); \ - al_array_remove_at(arr, i); \ -}) +#define al_array_pop(array) al_array_at(array, --(array).count) +#define al_array_pop_at(array, i, r) \ +AL_MACRO_WRAP \ +{ \ + r = al_array_at(array, i); \ + al_array_remove_at(array, i); \ +} AL_MACRO_END -#define al_array_remove(arr, elem) \ -AL_MACRO_WRAP \ -({ \ - for (u32 i = 0; i < (arr).size; i++) { \ - if (al_array_at(arr, i) == elem) { \ - al_array_remove_at(arr, i); \ - break; \ - } \ - } \ -}) +#define al_array_remove(array, elem) \ +AL_MACRO_WRAP \ +{ \ + for (u32 i = 0; i < (array).count; i++) { \ + if (al_array_at(array, i) == elem) { \ + al_array_remove_at(array, i); \ + break; \ + } \ + } \ +} AL_MACRO_END -#define al_array_check_remove(arr, elem, removed) \ -AL_MACRO_WRAP \ -({ \ - removed = false; \ - for (u32 i = 0; i < (arr).size; i++) { \ - if (al_array_at(arr, i) == elem) { \ - al_array_remove_at(arr, i); \ - removed = true; \ - break; \ - } \ - } \ -}) +#define al_array_check_remove(array, elem, removed) \ +AL_MACRO_WRAP \ +{ \ + removed = false; \ + for (u32 i = 0; i < (array).count; i++) { \ + if (al_array_at(array, i) == elem) { \ + al_array_remove_at(array, i); \ + removed = true; \ + break; \ + } \ + } \ +} AL_MACRO_END -#define al_array_remove_all(arr, elem) \ -AL_MACRO_WRAP \ -({ \ - for (u32 i = (arr).size; i-- > 0;) { \ - if (al_array_at(arr, i) == elem) { \ - al_array_remove_at(arr, i); \ - } \ - } \ -}) +#define al_array_remove_all(array, elem) \ +AL_MACRO_WRAP \ +{ \ + for (u32 i = (array).count; i-- > 0;) { \ + if (al_array_at(array, i) == elem) { \ + al_array_remove_at(array, i); \ + } \ + } \ +} AL_MACRO_END -#define al_array_remove_at(arr, i) \ -AL_MACRO_WRAP \ -({ \ - if (i == (arr).size - 1) { \ - (arr).size--; \ - } else { \ - al_memmove(al_array_offset(arr, i), al_array_offset(arr, i + 1), \ - al_array_item_size(arr) * (--(arr).size - i)); \ - } \ -}) +#define al_array_remove_at(array, i) \ +AL_MACRO_WRAP \ +{ \ + if (i == (array).count - 1) { \ + (array).count--; \ + } else { \ + al_memmove(al_array_offset(array, i), al_array_offset(array, i + 1), \ + al_array_item_size(array) * (--(array).count - i)); \ + } \ +} AL_MACRO_END -#define al_array_remove_range(arr, i, e) \ -AL_MACRO_WRAP \ -({ \ - if (e != (arr).size) { \ - al_memmove(al_array_offset(arr, i), al_array_offset(arr, e), \ - al_array_item_size(arr) * ((arr).size - e)); \ - } \ - (arr).size -= e - i; \ -}) +#define al_array_remove_range(array, start, end) \ +AL_MACRO_WRAP \ +{ \ + if (end != (array).count) { \ + al_memmove(al_array_offset(array, start), al_array_offset(array, end), \ + al_array_item_size(array) * ((array).count - end)); \ + } \ + (array).count -= end - start; \ +} AL_MACRO_END #ifdef AL_USE_STDLIB -#define al_array_sort(arr, type, cmp) AL_STDLIB_QSORT((arr).data, type, (arr).size, cmp) +#define al_array_sort(array, type, cmp) AL_STDLIB_QSORT((array).data, type, (array).count, cmp) #else -#define al_array_sort(arr, type, cmp) AL_INSERSION_SORT((arr).data, type, (arr).size, cmp) +#define al_array_sort(array, type, cmp) AL_INSERSION_SORT((array).data, type, (array).count, cmp) #endif -#define al_array_free(arr) \ - if ((arr).alloc) AL_ARRAY_FREE((arr).data) +#define al_array_free(array) \ + if ((array).alloc) AL_ARRAY_FREE((array).data) -#define al_array_foreach(arr, i, item) \ - for (u32 i = 0; (i < (arr).size && (item = al_array_at(arr, i), 1)); i++) +#define al_array_foreach(array, i, item) \ + for (u32 i = 0; (i < (array).count && (item = al_array_at(array, i), 1)); i++) -#define al_array_foreach_rev(arr, i, item) \ - for (u32 i = (arr).size; (i-- > 0 && (item = al_array_at(arr, i), 1));) +#define al_array_foreach_rev(array, i, item) \ + for (u32 i = (array).count; (i-- > 0 && (item = al_array_at(array, i), 1));) -#define al_array_foreach_ptr(arr, i, item) \ - for (u32 i = 0; (i < (arr).size && (item = al_array_offset(arr, i), 1)); i++) +#define al_array_foreach_ptr(array, i, item) \ + for (u32 i = 0; (i < (array).count && (item = al_array_offset(array, i), 1)); i++) -#define al_array_foreach_ptr_rev(arr, i, item) \ - for (u32 i = (arr).size; (i-- > 0 && (item = al_array_offset(arr, i), 1));) +#define al_array_foreach_ptr_rev(array, i, item) \ + for (u32 i = (array).count; (i-- > 0 && (item = al_array_offset(array, i), 1));) -#define al_array_remove_at_iter(arr, i) \ -AL_MACRO_WRAP \ -({ \ - al_array_remove_at(arr, i); \ - i--; \ -}) +#define al_array_remove_at_iter(array, i) \ +AL_MACRO_WRAP \ +{ \ + al_array_remove_at(array, i); \ + i--; \ +} AL_MACRO_END AL_UNUSED_FUNCTION_PUSH -static inline u32 _al_array_reserve(void **ptr, u32 item_size, u32 prev_size, u32 size) +static inline u32 _al_array_reserve(void **ptr, u32 prev_size, u32 size) { - if (size <= prev_size) { - return prev_size; - } - - size = size < al_grow_step ? al_next_power_of_two(size) : - (size + al_grow_step) & ~al_grow_step; - - *ptr = (!*ptr) ? AL_ARRAY_MALLOC(item_size * size) : AL_ARRAY_REALLOC(*ptr, item_size * size); - al_assert(*ptr); - - return size; + return al_default_growing_allocation(ptr, prev_size, size, AL_ARRAY_MALLOC, AL_ARRAY_REALLOC); } AL_UNUSED_FUNCTION_POP @@ -201,7 +201,24 @@ void al_set_alloc(void *(*malloc_func)(size_t), void *(*calloc_func)(size_t, siz } #endif -u32 al_grow_step = 0xfff; +u32 al_grow_step = 0x3fff; // 0b11111111111111 + +u32 al_default_growing_allocation(void **ptr, u32 prev_size, u32 size, + void *(*malloc_func)(size_t), void *(*realloc_func)(void *, size_t)) +{ + if (size <= prev_size) return prev_size; + al_assert(size > 0); + + size = size < al_grow_step ? al_next_power_of_two(size) : + al_add_wrap(size, al_grow_step, MAX_ALLOC_32) & ~al_grow_step; + // This should only happen after wrapping around MAX_ALLOC_32 (zero all possible bits). + if (size == 0) size = MAX_ALLOC_32; + + *ptr = (!*ptr) ? malloc_func(size) : realloc_func(*ptr, size); + al_assert(*ptr); + + return size; +} size_t al_page_size = 0x1000; void al_set_page_size(size_t size) @@ -1,14 +1,11 @@ #include "../include/al/log.h" +#include "../include/al/macros.h" //#define AL_LOG_SKIP -#define AL_LOG_USE_SECTION -#ifdef AL_LOG_USE_SECTION -#define AL_LOG_TEMPLATE "%s:%d %s(): %s -> (%s) " -#else #define AL_LOG_TEMPLATE "%s:%d %s(): %s -> " -#endif +#define AL_LOG_TEMPLATE_SECTION AL_LOG_TEMPLATE"(%s) " -#define AL_LOG_MESSAGE_MAX (AL_LOG_MESSAGE_SIZE - 1) // Space for newline. +#define AL_LOG_MESSAGE_MAX ((size_t)(AL_LOG_MESSAGE_SIZE - 1u)) // Space for newline. #ifndef AL_FORCE_DISABLE_OUTPUT static s32 (*_al_print)(void *, u8, char *) = NULL; @@ -23,39 +20,39 @@ void al_set_print(s32 (*print_func)(void *, u8, char *), void *userdata) static const char *levels[] = { "info", "warn", "error", "debug" }; #ifndef AL_LOG_SKIP -static char *get_message_buffer(u8 level, const char *section, const char *fmt, const char *name, const s32 line, const char *func, va_list args) +static __thread char messagebuf[AL_LOG_MESSAGE_SIZE]; + +static char *get_message_buffer(u8 level, const char *section, const char *name, const s32 line, const char *func, const char *fmt, va_list args) { - char *buffer = al_malloc(AL_LOG_MESSAGE_SIZE); s32 prefix = 0; -#ifdef AL_LOG_USE_SECTION - prefix = al_snprintf(buffer, AL_LOG_MESSAGE_MAX, AL_LOG_TEMPLATE, name, line, func, levels[level], section); -#else - (void)section; - prefix = al_snprintf(buffer, AL_LOG_MESSAGE_MAX, AL_LOG_TEMPLATE, name, line, func, levels[level]); -#endif - al_vsnprintf(buffer + prefix, AL_LOG_MESSAGE_MAX - prefix, fmt, args); - buffer[strcspn(buffer, "\r\n")] = '\0'; - return buffer; + if (!section) { + prefix = al_snprintf(messagebuf, AL_LOG_MESSAGE_MAX, AL_LOG_TEMPLATE, name, line, func, levels[level]); + } else { + prefix = al_snprintf(messagebuf, AL_LOG_MESSAGE_MAX, AL_LOG_TEMPLATE_SECTION, name, line, func, levels[level], section); + } + al_assert(prefix > 0); + al_vsnprintf(messagebuf + prefix, AL_LOG_MESSAGE_MAX - (size_t)prefix, fmt, args); + messagebuf[strcspn(messagebuf, "\r\n")] = '\0'; + return messagebuf; } #endif s32 _al_logv(u8 level, const char *section, const char *name, const s32 line, const char *func, const char *fmt, va_list args) { - if (!section) section = ""; #ifdef AL_LOG_SKIP s32 ret = 0; -#ifdef AL_LOG_USE_SECTION - ret += al_printf(AL_LOG_TEMPLATE, name, line, func, levels[level], section); -#else - ret += al_printf(AL_LOG_TEMPLATE, name, line, func, levels[level]); -#endif + if (!section) { + ret += al_printf(AL_LOG_TEMPLATE, name, line, func, levels[level]); + } else { + ret += al_printf(AL_LOG_TEMPLATE_SECTION, name, line, func, levels[level], section); + } ret += al_vprintf(fmt, args); if (strcspn(fmt, "\r\n") == al_strlen(fmt)) { ret += al_printf("\n"); } return ret; #else - return _al_print(_al_log_userdata, level, get_message_buffer(level, section, fmt, name, line, func, args)); + return _al_print(_al_log_userdata, level, get_message_buffer(level, section, name, line, func, fmt, args)); #endif } diff --git a/src/random.c b/src/random.c new file mode 100644 index 0000000..1bbf152 --- /dev/null +++ b/src/random.c @@ -0,0 +1,4 @@ +#include "../include/al/random.h" + +u32 al_rand_seed = 1738; +__thread bool al_rand_set = false; diff --git a/src/ring_buffer.c b/src/ring_buffer.c index 1c39b7b..ded0138 100644 --- a/src/ring_buffer.c +++ b/src/ring_buffer.c @@ -3,9 +3,12 @@ #include "../include/al/macros.h" // https://github.com/MusicPlayerDaemon/MPD/blob/master/src/util/RingBuffer.hxx +// +// This is no longer an implementation of a "contiguous" ring buffer but I used this as reference. +// Wrapping is handled in read() and write(). // https://andrea.lattuada.me/blog/2019/the-design-and-implementation-of-a-lock-free-ring-buffer-with-contiguous-reservations.html -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) { buf->start = data; buf->end = data + length; @@ -20,7 +23,7 @@ static inline u8 *previous(struct al_ring_buffer *buf, u8 *ptr) return --ptr; } -static inline void add(struct al_ring_buffer *buf, u8 **v, u8 *ptr, size_t n) +static inline void add(struct al_ring_buffer *buf, u8 **v, u8 *ptr, ptrdiff_t n) { ptr += n; al_assert(ptr <= buf->end); @@ -28,14 +31,14 @@ static inline void add(struct al_ring_buffer *buf, u8 **v, u8 *ptr, size_t n) al_atomic_store(void)(v, ptr, AL_ATOMIC_RELEASE); } -size_t al_ring_buffer_space(struct al_ring_buffer *buf) +ptrdiff_t al_ring_buffer_space(struct al_ring_buffer *buf) { u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_RELAXED); u8 *rp = previous(buf, (u8 *)al_atomic_load(void)(&buf->read, AL_ATOMIC_RELAXED)); return (wp <= rp) ? rp - wp : (buf->end - wp) + (rp - buf->start); } -u8 *al_ring_buffer_write_chunk(struct al_ring_buffer *buf, size_t *size) +u8 *al_ring_buffer_write_chunk(struct al_ring_buffer *buf, ptrdiff_t *size) { u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_ACQUIRE); u8 *rp = previous(buf, al_atomic_load(void)(&buf->read, AL_ATOMIC_RELAXED)); @@ -43,19 +46,19 @@ u8 *al_ring_buffer_write_chunk(struct al_ring_buffer *buf, size_t *size) return wp; } -void al_ring_buffer_append(struct al_ring_buffer *buf, u8 *ptr, size_t n) +void al_ring_buffer_append(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t n) { add(buf, &buf->write, ptr, n); } -size_t al_ring_buffer_occupied(struct al_ring_buffer *buf) +ptrdiff_t al_ring_buffer_occupied(struct al_ring_buffer *buf) { u8 *rp = al_atomic_load(void)(&buf->read, AL_ATOMIC_RELAXED); u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_RELAXED); return (rp <= wp) ? wp - rp : (buf->end - rp) + (wp - buf->start); } -u8 *al_ring_buffer_read_chunk(struct al_ring_buffer *buf, size_t *size) +u8 *al_ring_buffer_read_chunk(struct al_ring_buffer *buf, ptrdiff_t *size) { u8 *rp = al_atomic_load(void)(&buf->read, AL_ATOMIC_ACQUIRE); u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_RELAXED); @@ -63,24 +66,25 @@ u8 *al_ring_buffer_read_chunk(struct al_ring_buffer *buf, size_t *size) return rp; } -void al_ring_buffer_consume(struct al_ring_buffer *buf, u8 *ptr, size_t n) +void al_ring_buffer_consume(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t n) { add(buf, &buf->read, ptr, n); } -size_t al_ring_buffer_write(struct al_ring_buffer *buf, u8 *data, size_t n) +ptrdiff_t al_ring_buffer_write(struct al_ring_buffer *buf, u8 *data, ptrdiff_t n) { u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_ACQUIRE); u8 *rp = previous(buf, al_atomic_load(void)(&buf->read, AL_ATOMIC_RELAXED)); - size_t size = MIN((wp <= rp ? rp : buf->end) - wp, (ptrdiff_t)n); + ptrdiff_t size, wrap; - al_memcpy(wp, data, size); + size = MIN((wp <= rp ? rp : buf->end) - wp, n); + al_memcpy(wp, data, (size_t)size); wp += size; if (wp >= buf->end) { - size_t wrap = MIN(rp - buf->start, (ptrdiff_t)(n - size)); - al_memcpy(buf->start, data + size, wrap); + wrap = MIN(rp - buf->start, n - size); + al_memcpy(buf->start, data + size, (size_t)wrap); wp = buf->start + wrap; size += wrap; } @@ -90,19 +94,20 @@ size_t al_ring_buffer_write(struct al_ring_buffer *buf, u8 *data, size_t n) return size; } -size_t al_ring_buffer_read(struct al_ring_buffer *buf, u8 *ptr, size_t n) +ptrdiff_t al_ring_buffer_read(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t n) { u8 *rp = al_atomic_load(void)(&buf->read, AL_ATOMIC_ACQUIRE); u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_RELAXED); - size_t size = MIN((rp <= wp ? wp : buf->end) - rp, (ptrdiff_t)n); + ptrdiff_t size, wrap; - al_memcpy(ptr, rp, size); + size = MIN((rp <= wp ? wp : buf->end) - rp, n); + al_memcpy(ptr, rp, (size_t)size); rp += size; if (rp >= buf->end) { - size_t wrap = MIN(wp - buf->start, (ptrdiff_t)(n - size)); - al_memcpy(ptr + size, buf->start, wrap); + wrap = MIN(wp - buf->start, n - size); + al_memcpy(ptr + size, buf->start, (size_t)wrap); rp = buf->start + wrap; size += wrap; } @@ -112,27 +117,28 @@ size_t al_ring_buffer_read(struct al_ring_buffer *buf, u8 *ptr, size_t n) return size; } -size_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, size_t offset, size_t n) +ptrdiff_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, ptrdiff_t offset, ptrdiff_t n) { u8 *rp = al_atomic_load(void)(&buf->read, AL_ATOMIC_RELAXED); u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_RELAXED); - ptrdiff_t size; + ptrdiff_t size, wrap; if (rp <= wp) { rp += offset; - size = MIN(wp - rp, (ptrdiff_t)n); - if (size < 0) + size = MIN(wp - rp, n); + if (size < 0) { return 0; + } - al_memcpy(ptr, rp, size); + al_memcpy(ptr, rp, (size_t)size); } else { rp += offset; - size = MIN(buf->end - rp, (ptrdiff_t)n); + size = MIN(buf->end - rp, n); if (size > 0) { - al_memcpy(ptr, rp, size); + al_memcpy(ptr, rp, (size_t)size); n -= size; rp = buf->start; } else { @@ -140,9 +146,9 @@ size_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, size_t offset, s size = 0; } - ptrdiff_t wrap = MIN(wp - rp, (ptrdiff_t)n); + wrap = MIN(wp - rp, n); if (wrap > 0) { - al_memcpy(ptr + size, rp, wrap); + al_memcpy(ptr + size, rp, (size_t)wrap); size += wrap; } } @@ -150,16 +156,18 @@ size_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, size_t offset, s return size; } -size_t al_ring_buffer_discard(struct al_ring_buffer *buf, size_t n) +ptrdiff_t al_ring_buffer_discard(struct al_ring_buffer *buf, ptrdiff_t n) { u8 *rp = al_atomic_load(void)(&buf->read, AL_ATOMIC_ACQUIRE); u8 *wp = al_atomic_load(void)(&buf->write, AL_ATOMIC_RELAXED); - size_t discard = MIN((rp <= wp ? wp : buf->end) - rp, (ptrdiff_t)n); + ptrdiff_t discard, wrap; + + discard = MIN((rp <= wp ? wp : buf->end) - rp, n); rp += discard; if (rp >= buf->end) { - size_t wrap = MIN(wp - buf->start, (ptrdiff_t)(n - discard)); + wrap = MIN(wp - buf->start, n - discard); rp = buf->start + wrap; discard += wrap; } @@ -1,18 +1,18 @@ #include "../include/al/str.h" // https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/stdlib/strtol.c;h=09d4333ed05f497ec00b7192204644349441e58d;hb=HEAD#l130 -s64 al_str_to_long(str *s, s32 base) +s64 al_str_to_long(str *s, u32 base, bool *error) { u64 ret = 0; u32 i = 0; - s32 neg = 0; + bool neg = 0; - while (al_isspace(al_str_at(s, i)) && i < s->len) { + while (al_isspace(al_str_at(s, i)) && i < s->length) { i++; } if (al_str_at(s, i) == '-') { - neg = 1; + neg = true; i++; } else if (al_str_at(s, i) == '+') { i++; @@ -24,41 +24,35 @@ s64 al_str_to_long(str *s, s32 base) i += 2; } - u64 cutoff = neg ? -(u64)INT64_MIN : INT64_MAX; - s32 add_limit = cutoff % (u64)base; - cutoff /= (u64)base; + u64 cutoff = neg ? ((u64)INT64_MAX + 1) : (u64)INT64_MAX; + u64 add_limit = cutoff % base; + cutoff /= base; - bool error = false; + *error = false; - for (; i < s->len; i++) { - char c = al_str_at(s, i); + for (; i < s->length; i++) { + uchar c = (uchar)al_str_at(s, i); if (c >= '0' && c <= '9') c -= '0'; else if (c >= 'A' && c <= 'Z') c -= 'A' - 10; else if (c >= 'a' && c <= 'z') c -= 'a' - 10; else { // Character not valid for expressing a number. - error = true; + *error = true; break; } if (c >= base || ret > cutoff || (ret == cutoff && c > add_limit)) { // Either the character is invalid for the requested base or // attempting to add it would cause an overflow. - error = true; + *error = true; break; } ret = ret * base + c; } - if (error) { - ret = neg ? INT64_MIN : INT64_MAX; - } else if (neg) { - ret = -ret; - } - - return ret; + return neg ? -(s64)ret : (s64)ret; } bool al_str_get_line(str *buffer, char sep, str *line) @@ -70,17 +64,22 @@ bool al_str_get_line(str *buffer, char sep, str *line) *line = *buffer; } else { // Move to the start of the next line, if it exists. - line->data += line->len + 1; - if ((bytes = line->data - buffer->data) >= buffer->len) - return false; // No more lines. + line->data += line->length + 1; + if ((bytes = (u32)(line->data - buffer->data)) >= buffer->length) { + // No more lines. + return false; + } } - bytes = buffer->len - bytes; + if ((bytes = buffer->length - bytes) == 0) { + // buffer is completely empty. + return false; + } char *nl = (char *)al_memchr(line->data, sep, bytes); // If we couldn't find a separator, move to the end of the buffer. - line->len = !nl ? bytes : (u32)(nl - line->data); + line->length = !nl ? bytes : (u32)(nl - line->data); return true; } diff --git a/tests/array.c b/tests/array.c index 08d29eb..6cc88c6 100644 --- a/tests/array.c +++ b/tests/array.c @@ -3,23 +3,22 @@ #include <al/random.h> #include "array.h" - -struct test_t { - u32 i; -}; +#include "common.h" static bool array_test_init(void) { AL_TEST_START("array_init"); - array(u32) arr; - al_array_init(arr); + array(u32) ints; + al_array_init(ints); - AL_TEST_EQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 0, u32); - AL_TEST_EQ(arr.size, 0, u32); + AL_TEST_EQ(ints.count, 0, u32); + AL_TEST_EQ(ints.alloc, 0, u32); + AL_TEST_EQ(ints.data, NULL, ptr); - al_array_free(arr); + al_array_free(ints); + // Not a double-free. + al_array_free(ints); AL_TEST_END(); } @@ -28,25 +27,22 @@ static bool array_test_reserve(void) { AL_TEST_START("array_reserve"); - array(u32) arr; - al_array_init(arr); - - al_array_reserve(arr, 64); - - AL_TEST_NEQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 64, u32); + array(u32) ints; + al_array_init(ints); - al_array_reserve(arr, 128); + al_array_reserve(ints, 64); + AL_TEST_EQ(ints.alloc, 256, u32); + AL_TEST_NEQ(ints.data, NULL, ptr); - AL_TEST_NEQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 128, u32); + al_array_reserve(ints, 128); + AL_TEST_EQ(ints.alloc, 512, u32); + AL_TEST_NEQ(ints.data, NULL, ptr); - al_array_reserve(arr, 129); + al_array_reserve(ints, 129); + AL_TEST_EQ(ints.alloc, 1024, u32); + AL_TEST_NEQ(ints.data, NULL, ptr); - AL_TEST_NEQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 256, u32); - - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } @@ -55,15 +51,19 @@ static bool array_test_at(void) { AL_TEST_START("array_at"); - array(u32) arr; - al_array_init(arr); + array(u32) ints; + al_array_init(ints); u32 x = 5; - al_array_push(arr, x); + al_array_push(ints, x); + al_array_push(ints, (u32)4); - AL_TEST_EQ(al_array_at(arr, 0), 5, u32); + AL_TEST_EQ(al_array_at(ints, 0), 5, u32); + AL_TEST_EQ(al_array_at(ints, 1), 4, u32); + AL_TEST_EQ(ints.count, 2, u32); + AL_TEST_EQ(ints.alloc, 8, u32); - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } @@ -72,39 +72,69 @@ static bool array_test_last(void) { AL_TEST_START("array_last"); - array(u32) arr; - al_array_init(arr); + array(u32) ints; + al_array_init(ints); u32 x = 1, y = 2, z = 3; + al_array_push(ints, x); + al_array_push(ints, y); + al_array_push(ints, z); - al_array_push(arr, x); - al_array_push(arr, y); - al_array_push(arr, z); - - AL_TEST_EQ(al_array_last(arr), 3, u32); + AL_TEST_EQ(al_array_last(ints), z, u32); + AL_TEST_EQ(ints.count, 3, u32); + AL_TEST_EQ(ints.alloc, 16, u32); - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } +AL_UNUSED_FUNCTION_PUSH + +static s32 uint8_compare(const void *a, const void *b) +{ + if (*(u8 *)a < *(u8 *)b) return 1; + else if (*(u8 *)b < *(u8 *)a) return -1; + return 0; +} + +AL_UNUSED_FUNCTION_POP + static bool array_test_push(void) { AL_TEST_START("array_push"); - array(u32) arr; - al_array_init(arr); + array(u8) bytes; + al_array_init(bytes); - u32 x = 4, y = 5; + u8 increment = 0; + for (u32 i = 0; i < MAX_ALLOC_32; i++) { + al_array_push(bytes, (increment = (u8)al_add_wrap(increment, 2, UINT8_MAX))); + } + AL_TEST_EQ(bytes.count, MAX_ALLOC_32, u32); + AL_TEST_EQ(bytes.alloc, MAX_ALLOC_32, u32); + + AL_TEST_EQ(&al_array_at(bytes, MAX_ALLOC_32 - 1), &al_array_last(bytes), ptr); - al_array_push(arr, x); - al_array_push(arr, y); + AL_TEST_EQ(al_array_at(bytes, 3535), 187, u8); + AL_TEST_EQ(al_array_at(bytes, 2348151), 224, u8); + AL_TEST_EQ(al_array_at(bytes, 75134), 75, u8); + AL_TEST_EQ(al_array_at(bytes, 1735095), 152, u8); + AL_TEST_EQ(al_array_at(bytes, 191236099), 250, u8); + if (MAX_ALLOC_32 > INT32_MAX) { + AL_TEST_EQ(al_array_at(bytes, 3116282697), 236, u8); + AL_TEST_EQ(al_array_at(bytes, 2923789681), 199, u8); + } - AL_TEST_EQ(arr.size, 2, u32); - AL_TEST_EQ(al_array_at(arr, 0), x, u32); - AL_TEST_EQ(al_array_at(arr, 1), y, u32); +#if 0 // This is very slow. + al_array_sort(bytes, u8, uint8_compare); + AL_TEST_LTE(al_array_at(bytes, MAX_ALLOC_32 - 1), al_array_at(bytes, MAX_ALLOC_32 - 26000000), u8); + AL_TEST_LTE(al_array_at(bytes, MAX_ALLOC_32 - 27000000), al_array_at(bytes, MAX_ALLOC_32 - 52000000), u8); + AL_TEST_LTE(al_array_at(bytes, MAX_ALLOC_32 - 53000000), al_array_at(bytes, MAX_ALLOC_32 - 78000000), u8); + AL_TEST_LTE(al_array_at(bytes, MAX_ALLOC_32 - 79000000), al_array_at(bytes, MAX_ALLOC_32 - 104000000), u8); +#endif - al_array_free(arr); + al_array_free(bytes); AL_TEST_END(); } @@ -113,27 +143,23 @@ static bool array_test_pop(void) { AL_TEST_START("array_pop"); - array(u32) arr; - al_array_init(arr); + array(u32) ints; + al_array_init(ints); u32 x = 4, y = 5; + al_array_push(ints, x); + al_array_push(ints, y); - al_array_push(arr, x); - al_array_push(arr, y); - - u32 rx, ry; - - ry = al_array_pop(arr); + u32 ry = al_array_pop(ints); + AL_TEST_EQ(ints.count, 1, u32); - AL_TEST_EQ(arr.size, 1, u32); + u32 rx = al_array_pop(ints); + AL_TEST_EQ(ints.count, 0, u32); - rx = al_array_pop(arr); - - AL_TEST_EQ(arr.size, 0, u32); AL_TEST_EQ(rx, x, u32); AL_TEST_EQ(ry, y, u32); - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } @@ -142,28 +168,28 @@ static bool array_test_remove_at(void) { AL_TEST_START("array_remove_at"); - array(u32) arr; - al_array_init(arr); - al_array_reserve(arr, 12); + array(u32) ints; + al_array_init(ints); + al_array_reserve(ints, 12); u32 array_before[] = { 1, 2, 3, 4, 5, 6 }; u32 array_after[] = { 2, 3, 4 }; - for (u32 i = 0; i < sizeof(array_before) / sizeof(u32); i++) { - al_array_push(arr, array_before[i]); + for (u32 i = 0; i < ARRAY_SIZE(array_before); i++) { + al_array_push(ints, array_before[i]); } - al_array_remove_at(arr, 0); - al_array_remove_at(arr, 3); - al_array_remove_at(arr, 3); + al_array_remove_at(ints, 0); + al_array_remove_at(ints, 3); + al_array_remove_at(ints, 3); - AL_TEST_EQ(arr.size, (u32)(sizeof(array_after) / sizeof(u32)), u32); + AL_TEST_EQ(ints.count, ARRAY_SIZE(array_after), u32); - for (u32 i = 0; i < sizeof(array_after) / sizeof(u32); i++) { - AL_TEST_EQ(al_array_at(arr, i), array_after[i], u32); + for (u32 i = 0; i < ARRAY_SIZE(array_after); i++) { + AL_TEST_EQ(al_array_at(ints, i), array_after[i], u32); } - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } @@ -172,31 +198,33 @@ static bool array_test_remove_range(void) { AL_TEST_START("array_remove_range"); - array(u32) arr; - al_array_init(arr); - al_array_reserve(arr, 12); + array(u32) ints; + al_array_init(ints); + al_array_reserve(ints, 12); u32 array_before[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; u32 array_after[] = { 1, 3, 4, 7, 8, 11 }; - for (u32 i = 0; i < sizeof(array_before) / sizeof(u32); i++) { - al_array_push(arr, array_before[i]); + for (u32 i = 0; i < ARRAY_SIZE(array_before); i++) { + al_array_push(ints, array_before[i]); } - al_array_remove_range(arr, 1, 2); - al_array_remove_range(arr, 3, 5); - AL_TEST_EQ(arr.size, 9, u32); - al_array_remove_range(arr, 5, 7); - AL_TEST_EQ(arr.size, 7, u32); - al_array_remove_range(arr, 6, 7); + al_array_remove_range(ints, 1, 2); + al_array_remove_range(ints, 3, 5); + AL_TEST_EQ(ints.count, 9, u32); + + al_array_remove_range(ints, 5, 7); + AL_TEST_EQ(ints.count, 7, u32); + + al_array_remove_range(ints, 6, 7); - AL_TEST_EQ(arr.size, (u32)(sizeof(array_after) / sizeof(u32)), u32); + AL_TEST_EQ(ints.count, ARRAY_SIZE(array_after), u32); - for (u32 i = 0; i < sizeof(array_after) / sizeof(u32); i++) { - AL_TEST_EQ(al_array_at(arr, i), array_after[i], u32); + for (u32 i = 0; i < ARRAY_SIZE(array_after); i++) { + AL_TEST_EQ(al_array_at(ints, i), array_after[i], u32); } - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } @@ -205,33 +233,32 @@ static bool array_test_pop_at(void) { AL_TEST_START("array_pop_at"); - array(u32) arr; - al_array_init(arr); + array(u32) ints; + al_array_init(ints); u32 array_before[] = { 1, 2, 3, 4, 5, 6 }; u32 array_after[] = { 2, 3, 4 }; - for (u32 i = 0; i < sizeof(array_before) / sizeof(u32); i++) { - al_array_push(arr, array_before[i]); + for (u32 i = 0; i < ARRAY_SIZE(array_before); i++) { + al_array_push(ints, array_before[i]); } u32 x, y, z; - - al_array_pop_at(arr, 0, x); - al_array_pop_at(arr, 3, y); - al_array_pop_at(arr, 3, z); + al_array_pop_at(ints, 0, x); + al_array_pop_at(ints, 3, y); + al_array_pop_at(ints, 3, z); AL_TEST_EQ(x, array_before[0], u32); AL_TEST_EQ(y, array_before[4], u32); AL_TEST_EQ(z, array_before[5], u32); - AL_TEST_EQ(arr.size, (u32)(sizeof(array_after) / sizeof(u32)), u32); + AL_TEST_EQ(ints.count, ARRAY_SIZE(array_after), u32); - for (u32 i = 0; i < sizeof(array_after) / sizeof(u32); i++) { - AL_TEST_EQ(al_array_at(arr, i), array_after[i], u32); + for (u32 i = 0; i < ARRAY_SIZE(array_after); i++) { + AL_TEST_EQ(al_array_at(ints, i), array_after[i], u32); } - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } @@ -240,39 +267,36 @@ static bool array_test_insert(void) { AL_TEST_START("array_insert"); - array(struct test_t) arr; - al_array_init(arr); - - struct test_t x = { - .i = 5 - }; + array(struct test_t) objects; + al_array_init(objects); - struct test_t y = { - .i = 5 - }; + struct test_t x = { .d = 5 }; + struct test_t y = { .d = 2 }; + struct test_t z = { .d = 6 }; - struct test_t z = { - .i = 5 - }; + al_array_push(objects, (struct test_t){ .d = 7 }); + al_array_push(objects, (struct test_t){ .d = 9 }); + al_array_push(objects, (struct test_t){ .d = 444 }); - al_array_insert(arr, 1, x); - al_array_insert(arr, 0, y); - al_array_insert(arr, 1, z); + al_array_insert(objects, 1, x); + al_array_insert(objects, 0, y); + al_array_insert(objects, 1, z); - AL_TEST_EQ(al_array_at(arr, 3).i, x.i, u32); - AL_TEST_EQ(al_array_at(arr, 0).i, y.i, u32); - AL_TEST_EQ(al_array_at(arr, 1).i, z.i, u32); + AL_TEST_EQ(al_array_at(objects, 3).d, x.d, u32); + AL_TEST_EQ(al_array_at(objects, 0).d, y.d, u32); + AL_TEST_EQ(al_array_at(objects, 1).d, z.d, u32); - AL_TEST_EQ(arr.size, 4, u32); + AL_TEST_EQ(objects.count, 6, u32); - al_array_remove_at(arr, 3); + al_array_remove_at(objects, 3); - AL_TEST_EQ(arr.size, 3, u32); + AL_TEST_EQ(objects.count, 5, u32); - AL_TEST_EQ(al_array_at(arr, 0).i, y.i, u32); - AL_TEST_EQ(al_array_at(arr, 1).i, z.i, u32); + AL_TEST_NEQ(al_array_at(objects, 3).d, x.d, u32); + AL_TEST_EQ(al_array_at(objects, 0).d, y.d, u32); + AL_TEST_EQ(al_array_at(objects, 1).d, z.d, u32); - al_array_free(arr); + al_array_free(objects); AL_TEST_END(); } @@ -281,75 +305,75 @@ static bool array_test_foreach(void) { AL_TEST_START("array_foreach"); - array(u32) arr; - al_array_init(arr); + array(u32) ints; + al_array_init(ints); - al_array_push(arr, 0); - al_array_push(arr, 1); - al_array_push(arr, 2); - al_array_push(arr, 3); - al_array_push(arr, 4); - al_array_push(arr, 5); + al_array_push(ints, (u32)0); + al_array_push(ints, (u32)1); + al_array_push(ints, (u32)2); + al_array_push(ints, (u32)3); + al_array_push(ints, (u32)4); + al_array_push(ints, (u32)5); u32 iter0; - al_array_foreach(arr, i, iter0) { + al_array_foreach(ints, i, iter0) { AL_TEST_EQ(iter0, i, u32); } u32 iter1; - al_array_foreach_rev(arr, i, iter1) { + al_array_foreach_rev(ints, i, iter1) { AL_TEST_EQ(iter1, i, u32); } u32 *ptr0; - al_array_foreach_ptr(arr, i, ptr0) { + al_array_foreach_ptr(ints, i, ptr0) { AL_TEST_EQ(*ptr0, i, u32); } u32 iter2; - al_array_foreach_rev(arr, i, iter2) { + al_array_foreach_rev(ints, i, iter2) { AL_TEST_EQ(iter2, i, u32); - al_array_remove_at(arr, i); + al_array_remove_at(ints, i); } - AL_TEST_EQ(arr.size, 0, u32); + AL_TEST_EQ(ints.count, 0, u32); - al_array_push(arr, 0); - al_array_push(arr, 1); - al_array_push(arr, 2); - al_array_push(arr, 3); - al_array_push(arr, 4); - al_array_push(arr, 5); - al_array_push(arr, 6); - al_array_push(arr, 7); - al_array_push(arr, 8); - al_array_push(arr, 9); + al_array_push(ints, (u32)0); + al_array_push(ints, (u32)1); + al_array_push(ints, (u32)2); + al_array_push(ints, (u32)3); + al_array_push(ints, (u32)4); + al_array_push(ints, (u32)5); + al_array_push(ints, (u32)6); + al_array_push(ints, (u32)7); + al_array_push(ints, (u32)8); + al_array_push(ints, (u32)9); - u32 iter3, count0 = 0; - al_array_foreach(arr, i, iter3) { - AL_TEST_EQ(iter3, count0, u32); - count0++; - al_array_remove_at_iter(arr, i); + u32 iter3, count = 0; + al_array_foreach(ints, i, iter3) { + AL_TEST_EQ(iter3, count, u32); + count++; + al_array_remove_at_iter(ints, i); } - al_array_push(arr, 0); - al_array_push(arr, 1); - al_array_push(arr, 2); - al_array_push(arr, 3); - al_array_push(arr, 4); - al_array_push(arr, 5); - al_array_push(arr, 6); - al_array_push(arr, 7); - al_array_push(arr, 8); - al_array_push(arr, 9); + al_array_push(ints, (u32)0); + al_array_push(ints, (u32)1); + al_array_push(ints, (u32)2); + al_array_push(ints, (u32)3); + al_array_push(ints, (u32)4); + al_array_push(ints, (u32)5); + al_array_push(ints, (u32)6); + al_array_push(ints, (u32)7); + al_array_push(ints, (u32)8); + al_array_push(ints, (u32)9); u32 iter4; - al_array_foreach(arr, i, iter4) { + al_array_foreach(ints, i, iter4) { AL_TEST_EQ(iter4, i + i, u32); - al_array_remove_at(arr, i); + al_array_remove_at(ints, i); } - al_array_free(arr); + al_array_free(ints); AL_TEST_END(); } @@ -361,28 +385,81 @@ static s32 int32_compare(const void *a, const void *b) return 0; } +static s32 int32_compare_reverse(const void *a, const void *b) +{ + if (*(s32 *)a < *(s32 *)b) return -1; + else if (*(s32 *)b < *(s32 *)a) return 1; + return 0; +} + +static inline s64 sum_of_array(s32 *data, u32 count) +{ + s64 sum = 0; + while (count-- > 0) { + sum += *data++; + } + return sum; +} + static bool array_test_sort(void) { AL_TEST_START("array_sort"); - array(s32) arr; - al_array_init(arr); + array(s32) ints; + al_array_init(ints); + + al_array_push(ints, 0); + al_array_push(ints, 1); + al_array_push(ints, 2); + al_array_push(ints, 3); + al_array_push(ints, 4); + al_array_push(ints, 0); + al_array_push(ints, 99); + AL_TEST_EQ(sum_of_array(ints.data, ints.count), 109, s64); + + s32 end = al_rand() % 4000; + for (s32 i = 0; i < end; i++) { + al_array_push(ints, (al_rand() % 9001) - 9000); + } + + s64 before_sort_sum = sum_of_array(ints.data, ints.count); + al_array_sort(ints, s32, int32_compare); + AL_TEST_EQ(sum_of_array(ints.data, ints.count), before_sort_sum, s64); + + for (u32 i = 1; i < ints.count; i++) { + AL_TEST_GTE(al_array_at(ints, i - 1), al_array_at(ints, i), s32); + } - s32 end = al_rand() % 2000; + end = al_rand() % 4000; for (s32 i = 0; i < end; i++) { - al_array_push(arr, al_rand()); + al_array_push(ints, (al_rand() % 90001) - 90000); } - al_array_sort(arr, s32, int32_compare); + before_sort_sum = sum_of_array(ints.data, ints.count); + al_array_sort(ints, s32, int32_compare_reverse); + AL_TEST_EQ(sum_of_array(ints.data, ints.count), before_sort_sum, s64); - for (u32 i = 1; i < arr.size; i++) { - AL_TEST_GTE(al_array_at(arr, i - 1), al_array_at(arr, i), s32); + for (u32 i = 1; i < ints.count; i++) { + AL_TEST_LTE(al_array_at(ints, i - 1), al_array_at(ints, i), s32); + } + + end = al_rand() % 4000; + for (s32 i = 0; i < end; i++) { + al_array_push(ints, (al_rand() % 900001) - 900000); + } + + before_sort_sum = sum_of_array(ints.data, ints.count); + al_array_sort(ints, s32, int32_compare); + AL_TEST_EQ(sum_of_array(ints.data, ints.count), before_sort_sum, s64); + + for (u32 i = 1; i < ints.count; i++) { + AL_TEST_GTE(al_array_at(ints, i - 1), al_array_at(ints, i), s32); } AL_TEST_END(); } -bool array_tests_run(void) +bool array_tests_run(u32 skip_mask) { AL_TEST_START_GROUP("array"); @@ -390,7 +467,9 @@ bool array_tests_run(void) AL_TEST_RUN(array_test_reserve); AL_TEST_RUN(array_test_at); AL_TEST_RUN(array_test_last); - AL_TEST_RUN(array_test_push); + if (skip_mask & AL_TESTS_RUN_LIMITS) { + AL_TEST_RUN(array_test_push); + } AL_TEST_RUN(array_test_pop); AL_TEST_RUN(array_test_remove_at); AL_TEST_RUN(array_test_remove_range); diff --git a/tests/array.h b/tests/array.h index 881f936..8416bf2 100644 --- a/tests/array.h +++ b/tests/array.h @@ -3,6 +3,6 @@ #include <al/types.h> -bool array_tests_run(void); +bool array_tests_run(u32 skip_mask); #endif // _AL_ARRAY_TESTS_H diff --git a/tests/array_typed.c b/tests/array_typed.c index edaa52b..f61634d 100644 --- a/tests/array_typed.c +++ b/tests/array_typed.c @@ -2,28 +2,32 @@ #include <al/array_typed.h> #include "array_typed.h" +#include "common.h" -struct test_t { - u32 i; -}; +AL_UNUSED_FUNCTION_PUSH AL_ARRAY_INLINE_STORAGE_DEFINE(u32, u32, 64) AL_ARRAY_DEFINE(u32, u32) +AL_ARRAY_DEFINE(u8, u8) AL_ARRAY_INLINE_STORAGE_DEFINE(test_t, struct test_t, 16) +AL_UNUSED_FUNCTION_POP + static bool array_test_init(void) { AL_TEST_START("array_init"); - array(u32, 64) arr; - al_array_init(u32, 64)(&arr); + array(u32, 64) ints; + al_array_init(u32, 64)(&ints); - AL_TEST_EQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 64, u32); - AL_TEST_EQ(arr.size, 0, u32); - AL_TEST_EQ(sizeof(arr.inline_data), 64 * sizeof(u32), size_t); + AL_TEST_EQ(ints.count, 0, u32); + AL_TEST_EQ(ints.alloc, 256, u32); + AL_TEST_EQ(ints.data, NULL, ptr); + AL_TEST_EQ(sizeof(ints.inline_data), 64 * sizeof(u32), size_t); - al_array_free(u32, 64)(&arr); + al_array_free(u32, 64)(&ints); + // Not a double-free. + al_array_free(u32, 64)(&ints); AL_TEST_END(); } @@ -32,25 +36,22 @@ static bool array_test_reserve(void) { AL_TEST_START("array_reserve"); - array(u32, 64) arr; - al_array_init(u32, 64)(&arr); - - al_array_reserve(u32, 64)(&arr, 64); - - AL_TEST_EQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 64, u32); - - al_array_reserve(u32, 64)(&arr, 128); + array(u32, 64) ints; + al_array_init(u32, 64)(&ints); - AL_TEST_NEQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 128, u32); + al_array_reserve(u32, 64)(&ints, 64); + AL_TEST_EQ(ints.alloc, 256, u32); + AL_TEST_EQ(ints.data, NULL, ptr); - al_array_reserve(u32, 64)(&arr, 129); + al_array_reserve(u32, 64)(&ints, 128); + AL_TEST_EQ(ints.alloc, 512, u32); + AL_TEST_NEQ(ints.data, NULL, ptr); - AL_TEST_NEQ((void *)arr.data, NULL, ptr); - AL_TEST_EQ(arr.alloc, 256, u32); + al_array_reserve(u32, 64)(&ints, 129); + AL_TEST_EQ(ints.alloc, 1024, u32); + AL_TEST_NEQ(ints.data, NULL, ptr); - al_array_free(u32, 64)(&arr); + al_array_free(u32, 64)(&ints); AL_TEST_END(); } @@ -59,15 +60,19 @@ static bool array_test_at(void) { AL_TEST_START("array_at"); - array(u32, 64) arr; - al_array_init(u32, 64)(&arr); + array(u32, 64) ints; + al_array_init(u32, 64)(&ints); u32 x = 5; - al_array_push(u32, 64)(&arr, &x); + al_array_push(u32, 64)(&ints, x); + al_array_push(u32, 64)(&ints, (u32)4); - AL_TEST_EQ(*al_array_at(u32, 64)(&arr, 0), 5, u32); + AL_TEST_EQ(*al_array_at(u32, 64)(&ints, 0), 5, u32); + AL_TEST_EQ(*al_array_at(u32, 64)(&ints, 1), 4, u32); + AL_TEST_EQ(ints.count, 2, u32); + AL_TEST_EQ(ints.alloc, 256, u32); - al_array_free(u32, 64)(&arr); + al_array_free(u32, 64)(&ints); AL_TEST_END(); } @@ -76,18 +81,19 @@ static bool array_test_last(void) { AL_TEST_START("array_last"); - array(u32, 64) arr; - al_array_init(u32, 64)(&arr); + array(u32, 64) ints; + al_array_init(u32, 64)(&ints); u32 x = 1, y = 2, z = 3; + al_array_push(u32, 64)(&ints, x); + al_array_push(u32, 64)(&ints, y); + al_array_push(u32, 64)(&ints, z); - al_array_push(u32, 64)(&arr, &x); - al_array_push(u32, 64)(&arr, &y); - al_array_push(u32, 64)(&arr, &z); + AL_TEST_EQ(*al_array_last(u32, 64)(&ints), 3, u32); + AL_TEST_EQ(ints.count, 3, u32); + AL_TEST_EQ(ints.alloc, 256, u32); - AL_TEST_EQ(*al_array_last(u32, 64)(&arr), 3, u32); - - al_array_free(u32, 64)(&arr); + al_array_free(u32, 64)(&ints); AL_TEST_END(); } @@ -96,19 +102,21 @@ static bool array_test_push(void) { AL_TEST_START("array_push"); - array(u32, 64) arr; - al_array_init(u32, 64)(&arr); - - u32 x = 4, y = 5; + array(u8) bytes; + al_array_init(u8)(&bytes); - al_array_push(u32, 64)(&arr, &x); - al_array_push(u32, 64)(&arr, &y); + for (u32 i = 0; i < MAX_ALLOC_32; i++) { + al_array_push(u8)(&bytes, (u8)1); + } + AL_TEST_EQ(bytes.count, MAX_ALLOC_32, u32); + AL_TEST_EQ(bytes.alloc, MAX_ALLOC_32, u32); - AL_TEST_EQ(arr.size, 2, u32); - AL_TEST_EQ(*al_array_at(u32, 64)(&arr, 0), x, u32); - AL_TEST_EQ(*al_array_at(u32, 64)(&arr, 1), y, u32); + AL_TEST_EQ(al_array_at(u8)(&bytes, MAX_ALLOC_32 - 1), al_array_last(u8)(&bytes), ptr); + AL_TEST_EQ(*al_array_at(u8)(&bytes, MAX_ALLOC_32 - 2), 1, u8); + AL_TEST_EQ(*al_array_at(u8)(&bytes, MAX_ALLOC_32 - 3), 1, u8); + AL_TEST_EQ(*al_array_at(u8)(&bytes, MAX_ALLOC_32 - 4), 1, u8); - al_array_free(u32, 64)(&arr); + al_array_free(u8)(&bytes); AL_TEST_END(); } @@ -117,27 +125,25 @@ static bool array_test_pop(void) { AL_TEST_START("array_pop"); - array(u32, 64) arr; - al_array_init(u32, 64)(&arr); + array(u32, 64) ints; + al_array_init(u32, 64)(&ints); u32 x = 4, y = 5; - - al_array_push(u32, 64)(&arr, &x); - al_array_push(u32, 64)(&arr, &y); + al_array_push(u32, 64)(&ints, x); + al_array_push(u32, 64)(&ints, y); u32 rx, ry; - al_array_pop(u32, 64)(&arr, &ry); - - AL_TEST_EQ(arr.size, 1, u32); + al_array_pop(u32, 64)(&ints, &ry); + AL_TEST_EQ(ints.count, 1, u32); - al_array_pop(u32, 64)(&arr, &rx); + al_array_pop(u32, 64)(&ints, &rx); + AL_TEST_EQ(ints.count, 0, u32); - AL_TEST_EQ(arr.size, 0, u32); AL_TEST_EQ(rx, x, u32); AL_TEST_EQ(ry, y, u32); - al_array_free(u32, 64)(&arr); + al_array_free(u32, 64)(&ints); AL_TEST_END(); } @@ -146,28 +152,28 @@ static bool array_test_remove_at(void) { AL_TEST_START("array_remove_at"); - array(u32) arr; - al_array_init(u32)(&arr); - al_array_reserve(u32)(&arr, 12); + array(u32) ints; + al_array_init(u32)(&ints); + al_array_reserve(u32)(&ints, 12); u32 array_before[] = { 1, 2, 3, 4, 5, 6 }; u32 array_after[] = { 2, 3, 4 }; - for (u32 i = 0; i < sizeof(array_before) / sizeof(u32); i++) { - al_array_push(u32)(&arr, &array_before[i]); + for (u32 i = 0; i < ARRAY_SIZE(array_before); i++) { + al_array_push(u32)(&ints, array_before[i]); } - al_array_remove_at(u32)(&arr, 0); - al_array_remove_at(u32)(&arr, 3); - al_array_remove_at(u32)(&arr, 3); + al_array_remove_at(u32)(&ints, 0); + al_array_remove_at(u32)(&ints, 3); + al_array_remove_at(u32)(&ints, 3); - AL_TEST_EQ(arr.size, (u32)(sizeof(array_after) / sizeof(u32)), u32); + AL_TEST_EQ(ints.count, ARRAY_SIZE(array_after), u32); - for (u32 i = 0; i < sizeof(array_after) / sizeof(u32); i++) { - AL_TEST_EQ(*al_array_at(u32)(&arr, i), array_after[i], u32); + for (u32 i = 0; i < ARRAY_SIZE(array_after); i++) { + AL_TEST_EQ(*al_array_at(u32)(&ints, i), array_after[i], u32); } - al_array_free(u32)(&arr); + al_array_free(u32)(&ints); AL_TEST_END(); } @@ -176,33 +182,31 @@ static bool array_test_pop_at(void) { AL_TEST_START("array_pop_at"); - array(u32, 64) arr; - al_array_init(u32, 64)(&arr); + array(u32, 64) ints; + al_array_init(u32, 64)(&ints); u32 array_before[] = { 1, 2, 3, 4, 5, 6 }; u32 array_after[] = { 2, 3, 4 }; - for (u32 i = 0; i < sizeof(array_before) / sizeof(u32); i++) { - al_array_push(u32, 64)(&arr, &array_before[i]); + for (u32 i = 0; i < ARRAY_SIZE(array_before); i++) { + al_array_push(u32, 64)(&ints, array_before[i]); } - u32 x, y, z; - - al_array_pop_at(u32, 64)(&arr, 0, &x); - al_array_pop_at(u32, 64)(&arr, 3, &y); - al_array_pop_at(u32, 64)(&arr, 3, &z); + u32 x = al_array_pop_at(u32, 64)(&ints, 0); + u32 y = al_array_pop_at(u32, 64)(&ints, 3); + u32 z = al_array_pop_at(u32, 64)(&ints, 3); AL_TEST_EQ(x, array_before[0], u32); AL_TEST_EQ(y, array_before[4], u32); AL_TEST_EQ(z, array_before[5], u32); - AL_TEST_EQ(arr.size, (u32)(sizeof(array_after) / sizeof(u32)), u32); + AL_TEST_EQ(ints.count, ARRAY_SIZE(array_after), u32); - for (u32 i = 0; i < sizeof(array_after) / sizeof(u32); i++) { - AL_TEST_EQ(*al_array_at(u32, 64)(&arr, i), array_after[i], u32); + for (u32 i = 0; i < ARRAY_SIZE(array_after); i++) { + AL_TEST_EQ(*al_array_at(u32, 64)(&ints, i), array_after[i], u32); } - al_array_free(u32, 64)(&arr); + al_array_free(u32, 64)(&ints); AL_TEST_END(); } @@ -211,44 +215,41 @@ static bool array_test_insert(void) { AL_TEST_START("array_insert"); - array(test_t, 16) arr; - al_array_init(test_t, 16)(&arr); - - struct test_t x = { - .i = 5 - }; + array(test_t, 16) objects; + al_array_init(test_t, 16)(&objects); - struct test_t y = { - .i = 5 - }; + struct test_t x = { .d = 5 }; + struct test_t y = { .d = 2 }; + struct test_t z = { .d = 6 }; - struct test_t z = { - .i = 5 - }; + al_array_push(test_t, 16)(&objects, (struct test_t){ .d = 7 }); + al_array_push(test_t, 16)(&objects, (struct test_t){ .d = 9 }); + al_array_push(test_t, 16)(&objects, (struct test_t){ .d = 444 }); - al_array_insert(test_t, 16)(&arr, 1, &x); - al_array_insert(test_t, 16)(&arr, 0, &y); - al_array_insert(test_t, 16)(&arr, 1, &z); + al_array_insert(test_t, 16)(&objects, 1, x); + al_array_insert(test_t, 16)(&objects, 0, y); + al_array_insert(test_t, 16)(&objects, 1, z); - AL_TEST_EQ(al_array_at(test_t, 16)(&arr, 3)->i, x.i, u32); - AL_TEST_EQ(al_array_at(test_t, 16)(&arr, 0)->i, y.i, u32); - AL_TEST_EQ(al_array_at(test_t, 16)(&arr, 1)->i, z.i, u32); + AL_TEST_EQ(al_array_at(test_t, 16)(&objects, 3)->d, x.d, u32); + AL_TEST_EQ(al_array_at(test_t, 16)(&objects, 0)->d, y.d, u32); + AL_TEST_EQ(al_array_at(test_t, 16)(&objects, 1)->d, z.d, u32); - AL_TEST_EQ(arr.size, 4, u32); + AL_TEST_EQ(objects.count, 6, u32); - al_array_remove_at(test_t, 16)(&arr, 3); + al_array_remove_at(test_t, 16)(&objects, 3); - AL_TEST_EQ(arr.size, 3, u32); + AL_TEST_EQ(objects.count, 5, u32); - AL_TEST_EQ(al_array_at(test_t, 16)(&arr, 0)->i, y.i, u32); - AL_TEST_EQ(al_array_at(test_t, 16)(&arr, 1)->i, z.i, u32); + AL_TEST_NEQ(al_array_at(test_t, 16)(&objects, 0)->d, x.d, u32); + AL_TEST_EQ(al_array_at(test_t, 16)(&objects, 0)->d, y.d, u32); + AL_TEST_EQ(al_array_at(test_t, 16)(&objects, 1)->d, z.d, u32); - al_array_free(test_t, 16)(&arr); + al_array_free(test_t, 16)(&objects); AL_TEST_END(); } -bool array_typed_tests_run(void) +bool array_typed_tests_run(u32 skip_mask) { AL_TEST_START_GROUP("array_typed"); @@ -256,7 +257,9 @@ bool array_typed_tests_run(void) AL_TEST_RUN(array_test_reserve); AL_TEST_RUN(array_test_at); AL_TEST_RUN(array_test_last); - AL_TEST_RUN(array_test_push); + if (skip_mask & AL_TESTS_RUN_LIMITS) { + AL_TEST_RUN(array_test_push); + } AL_TEST_RUN(array_test_pop); AL_TEST_RUN(array_test_remove_at); AL_TEST_RUN(array_test_pop_at); diff --git a/tests/array_typed.h b/tests/array_typed.h index 644cce6..2241ede 100644 --- a/tests/array_typed.h +++ b/tests/array_typed.h @@ -3,6 +3,6 @@ #include <al/types.h> -bool array_typed_tests_run(void); +bool array_typed_tests_run(u32 skip_mask); #endif // _AL_ARRAY_TYPED_TESTS_H diff --git a/tests/common.h b/tests/common.h new file mode 100644 index 0000000..cd8e6ab --- /dev/null +++ b/tests/common.h @@ -0,0 +1,12 @@ +#pragma once + +#include <al/types.h> + +enum { + AL_TESTS_RUN_LIMITS = 1 +}; + +struct test_t { + u32 d; + u32 j; +}; diff --git a/tests/lib.c b/tests/lib.c index 0923cf1..48b6c02 100644 --- a/tests/lib.c +++ b/tests/lib.c @@ -1,37 +1,62 @@ -#include <al/array.h> #include <al/test.h> +#include <al/array.h> #include <al/lib.h> -#include <al/random.h> -struct test_t { - u32 d; -}; +#include "lib.h" +#include "common.h" static bool lib_test_add_wrap(void) { AL_TEST_START("lib_add_wrap"); - u32 x = UINT32_MAX - 5; - x = al_u32_add_wrap(x, 15); - AL_TEST_EQ(x, 10, u32); + // u32 + u32 counter32 = UINT32_MAX - 5; + counter32 = al_u32_add_wrap(counter32, 15); + AL_TEST_EQ(counter32, 10, u32); + + counter32 = UINT32_MAX; + counter32 = al_u32_add_wrap(counter32, 1); + AL_TEST_EQ(counter32, 1, u32); + + counter32 = UINT32_MAX - 25; + counter32 = al_u32_add_wrap(counter32, 20); + AL_TEST_EQ(counter32, UINT32_MAX - 5, u32); + counter32 = al_u32_add_wrap(counter32, 6); + AL_TEST_EQ(counter32, 1, u32); - x = UINT32_MAX; - x = al_u32_add_wrap(x, 1); - AL_TEST_EQ(x, 1, u32); + // u16 + u16 counter16 = UINT16_MAX - 5; + counter16 = al_u16_add_wrap(counter16, 15); + AL_TEST_EQ(counter16, 10, u16); - x = UINT32_MAX - 25; - x = al_u32_add_wrap(x, 20); - AL_TEST_EQ(x, UINT32_MAX - 5, u32); - x = al_u32_add_wrap(x, 6); - AL_TEST_EQ(x, 1, u32); + counter16 = UINT16_MAX; + counter16 = al_u16_add_wrap(counter16, 1); + AL_TEST_EQ(counter16, 1, u16); - u16 y = UINT16_MAX - 5; - y = al_u16_add_wrap(y, 15); - AL_TEST_EQ(y, 10, u16); + counter16 = UINT16_MAX - 25; + counter16 = al_u16_add_wrap(counter16, 20); + AL_TEST_EQ(counter16, UINT16_MAX - 5, u16); + counter16 = al_u16_add_wrap(counter16, 6); + AL_TEST_EQ(counter16, 1, u16); - y = UINT16_MAX; - y = al_u16_add_wrap(y, 1); - AL_TEST_EQ(y, 1, u16); + // inc_wrap + counter16 = UINT16_MAX - 4; + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), UINT16_MAX - 3, u16); + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), UINT16_MAX - 2, u16); + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), UINT16_MAX - 1, u16); + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), UINT16_MAX, u16); + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), 0, u16); + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), 1, u16); + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), 2, u16); + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), 3, u16); + + // From 3 to UINT16_MAX. + for (u16 i = 0; i < UINT16_MAX - 3; i++) { + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), i + 4, u16); + } + + // Wrap to 0. + AL_TEST_EQ((counter16 = al_u16_inc_wrap(counter16)), 0, u16); AL_TEST_END(); } @@ -40,62 +65,53 @@ static bool lib_test_offset_vs_at_and_ref(void) { AL_TEST_START("lib_offset_vs_at_and_ref"); - array(struct test_t *) ar; - al_array_init(ar); + // Test that &al_array_at and al_array_offset are equivalent and that we can + // have NULL entires in an array of pointers. + + array(struct test_t *) objects; + al_array_init(objects); for (u32 i = 0; i < 10; i++) { if (i == 4) { - al_array_push(ar, NULL); + al_array_push(objects, (struct test_t *)NULL); } else { - al_array_push(ar, al_alloc_object(struct test_t)); + al_array_push(objects, al_alloc_object(struct test_t)); } } - AL_TEST_EQ(&al_array_at(ar, 2), al_array_offset(ar, 2), ptr); - AL_TEST_EQ(&al_array_at(ar, 3), al_array_offset(ar, 3), ptr); - AL_TEST_NEQ(*&al_array_at(ar, 3), NULL, ptr); - AL_TEST_EQ(&al_array_at(ar, 4), al_array_offset(ar, 4), ptr); - AL_TEST_EQ(*&al_array_at(ar, 4), *al_array_offset(ar, 4), ptr); - AL_TEST_NEQ(*&al_array_at(ar, 4), *al_array_offset(ar, 2), ptr); - AL_TEST_EQ(*&al_array_at(ar, 4), NULL, ptr); - AL_TEST_EQ(&al_array_at(ar, 5), al_array_offset(ar, 5), ptr); + AL_TEST_EQ(&al_array_at(objects, 2), al_array_offset(objects, 2), ptr); + AL_TEST_EQ(&al_array_at(objects, 3), al_array_offset(objects, 3), ptr); + AL_TEST_NEQ(*&al_array_at(objects, 3), NULL, ptr); + + AL_TEST_EQ(&al_array_at(objects, 4), al_array_offset(objects, 4), ptr); + AL_TEST_EQ(*&al_array_at(objects, 4), *al_array_offset(objects, 4), ptr); + + AL_TEST_NEQ(*&al_array_at(objects, 4), *al_array_offset(objects, 2), ptr); + + AL_TEST_EQ(*&al_array_at(objects, 4), NULL, ptr); + AL_TEST_EQ(&al_array_at(objects, 5), al_array_offset(objects, 5), ptr); for (u32 i = 0; i < 10; i++) { if (i != 4) { - al_free(al_array_at(ar, i)); + al_free(al_array_at(objects, i)); } } AL_TEST_END(); } -/* -static bool lib_test_add_wrap_atomic(void) +static bool lib_test_min_max(void) { - AL_TEST_START("lib_add_wrap_atomic"); - - for (u32 i = 0; i < UINT16_MAX - 3; i++) { - al_inc_u16(); - } - - AL_TEST_EQ(al_inc_u16(), UINT16_MAX - 3, u16); - AL_TEST_EQ(al_inc_u16(), UINT16_MAX - 2, u16); - AL_TEST_EQ(al_inc_u16(), UINT16_MAX - 1, u16); - AL_TEST_EQ(al_inc_u16(), UINT16_MAX, u16); - AL_TEST_EQ(al_inc_u16(), 0, u16); - AL_TEST_EQ(al_inc_u16(), 1, u16); - AL_TEST_EQ(al_inc_u16(), 2, u16); - AL_TEST_EQ(al_inc_u16(), 3, u16); - - for (u32 i = 0; i < UINT16_MAX - 3; i++) { - AL_TEST_EQ(al_inc_u16(), i + 4, u16); - } + AL_TEST_START("lib_min_max"); - AL_TEST_EQ(al_inc_u16(), 0, u16); + AL_TEST_EQ(MAX((u32)4, (u32)3), 4, u32); + AL_TEST_EQ(MIN((u32)4, (u32)3), 3, u32); + // Compiler warnings. + //AL_TEST_EQ(MAX(4, (u32)3), 4, u32); + //AL_TEST_EQ(MIN(4, (u32)3), 3, u32); AL_TEST_END(); } -*/ bool lib_tests_run(void) { @@ -103,7 +119,7 @@ bool lib_tests_run(void) AL_TEST_RUN(lib_test_add_wrap); AL_TEST_RUN(lib_test_offset_vs_at_and_ref); - //AL_TEST_RUN(lib_test_add_wrap_atomic); + AL_TEST_RUN(lib_test_min_max); AL_TEST_END_GROUP(); } diff --git a/tests/main.c b/tests/main.c index 5964a04..672511a 100644 --- a/tests/main.c +++ b/tests/main.c @@ -1,5 +1,7 @@ #include <al/lib.h> +#include <al/str.h> #include <al/random.h> +#include <time.h> #include "lib.h" #include "array.h" @@ -8,18 +10,31 @@ #endif #include "str.h" #include "ring_buffer.h" +#include "common.h" -s32 main(void) +s32 main(s32 argc, char *argv[]) { - al_rand_init(); + al_rand_init((u32)time(NULL)); - if (!lib_tests_run()) goto fail; - if (!array_tests_run()) goto fail; + u32 skip_mask = 0; + if (argc > 1) { + for (s32 i = 1; i < argc; i++) { + if (al_str_eq(&al_str_cr(argv[i]), &al_str_c("run_limits"))) { + skip_mask |= AL_TESTS_RUN_LIMITS; + al_printf("MAX_ALLOC_32: %u, INT32_MAX: %u, UINT32_MAX: %u.\n", + MAX_ALLOC_32, INT32_MAX, UINT32_MAX); + } + } + } + + + if (!array_tests_run(skip_mask)) goto fail; #ifdef AL_HAVE_GNU_EXTENSIONS - if (!array_typed_tests_run()) goto fail; + if (!array_typed_tests_run(skip_mask)) goto fail; #endif - if (!str_tests_run()) goto fail; + if (!str_tests_run(skip_mask)) goto fail; if (!ring_buffer_tests_run()) goto fail; + if (!lib_tests_run()) goto fail; return EXIT_SUCCESS; diff --git a/tests/ring_buffer.c b/tests/ring_buffer.c index 30b583e..97286a3 100644 --- a/tests/ring_buffer.c +++ b/tests/ring_buffer.c @@ -2,22 +2,24 @@ #include <al/ring_buffer.h> #include <al/atomic.h> -#define TEST_BUFFER_SIZE 32 +#include "ring_buffer.h" -static u8 buffer[TEST_BUFFER_SIZE]; -static u8 buffer_ret[TEST_BUFFER_SIZE]; -static u8 buffer_half[TEST_BUFFER_SIZE / 2] = {0}; -static u8 buffer_over[TEST_BUFFER_SIZE + 16] = {0}; +#define BUFFER_SIZE 32 + +static u8 buffer[BUFFER_SIZE]; +static u8 output[BUFFER_SIZE]; +static u8 half_buffer[BUFFER_SIZE / 2] = { 0 }; +static u8 bigger_buffer[BUFFER_SIZE + 16] = { 0 }; static bool ring_buffer_test_init(void) { AL_TEST_START("ring_buffer_init"); struct al_ring_buffer buf; - al_ring_buffer_init(&buf, buffer, TEST_BUFFER_SIZE); + al_ring_buffer_init(&buf, buffer, BUFFER_SIZE); AL_TEST_EQ(buf.start, buffer, ptr); - AL_TEST_EQ(buf.end, buffer + TEST_BUFFER_SIZE, ptr); + AL_TEST_EQ(buf.end, buffer + BUFFER_SIZE, ptr); AL_TEST_EQ(al_atomic_load(void)(&buf.read, AL_ATOMIC_RELAXED), buffer, ptr); AL_TEST_EQ(al_atomic_load(void)(&buf.write, AL_ATOMIC_RELAXED), buffer, ptr); @@ -29,9 +31,9 @@ static bool ring_buffer_test_space(void) AL_TEST_START("ring_buffer_space"); struct al_ring_buffer buf; - al_ring_buffer_init(&buf, buffer, TEST_BUFFER_SIZE); + al_ring_buffer_init(&buf, buffer, BUFFER_SIZE); - AL_TEST_EQ(al_ring_buffer_space(&buf), TEST_BUFFER_SIZE - 1, size_t); + AL_TEST_EQ(al_ring_buffer_space(&buf), BUFFER_SIZE - 1, ptrdiff_t); AL_TEST_END(); } @@ -41,9 +43,9 @@ static bool ring_buffer_test_occupied(void) AL_TEST_START("ring_buffer_occupied"); struct al_ring_buffer buf; - al_ring_buffer_init(&buf, buffer, TEST_BUFFER_SIZE); + al_ring_buffer_init(&buf, buffer, BUFFER_SIZE); - AL_TEST_EQ(al_ring_buffer_occupied(&buf), 0, size_t); + AL_TEST_EQ(al_ring_buffer_occupied(&buf), 0, ptrdiff_t); AL_TEST_END(); } @@ -53,29 +55,28 @@ static bool ring_buffer_test_read_and_write_chunk(void) AL_TEST_START("ring_buffer_read_and_write_chunk"); struct al_ring_buffer buf; - al_ring_buffer_init(&buf, buffer, TEST_BUFFER_SIZE); + al_ring_buffer_init(&buf, buffer, BUFFER_SIZE); char *data = "TestTEST"; - size_t data_size = al_strlen(data) + 1; + ptrdiff_t data_size = (ptrdiff_t)(al_strlen(data) + 1); - size_t size; + ptrdiff_t size; u8 *ptr = al_ring_buffer_write_chunk(&buf, &size); + AL_TEST_EQ(size, BUFFER_SIZE - 1, ptrdiff_t); - AL_TEST_EQ(size, TEST_BUFFER_SIZE - 1, size_t); + al_memcpy(ptr, data, (size_t)data_size); - al_memcpy(ptr, data, data_size); al_ring_buffer_append(&buf, ptr, data_size); - - AL_TEST_EQ(al_ring_buffer_occupied(&buf), data_size, size_t); - AL_TEST_EQ(al_ring_buffer_space(&buf), (TEST_BUFFER_SIZE - 1) - data_size, size_t); + AL_TEST_EQ(al_ring_buffer_occupied(&buf), data_size, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_space(&buf), (BUFFER_SIZE - 1) - data_size, ptrdiff_t); ptr = al_ring_buffer_read_chunk(&buf, &size); - AL_TEST_EQ(size, data_size, size_t); - AL_TEST_TRUE((strcmp((char *)ptr, data) == 0)); - al_ring_buffer_consume(&buf, ptr, size); + AL_TEST_EQ(size, data_size, ptrdiff_t); + AL_TEST_TRUE(strcmp((char *)ptr, data) == 0); - AL_TEST_EQ(al_ring_buffer_occupied(&buf), 0, size_t); - AL_TEST_EQ(al_ring_buffer_space(&buf), TEST_BUFFER_SIZE - 1, size_t); + al_ring_buffer_consume(&buf, ptr, size); + AL_TEST_EQ(al_ring_buffer_occupied(&buf), 0, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_space(&buf), BUFFER_SIZE - 1, ptrdiff_t); AL_TEST_END(); } @@ -85,40 +86,45 @@ static bool ring_buffer_test_read_and_write(void) AL_TEST_START("ring_buffer_read_and_write"); struct al_ring_buffer buf; - al_ring_buffer_init(&buf, buffer, TEST_BUFFER_SIZE); + al_ring_buffer_init(&buf, buffer, BUFFER_SIZE); char *data = "TestTEST"; - size_t data_size = al_strlen(data) + 1; + ptrdiff_t data_size = (ptrdiff_t)(al_strlen(data) + 1); al_ring_buffer_write(&buf, (u8 *)data, data_size); + AL_TEST_EQ(al_ring_buffer_occupied(&buf), data_size, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_space(&buf), (BUFFER_SIZE - 1) - data_size, ptrdiff_t); - AL_TEST_EQ(al_ring_buffer_occupied(&buf), data_size, size_t); - AL_TEST_EQ(al_ring_buffer_space(&buf), (TEST_BUFFER_SIZE - 1) - data_size, size_t); - - al_ring_buffer_read(&buf, (u8 *)buffer_ret, data_size); - AL_TEST_TRUE((strcmp((char *)buffer_ret, data) == 0)); + al_ring_buffer_read(&buf, (u8 *)output, data_size); + AL_TEST_TRUE(strcmp((char *)output, data) == 0); + AL_TEST_EQ(al_ring_buffer_occupied(&buf), 0, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_space(&buf), BUFFER_SIZE - 1, ptrdiff_t); - AL_TEST_EQ(al_ring_buffer_occupied(&buf), 0, size_t); - AL_TEST_EQ(al_ring_buffer_space(&buf), TEST_BUFFER_SIZE - 1, size_t); + al_memset(half_buffer, 'd', sizeof(half_buffer)); + AL_TEST_EQ(al_ring_buffer_write(&buf, half_buffer, sizeof(half_buffer)), sizeof(half_buffer), ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_occupied(&buf), sizeof(half_buffer), ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_space(&buf), (BUFFER_SIZE - 1) - sizeof(half_buffer), ptrdiff_t); - al_memset(buffer_half, 'd', sizeof(buffer_half)); + // Arbitrarily read data_size from what was written above. + AL_TEST_EQ(al_ring_buffer_read(&buf, (u8 *)output, data_size), data_size, ptrdiff_t); + AL_TEST_TRUE(al_memcmp(output, "ddddddddd", (size_t)data_size) == 0); - AL_TEST_EQ(al_ring_buffer_write(&buf, buffer_half, sizeof(buffer_half)), sizeof(buffer_half), size_t); - AL_TEST_EQ(al_ring_buffer_occupied(&buf), sizeof(buffer_half), size_t); - AL_TEST_EQ(al_ring_buffer_space(&buf), (TEST_BUFFER_SIZE - 1) - sizeof(buffer_half), size_t); - - AL_TEST_EQ(al_ring_buffer_read(&buf, (u8 *)buffer_ret, data_size), data_size, size_t); - AL_TEST_TRUE(al_memcmp(buffer_ret, "ddddddddd", data_size) == 0); - - AL_TEST_EQ(al_ring_buffer_write(&buf, buffer_half, sizeof(buffer_half)), sizeof(buffer_half), size_t); - AL_TEST_EQ(al_ring_buffer_occupied(&buf), TEST_BUFFER_SIZE - data_size, size_t); + AL_TEST_EQ(al_ring_buffer_write(&buf, half_buffer, sizeof(half_buffer)), sizeof(half_buffer), ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_occupied(&buf), BUFFER_SIZE - data_size, ptrdiff_t); + // Test that we wrapped. AL_TEST_LT(al_atomic_load(void)(&buf.write, AL_ATOMIC_RELAXED), al_atomic_load(void)(&buf.read, AL_ATOMIC_RELAXED), ptr); - size_t space = al_ring_buffer_space(&buf); - AL_TEST_EQ(space, data_size - 1, size_t); - AL_TEST_EQ(al_ring_buffer_write(&buf, buffer_over, sizeof(buffer_over)), space, size_t); + ptrdiff_t space = al_ring_buffer_space(&buf); + // Maximum space is BUFFER_SIZE - 1. + AL_TEST_EQ(space, data_size - 1, ptrdiff_t); + al_memset(bigger_buffer, 'r', sizeof(bigger_buffer)); + // Test impartial write because we're out of space. + AL_TEST_EQ(al_ring_buffer_write(&buf, bigger_buffer, sizeof(bigger_buffer)), space, ptrdiff_t); - AL_TEST_EQ(al_ring_buffer_read(&buf, buffer_ret, sizeof(buffer_over)), TEST_BUFFER_SIZE - 1, size_t); + // Test we can read everything. + AL_TEST_EQ(al_ring_buffer_read(&buf, output, sizeof(bigger_buffer)), BUFFER_SIZE - 1, ptrdiff_t); + AL_TEST_TRUE(al_memcmp(output, half_buffer, sizeof(half_buffer)) == 0); + AL_TEST_TRUE(al_memcmp(output + ((BUFFER_SIZE - 1) - space), bigger_buffer, (size_t)space) == 0); AL_TEST_END(); } @@ -128,58 +134,63 @@ static bool ring_buffer_test_peek(void) AL_TEST_START("ring_buffer_peek"); struct al_ring_buffer buf; - al_ring_buffer_init(&buf, buffer, TEST_BUFFER_SIZE); + al_ring_buffer_init(&buf, buffer, BUFFER_SIZE); - char *data = "abcabcabcabcabcabcabcabcabcabca"; - size_t data_size = al_strlen(data); - AL_TEST_EQ(data_size, TEST_BUFFER_SIZE - 1, size_t); + char *data = "aBcDeFgHiJkLmNoPqRsTuVwXyZ12345"; + ptrdiff_t data_size = (ptrdiff_t)al_strlen(data); + AL_TEST_EQ(data_size, BUFFER_SIZE - 1, ptrdiff_t); al_ring_buffer_write(&buf, (u8 *)data, data_size); - al_ring_buffer_peek(&buf, buffer_ret, 0, data_size); - AL_TEST_TRUE(al_memcmp(buffer_ret, data, data_size) == 0); + // Peek everything. + al_ring_buffer_peek(&buf, output, 0, data_size); + AL_TEST_TRUE(al_memcmp(output, data, (size_t)data_size) == 0); - al_ring_buffer_peek(&buf, buffer_ret, 3, data_size); - AL_TEST_TRUE(al_memcmp(buffer_ret, data + 3, data_size - 3) == 0); + // Peek from an offset of 3. + al_ring_buffer_peek(&buf, output, 3, data_size); + AL_TEST_TRUE(al_memcmp(output, data + 3, (size_t)(data_size - 3)) == 0); - al_ring_buffer_read(&buf, buffer_ret, 3); - AL_TEST_TRUE(al_memcmp(buffer_ret, data, 3) == 0); + // Read 3 bytes and peek the remaining + al_ring_buffer_read(&buf, output, 3); + AL_TEST_TRUE(al_memcmp(output, data, 3) == 0); al_ring_buffer_write(&buf, (u8 *)data, data_size); - al_ring_buffer_peek(&buf, buffer_ret, 3, data_size); - AL_TEST_TRUE(al_memcmp(buffer_ret, data + 6, data_size - 6) == 0); - - al_ring_buffer_peek(&buf, buffer_ret, 0, data_size); - AL_TEST_TRUE(al_memcmp(buffer_ret, "abcabcabcabcabcabcabcabcabcaabc", data_size) == 0); - - al_ring_buffer_read(&buf, buffer_ret, (TEST_BUFFER_SIZE - 1) - 4); - - al_ring_buffer_peek(&buf, buffer_ret, 0, data_size); - AL_TEST_TRUE(al_memcmp(buffer_ret, "aab", 3) == 0); + ptrdiff_t occupied = al_ring_buffer_occupied(&buf); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 3, data_size), occupied - 3, ptrdiff_t); + AL_TEST_TRUE(al_memcmp(output, data + 6, (size_t)(data_size - 6)) == 0); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 10, data_size), 0, size_t); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 3, data_size), 1, size_t); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 4, data_size), 0, size_t); + // Peek everything again. + al_ring_buffer_peek(&buf, output, 0, data_size); + AL_TEST_TRUE(al_memcmp(output, "DeFgHiJkLmNoPqRsTuVwXyZ12345aBc", (size_t)data_size) == 0); - al_ring_buffer_peek(&buf, buffer_ret, 1, data_size); - AL_TEST_TRUE(al_memcmp(buffer_ret, "abc", 3) == 0); + // Read up to the last 4 bytes. + al_ring_buffer_read(&buf, output, (BUFFER_SIZE - 1) - 4); + al_ring_buffer_peek(&buf, output, 0, data_size); + AL_TEST_TRUE(al_memcmp(output, "5aBc", 4) == 0); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 2, data_size), 2, size_t); - AL_TEST_TRUE(al_memcmp(buffer_ret, "bc", 2) == 0); + // Test peek truncating the offset. + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 10, data_size), 0, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 3, data_size), 1, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 4, data_size), 0, ptrdiff_t); - al_ring_buffer_read(&buf, buffer_ret, 1); + // Incrementally read the rest of the buffer. + al_ring_buffer_peek(&buf, output, 1, data_size); + AL_TEST_TRUE(al_memcmp(output, "aBc", 3) == 0); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 0, data_size), 3, size_t); - AL_TEST_TRUE(al_memcmp(buffer_ret, "abc", 3) == 0); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 2, data_size), 2, ptrdiff_t); + AL_TEST_TRUE(al_memcmp(output, "Bc", 2) == 0); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 3, data_size), 0, size_t); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 4, data_size), 0, size_t); + al_ring_buffer_read(&buf, output, 1); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 0, data_size), 3, ptrdiff_t); + AL_TEST_TRUE(al_memcmp(output, "aBc", 3) == 0); - AL_TEST_EQ(al_ring_buffer_read(&buf, buffer_ret, 4), 3, size_t); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 3, data_size), 0, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 4, data_size), 0, ptrdiff_t); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 0, data_size), 0, size_t); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 1, data_size), 0, size_t); - AL_TEST_EQ(al_ring_buffer_peek(&buf, buffer_ret, 2, data_size), 0, size_t); + AL_TEST_EQ(al_ring_buffer_read(&buf, output, 4), 3, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 0, data_size), 0, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 1, data_size), 0, ptrdiff_t); + AL_TEST_EQ(al_ring_buffer_peek(&buf, output, 999, data_size), 0, ptrdiff_t); AL_TEST_END(); } diff --git a/tests/str.c b/tests/str.c index 72ed81f..51b0bf1 100644 --- a/tests/str.c +++ b/tests/str.c @@ -2,30 +2,41 @@ #include <al/str.h> #include <al/wstr.h> +#include "str.h" +#include "common.h" + static bool str_test_cmp(void) { AL_TEST_START("str_cmp"); - str *s0 = al_str_c("testtestasdf"); - str *s1 = al_str_c("testtestasdf"); - str *s2 = al_str_c("testtedfdfstasdf"); - str *s3 = al_str_c("dtesttedfdfstasdf"); + str string0 = al_str_c("The Snake"); + str string1 = al_str_c("The Rat"); + str string2 = al_str_c("The Cat"); + str string3 = al_str_c("The Dog"); + str string4 = al_str_c("The Snake"); + + AL_TEST_FALSE(al_str_eq(&string0, &string1)); + AL_TEST_FALSE(al_str_eq(&string0, &string2)); + AL_TEST_FALSE(al_str_eq(&string0, &string3)); + AL_TEST_FALSE(al_str_eq(&string2, &string3)); + AL_TEST_TRUE(al_str_eq(&string3, &string3)); + AL_TEST_TRUE(al_str_eq(&string4, &string0)); - AL_TEST_TRUE(al_str_eq(s0, s1)); - AL_TEST_FALSE(al_str_eq(s0, s2)); - AL_TEST_FALSE(al_str_eq(s0, s3)); - AL_TEST_FALSE(al_str_eq(s2, s3)); + AL_TEST_EQ(al_str_cmp(&string0, &al_str_c("The "), 0, 4), 0, s32); + AL_TEST_EQ(al_str_cmp(&string0, &al_str_c(" S"), 3, 2), 0, s32); + // Sn[a]ke + AL_TEST_LT(al_str_cmp(&string0, &al_str_c("b"), 6, 1), 0, s32); + AL_TEST_GT(al_str_cmp(&string0, &al_str_cs(((char[]){ 'a' - 1 })), 6, 1), 0, s32); - AL_TEST_EQ(al_str_cmp(s0, al_str_c("test"), 0, 4), 0, u32); - AL_TEST_EQ(al_str_cmp(s0, al_str_c("tt"), 3, 2), 0, u32); - AL_TEST_GT(al_str_cmp(s0, al_str_c("r"), 6, 1), 0, u32); - AL_TEST_EQ(al_str_cmp(s0, al_str_c("ee"), 15, 2), -1, u32); + AL_TEST_EQ(al_str_cmp(&string0, &al_str_c("XX"), 15, 2), -1, s32); + AL_TEST_EQ(al_str_cmp(&al_str_c("YY"), &string2, string2.length, 1), -1, s32); + AL_TEST_EQ(al_str_cmp(&string2, &al_str_c("ZZ"), 0, string2.length), 1, s32); - str *s4 = al_str_c("page"); - str *s5 = al_str_c("page.json"); + str string5 = al_str_c("the_database"); + str string6 = al_str_c("the_database.json"); - AL_TEST_FALSE(al_str_eq(s5, s4)); - AL_TEST_FALSE(al_str_eq(s4, s5)); + AL_TEST_FALSE(al_str_eq(&string6, &string5)); + AL_TEST_FALSE(al_str_eq(&string5, &string6)); AL_TEST_END(); } @@ -34,18 +45,18 @@ static bool str_test_cat(void) { AL_TEST_START("str_cat"); - str *result = al_str_c("testtestasdf"); + str result = al_str_c("hygshiylitf"); - str s0; - al_str_from(&s0, ""); + str string0; + al_str_from(&string0, "hyg"); - AL_TEST_EQ(s0.len, 0, u32); + AL_TEST_EQ(string0.length, 3, u32); - al_str_cat(&s0, al_str_c("testtestasdf")); + al_str_cat(&string0, &al_str_c("shiylitf")); - AL_TEST_TRUE(al_str_eq(result, &s0)); + AL_TEST_TRUE(al_str_eq(&result, &string0)); - al_str_free(&s0); + al_str_free(&string0); AL_TEST_END(); } @@ -54,20 +65,18 @@ static bool str_test_insert(void) { AL_TEST_START("str_insert"); - str *insert_result = al_str_c("testtest[insert]asdf"); - str *prepend_result = al_str_c("[prepend]testtestasdf"); + str insert_result = al_str_c("^_-^-^--__-^[insert]_-^--^___"); + str prepend_result = al_str_c("[prepend]-^_^_^---^^-__"); str insert_str; - str prepend_str; - - al_str_from(&insert_str, "testtestasdf"); - al_str_from(&prepend_str, "testtestasdf"); + al_str_from(&insert_str, "^_-^-^--__-^_-^--^___"); + al_str_insert(&insert_str, 12, &al_str_c("[insert]")); + AL_TEST_TRUE(al_str_eq(&insert_result, &insert_str)); - al_str_insert(&insert_str, 8, al_str_c("[insert]")); - al_str_prepend(&prepend_str, al_str_c("[prepend]")); - - AL_TEST_TRUE(al_str_eq(insert_result, &insert_str)); - AL_TEST_TRUE(al_str_eq(prepend_result, &prepend_str)); + str prepend_str; + al_str_from(&prepend_str, "-^_^_^---^^-__"); + al_str_prepend(&prepend_str, &al_str_c("[prepend]")); + AL_TEST_TRUE(al_str_eq(&prepend_result, &prepend_str)); al_str_free(&insert_str); al_str_free(&prepend_str); @@ -79,12 +88,14 @@ static bool str_test_find_rfind(void) { AL_TEST_START("str_find_rfind"); - str *s0 = al_str_c("testtes\\ta\\sdf"); + str string0 = al_str_c("wowowow\\$$\\wow"); - AL_TEST_EQ(al_str_find(s0, '\\'), 7, s32); - AL_TEST_EQ(al_str_rfind(s0, '\\'), 10, s32); + AL_TEST_EQ(al_str_find(&string0, '\\'), 7, u32); + AL_TEST_EQ(al_str_rfind(&string0, '\\'), 10, u32); + AL_TEST_EQ(al_str_find(&string0, 'P'), al_str_npos, u32); - AL_TEST_EQ(al_str_find_str(s0, al_str_c("tes\\")), 4, s32); + AL_TEST_EQ(al_str_find_str(&string0, &al_str_c("wow\\")), 4, u32); + AL_TEST_EQ(al_str_find_str(&string0, &al_str_c("John\\")), al_str_npos, u32); AL_TEST_END(); } @@ -93,25 +104,25 @@ static bool str_test_substr(void) { AL_TEST_START("str_substr"); - str *result0 = al_str_c("[cutout0]"); - str *result1 = al_str_c("[cutout1]"); + str result0 = al_str_c("[cutout0]"); + str result1 = al_str_c("[cutout1]"); str s; - al_str_from(&s, "dfkdljfd[cutout0]dfd[cutout1]fd"); + al_str_from(&s, "88888888[cutout0]999[cutout1]10"); - s32 start = al_str_find(&s, '['); - s32 end = al_str_find(&s, ']') + 1; + u32 start = al_str_find(&s, '['); + u32 end = al_str_find(&s, ']') + 1; - str *cutout0 = al_str_substr(&s, start, end); - str *sub = al_str_substr(&s, end, s.len); + str cutout0 = al_str_substr(&s, start, end); + str sub = al_str_substr(&s, end, s.length); - start = al_str_find(sub, '['); - end = al_str_find(sub, ']') + 1; + start = al_str_find(&sub, '['); + end = al_str_find(&sub, ']') + 1; - str *cutout1 = al_str_substr(sub, start, end); + str cutout1 = al_str_substr(&sub, start, end); - AL_TEST_TRUE(al_str_eq(result0, cutout0)); - AL_TEST_TRUE(al_str_eq(result1, cutout1)); + AL_TEST_TRUE(al_str_eq(&result0, &cutout0)); + AL_TEST_TRUE(al_str_eq(&result1, &cutout1)); al_str_free(&s); @@ -122,47 +133,38 @@ static bool str_test_get_line(void) { AL_TEST_START("str_get_line"); - str *lines0 = al_str_c("test\nasdf\ntest2\na\n "); - str *lines_array0[] = { al_str_c("test"), al_str_c("asdf"), al_str_c("test2"), al_str_c("a"), al_str_c(" ") }; + str string0 = al_str_c("Yellow\nmellow\nhello,\nhello\n,\n"); + str array0[] = { al_str_c("Yellow"), al_str_c("mellow"), al_str_c("hello,"), al_str_c("hello"), al_str_c(",") }; - str *lines1 = al_str_c("test\nasdf\ntest2\na\n"); - str *lines_array1[] = { al_str_c("test"), al_str_c("asdf"), al_str_c("test2"), al_str_c("a") }; + str string1 = al_str_c("Yellow\nmellow\nhello,\nhello\n,\n "); + str array1[] = { al_str_c("Yellow"), al_str_c("mellow"), al_str_c("hello,"), al_str_c("hello"), al_str_c(","), al_str_c(" ") }; - str *lines; - str **lines_array; - u32 lines_array_size; + str strings[] = { string0, string1 }; + str *arrays[] = { array0, array1 }; + u32 sizes[] = { ARRAY_SIZE(array0), ARRAY_SIZE(array1) }; - for (u32 i = 0; i < 2; i++) { - if (i == 0) { - lines = lines0; - lines_array = lines_array0; - lines_array_size = (u32)ARRAY_SIZE(lines_array0); - } else if (i == 1) { - lines = lines1; - lines_array = lines_array1; - lines_array_size = (u32)ARRAY_SIZE(lines_array1); + for (u32 i = 0; i < ARRAY_SIZE(strings); i++) { + u32 counter = 0; + str line = al_str_null(); + for (; al_str_get_line(&strings[i], '\n', &line); ) { + AL_TEST_LT(counter, sizes[i], u32); + AL_TEST_TRUE(al_str_eq(&line, &arrays[i][counter++])); } - str line = al_str_zero(); - u32 c = 0; + AL_TEST_EQ(counter, sizes[i], u32); - for (; al_str_get_line(lines, '\n', &line); ) { - AL_TEST_LT(c, lines_array_size, u32); - AL_TEST_TRUE(al_str_eq(&line, lines_array[c++])); + counter = 0; + line = al_str_null(); + // One empty line. + for (; al_str_get_line(&al_str_c("\n"), '\n', &line); ) { + AL_TEST_LT(counter, 1, u32); + AL_TEST_EQ(line.length, 0, u32); + counter++; } - AL_TEST_EQ(c, lines_array_size, u32); - - lines = al_str_c(""); - - al_memset(&line, 0, sizeof(str)); - c = 0; - - for (; al_str_get_line(lines, '\n', &line); ) { - AL_TEST_LT(c, 1, u32); - AL_TEST_EQ(line.len, 0, u32); - c++; - } + // No lines. + line = al_str_null(); + AL_TEST_FALSE(al_str_get_line(&al_str_c(""), '\n', &line)); } AL_TEST_END(); @@ -172,54 +174,155 @@ static bool str_test_to_long(void) { AL_TEST_START("str_to_long"); - str *s0 = al_str_c("10353"); - str *s1 = al_str_c("-3563"); - str *s2 = al_str_c("-2147483647"); // 1 away - // This fails on MSVC for some reason. - str *s2_1 = al_str_c("-2147483648"); // min - str *s2_2 = al_str_c("-2147483649"); // underflow - str *s3 = al_str_c("0xfff"); - str *s4 = al_str_c("0b01010"); - str *s5 = al_str_c("01010"); - str *s6 = al_str_c("99999999999999999999"); - str *s7 = al_str_c(" -99999999999999999999"); - str *s8 = al_str_c("---99999999999999999999"); - str *s9 = al_str_c("'p[afd'dfdfs[f"); - str *s10 = al_str_c("\n\t 01010"); - str *s11 = al_str_c("-9223372036854775809"); - str *s12 = al_str_c("6519749124957310923"); - str *s13 = al_str_c("-3519311901742109472"); + str string0 = al_str_c("1738"); + str string1 = al_str_c("-3563"); + str string2 = al_str_c("-2147483647"); // 1 away (32 bit) + str string2_1 = al_str_c("-2147483648"); // min (32 bit) + str string2_2 = al_str_c("-2147483649"); // underflow (32 bit) + str string2_3 = al_str_c("9223372036854775807"); // int64_max + str string2_4 = al_str_c("9223372036854775806"); + str string2_5 = al_str_c("9223372036854775808"); // overflow + str string2_6 = al_str_c("0x7fffffffffffffff"); // int64_max + str string2_7 = al_str_c("0x8000000000000000"); // overflow + str string2_8 = al_str_c("-0x8000000000000000"); // int64_min + str string2_9 = al_str_c("0b111111111111111111111111111111111111111111111111111111111111111"); // int64_max + str string2_10 = al_str_c("0b1000000000000000000000000000000000000000000000000000000000000000"); // overflow + str string2_11 = al_str_c("-0b1000000000000000000000000000000000000000000000000000000000000000"); // int64_min + str string3 = al_str_c("0xfff"); + str string4 = al_str_c("0b01010"); + str string5 = al_str_c("01010"); + str string6 = al_str_c("9999999999999999999999999999999999"); + str string7 = al_str_c(" -99999999999999999999"); + str string8 = al_str_c("---99999999999999999999"); + str string9 = al_str_c("'p[afd'dfdfs[f"); + str string10 = al_str_c("\n\t 01010"); + str string11 = al_str_c("-9223372036854775809"); + str string12 = al_str_c("6519749124957310923"); + str string13 = al_str_c("-3519311901742109472"); + str string14 = al_str_c("18446744073709551614"); + str string15 = al_str_c("-18446744073709551616"); + + bool error; + + AL_TEST_EQ(al_str_to_long(&string0, 10, &error), 1738LL, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string1, 10, &error), -3563LL, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string2, 10, &error), -2147483647LL, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string2_1, 10, &error), -2147483648LL, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string2_2, 10, &error), -2147483649LL, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string2_3, 10, &error), INT64_MAX, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string2_4, 10, &error), INT64_MAX - 1, s64); + AL_TEST_FALSE(error); + + al_str_to_long(&string2_5, 10, &error); + AL_TEST_TRUE(error); + + AL_TEST_EQ(al_str_to_long(&string2_6, 16, &error), INT64_MAX, s64); + AL_TEST_FALSE(error); + + al_str_to_long(&string2_7, 16, &error); + AL_TEST_TRUE(error); + + AL_TEST_EQ(al_str_to_long(&string2_8, 16, &error), INT64_MIN, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string2_9, 2, &error), INT64_MAX, s64); + AL_TEST_FALSE(error); + + al_str_to_long(&string2_10, 2, &error); + AL_TEST_TRUE(error); + + AL_TEST_EQ(al_str_to_long(&string2_11, 2, &error), INT64_MIN, s64); + AL_TEST_FALSE(error); - AL_TEST_EQ(al_str_to_long(s0, 10), 10353L, s64); - AL_TEST_EQ(al_str_to_long(s1, 10), -3563L, s64); - AL_TEST_EQ(al_str_to_long(s2, 10), -2147483647L, s64); - AL_TEST_EQ(al_str_to_long(s2_1, 10), -2147483648L, s64); - AL_TEST_EQ(al_str_to_long(s2_2, 10), -2147483649L, s64); - AL_TEST_EQ(al_str_to_long(s3, 16), 0xfff, s64); - AL_TEST_EQ(al_str_to_long(s4, 2), 0b01010, s64); - AL_TEST_EQ(al_str_to_long(s5, 2), 0b01010, s64); - AL_TEST_EQ(al_str_to_long(s6, 10), INT64_MAX, s64); - AL_TEST_EQ(al_str_to_long(s7, 10), INT64_MIN, s64); - AL_TEST_EQ(al_str_to_long(s8, 10), INT64_MIN, s64); - AL_TEST_EQ(al_str_to_long(s9, 10), INT64_MAX, s64); - AL_TEST_EQ(al_str_to_long(s10, 2), 0b01010, s64); - AL_TEST_EQ(al_str_to_long(s11, 10), INT64_MIN, s64); - AL_TEST_EQ(al_str_to_long(s12, 10), 6519749124957310923L, s64); - AL_TEST_EQ(al_str_to_long(s13, 10), -3519311901742109472, s64); + AL_TEST_EQ(al_str_to_long(&string3, 16, &error), 0xfff, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string4, 2, &error), 0b01010, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string5, 2, &error), 0b01010, s64); + AL_TEST_FALSE(error); + + al_str_to_long(&string6, 10, &error); + AL_TEST_TRUE(error); + + al_str_to_long(&string7, 10, &error); + AL_TEST_TRUE(error); + + al_str_to_long(&string8, 10, &error); + AL_TEST_TRUE(error); + + al_str_to_long(&string9, 10, &error); + AL_TEST_TRUE(error); + + AL_TEST_EQ(al_str_to_long(&string10, 2, &error), 0b01010, s64); + AL_TEST_FALSE(error); + + al_str_to_long(&string11, 10, &error); + AL_TEST_TRUE(error); + + AL_TEST_EQ(al_str_to_long(&string12, 10, &error), 6519749124957310923LL, s64); + AL_TEST_FALSE(error); + + AL_TEST_EQ(al_str_to_long(&string13, 10, &error), -3519311901742109472, s64); + AL_TEST_FALSE(error); + + al_str_to_long(&string14, 10, &error); + AL_TEST_TRUE(error); + + al_str_to_long(&string15, 10, &error); + AL_TEST_TRUE(error); AL_TEST_END(); } -static str *global __attribute__((unused)) = al_str_c("yeeee"); +// Very non-exhaustive. +static bool str_test_limits(void) +{ + AL_TEST_START("str_limits"); + + str s = al_str_null(); + + for (u32 i = 0; i < MAX_ALLOC_32 - 1; i++) { + al_str_cat(&s, &al_str_c("y")); + } + al_str_cat(&s, &al_str_c("x")); + + AL_TEST_TRUE(al_str_atr(&s, 1) == 'x'); + AL_TEST_TRUE(al_str_at(&s, MAX_ALLOC_32 - 1) == 'x'); + AL_TEST_TRUE(al_str_atr(&s, 2) == 'y'); + + AL_TEST_END(); +} + +#ifndef _MSC_VER +static str global_string = al_str_c("yeeee"); +#endif static bool str_test_error_prone_str_c(void) { AL_TEST_START("str_error_prone_str_c"); - str *n = al_str_c("yoyoyoyoyo"); - AL_TEST_EQ(n->len, al_strlen("yoyoyoyoyo"), u32); - AL_TEST_EQ(n->alloc, 0, u32); - AL_TEST_EQ(al_memcmp(n->data, "yoyoyoyoyo", al_strlen("yoyoyoyoyo") + 1), 0, u32); + str n = al_str_c("yoyoyoyoyo"); + AL_TEST_EQ(n.length, al_strlen("yoyoyoyoyo"), u32); + AL_TEST_EQ(n.alloc, 0, u32); + AL_TEST_EQ(al_memcmp(n.data, "yoyoyoyoyo", al_strlen("yoyoyoyoyo") + 1), 0, s32); + +#ifndef _MSC_VER + AL_TEST_TRUE(al_str_eq(&global_string, &al_str_c("yeeee"))); +#endif // This is expected to cause a compile error. #if 0 @@ -229,8 +332,8 @@ static bool str_test_error_prone_str_c(void) str ds = *al_str_c(d); str ss = *al_str_cr(d); - AL_TEST_EQ(ds.len, (u32)(sizeof(char *) - 1), u32); - AL_TEST_EQ(ss.len, (u32)al_strlen(s), u32); + AL_TEST_EQ(ds.length, (u32)(sizeof(char *) - 1), u32); + AL_TEST_EQ(ss.length, (u32)al_strlen(s), u32); al_free(d); @@ -240,8 +343,8 @@ static bool str_test_error_prone_str_c(void) wstr wds = *al_wstr_c(wd); wstr wss = *al_wstr_cr(wd); - AL_TEST_EQ(wds.len, (u32)((sizeof(wchar_t *) / sizeof(wchar_t)) - 1), u32); - AL_TEST_EQ(wss.len, (u32)wcslen(ws), u32); + AL_TEST_EQ(wds.length, (u32)((sizeof(wchar_t *) / sizeof(wchar_t)) - 1), u32); + AL_TEST_EQ(wss.length, (u32)wcslen(ws), u32); al_free(wd); #endif @@ -249,7 +352,7 @@ static bool str_test_error_prone_str_c(void) AL_TEST_END(); } -bool str_tests_run(void) +bool str_tests_run(u32 skip_mask) { AL_TEST_START_GROUP("str"); @@ -260,6 +363,9 @@ bool str_tests_run(void) AL_TEST_RUN(str_test_substr); AL_TEST_RUN(str_test_get_line); AL_TEST_RUN(str_test_to_long); + if (skip_mask & AL_TESTS_RUN_LIMITS) { + AL_TEST_RUN(str_test_limits); + } AL_TEST_RUN(str_test_error_prone_str_c); AL_TEST_END_GROUP(); diff --git a/tests/str.h b/tests/str.h index 64eb0fa..044f8f6 100644 --- a/tests/str.h +++ b/tests/str.h @@ -3,6 +3,6 @@ #include <al/types.h> -bool str_tests_run(void); +bool str_tests_run(u32 skip_mask); #endif // _AL_STR_TESTS_H |