diff options
| author | 2023-10-07 17:12:38 -0400 | |
|---|---|---|
| committer | 2023-10-07 17:44:10 -0400 | |
| commit | b07771669ae029d07b76a58ed526da7bc29ec27b (patch) | |
| tree | a379a6b638a64060de1c220537f803b6402ffdea /src | |
| download | libalabaster-b07771669ae029d07b76a58ed526da7bc29ec27b.tar.gz libalabaster-b07771669ae029d07b76a58ed526da7bc29ec27b.tar.bz2 libalabaster-b07771669ae029d07b76a58ed526da7bc29ec27b.zip | |
Add libalabaster
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src')
| -rw-r--r-- | src/array.c | 105 | ||||
| -rw-r--r-- | src/lib.c | 184 | ||||
| -rw-r--r-- | src/log.c | 90 | ||||
| -rw-r--r-- | src/ring_buffer.c | 169 | ||||
| -rw-r--r-- | src/str.c | 113 |
5 files changed, 661 insertions, 0 deletions
diff --git a/src/array.c b/src/array.c new file mode 100644 index 0000000..ffdc36b --- /dev/null +++ b/src/array.c @@ -0,0 +1,105 @@ +#include "../include/al/lib.h" +#include "../include/al/array_sort.h" + +// based on: https://github.com/lifthrasiir/angolmois/blob/master/angolmois.c#L79 + +#define array(type) \ + struct { \ + u32 size; \ + u32 alloc; \ + type *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_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_clone(dest, src) \ + do { \ + al_array_init(dest); \ + al_array_reserve(dest, (src).size); \ + al_memcpy(&al_array_at(dest, 0), &al_array_at(src, 0), \ + al_array_item_size(dest) * (dest).size); \ + (dest).size = (src).size; \ + } while (0) + +#define al_array_push(arr, item) \ + do { \ + al_array_reserve(arr, (arr).size + 1); \ + al_array_at(arr, (arr).size++) = item; \ + } while (0) + +#define al_array_insert(arr, item, i) \ + do { \ + if ((arr).size > i) { \ + al_array_reserve(arr, (arr).size + 1); \ + al_memmove(&al_array_at(arr, i + 1), &al_array_at(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; \ + } while (0) + +#define al_array_pop_at(arr, i, r) \ + do { \ + r = al_array_at((arr), i); \ + al_array_remove_at((arr), i); \ + } while (0) + +#define al_array_pop(arr) (al_array_at(arr, --(arr).size)) +#define al_array_pop_front(arr) al_array_pop_at(arr, 0) + +#define al_array_remove_at(arr, i) \ + do { \ + if (i == (arr).size - 1) { \ + (arr).size--; \ + } else { \ + al_memmove(&al_array_at(arr, i), &al_array_at(arr, i + 1), \ + al_array_item_size(arr) * (--(arr).size - i)); \ + } \ + } while (0) + +#define al_array_sort(arr, type, cmp) \ + AL_INSERSION_SORT((arr).data, type, (arr).size, cmp) + +#define al_array_free(arr) \ + if ((arr).alloc) AL_ARRAY_FREE((arr).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_ptr(arr, i, item) \ + for (u32 i = 0; (i < (arr).size && (item = &al_array_at(arr, i), 1)); i++) + +#define al_array_remove_at_iter(arr, i) \ + do { \ + al_array_remove_at(arr, i); \ + i--; \ + } while (0) + +AL_UNUSED_FUNCTION_PUSH + +static inline u32 _al_array_reserve(void **ptr, u32 item_size, u32 prev_size, u32 size) +{ + if (size < al_grow_limit) { + size = (u32)al_next_power_of_two(size + 1); + } else { + size = (u32)((size + al_grow_limit) & ~al_grow_limit); + } + + if (size <= prev_size) return prev_size; + + *ptr = (!*ptr) ? AL_ARRAY_MALLOC(item_size * size) : AL_ARRAY_REALLOC(*ptr, item_size * size); + + return size; +} + +AL_UNUSED_FUNCTION_POP diff --git a/src/lib.c b/src/lib.c new file mode 100644 index 0000000..2df40d3 --- /dev/null +++ b/src/lib.c @@ -0,0 +1,184 @@ +// Testing allocation tracking stuff. (Very scuffed) + +#include "../include/al/lib.h" + +#if AL_USE_STDLIB +static void *(*_al_malloc)(size_t) = malloc; +static void *(*_al_calloc)(size_t, size_t) = calloc; +static void *(*_al_realloc)(void *, size_t) = realloc; +static void (*_al_free)(void *) = free; +#ifdef HAVE_POSIX_MEMALIGN +s32 (*_al_posix_memalign)(void **, size_t, size_t) = posix_memalign; +#endif + +#define MEMORY_TRACKING 0 + +#if MEMORY_TRACKING +struct alloc_t { + void *ptr; + size_t size; +}; + +// Use stdlib allocation for the allocation tracking array. +// This avoids a circular loop and also keeps allocations +// from the memory tracking code out of the stats. +#include "../include/al/types.h" +#define AL_ARRAY_MALLOC malloc +#define AL_ARRAY_REALLOC realloc +#define AL_ARRAY_FREE free +#include "array.c" + +static array(struct alloc_t) allocations; +static u32 total_allocated = 0; +static u32 currently_allocated = 0; +static u32 peak_allocated = 0; +#endif + +void al_malloc_init(void) +{ +#if MEMORY_TRACKING + al_array_init(allocations); +#endif +} + +void *al_malloc(size_t n) +{ + void *ptr = _al_malloc(n); +#if MEMORY_TRACKING + printf("**al_malloc(%zu)\n", n); + struct alloc_t a = { + .ptr = ptr, + .size = n + }; + total_allocated += a.size; + currently_allocated += a.size; + if (currently_allocated > peak_allocated) { + peak_allocated = currently_allocated; + } + al_array_push(allocations, a); +#endif + return ptr; +} + +void *al_calloc(size_t n, size_t size) +{ + void *ptr = _al_calloc(n, size); +#if MEMORY_TRACKING + printf("**al_calloc(%zu, %zu)\n", n, size); + struct alloc_t a = { + .ptr = ptr, + .size = n * size + }; + total_allocated += a.size; + currently_allocated += a.size; + if (currently_allocated > peak_allocated) { + peak_allocated = currently_allocated; + } + al_array_push(allocations, a); +#endif + return ptr; +} + +void *al_realloc(void *ptr, size_t n) +{ + void *nptr = _al_realloc(ptr, n); +#if MEMORY_TRACKING + printf("**al_realloc(%p, %zu)\n", ptr, n); + for (u32 i = 0; i < allocations.size; i++) { + struct alloc_t *a = &al_array_at(allocations, i); + if (a->ptr == ptr) { + currently_allocated += (ssize_t)n - (ssize_t)a->size; + if (currently_allocated > peak_allocated) { + peak_allocated = currently_allocated; + } + a->ptr = nptr; + a->size = n; + } + } + if (!ptr) { + struct alloc_t a = { + .ptr = nptr, + .size = n + }; + currently_allocated += a.size; + if (currently_allocated > peak_allocated) { + peak_allocated = currently_allocated; + } + al_array_push(allocations, a); + } + total_allocated += n; +#endif + return nptr; +} + +void al_free(void *ptr) +{ +#if MEMORY_TRACKING + printf("**al_free(%p)\n", ptr); + for (u32 i = 0; i < allocations.size; i++) { + struct alloc_t *a = &al_array_at(allocations, i); + if (a->ptr == ptr) { + currently_allocated -= a->size; + al_array_remove_at(allocations, i); + break; + } + } +#endif + _al_free(ptr); +} + +#ifdef HAVE_POSIX_MEMALIGN +s32 al_posix_memalign(void **ptr, size_t alignment, size_t n) +{ + s32 res = _al_posix_memalign(ptr, alignment, n); +#if MEMORY_TRACKING + size_t actual_size = (n + alignment) - ((n + alignment) % alignment); + printf("**al_posix_memalign(%zu, %zu(%zu))\n", alignment, n, actual_size); + struct alloc_t a = { + .ptr = *ptr, + .size = actual_size + }; + total_allocated += actual_size; + currently_allocated += actual_size; + if (currently_allocated > peak_allocated) { + peak_allocated = currently_allocated; + } + al_array_push(allocations, a); +#endif + return res; +} +#endif + +void al_malloc_stats(u32 *current, u32 *peak, u32 *total) +{ +#if MEMORY_TRACKING + *current = currently_allocated; + *peak = peak_allocated; + *total = total_allocated; +#else + *current = 0; + *peak = 0; + *total = 0; +#endif +} + +void al_set_alloc(void *(*malloc_func)(size_t), void *(*calloc_func)(size_t, size_t), + void *(*realloc_func)(void *, size_t), void (*free_func)(void *)) +{ + _al_malloc = malloc_func; + _al_calloc = calloc_func; + _al_realloc = realloc_func; + _al_free = free_func; +} +#endif + +// Default to 4KB. +size_t al_page_size = 0x1000; +size_t al_grow_limit = 0xfff; + +void al_set_page_size(size_t size) +{ + al_assert(size > 0); + al_page_size = size; + al_grow_limit = al_page_size - 1; +} diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..0444ed6 --- /dev/null +++ b/src/log.c @@ -0,0 +1,90 @@ +#include "../include/al/log.h" + +#define AL_LOG_SKIP 1 +#define AL_LOG_MESSAGE_SIZE 511 +#define AL_LOG_TEMPLATE "%s:%d %s(): %s -> (%s) " + +static void *al_log_userdata = NULL; + +static s32 al_print_default(void *userdata, char *s) +{ + (void)userdata; + s32 ret = al_printf("%*.*s\n", 0, AL_LOG_MESSAGE_SIZE, s); + al_free(s); + return ret; +} + +static s32 (*_al_print)(void *, char *) = al_print_default; + +void al_set_print(void *userdata, s32 (*print_func)(void *, char *)) +{ + al_log_userdata = userdata; + _al_print = print_func; +} + +#if !AL_LOG_SKIP +static char *get_buffer(const char *level, const char *section, const char *fmt, + const char *name, const s32 line, const char *func, va_list args) +{ + char *buffer = al_calloc(1, AL_LOG_MESSAGE_SIZE + 1); + al_memset(buffer, 0, AL_LOG_MESSAGE_SIZE); + s32 prefix = snprintf(buffer, AL_LOG_MESSAGE_SIZE, AL_LOG_TEMPLATE, name, line, func, level, section); + prefix += vsnprintf(buffer + prefix, AL_LOG_MESSAGE_SIZE - prefix, fmt, args); + buffer[strcspn(buffer, "\r\n")] = '\0'; + return buffer; +} +#endif + +s32 _al_log_nop(const char *section, const char *fmt, ...) +{ + (void)section; + (void)fmt; + return 0; +} + +s32 _al_log(const char *level, const char *section, const char *name, const s32 line, + const char *func, const char *fmt, ...) +{ +#if AL_FORCE_DISABLE_OUTPUT + return 0; +#else + va_list args; + va_start(args, fmt); +#if AL_LOG_SKIP + s32 ret = printf(AL_LOG_TEMPLATE, name, line, func, level, section); + ret += vprintf(fmt, args); + size_t cspn = strcspn(fmt, "\r\n"); + if (cspn == strlen(fmt)) ret += printf("\n"); +#else + char *buffer = get_buffer(level, section, fmt, name, line, func, args); +#endif + va_end(args); +#if AL_LOG_SKIP + return ret; +#else + return _al_print(al_log_userdata, buffer); +#endif +#endif +} + +s32 _al_logv(const char *level, const char *section, const char *name, const s32 line, + const char *func, const char *fmt, va_list args) +{ +#if AL_FORCE_DISABLE_OUTPUT + return 0; +#else +#if AL_LOG_SKIP + s32 ret = printf(AL_LOG_TEMPLATE, name, line, func, level, section); + ret += vprintf(fmt, args); + size_t cspn = strcspn(fmt, "\r\n"); + if (cspn == strlen(fmt)) ret += printf("\n"); +#else + char *buffer = get_buffer(level, section, fmt, name, line, func, args); +#endif +#if AL_LOG_SKIP + return ret; +#else + return _al_print(al_log_userdata, buffer); +#endif +#endif +} diff --git a/src/ring_buffer.c b/src/ring_buffer.c new file mode 100644 index 0000000..eaff139 --- /dev/null +++ b/src/ring_buffer.c @@ -0,0 +1,169 @@ +#include "../include/al/lib.h" +#include "../include/al/ring_buffer.h" + +// https://github.com/MusicPlayerDaemon/MPD/blob/master/src/util/RingBuffer.hxx +// 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) +{ + buf->start = data; + buf->end = data + length; + al_ring_buffer_reset(buf); +} + +static inline u8 *previous(struct al_ring_buffer *buf, u8 *ptr) +{ + if (ptr == buf->start) ptr = buf->end; + return --ptr; +} + +static inline void add(struct al_ring_buffer *buf, volatile u8 **v, u8 *ptr, size_t n) +{ + ptr += n; + al_assert(ptr <= buf->end); + if (ptr == buf->end) ptr = buf->start; + al_atomic_ptr_store(v, ptr, AL_ATOMIC_RELEASE); +} + +size_t al_ring_buffer_space(struct al_ring_buffer *buf) +{ + u8 *wp = al_atomic_ptr_load(&buf->write, AL_ATOMIC_RELAXED); + u8 *rp = previous(buf, (u8 *)al_atomic_ptr_load(&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 *wp = al_atomic_ptr_load(&buf->write, AL_ATOMIC_ACQUIRE); + u8 *rp = previous(buf, al_atomic_ptr_load(&buf->read, AL_ATOMIC_RELAXED)); + *size = (wp <= rp ? rp : buf->end) - wp; + return wp; +} + +void al_ring_buffer_append(struct al_ring_buffer *buf, u8 *ptr, size_t n) +{ + add(buf, &buf->write, ptr, n); +} + +size_t al_ring_buffer_occupied(struct al_ring_buffer *buf) +{ + u8 *rp = al_atomic_ptr_load(&buf->read, AL_ATOMIC_RELAXED); + u8 *wp = al_atomic_ptr_load(&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 *rp = al_atomic_ptr_load(&buf->read, AL_ATOMIC_ACQUIRE); + u8 *wp = al_atomic_ptr_load(&buf->write, AL_ATOMIC_RELAXED); + *size = (rp <= wp ? wp : buf->end) - rp; + return rp; +} + +void al_ring_buffer_consume(struct al_ring_buffer *buf, u8 *ptr, size_t n) +{ + add(buf, &buf->read, ptr, n); +} + +size_t al_ring_buffer_write(struct al_ring_buffer *buf, u8 *data, size_t n) +{ + u8 *wp = al_atomic_ptr_load(&buf->write, AL_ATOMIC_ACQUIRE); + u8 *rp = previous(buf, al_atomic_ptr_load(&buf->read, AL_ATOMIC_RELAXED)); + + size_t size = AL_MIN((wp <= rp ? rp : buf->end) - wp, (ptrdiff_t)n); + + al_memcpy(wp, data, size); + wp += size; + + if (wp >= buf->end) { + size_t wrap = AL_MIN(rp - buf->start, (ptrdiff_t)(n - size)); + al_memcpy(buf->start, data + size, wrap); + wp = buf->start + wrap; + size += wrap; + } + + al_atomic_ptr_store(&buf->write, wp, AL_ATOMIC_RELEASE); + + return size; +} + +size_t al_ring_buffer_read(struct al_ring_buffer *buf, u8 *ptr, size_t n) +{ + u8 *rp = al_atomic_ptr_load(&buf->read, AL_ATOMIC_ACQUIRE); + u8 *wp = al_atomic_ptr_load(&buf->write, AL_ATOMIC_RELAXED); + + size_t size = AL_MIN((rp <= wp ? wp : buf->end) - rp, (ptrdiff_t)n); + + al_memcpy(ptr, rp, size); + rp += size; + + if (rp >= buf->end) { + size_t wrap = AL_MIN(wp - buf->start, (ptrdiff_t)(n - size)); + al_memcpy(ptr + size, buf->start, wrap); + rp = buf->start + wrap; + size += wrap; + } + + al_atomic_ptr_store(&buf->read, rp, AL_ATOMIC_RELEASE); + + return size; +} + +size_t al_ring_buffer_peek(struct al_ring_buffer *buf, u8 *ptr, size_t offset, size_t n) +{ + u8 *rp = al_atomic_ptr_load(&buf->read, AL_ATOMIC_RELAXED); + u8 *wp = al_atomic_ptr_load(&buf->write, AL_ATOMIC_RELAXED); + + ptrdiff_t size; + + if (rp <= wp) { + rp += offset; + size = AL_MIN(wp - rp, (ptrdiff_t)n); + if (size < 0) return 0; + al_memcpy(ptr, rp, size); + } else { + rp += offset; + size = AL_MIN(buf->end - rp, (ptrdiff_t)n); + if (size > 0) { + al_memcpy(ptr, rp, size); + n -= size; + rp = buf->start; + } else { + rp = buf->start - size; + size = 0; + } + ptrdiff_t wrap = AL_MIN(wp - rp, (ptrdiff_t)n); + if (wrap > 0) { + al_memcpy(ptr + size, rp, wrap); + size += wrap; + } + } + + return size; +} + +size_t al_ring_buffer_discard(struct al_ring_buffer *buf, size_t n) +{ + u8 *rp = al_atomic_ptr_load(&buf->read, AL_ATOMIC_ACQUIRE); + u8 *wp = al_atomic_ptr_load(&buf->write, AL_ATOMIC_RELAXED); + + size_t discard = AL_MIN((rp <= wp ? wp : buf->end) - rp, (ptrdiff_t)n); + rp += discard; + + if (rp >= buf->end) { + size_t wrap = AL_MIN(wp - buf->start, (ptrdiff_t)(n - discard)); + rp = buf->start + wrap; + discard += wrap; + } + + al_atomic_ptr_store(&buf->read, rp, AL_ATOMIC_RELEASE); + + return discard; +} + +// Not thread-safe. +void al_ring_buffer_reset(struct al_ring_buffer *buf) +{ + al_atomic_ptr_store(&buf->read, buf->start, AL_ATOMIC_RELAXED); + al_atomic_ptr_store(&buf->write, buf->start, AL_ATOMIC_RELAXED); +} diff --git a/src/str.c b/src/str.c new file mode 100644 index 0000000..a3c0946 --- /dev/null +++ b/src/str.c @@ -0,0 +1,113 @@ +#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) +{ + u64 ret = 0; + u32 i = 0; + s32 neg = 0; + + while (al_isspace(al_str_at(s, i)) && i < s->len) i++; + + if (al_str_at(s, i) == '-') { + neg = 1; + i++; + } else if (al_str_at(s, i) == '+') { + i++; + } + + if ((base == 16 && al_str_at(s, i) == '0' && // 0xbecd + (al_str_at(s, i + 1) == 'x' || al_str_at(s, i + 1) == 'X')) || + (base == 2 && al_str_at(s, i) == '0' && // 0b01011101 + (al_str_at(s, i + 1) == 'b' || al_str_at(s, i + 1) == 'B'))) { + i += 2; + } + + u64 cutoff = neg ? -(u64)INT64_MIN : INT64_MAX; + s32 add_limit = cutoff % (u64)base; + cutoff /= (u64)base; + + bool error = false; + + for (; i < s->len; i++) { + char c = 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 when expressing a number. + error = true; + break; + } + + // Character invalid for given base or trying to add the next digit + // would cause an overflow/underflow. + if (c >= base || ret > cutoff || (ret == cutoff && c > add_limit)) { + error = true; + break; + } + + ret = (ret * base) + c; + } + + if (error) ret = neg ? INT64_MIN : INT64_MAX; + else if (neg) ret = -ret; + + return ret; +} + +bool al_str_tok(str *s, char sep, str *tok) +{ + if (tok->alloc == s->len + 1) { + return false; + } + u32 r = s->len - tok->alloc; +#ifndef __cplusplus + s32 i = al_str_find(al_str_w(s->data, tok->alloc, r), sep); +#else + str w = al_str_ww(s->data, tok->alloc, r); + s32 i = al_str_find(&w, sep); +#endif + if (i == -1) i = r; + tok->data = s->data + tok->alloc; + tok->len = (u32)i; + tok->alloc += tok->len + 1; + return true; +} + +bool al_str_get_line(str *buffer, str *line) +{ + s32 bytes_processed = 0; + + if (line->data == NULL) { + *line = *buffer; + } else { + // Move to the end of the current line. + line->data += line->len; + + bytes_processed = (s32)((line->data - buffer->data) + 1); + + // If line->ptr is at the last character, we are done. + // Also, if bytes_processed <= 0 buffer is empty or invalid, so return. + if (bytes_processed >= (s32)buffer->len || bytes_processed <= 0) { + return false; + } + + // Else, move line->ptr to the first character on the next line. + line->data++; + } + + // Find the next newline character. + char *nl = (char *)memchr(line->data, '\n', buffer->len - bytes_processed); + + // If there is no newline character move to the end of the buffer. + if (!nl) nl = buffer->data + buffer->len; + + line->len = (u32)(nl - line->data); + + return true; +} |