summaryrefslogtreecommitdiff
path: root/include/al
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-01-17 11:20:17 -0500
committerAndrew Opalach <andrew@akon.city> 2025-01-17 11:20:17 -0500
commit209310c561d89c9ed2040aa7053b320d32cc393c (patch)
treecb26e18d275fa1d37afa903134e90e7c4c260484 /include/al
parent27b5e13d75c58d5e32538a4b5aef65ab86f4bb38 (diff)
downloadlibalabaster-209310c561d89c9ed2040aa7053b320d32cc393c.tar.gz
libalabaster-209310c561d89c9ed2040aa7053b320d32cc393c.tar.bz2
libalabaster-209310c561d89c9ed2040aa7053b320d32cc393c.zip
all: Consider types and numerical limits
- Vastly improve tests. - Change array/str/wstr APIs. - Use ptrdiff_t instead of size_t in ring buffer. - Make random seed thread-local. - Log now uses a static thread-local buffer instead of a malloc'd buffer. Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'include/al')
-rw-r--r--include/al/array_sort.h37
-rw-r--r--include/al/array_typed.h116
-rw-r--r--include/al/atomic.h6
-rw-r--r--include/al/lib.h37
-rw-r--r--include/al/log.h4
-rw-r--r--include/al/macros.h30
-rw-r--r--include/al/random.h15
-rw-r--r--include/al/ring_buffer.h22
-rw-r--r--include/al/str.h128
-rw-r--r--include/al/test.h49
-rw-r--r--include/al/types.h3
-rw-r--r--include/al/wstr.h119
12 files changed, 337 insertions, 229 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(&copy, 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 **)&copy_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 **)&copy_data, length, &mbstate);
+ al_assert(s->length == (u32)length);
+ al_wstr_free(&copy);
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