diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | LICENCE | 19 | ||||
-rw-r--r-- | README.md | 20 | ||||
-rwxr-xr-x | build.sh | 49 | ||||
-rw-r--r-- | src/un_list.c | 83 | ||||
-rw-r--r-- | src/un_log.c | 71 | ||||
-rw-r--r-- | src/un_memory.c | 229 | ||||
-rw-r--r-- | src/un_strings.c | 381 | ||||
-rw-r--r-- | src/ungrateful.c | 6 | ||||
-rw-r--r-- | src/ungrateful.h | 202 | ||||
-rw-r--r-- | tests/allocs.c | 39 | ||||
-rw-r--r-- | tests/hello_world.c | 7 | ||||
-rw-r--r-- | tests/lists.c | 50 | ||||
-rw-r--r-- | tests/memctl.c | 42 | ||||
-rw-r--r-- | tests/strings.c | 103 |
15 files changed, 1303 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85d0bbc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +lib/ @@ -0,0 +1,19 @@ +Copyright (C) 2025 Bogdan Masyutin (bonmas14) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +Bogdan Masyutin - bonmas14@gmail.com diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d40b34 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Ungrateful + +C99 standard library for game development. + +# Note: + +Only core features of C99 were used: + +- stdint; +- normal comments; +- no implicit function declarations and int; +- long long; +- restrict keyword; + +# TODO: + +- File IO. +- Memory allocation. +- Threading. +- Strings. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..4cf5e42 --- /dev/null +++ b/build.sh @@ -0,0 +1,49 @@ +#!/bin/env bash + +cc="gcc" +ld="gcc" + +rm -rf ./lib/ +rm -rf ./bin/ +mkdir ./lib/ +mkdir ./bin/ + +echo "[BUILD] entry.c" + +cflags="-std=c99 -Wall -Wextra -g -Wno-error -pedantic" +$cc $cflags \ + -c -o lib/ungrateful.o \ + -g src/ungrateful.c + +if [[ $? -ne 0 ]]; then + exit +fi + +if [[ $1 == "no_tests" ]]; then + exit +fi + +echo + +for test in tests/*.c; do + fname=$(basename -- "$test") + fname="${fname%.*}" + + echo "[BUILD] $test" + + $cc $cflags -o bin/$fname $test lib/ungrateful.o -Isrc/ +done + +for case in bin/*; do + if [[ $1 == "quiet" ]]; then + $case > /dev/null 2>&1 + else + $case + fi + + if [[ $? -eq 0 ]]; then + echo "[DONE] $case" + else + echo "[FAIL] $case" + fi +done diff --git a/src/un_list.c b/src/un_list.c new file mode 100644 index 0000000..a670de0 --- /dev/null +++ b/src/un_list.c @@ -0,0 +1,83 @@ +List un_list_create(u64 start_capacity, u64 element_size, Allocator alloc) { + List list; + + list.count = 0; + list.capacity = start_capacity; + list.element_size = element_size; + list.alloc = alloc; + + list.data = un_memory_alloc(list.capacity * list.element_size, alloc); + + assert(list.data != NULL); + return list; +} + +void un_list_destroy(List *list) { + un_memory_destroy(&list->alloc); + UN_CLEAR(*list); +} + +List un_list_clone(List *list, Allocator alloc) { + List result; + + result = *list; + result.alloc = alloc; + result.data = un_memory_alloc(result.capacity * result.element_size, alloc); + assert(result.data != NULL); + + un_memory_copy(result.data, list->data, result.count * result.element_size); + + return result; +} + +static b32 list_grow_if_needed(List *list) { + if ((list->count + 1) <= list->capacity) { + return true; + } else { + void *mem = un_memory_realloc(list->data, list->element_size * list->capacity * 2, list->alloc); + + if (!mem) { + return false; + } + + list->data = mem; + list->capacity *= 2; + } + + return true; +} + +void un_list_append(List *list, void *data) { + void *addr; + + if (list_grow_if_needed(list)) { + addr = (u8*)list->data + list->count * list->element_size; + un_memory_copy(addr, data, list->element_size); + list->count++; + } else { + un_log_write_cstring(UN_LOG_ERROR, UN_CSTR "Failed to grow list"); + } +} + +void *un_list_get(List *list, u64 index) { + if (index >= list->count) return NULL; + + return (u8*)list->data + index * list->element_size; +} + +void un_list_remove(List *list, u64 index) { + void *addr; + u64 move_elements; + + if (index >= list->count) return; + + addr = (u8*)list->data + index * list->element_size; + + move_elements = list->count - (index + 1); + + if (move_elements) { + un_memory_move(addr, (u8*)addr + list->element_size, move_elements * list->element_size); + } + + list->count--; +} diff --git a/src/un_log.c b/src/un_log.c new file mode 100644 index 0000000..ef5bb02 --- /dev/null +++ b/src/un_log.c @@ -0,0 +1,71 @@ +#include <stdio.h> + +Log_Level un_current_log_level = UN_LOG_INFO; + +u8 log_buffer[UN_KB(4)]; + +void un_log_write_internal(Log_Level level, String format, va_list vaptr) { + if (level < un_current_log_level) return; + + switch (level) { + case UN_LOG_TRACE: + fprintf(stderr, "[TRACE] "); + break; + case UN_LOG_DEBUG: + fprintf(stderr, "[DEBUG] "); + break; + case UN_LOG_INFO: + fprintf(stderr, "[INFO] "); + break; + case UN_LOG_WARNING: + fprintf(stderr, "[WARNING] "); + break; + case UN_LOG_ERROR: + fprintf(stderr, "[ERROR] "); + break; + case UN_LOG_FATAL: + fprintf(stderr, "[FATAL] "); + break; + default: + break; + } + + sprintf(CSTR log_buffer, "%.*s", (int)format.size, format.data); + vfprintf(stderr, CSTR log_buffer, vaptr); + + switch (level) { + case UN_LOG_RAW: + break; + case UN_LOG_FATAL: + fprintf(stderr, "\n"); + assert(false); + break; + default: + fprintf(stderr, "\n"); + break; + } +} + +extern void un_log_write_cstring(Log_Level level, u8 *format, ...) { + va_list vaptr; + String temp; + + if (level < un_current_log_level) return; + + temp.size = un_string_get_length(format); + temp.data = format; + + va_start(vaptr, format); + un_log_write_internal(level, temp, vaptr); + va_end(vaptr); +} + +void un_log_write(Log_Level level, String format, ...) { + va_list vaptr; + + if (level < un_current_log_level) return; + + va_start(vaptr, format); + un_log_write_internal(level, format, vaptr); + va_end(vaptr); +} diff --git a/src/un_memory.c b/src/un_memory.c new file mode 100644 index 0000000..9ff316b --- /dev/null +++ b/src/un_memory.c @@ -0,0 +1,229 @@ +#include <stdlib.h> + +/// -------- Basic functions + +void un_memory_set(u8 *buffer, u8 value, u64 size) { + if (size == 0) return; + assert(buffer != NULL); + + while (size-- > 0) { + *buffer++ = value; + } +} + +void un_memory_copy(u8 *dest, u8 *source, u64 size) { + if (size == 0) return; + + assert(dest != NULL); + assert(source != NULL); + +#ifdef DEBUG + + if (dest < source) { + assert(((ptrdiff_t)source - (ptrdiff_t)dest) >= (ptrdiff_t)size); + } else { + assert(((ptrdiff_t)dest - (ptrdiff_t)source) >= (ptrdiff_t)size); + } +#endif // DEBUG + + while (size-- > 0) { + *dest++ = *source++; + } +} + +s32 un_memory_compare(u8 *left, u8 *right, u64 size) { + while (size-- > 0) { + if (*left++ == *right++) + continue; + + return left[-1] > right[-1] ? 1 : -1; + } + + return 0; +} + +void un_memory_move(u8 *dest, u8 *src, u64 size) { + Allocator talloc = un_allocator_get_temporary(); + + void *temp = un_memory_alloc(size, talloc); + + un_memory_copy(temp, src, size); + un_memory_copy(dest, temp, size); +} + +void *un_memory_alloc(u64 size, Allocator alloc) { + assert(alloc.proc != NULL); + return alloc.proc(NULL, size, UN_ALLOC_MSG_ALLOCATE, alloc.data); +} + +void *un_memory_realloc(void *ptr, u64 size, Allocator alloc) { + assert(alloc.proc != NULL); + return alloc.proc(ptr, size, UN_ALLOC_MSG_REALLOCATE, alloc.data); +} + +void *un_memory_free(void *ptr, Allocator alloc) { + assert(alloc.proc != NULL); + return alloc.proc(ptr, 0, UN_ALLOC_MSG_FREE, alloc.data); +} + +void un_memory_destroy(Allocator *alloc) { + assert(alloc != NULL); + assert(alloc->proc != NULL); + + alloc->proc(NULL, 0, UN_ALLOC_MSG_SELF_DELETE, alloc->data); + + UN_CLEAR(*alloc); +} + +/// -------- Stdlib allocator + +static ALLOCATOR_PROC_SIGNATURE(un_std_alloc_proc) { + UNUSED(data); + + switch (message) { + case UN_ALLOC_MSG_ALLOCATE: + return calloc(1, size); + case UN_ALLOC_MSG_REALLOCATE: + return realloc(p, size); + case UN_ALLOC_MSG_FREE: + free(p); + break; + case UN_ALLOC_MSG_SELF_DELETE: break; + } + + return NULL; +} + +Allocator un_allocator_get_standard(void) { + return CLITERAL(Allocator) { .proc = un_std_alloc_proc }; +} + +/// -------- Temp allocator + +static struct { + u64 index; + u8 data[UN_TEMP_SIZE]; +} temp; + +static void temp_reset(void) { + temp.index = 0; +} + +static void *temp_alloc(u64 size) { + if ((temp.index + size) > UN_TEMP_SIZE) { + un_log_write_cstring(UN_LOG_ERROR, UN_CSTR "Temp allocator wrapped!"); + temp_reset(); + } + + if ((temp.index + size) > UN_TEMP_SIZE) { + un_log_write_cstring(UN_LOG_ERROR, UN_CSTR "Too much space requested!"); + return NULL; + } + + void *ptr = (u8*)temp.data + temp.index; + temp.index += size; + un_memory_set(ptr, 0x00, size); + + return ptr; +} + +static ALLOCATOR_PROC_SIGNATURE(un_temp_alloc_proc) { + UNUSED(data); + UNUSED(p); + + switch (message) { + case UN_ALLOC_MSG_ALLOCATE: + return temp_alloc(size); + case UN_ALLOC_MSG_REALLOCATE: + case UN_ALLOC_MSG_FREE: + break; + case UN_ALLOC_MSG_SELF_DELETE: break; + } + + return NULL; +} + +Allocator un_allocator_get_temporary(void) { + return CLITERAL(Allocator) { .proc = un_temp_alloc_proc }; +} + +/// Arena allocator + +typedef struct Arena { + u64 size; + u64 occupied; + struct Arena *next; + u8 data[1]; +} Arena; + +static Arena *arena_create(u64 size) { + Allocator alloc; + Arena *arena; + + alloc = un_allocator_get_standard(); + arena = CLITERAL(Arena*)un_memory_alloc(size, alloc); + + if (!arena) { + un_log_write_cstring(UN_LOG_FATAL, UN_CSTR "Buy mem, failed to create arena!"); + return NULL; + } + + UN_CLEAR(*arena); + arena->size = size - sizeof(Arena); + return arena; +} + +static void arena_delete(Arena *arena) { + if (arena->next != NULL) { + arena_delete(arena->next); + } + + Allocator alloc = un_allocator_get_standard(); + un_memory_free(arena, alloc); +} + +static void *arena_allocate(u64 size, Arena *arena) { + u8* pos; + + if (size <= (arena->size - arena->occupied)) { + pos = arena->data + arena->occupied; + arena->occupied += size; + return (void*)pos; + } + + if (!arena->next) { + arena->next = arena_create((arena->size + sizeof(Arena)) * 2LL); + } + + return arena_allocate(size, arena->next); +} + +static ALLOCATOR_PROC_SIGNATURE(un_arena_alloc_proc) { + assert(data != NULL); + UNUSED(p); + + switch (message) { + case UN_ALLOC_MSG_ALLOCATE: + return arena_allocate(size, (Arena*)data); + case UN_ALLOC_MSG_REALLOCATE: + un_log_write_cstring(UN_LOG_ERROR, UN_CSTR "Arena doesn't reallocate."); + break; + case UN_ALLOC_MSG_FREE: + un_log_write_cstring(UN_LOG_ERROR, UN_CSTR "Arena doesn't free it's memory, please destroy arena itself."); + break; + case UN_ALLOC_MSG_SELF_DELETE: + arena_delete((Arena*)data); + break; + } + + return NULL; +} + +Allocator un_allocator_create_arena(u64 size) { + Allocator alloc = { 0 }; + + alloc.proc = un_arena_alloc_proc; + alloc.data = arena_create(size); + + return alloc; +} diff --git a/src/un_strings.c b/src/un_strings.c new file mode 100644 index 0000000..1499726 --- /dev/null +++ b/src/un_strings.c @@ -0,0 +1,381 @@ +u64 un_string_get_length(u8 *cstring) { + u8* start = cstring; + + while (*start != '\0') { + start++; + } + + return start - cstring; +} + +String un_string_from_cstring(u8* cstring) { + String result; + u64 length = un_string_get_length(cstring); + assert(length < LLONG_MAX); + result.size = length; + result.data = cstring; + return result; +} + +u8* un_string_to_cstring(String string, Allocator alloc) { + u8 *mem; + + assert(string.size > 0); + + mem = (u8*)un_memory_alloc(string.size + 1, alloc); + un_memory_copy(mem, string.data, (u64)string.size); + + return mem; +} + +String un_string_copy(String source, Allocator alloc) { + u8 *mem; + String result; + + assert(source.size >= 0); + + mem = (u8*)un_memory_alloc(source.size, alloc); + un_memory_copy(mem, source.data, (u64)source.size); + + result.size = source.size; + result.data = mem; + + return result; +} + +s32 un_string_compare(String left, String right) { + s32 result; + + if (left.size == right.size && left.size == 0) return 0; + if (left.size == 0) return -1; + if (right.size == 0) return 1; + + result = un_memory_compare(left.data, right.data, left.size); + + if (result == 0) { + if (left.size > right.size) return 1; + if (left.size < right.size) return -1; + return 0; + } else { + return result; + } +} + +String un_string_concat(String left, String right, Allocator alloc) { + String result = { 0 }; + + assert(left.size >= 0 && right.size >= 0); + + if (left.size == right.size && left.size == 0) { + return result; + } + + u8* data = (u8*)un_memory_alloc(left.size + right.size, alloc); + + if (left.size > 0) { + assert(left.data != NULL); + un_memory_copy(data, left.data, left.size); + } + + if (right.size > 0) { + assert(right.data != NULL); + un_memory_copy((data + left.size), right.data, right.size); + } + + result.size = left.size + right.size; + result.data = data; + + return result; +} + +String un_string_swap(String input, u8 from, u8 to, Allocator alloc) { + s64 i; + String output; + + output = un_string_copy(input, alloc); + + for (i = 0; i < input.size; i++) { + if (output.data[i] != from) + continue; + + output.data[i] = to; + } + + return output; +} + +List un_string_split(String input, String pattern, Allocator alloc) { + List splits; + String string; + s64 i, start; + u64 matches, size; + u8 *buffer; + + UN_CLEAR(splits); + + if (input.size <= pattern.size) { + return splits; + } + + if (pattern.size == 0) { + return splits; + } + + for (i = 0, matches = 1; i < (input.size - (pattern.size - 1)); i++) { + if (un_memory_compare(input.data + i, pattern.data, pattern.size) != 0) { + continue; + } + + matches++; + } + + splits = un_list_create(matches, sizeof(String), alloc); + + start = 0; + + for (i = 0; i < (input.size - (pattern.size - 1)); i++) { + if (un_memory_compare(input.data + i, pattern.data, pattern.size) != 0) { + continue; + } + + size = i - start; + + if (size == 0) { + start = i + pattern.size; + continue; + } + + buffer = (u8*)un_memory_alloc(size, alloc); + un_memory_copy(buffer, input.data + start, size); + string = CLITERAL(String) { .size = size, .data = buffer }; + un_list_append(&splits, &string); + start = i + pattern.size; + } + + if (start != input.size - pattern.size) { + if (un_memory_compare(input.data + start, pattern.data, pattern.size) == 0) { + return splits; + } + + size = input.size - start; + if (size == 0) { + return splits; + } + + buffer = (u8*)un_memory_alloc(size, alloc); + un_memory_copy(buffer, input.data + start, size); + string = CLITERAL(String) { .size = size, .data = buffer }; + un_list_append(&splits, &string); + } + + return splits; +} + +String un_string_join(List string_list, String separator, Allocator alloc) { + String cont, temp; + u64 i; + Allocator talloc; + + UN_CLEAR(cont); + + talloc = un_allocator_get_temporary(); + + assert(string_list.element_size == sizeof(String)); + + + for (i = 0; i < string_list.count; i++) { + temp = *(String *)un_list_get(&string_list, i); + + cont = un_string_concat(cont, temp, talloc); + + if (i != (string_list.count - 1)) { + cont = un_string_concat(cont, separator, talloc); + } + } + + return un_string_copy(cont, alloc); +} + +String un_string_substring(String input, s64 start, s64 max_size) { + String slice; + + assert(start >= 0); + assert(max_size > 0); + + slice.size = UN_MIN(max_size, (input.size - (s64)max_size)); + slice.data = input.data + start; + + + if (start > (input.size - max_size)) { + slice.size = UN_MIN(max_size, (input.size - (s64)max_size)); + } else { + slice.size = max_size; + } + + slice.data = input.data + start; + return slice; +} + +s64 un_string_index_of(String input, u8 value, u64 skip_count) { + s64 i; + + for (i = 0; i < input.size; i++) { + if (input.data[i] != value) { + continue; + } + + if (skip_count) { + skip_count--; + } else { + return i; + } + } + + return -1; +} +extern s64 un_string_index_of_last(String input, u8 value) { + s64 i, index; + + index = -1; + + for (i = 0; i < input.size; i++) { + if (input.data[i] == value) index = i; + } + + return index; +} + +static String format_u64(u64 value) { + String output; + u64 l, r, size; + u8 t; + u8 buffer[32]; + + size = 0; + + if (value == 0) { + return UN_STR("0"); + } + + while (value) { + buffer[size++] = '0' + (value % 10); + value /= 10; + } + + assert(size <= 20); + + for (l = 0, r = size - 1; l < r; l++, r--) { + t = buffer[l]; + buffer[l] = buffer[r]; + buffer[r] = t; + } + + output.size = size; + output.data = buffer; + return un_string_copy(output, un_allocator_get_temporary()); +} + +static String format_s64(s64 value) { + String output; + u64 l, r, size; + b32 negative; + u8 t; + u8 buffer[32]; + + size = 0; + + if (value == 0) { + return UN_STR("0"); + } + + negative = false; + + if (value < 0) { + value = -value; + negative = true; + } + + while (value) { + buffer[size++] = '0' + (value % 10); + value /= 10; + } + + if (negative) { + buffer[size++] = '-'; + } + + assert(size <= 20); + + for (l = 0, r = size - 1; l < r; l++, r--) { + t = buffer[l]; + buffer[l] = buffer[r]; + buffer[r] = t; + } + + output.size = size; + output.data = buffer; + return un_string_copy(output, un_allocator_get_temporary()); +} + +String un_string_format(Allocator alloc, String buffer, ...) { + va_list args; + String s; + List output; + s64 i, j; + + Allocator talloc = un_allocator_get_temporary(); + output = un_list_create(UN_KB(1), sizeof(u8), talloc); + + va_start(args, buffer); + + for (i = 0; i < buffer.size; i++) { + if (buffer.data[i] != '%') { + un_list_append(&output, buffer.data + i); + continue; + } + + switch (buffer.data[++i]) { + case '%': + { + un_list_append(&output, buffer.data + i); + } break; + case 'c': + { + u32 b = va_arg(args, u32); + un_list_append(&output, &b); + } break; + + case 'u': + { + s = format_u64(va_arg(args, u64)); + for (j = 0; j < s.size; j++) { + un_list_append(&output, s.data + j); + } + } break; + + case 'd': + { + s = format_s64(va_arg(args, u64)); + for (j = 0; j < s.size; j++) { + un_list_append(&output, s.data + j); + } + } break; + + case 's': + { + s = va_arg(args, String); + for (j = 0; j < s.size; j++) { + un_list_append(&output, s.data + j); + } + } break; + default: break; + } + } + + va_end(args); + + s.size = (s64)output.count; + s.data = output.data; + + return un_string_copy(s, alloc); +} diff --git a/src/ungrateful.c b/src/ungrateful.c new file mode 100644 index 0000000..1ceefd6 --- /dev/null +++ b/src/ungrateful.c @@ -0,0 +1,6 @@ +#include "ungrateful.h" + +#include "un_memory.c" +#include "un_strings.c" +#include "un_list.c" +#include "un_log.c" diff --git a/src/ungrateful.h b/src/ungrateful.h new file mode 100644 index 0000000..8d1e70e --- /dev/null +++ b/src/ungrateful.h @@ -0,0 +1,202 @@ +#if !defined(UNGRATEFUL_H) +# define UNGRATEFUL_H + +/* + Ungrateful - standard library for game development. + + LICENSE: + + Copyright (C) 2025 Bogdan Masyutin (bonmas14) + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Bogdan Masyutin - bonmas14@gmail.com +*/ + +#if defined(__clang__) || defined(__GNUC__) +# define CLANG_COMPILER +# define __TRAP() __builtin_trap() +#elif _MSC_VER >= 1939 +# define MSVC_COMPILER +# include <intrin.h> +# define __TRAP() __debugbreak() +#else +# error "Unknown compiler" +#endif + +#if defined(__cplusplus) +# define CLITERAL(type) type +#else +# define CLITERAL(type) (type) +#endif + +#if defined(_WIN32) +# define OS_WINDOWS +#elif defined(__linux__) +# define OS_LINUX +#else +# error "unknown platform!" +#endif + +#define UN_TEMP_SIZE UN_MB(50) + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> +#include <limits.h> +#include <assert.h> + +#define UNUSED(x) (void)(x) + +#define UN_KB(s) ((u64)(s) * 1024LL) +#define UN_MB(s) (UN_KB(s) * 1024LL) +#define UN_GB(s) (UN_MB(s) * 1024LL) + +#define UN_MAX(a, b) (a) > (b) ? (a) : (b) +#define UN_MIN(a, b) (a) < (b) ? (a) : (b) + +#define UN_CSTR (u8*) +#define UN_STR(cstr) un_string_from_cstring(UN_CSTR cstr) +#define CSTR (char*) + +#define UN_CLEAR(var) un_memory_set((void*)&var, 0, sizeof(var)) + +#if defined(__cplusplus) +extern "C" { +#endif + + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +typedef int8_t b32; +typedef int8_t b8; + +typedef float f32; +typedef double f64; + +/* ---- Memory Allocators API ---- */ + +typedef enum { + UN_ALLOC_MSG_ALLOCATE, + UN_ALLOC_MSG_REALLOCATE, + UN_ALLOC_MSG_FREE, + UN_ALLOC_MSG_SELF_DELETE, +} Allocator_Message; + +#define ALLOCATOR_PROC_SIGNATURE(name)\ + void *name(void *p, u64 size, Allocator_Message message, void *data) + +typedef ALLOCATOR_PROC_SIGNATURE(Allocator_Proc); + +typedef struct { + Allocator_Proc *proc; + void *data; +} Allocator; + +// extern Allocator un_allocator_create_heap(s64 chunk_size); /* ... for large things */ +extern Allocator un_allocator_create_arena(u64 initial_size); /* Grouping allocator, that will recursively grow */ + +extern Allocator un_allocator_get_standard(void); +extern Allocator un_allocator_get_temporary(void); + +extern void *un_memory_alloc(u64 size, Allocator alloc); +extern void *un_memory_realloc(void *ptr, u64 size, Allocator alloc); +extern void *un_memory_free(void *ptr, Allocator alloc); +extern void un_memory_destroy(Allocator *alloc); + +extern void un_memory_set(u8 *dest, u8 value, u64 size); +extern void un_memory_copy(u8 *dest, u8 *src, u64 size); +extern void un_memory_move(u8 *dest, u8 *src, u64 size); +extern s32 un_memory_compare(u8 *left, u8 *right, u64 size); /* checks for every byte in arrays for condition: if left is bigger that right. */ + +/* ---- Generic list structure ---- */ + +typedef struct { + u64 count; + u64 capacity; + u64 element_size; + Allocator alloc; + void *data; +} List; + +extern List un_list_create(u64 start_capacity, u64 element_size, Allocator alloc); +extern void un_list_destroy(List *list); + +extern List un_list_clone(List *list, Allocator alloc); +extern void un_list_append(List *list, void *data); +extern void *un_list_get(List *list, u64 index); +extern void un_list_remove(List *list, u64 index); + +/* ---- no-wide string API ---- */ + +typedef struct { + s64 size; + u8 *data; +} String; + +extern u64 un_string_get_length(u8 *cstring); + +extern String un_string_from_cstring(u8* cstring); +extern u8* un_string_to_cstring(String string, Allocator alloc); + +extern String un_string_copy(String source, Allocator alloc); +extern s32 un_string_compare(String left, String right); +extern String un_string_concat(String left, String right, Allocator alloc); + +extern String un_string_swap(String input, u8 from, u8 to, Allocator alloc); +extern List un_string_split(String input, String pattern, Allocator alloc); +extern String un_string_join(List string_list, String separator, Allocator alloc); + +extern String un_string_substring(String input, s64 start, s64 max_size); + +extern s64 un_string_index_of(String input, u8 value, u64 skip_count); +extern s64 un_string_index_of_last(String input, u8 value); + +extern String un_string_format(Allocator alloc, String buffer, ...); +extern String un_string_tformat(String buffer, ...); + +/* ---- Logging API ---- */ + +typedef enum { + UN_LOG_TRACE, + UN_LOG_DEBUG, + UN_LOG_INFO, + UN_LOG_WARNING, + UN_LOG_ERROR, + UN_LOG_FATAL, + UN_LOG_RAW +} Log_Level; + +extern Log_Level un_current_log_level; + +extern void un_log_write(Log_Level level, String format, ...); +extern void un_log_write_cstring(Log_Level level, u8 *format, ...); + +#if defined(__cplusplus) +} +#endif + +#endif // UNGRATEFUL_H diff --git a/tests/allocs.c b/tests/allocs.c new file mode 100644 index 0000000..608d9f4 --- /dev/null +++ b/tests/allocs.c @@ -0,0 +1,39 @@ +#include <ungrateful.h> + +int main(void) { + u64 i, size, *value; + size = UN_KB(1); + + { // std + Allocator std = un_allocator_get_standard(); + void *mem = un_memory_alloc(size, std); + + if (mem != NULL) { + un_memory_free(mem, std); + } + } + + { // temp + Allocator temp = un_allocator_get_temporary(); + + u8 *mem = (u8*) un_memory_alloc(size, temp); + assert(mem != NULL); + + for (i = 0; i < size; i++) { + mem[i] = 0xAB; + } + + un_memory_destroy(&temp); + } + + { // arena + Allocator arena = un_allocator_create_arena(size); + + for (i = 0; i < 1000; i++) { + value = un_memory_alloc(8, arena); + *value = 0xAC; + } + + un_memory_destroy(&arena); + } +} diff --git a/tests/hello_world.c b/tests/hello_world.c new file mode 100644 index 0000000..78f6c29 --- /dev/null +++ b/tests/hello_world.c @@ -0,0 +1,7 @@ +#include <ungrateful.h> + +int main(void) { + un_log_write(UN_LOG_INFO, un_string_from_cstring(UN_CSTR "Hello world!")); + + return 0; +} diff --git a/tests/lists.c b/tests/lists.c new file mode 100644 index 0000000..a8627ac --- /dev/null +++ b/tests/lists.c @@ -0,0 +1,50 @@ +#include <ungrateful.h> + +int main(void) { + u64 i, v, times, size; + List list; + Allocator alloc; + + size = 16; + times = 4; + + { + alloc = un_allocator_get_standard(); + list = un_list_create(size, sizeof(u64), alloc); + + assert(un_list_get(&list, 0) == NULL); + + for (i = 0; i < size; i++) { + v = i * times; + + un_list_append(&list, (void *)(&v)); + } + + assert(*(u64*)un_list_get(&list, size - 1) == ((size - 1) * times)); + + for (i = 0; i < size; i++) { + v = *(u64*)un_list_get(&list, i); + assert(v == i * times); + } + + un_list_remove(&list, 0); + assert(*(u64*)un_list_get(&list, 0) == times); + + un_list_remove(&list, list.count); // will be ignored + un_list_remove(&list, list.count - 1); + + assert(list.count == (size - 2)); + + for (i = 0; i < size; i++) { + v = i * times; + un_list_append(&list, (void *)(&v)); + } + + assert(list.capacity == (size * 2)); + + un_list_destroy(&list); + + assert(list.data == NULL); + } +} + diff --git a/tests/memctl.c b/tests/memctl.c new file mode 100644 index 0000000..1614442 --- /dev/null +++ b/tests/memctl.c @@ -0,0 +1,42 @@ +#include <ungrateful.h> + +#define SIZE UN_KB(1) + +u8 buffer_a[SIZE]; +u8 buffer_b[SIZE]; + +int main(void) { + u64 i; + for (i = 0; i < SIZE; i++) { + buffer_a[i] += i << 2; + } + + { + un_memory_copy(buffer_b, buffer_a, SIZE); + + for (i = 0; i < SIZE; i++) { + assert(buffer_b[i] == buffer_a[i]); + } + + assert(un_memory_compare(buffer_a, buffer_b, SIZE) == 0); + + buffer_b[SIZE - 10] = 0; + assert(un_memory_compare(buffer_a, buffer_b, SIZE) == 1); + } + + { + un_memory_move(buffer_a + SIZE / 4, buffer_a, SIZE / 2); + + assert(un_memory_compare(buffer_b, buffer_a + SIZE / 4, SIZE / 2) == 0); + } + + { + un_memory_set(buffer_a, 0, SIZE); + + for (i = 0; i < SIZE; i++) { + assert(buffer_a[i] == 0); + } + } + + return 0; +} diff --git a/tests/strings.c b/tests/strings.c new file mode 100644 index 0000000..d49d525 --- /dev/null +++ b/tests/strings.c @@ -0,0 +1,103 @@ +#include <ungrateful.h> + +int main(void) { + List splits; + Allocator talloc; + String result; + + assert(un_string_get_length(UN_CSTR "Hello") == 5); + assert(un_string_get_length(UN_CSTR "") == 0); + assert(un_string_get_length(UN_CSTR "What") == 4); + + talloc = un_allocator_get_temporary(); + + result = un_string_concat(UN_STR("Hello "), UN_STR("world!"), talloc); + assert(!un_string_compare(result, UN_STR("Hello world!"))); + + assert(!un_string_compare(un_string_copy(result, talloc), result)); + + splits = un_string_split(UN_STR("Eatin burger wit no honey mustard"), UN_STR(" "), talloc); + + assert(splits.count == 6); + + assert(!un_string_compare(*(String*)un_list_get(&splits, 0), UN_STR("Eatin"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 1), UN_STR("burger"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 2), UN_STR("wit"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 3), UN_STR("no"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 4), UN_STR("honey"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 5), UN_STR("mustard"))); + + un_list_destroy(&splits); + + splits = un_string_split(UN_STR("Eatin||burger||wit||no||honey||mustard"), UN_STR("||"), talloc); + + assert(splits.count == 6); + + assert(!un_string_compare(*(String*)un_list_get(&splits, 0), UN_STR("Eatin"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 1), UN_STR("burger"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 2), UN_STR("wit"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 3), UN_STR("no"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 4), UN_STR("honey"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 5), UN_STR("mustard"))); + + un_list_destroy(&splits); + + splits = un_string_split(UN_STR("Eatin||burger||wit||no||honey||mustard||a"), UN_STR("||"), talloc); + + assert(splits.count == 7); + + assert(!un_string_compare(*(String*)un_list_get(&splits, 0), UN_STR("Eatin"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 1), UN_STR("burger"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 2), UN_STR("wit"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 3), UN_STR("no"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 4), UN_STR("honey"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 5), UN_STR("mustard"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 6), UN_STR("a"))); + + un_list_destroy(&splits); + + splits = un_string_split(UN_STR("a||Eatin||burger||wit||no||honey||mustard||a"), UN_STR("||"), talloc); + + assert(splits.count == 8); + + assert(!un_string_compare(*(String*)un_list_get(&splits, 0), UN_STR("a"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 1), UN_STR("Eatin"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 2), UN_STR("burger"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 3), UN_STR("wit"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 4), UN_STR("no"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 5), UN_STR("honey"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 6), UN_STR("mustard"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 7), UN_STR("a"))); + + un_list_destroy(&splits); + + splits = un_string_split(UN_STR("||Eatin||burger||wit||no||honey||mustard||"), UN_STR("||"), talloc); + + assert(splits.count == 6); + + assert(!un_string_compare(*(String*)un_list_get(&splits, 0), UN_STR("Eatin"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 1), UN_STR("burger"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 2), UN_STR("wit"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 3), UN_STR("no"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 4), UN_STR("honey"))); + assert(!un_string_compare(*(String*)un_list_get(&splits, 5), UN_STR("mustard"))); + + assert(!un_string_compare(un_string_join(splits, UN_STR(" "), talloc), UN_STR("Eatin burger wit no honey mustard"))); + + un_list_destroy(&splits); + + assert(!un_string_compare(un_string_substring(UN_STR("HelloWorld!"), 5, 6), UN_STR("World!"))); + assert(!un_string_compare(un_string_substring(UN_STR("HelloWorld!"), 0, 11), UN_STR("HelloWorld!"))); + assert(!un_string_compare(un_string_substring(UN_STR("HelloWorld!"), 10, 1), UN_STR("!"))); + + assert(un_string_index_of(UN_STR("CP/M"), (u8)'/', 0) == 2); + assert(un_string_index_of_last(UN_STR("https://github.com/bonmas14"), (u8)'/') == 18); + + assert(!un_string_compare(un_string_swap(UN_STR("/path/from/unix/systems/"), (u8)'/', (u8) '\\', talloc), UN_STR("\\path\\from\\unix\\systems\\"))); + + assert(!un_string_compare( + un_string_format(talloc, UN_STR("/path/%s/unix/a %d %u %%"), UN_STR("test"), (s64)-100, (u64)404), + UN_STR("/path/test/unix/a -100 404 %") + ) + ); +} |