From aee9aec828994d5a0ca9096d16682e5bd908fa17 Mon Sep 17 00:00:00 2001 From: Sara Date: Thu, 25 Jan 2024 14:08:23 +0100 Subject: [PATCH] feat: added code --- debug.c | 9 +++ debug.h | 69 +++++++++++++++++ dictionary.c | 116 ++++++++++++++++++++++++++++ dictionary.h | 46 +++++++++++ list.c | 176 ++++++++++++++++++++++++++++++++++++++++++ list.h | 38 ++++++++++ mirror.c | 21 +++++ mirror.h | 126 ++++++++++++++++++++++++++++++ strutil.c | 54 +++++++++++++ strutil.h | 17 +++++ typeclass_helpers.h | 10 +++ variant.c | 61 +++++++++++++++ variant.h | 35 +++++++++ vmath.h | 181 ++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 959 insertions(+) create mode 100644 debug.c create mode 100644 debug.h create mode 100644 dictionary.c create mode 100644 dictionary.h create mode 100644 list.c create mode 100644 list.h create mode 100644 mirror.c create mode 100644 mirror.h create mode 100644 strutil.c create mode 100644 strutil.h create mode 100644 typeclass_helpers.h create mode 100644 variant.c create mode 100644 variant.h create mode 100644 vmath.h diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..4f9d6aa --- /dev/null +++ b/debug.c @@ -0,0 +1,9 @@ +#include "debug.h" + +#if NDEBUG +int g_debug_error_abort = 0; +int g_debug_log_lvl = 0; +#else +int g_debug_error_abort = 1; +int g_debug_log_lvl = 3; +#endif diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..1095262 --- /dev/null +++ b/debug.h @@ -0,0 +1,69 @@ +#ifndef _fencer_debug_h +#define _fencer_debug_h + +#include +#include + +#ifndef NDEBUG +#include +#endif + +extern int g_debug_error_abort; +extern int g_debug_log_lvl; + +#define LOG_INFO(...) do {\ + if(g_debug_log_lvl < 3) break;\ + printf("[%s:%d] INFO | ", __FILE__, __LINE__);\ + printf(__VA_ARGS__);\ + printf("\n");\ +} while(0) + +#define LOG_ERROR(...) do {\ + if(g_debug_log_lvl >= 1) {\ + printf("[%s:%d] ERROR | ", __FILE__, __LINE__);\ + printf(__VA_ARGS__);\ + printf("\n");\ + fflush(stdout);\ + }\ + if(g_debug_error_abort != 0) abort();\ +} while(0) + +#define LOG_WARNING(...) do {\ + if(g_debug_log_lvl < 2) break;\ + printf("[%s:%d] WARNING | ", __FILE__, __LINE__);\ + printf(__VA_ARGS__);\ + printf("\n");\ +} while(0) + + +#define RETURN_ERROR(__VALUE, ...) do {\ + LOG_ERROR(__VA_ARGS__);\ + return __VALUE;\ +} while(0) + +#define RETURN_WARNING(__VALUE, ...) do {\ + LOG_WARNING(__VA_ARGS__);\ + return __VALUE;\ +} while(0) + +#define ASSERT_RETURN(__ASSERT, __RETURN, ...) do {\ + if(!(__ASSERT)) {\ + LOG_ERROR(__VA_ARGS__);\ + return __RETURN;\ + }\ +} while(0) + +#define CHECK(__ASSERT, ...) do {\ + if(!(__ASSERT)) {\ + LOG_ERROR(__VA_ARGS__);\ + }\ +} while(0) + +#define ASSERT_RETURN_WARN(__ASSERT, __RETURN, ...) do {\ + if(!(__ASSERT)) {\ + LOG_WARNING(__VA_ARGS__);\ + return __RETURN;\ + }\ +} while(0) + +#endif // !_fencer_debug_h diff --git a/dictionary.c b/dictionary.c new file mode 100644 index 0000000..397297a --- /dev/null +++ b/dictionary.c @@ -0,0 +1,116 @@ +#include "dictionary.h" +#include "strutil.h" +#include "string.h" +#include "math.h" + +typedef struct Key { + char* key; + uintptr_t hash; +} Key; + +Dictionary dictionary_new(size_t element_size) { + return (Dictionary) { + .element_size = element_size, + .list = list_init(sizeof(Key) + element_size) + }; +} + +Key* internal_dictionary_get(Dictionary* self, uintptr_t keyhash, size_t* out_index) { + if(self->list.len == 0) { + if(out_index) *out_index = 0; + return NULL; + } + Key* element; + signed long l = 0, r = self->list.len, m; + while(l <= r) { + m = floor((double)(l+r)/2.0); + if(out_index != NULL) *out_index = m; + element = list_at_unchecked(&self->list, m); + if(keyhash < element->hash) + r = m-1; + else if(keyhash > element->hash) + l = m+1; + else + return element; + } + m = m >= 0 ? (m < self->list.len ? m : self->list.len - 1) : 0; + return list_at_as(Key, &self->list, m); +} + +void* dictionary_get_raw(Dictionary* self, const char* key) { + uintptr_t hash = strhash(key); + Key* keyblock = internal_dictionary_get(self, hash, NULL); + return keyblock == NULL || strcmp(key, keyblock->key) != 0 + ? NULL + : ((char*)keyblock) + sizeof(Key); +} + +static inline +int dictionary_insert_kvp(Dictionary* self, Key key, void* value, size_t index) { + // allocate a block of memory to insert into the list + char* insert_data = malloc(self->list.element_size); + if(insert_data == NULL) + return -1; // OOM + // fill block with key and value + memcpy(insert_data, &key, sizeof(Key)); + memcpy(insert_data + sizeof(Key), value, self->element_size); + // decide add or insert based on current length of list + if(self->list.len == 0 || index >= self->list.len) + list_add(&self->list, insert_data); + else + list_insert(&self->list, insert_data, index); + // free allocated key-value block + free(insert_data); + return 0; +} + +int dictionary_set_raw(Dictionary* self, const char* key, void* data) { + const uintptr_t keyhash = strhash(key); + size_t closest_index; + Key* closest = internal_dictionary_get(self, keyhash, &closest_index); + // get index of closest hash + // prepare key data + Key keydata = { + .hash = keyhash, + .key = malloc(strlen(key)+1) + }; + strcpy(keydata.key, key); + if(closest == NULL) { + return dictionary_insert_kvp(self, keydata, data, closest_index); + } else if(closest->hash == keyhash && strcmp(closest->key, key) == 0) { + memcpy(closest + 1, data, self->element_size); + return 0; + } else if(keyhash < closest->hash) { // insert before + return dictionary_insert_kvp(self, keydata, data, closest_index); + } else { + return dictionary_insert_kvp(self, keydata, data, closest_index + 1); + } +} + +int dictionary_erase(Dictionary* self, const char* key) { + const uintptr_t keyhash = strhash(key); + size_t index; + Key* element = internal_dictionary_get(self, keyhash, &index); + if(element->hash != keyhash || strcmp(key, element->key) != 0) + return -1; // not a match + // free the key string + free(element->key); + list_erase(&self->list, index); + return 0; +} + +void dictionary_empty(Dictionary* self) { + list_empty(&self->list); +} + +int dictionary_has_key(Dictionary* self, const char* key) { + return dictionary_get_raw(self, key) != NULL; +} + +int dictionary_try_get(Dictionary* self, const char* key, void* out) { + void* found = dictionary_get_raw(self, key); + if(found == NULL) + return 0; + memmove(out, found, self->element_size); + return 1; +} diff --git a/dictionary.h b/dictionary.h new file mode 100644 index 0000000..3c67068 --- /dev/null +++ b/dictionary.h @@ -0,0 +1,46 @@ +#ifndef _fencer_hash_map_h +#define _fencer_hash_map_h + +#include "list.h" + +typedef struct Dictionary { + List list; + size_t element_size; +} Dictionary; + +// returns a newly initialized dictionary +extern Dictionary dictionary_new(size_t element_size); +// gets a voidptr to the value identified by key +extern void* dictionary_get_raw(Dictionary* self, const char* key); +// inserts or overwrites a new entry identified by key +extern int dictionary_set_raw(Dictionary* self, const char* key, void* data); +// remove entry identified by key from dictionary +extern int dictionary_erase(Dictionary* self, const char* key); +// clear the contents of the dictionary +// erases all heap-allocated memory, the dictionary can be safely freed after +extern void dictionary_empty(Dictionary* self); +// find out if the dictionary contains a value identified by this key +// returns 1 if the key was found +// returns 0 if the key was not found +extern int dictionary_has_key(Dictionary* self, const char* key); +// try to find value identified as key in the dictionary. +// return 1 if the value was found, 0 if not. +// if dictionary_try_get returns 0, memory at out will be unchanged, +// if dictionary_try_get returns 1, found value will be copied into out +extern int dictionary_try_get(Dictionary* self, const char* key, void* out); + +// insert or override value identified by key from a value type (supports rvalues) +#define dictionary_set_value(Type_, Dict_, Key_, Value_)\ +{ Type_ __value = (Type_)Value_; dictionary_set_raw(Dict_, Key_, &__value); } + +// initialize dictionary to fit values of Type_ +#define dictionary_from_type(Type_)\ +dictionary_new(sizeof(Type_)) +// get a pointer to value identified by Key_ +#define dictionary_get_as(Type_, Dict_, Key_)\ +(Type_*)dictionary_get_raw(Dict_, Key_) +// get dereferenced value identified by Key_ +#define dictionary_get_value(Type_, Dict_, Key_)\ +*((Type_*)dictionary_get_raw(Dict_, Key_)) + +#endif // !_fencer_hash_map_h diff --git a/list.c b/list.c new file mode 100644 index 0000000..1f92ba1 --- /dev/null +++ b/list.c @@ -0,0 +1,176 @@ +#include "list.h" +#include "stdint.h" +#include "stdlib.h" +#include "string.h" +#include "debug.h" + +#ifndef LIST_DEFAULT_RESERVE +#define LIST_DEFAULT_RESERVE 4 +#endif + +List list_init(size_t element_size) { + List self = { + .element_size = element_size, + .cap = LIST_DEFAULT_RESERVE, + .len = 0, + .data = malloc(element_size * LIST_DEFAULT_RESERVE), + }; + + if(self.data == NULL) { + LOG_ERROR("Failed to allocate list with starting capacity of %d", LIST_DEFAULT_RESERVE); + self.cap = 0; + } + + return self; +} + +List list_copy(const List* source) { + List self = list_init(source->element_size); + list_reserve(&self, source->cap); + if(self.cap > 0) { + memcpy(self.data, source->data, source->element_size * source->len); + self.len = source->len; + } else { + LOG_ERROR("Failed to reserve space"); + } + return self; +} + +void list_empty(List* self) { + if(self->data == NULL || self->cap == 0) + return; + self->data = NULL; + self->cap = 0; + self->len = 0; +} + +void list_reserve(List* self, size_t at_least) { + if(at_least < self->cap) + return; + + size_t new_cap = self->cap > 0 ? self->cap : LIST_DEFAULT_RESERVE; + while(at_least >= new_cap) { + new_cap *= 2; + } + + void* new; + if(self->data == NULL) + new = malloc(new_cap * self->element_size); + else + new = realloc(self->data, new_cap * self->element_size); + ASSERT_RETURN(new != NULL,, "Failed to reserve space for %zu extra elements in list", new_cap); + + self->data = new; + self->cap = new_cap; +} + +void* list_at_unchecked(List* self, size_t at) { + union { + uint8_t* as_byte; + void* as_void; + } data = { + .as_void = self->data + }; + + return data.as_byte + self->element_size * at; +} + +void* list_at(List* self, size_t at) { + ASSERT_RETURN(at < self->len, NULL, "Index %zu out of bounds", at); + return list_at_unchecked(self, at); +} + +size_t list_add(List* self, void* item) { + list_reserve(self, self->len + 1); + union { + uint8_t* as_byte; + void* as_void; + } data = { + .as_void = self->data + }; + + uint8_t* into = data.as_byte + (self->element_size * self->len); + + memcpy(into, item, self->element_size); + ++self->len; + + return self->len - 1; +} + +void list_insert(List* self, void* item, size_t at) { + list_reserve(self, self->len + 1); + + if(at >= self->len) { + list_add(self, item); + return; + } + + union { + uint8_t* as_byte; + void* as_void; + } data = { + .as_void = self->data + }; + uint8_t* from = data.as_byte + (self->element_size * at); + uint8_t* into = data.as_byte + (self->element_size * (at + 1)); + uint8_t* end = data.as_byte + (self->element_size * self->len); + memmove(into, from, end - from); + memcpy(from, item, self->element_size); + ++self->len; +} + +void list_erase(List* self, size_t at) { + ASSERT_RETURN(at < self->len,, "Index %zu out of bounds", at); + + union { + uint8_t* as_byte; + void* as_void; + } data = { + .as_void = self->data + }; + + uint8_t* into = data.as_byte + at * self->element_size; + uint8_t* from = data.as_byte + (at + 1) * self->element_size; + + if(at < self->len - 1) + memmove(into, from, (self->len - at) * self->element_size); + --self->len; + + size_t new_cap = self->cap; + while(new_cap > self->len) { + new_cap /= 2; + } + new_cap *= 2; + + + if(new_cap == self->cap) + return; + + void* shrunk = realloc(self->data, new_cap * self->element_size); + ASSERT_RETURN(shrunk != NULL || new_cap == 0,, "Failed to shrink List to %zu", new_cap); + + self->data = shrunk; + self->cap = new_cap; +} + +void* list_iterator_begin(List* self) { + return list_at_unchecked(self, 0); +} + +void* list_iterator_end(List* self) { + return list_at_unchecked(self, self->len); +} + +size_t list_contains(List* self, void* query) { + union { + uint8_t* as_byte; + void* as_void; + } data = { + .as_void = self->data + }; + for(size_t i = 0; i < self->len; ++i) { + if(memcmp(data.as_byte + (i * self->element_size), query, self->element_size) == 0) + return i; + } + return self->len; +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..0d34494 --- /dev/null +++ b/list.h @@ -0,0 +1,38 @@ +#ifndef _fencer_list_h +#define _fencer_list_h + +#include "stddef.h" + +typedef struct List List; +struct List { + void* data; + size_t cap; + size_t len; + + size_t element_size; +}; + +extern List list_init(size_t element_size); +extern List list_copy(const List* source); +extern void list_empty(List* list); + +extern void list_reserve(List* self, size_t at_least); +extern void* list_at(List* list, size_t at); +extern void* list_at_unchecked(List* self, size_t at); + +extern size_t list_add(List* self, void* item); +extern void list_insert(List* self, void* item, size_t at); +extern void list_erase(List* self, size_t at); + +extern void* list_iterator_begin(List* self); +extern void* list_iterator_end(List* self); + +extern size_t list_contains(List* self, void* query); + +#define list_from_type(T) list_init(sizeof(T)) +#define list_foreach(T, iter, list) for(T iter = list_iterator_begin(list); iter != (T)list_iterator_end(list); ++iter) +#define list_at_as(T, __list, __i) ((T*)(list_at(__list, __i))) + +#define list_iterator_begin_as(T, __list) ((T*)(list_iterator_begin(__list))) + +#endif // !_fencer_list_h diff --git a/mirror.c b/mirror.c new file mode 100644 index 0000000..ab6b794 --- /dev/null +++ b/mirror.c @@ -0,0 +1,21 @@ +#include "mirror.h" + +MirroredTypeclass* internal_mirror_get_typeclass(void* self, IMirror const* tc, const char* typeclass) { + return dictionary_get_raw(tc->get_typeclasses(self), typeclass); +} + +const void* mirror_get_typeclass(void* data, IMirror const* tc, const char* typeclass) { + MirroredTypeclass* class = internal_mirror_get_typeclass(data, tc, typeclass); + if(class != NULL) + return class->typeclass; + else + return NULL; +} + +void* mirror_get_function(void* data, IMirror const* tc, const char* typeclass_name) { + MirroredTypeclass* class = internal_mirror_get_typeclass(data, tc, typeclass_name); + if(class != NULL) + return class->function; + else + return NULL; +} diff --git a/mirror.h b/mirror.h new file mode 100644 index 0000000..398a454 --- /dev/null +++ b/mirror.h @@ -0,0 +1,126 @@ +#ifndef _fencer_mirror_h +#define _fencer_mirror_h + +#include "typeclass_helpers.h" +#include "stdint.h" +#include "string.h" +#include "dictionary.h" +#include "strutil.h" // included because the impl macros require strhash + +typedef uintptr_t typeid; + +typedef struct { + const char* (*const get_typestring)(void* self); + typeid (*const get_typeid)(void* self); + Dictionary* (*const get_typeclasses)(void* self); +} IMirror; + +typedef struct { + void* data; + union { + IMirror const* tc; + // this is cursed, but it allows the TC_CAST macro to work + IMirror const* mirror; + }; +} Mirror; + +typedef struct { + const void* typeclass; + void* function; +} MirroredTypeclass; + +static inline int mirror_is_typeid(const Mirror* mirror, typeid id) { + return mirror->tc->get_typeid(mirror->data) == id; +} +static inline int mirror_is_typestring(const Mirror* mirror, const char* id) { + return strcmp(id, mirror->tc->get_typestring(mirror->data)) == 0; +} +static inline int mirror_eq(const Mirror* lhs, const Mirror* rhs) { + return lhs->tc->get_typeid(lhs->data) == rhs->tc->get_typeid(rhs->data); +} + +extern const void* mirror_get_typeclass(void* data, IMirror const* tc, const char* typeclass); +// get the wrapper function for a given typeclass name +// example: +// mirror_get_function(physics_entity.data, physics_entity.mirror, "BehaviourEntity") +extern void* mirror_get_function(void* data, IMirror const* tc, const char* typeclass_name); + +// macro reexport of mirror_get_function which will cast the function so it can be called immediately +// example: +// MIRROR_GET_WRAP_FUNC(physics_entity.data, physics_entity.mirror, BehaviourEntity)(physics_entity.data) +#define MIRROR_GET_WRAP_FUNC(Data_, MirrorTc_, Typeclass_)\ +((Typeclass_(*)(void*))mirror_get_function(Data_, MirrorTc_, #Typeclass_)) + +// casting only works if the typeclass in question exposes IMirror as .mirror +// will segfault if the Mirror does not expose To_ +// example: +// TC_CAST(physics_entity, BehaviourEntity) +#define TC_CAST(From_, To_)\ +MIRROR_GET_WRAP_FUNC(From_.data, From_.mirror, To_)(From_.data) + +#define TC_MIRRORS(From_, To_)\ +MIRROR_GET_WRAP_FUNC(From_.data, From_.mirror, To_) != NULL + +#define MIRROR_TRY_WRAP(Into_, Mirror_, Typeclass_){\ + MirroredTypeclassWrapFunc fn_ = mirror_get_typeclass(Mirror_, #Typeclass_);\ + if(fn_ != NULL) {\ + Into_ = (TypeClass_)fn(Mirror_->data);\ + }\ +} + +#define impl_Mirror_for(T, get_typestring_f, get_typeid_f, get_typeclasses_f)\ +Mirror T##_as_Mirror(T* x) {\ + TC_FN_TYPECHECK(const char*, get_typestring_f, T*);\ + TC_FN_TYPECHECK(typeid, get_typeid_f, T*);\ + TC_FN_TYPECHECK(Dictionary*, get_typeclasses_f, T*);\ + static IMirror const tc = {\ + .get_typestring = (const char*(*const)(void*)) get_typestring_f,\ + .get_typeid = (typeid (*const)(void*)) get_typeid_f,\ + .get_typeclasses = (Dictionary* (*const)(void*)) get_typeclasses_f,\ + };\ + return (Mirror){.tc = &tc, .data = x};\ +} + +#define DECL_REFLECT(T)\ +extern const char* T##_get_typestring(T* self);\ +extern typeid T##_get_typeid(T* self);\ +extern Dictionary* T##_get_typeclasses(T* self);\ +decl_typeclass_impl(Mirror, T) + +#define START_REFLECT(T)\ +const char* T##_get_typestring(T* self) {\ + static const char* const typestring = #T;\ + return typestring;\ +}\ +typeid T##_get_typeid(T* self) {\ + static char init_flag = 0;\ + static typeid id = 0;\ + if(!init_flag) {\ + init_flag = 1;\ + id = strhash(#T);\ + }\ + return id;\ +}\ +Dictionary* T##_get_typeclasses(T* self) {\ + static char init_flag = 0;\ + static Dictionary typeclasses;\ + if(!init_flag) {\ + init_flag = 1;\ + typeclasses = dictionary_new(sizeof(MirroredTypeclass));\ + MirroredTypeclass tc;\ + REFLECT_TYPECLASS(T, Mirror) + +#define REFLECT_TYPECLASS(T, TypeClass_)\ + tc = (MirroredTypeclass){\ + .typeclass = (void*)T##_as_##TypeClass_(NULL).tc,\ + .function = (void*)T##_as_##TypeClass_\ + };\ + dictionary_set_raw(&typeclasses, #TypeClass_, &tc); + +#define END_REFLECT(T)\ + }\ + return &typeclasses;\ +}\ +impl_Mirror_for(T, T##_get_typestring, T##_get_typeid, T##_get_typeclasses) + +#endif // !_fencer_mirror_h diff --git a/strutil.c b/strutil.c new file mode 100644 index 0000000..c02490f --- /dev/null +++ b/strutil.c @@ -0,0 +1,54 @@ +#include "strutil.h" +#include "string.h" + +uintptr_t strnhash(const char* s, size_t n) { + static const size_t shift = sizeof(uintptr_t) * 8 - 4; + uintptr_t hash = 0; + for(size_t i = 0; i < n; ++i) { + hash = ((hash << 4) | s[i]) ^ (((uintptr_t)0xF << shift) & hash); + } + return hash; +} + +uintptr_t strhash(const char* s) { + return strnhash(s, strlen(s)); +} + +long strlast(const char* rbegin, const char* rend, char search) { + const char* itr = rbegin; + while(itr < rend && *itr != '\0') + if(*itr == search) return itr - rend; + else itr--; + return -1; +} + +long strfirst(const char* begin, const char* end, char search) { + const char* itr = begin; + while(itr < end && *itr != '\0') + if(*itr == search) return itr - begin; + else itr++; + return -1; +} + +long strcount(const char* begin, const char* end, char search) { + long count = 0; + for(;begin < end && *begin != '\0'; ++begin) + if(*begin == search) ++count; + return count; +} + +long strfirst_pred(const char* begin, const char* end, CharPredFn pred) { + const char* itr = begin; + while(itr < end && *itr != '\0') + if(pred(*itr)) return itr - begin; + else itr++; + return -1; +} + +long strlast_pred(const char* rbegin, const char* rend, CharPredFn pred) { + const char* itr = rbegin; + while(itr < rend && *itr != '\0') + if(pred(*itr)) return itr - rend; + else itr--; + return -1; +} diff --git a/strutil.h b/strutil.h new file mode 100644 index 0000000..ae8592a --- /dev/null +++ b/strutil.h @@ -0,0 +1,17 @@ +#ifndef _fencer_strutil_h +#define _fencer_strutil_h + +#include "stdlib.h" +#include "stdint.h" + +typedef int(*CharPredFn)(int); + +extern uintptr_t strnhash(const char* s, size_t n); +extern uintptr_t strhash(const char* s); +extern long strlast(const char* rbegin, const char* rend, char search); +extern long strfirst(const char* begin, const char* end, char search); +extern long strcount(const char* begin, const char* end, char search); +extern long strfirst_pred(const char* begin, const char* end, CharPredFn pred); +extern long strlast_pred(const char* rbegin, const char* rend, CharPredFn pred); + +#endif // !_fencer_strutil_h diff --git a/typeclass_helpers.h b/typeclass_helpers.h new file mode 100644 index 0000000..5f24513 --- /dev/null +++ b/typeclass_helpers.h @@ -0,0 +1,10 @@ +#ifndef _fencer_typeclass_helpers_h +#define _fencer_typeclass_helpers_h + +#define TC_FN_TYPECHECK(__Return, __Name, ...)\ +__Return (*const __Name##_)(__VA_ARGS__) = __Name; (void)__Name##_ + +#define decl_typeclass_impl(__Typeclass, __Type)\ +extern __Typeclass __Type##_as_##__Typeclass(__Type*); + +#endif // !_fencer_typeclass_helpers_h diff --git a/variant.c b/variant.c new file mode 100644 index 0000000..2f63dd4 --- /dev/null +++ b/variant.c @@ -0,0 +1,61 @@ +#include "variant.h" +#include "debug.h" +#include "ctype.h" +#include "string.h" +#include "strutil.h" + +Variant variant_from_str(const char* str) { + size_t length = strlen(str); + if(isdigit(str[0])) { + // variant is a number + // convert to double + return NumberVariant(atof(str)); + } else if(str[0] == '"') { + // variant should be a string + // format: "" + // copy the input string without the quotes + size_t result_length = strfirst(str+1, str+length, '"'); + char* string = malloc(result_length); + strncpy(string, str+1, result_length-1); + string[result_length] = '\0'; + return StringVariant(string, result_length); + } else if(strncmp(str, "Vector(", 7) == 0) { + // variant is a vector + // format: Vector(, ) + // get the location of the brackets + long close_bracket = strfirst(str, str + length, ')'); + if(close_bracket == -1) goto variant_from_str_err; + char buf[24]; buf[23] = '\0'; + // copy what is within the brackets + // 7 is the number of characters in "Vector(", which should be skipped + strncpy(buf, str + 7, close_bracket - 7); + length = strlen(buf); + long comma_ind = strfirst(buf, buf+length, ','); + if(comma_ind == -1) goto variant_from_str_err; + // split the string at the comma by inserting a null terminator + buf[comma_ind] = '\0'; + return VectorVariant(MakeVector(atof(buf), atof(buf + comma_ind + 1))); + } +variant_from_str_err: + return UndefinedVariant(); +} + +void destroy_variant(Variant* self) { + destroy_contained(self); + free(self); +} + +void destroy_contained(Variant* self) { + switch(self->type) { + default: break; + case Variant_String: + if(self->as_string != NULL) + free(self->as_string); + self->as_string = NULL; + break; + case Variant_Object: + free(self->as_object); + self->as_object = NULL; + break; + } +} diff --git a/variant.h b/variant.h new file mode 100644 index 0000000..2446d30 --- /dev/null +++ b/variant.h @@ -0,0 +1,35 @@ +#ifndef _fencer_variant_h +#define _fencer_variant_h + +#include "vmath.h" + +typedef enum VariantType { + Variant_Undefined = 0x0, + Variant_Number = 0x1, + Variant_Vector = 0x2, + Variant_Object = 0x3, + Variant_String = 0x4, +} VariantType; + +typedef struct Variant { + VariantType type; + size_t string_size; + union { + double as_number; + Vector as_vector; + void* as_object; + char* as_string; + }; +} Variant; + +extern Variant variant_from_str(const char* str); +extern void destroy_variant(Variant* self); +extern void destroy_contained(Variant* self); + +#define NumberVariant(Value_) (Variant){.type = Variant_Number, .as_number = Value_} +#define VectorVariant(Value_) (Variant){.type = Variant_Vector, .as_vector = Value_} +#define ObjectVariant(Value_) (Variant){.type = Variant_Object, .as_object = Value_} +#define StringVariant(Value_, BufSize_) (Variant){.type = Variant_String, .as_string = Value_, .string_size = BufSize_} +#define UndefinedVariant() (Variant){.type = Variant_Undefined } + +#endif // !_fencer_variant_h diff --git a/vmath.h b/vmath.h new file mode 100644 index 0000000..7c99083 --- /dev/null +++ b/vmath.h @@ -0,0 +1,181 @@ +#ifndef _fencer_vmath_h +#define _fencer_vmath_h + +#include "stddef.h" +#include + +#if defined _WIN32 && ! defined isnanf +# define isnanf(x) _isnanf(x) +# define HAVE_ISNANF +#endif + +#if VMATH_SDL == 1 + +#include +typedef SDL_FPoint Vector; +typedef SDL_Point IVector; + +#else + +typedef struct Vector { + float x; + float y; +} Vector; + +typedef struct IVector { + int x; + int y; +} IVector; + +#endif + +// Vector Constant Macros +#define ZeroVector (Vector){0.0f, 0.0f} +#define InfinityVector (Vector){INFINITY, INFINITY} +#define OneVector (Vector){1.0f,1.0f} + +#define UpVector (Vector){0.0f,-1.0f} +#define RightVector (Vector){1.0f,0.0f} +#define LeftVector (Vector){-1.0f,0.0f} +#define DownVector (Vector){0.0f,1.0f} + +#define MakeVector(__X, __Y) (Vector){__X, __Y} +#define VectorFrom(__A) (Vector){__A, __A} + +// Integer Vector Constant Macros +#define ZeroIVector (IVector){0,0} +#define OneIVector (IVector){1,1} + +#define UpIVector (IVector){-1,0} +#define DownIVector (IVector){1,0} +#define RightIVector (IVector){1,0} +#define LeftIVector (IVector){-1,0} + +#define MakeIVector(__X, __Y) (IVector){__X, __Y} +#define IVectorFrom(__A) (IVector){__A, __A} + +/// +// Floating point vector maths functions. +/// +static inline +int veqf(Vector a, Vector b) { + const float e = 0.0001f; + return fabsf(a.x - b.x) + fabsf(a.y - b.y) < e; +} +static inline +int visnanf(Vector a) { + return isnanf(a.x) || isnanf(a.y); +} +static inline +Vector vaddf(Vector a, Vector b) { + return (Vector){a.x + b.x, a.y + b.y}; +} +static inline +Vector vsubf(Vector a, Vector b) { + return (Vector){a.x - b.x, a.y - b.y}; +} +static inline +Vector vmulff(Vector a, float b) { + return (Vector){a.x * b, a.y * b}; +} +static inline +Vector vmulf(Vector a, Vector b) { + return (Vector) {a.x * b.x, a.y * b.y}; +} +static inline +Vector vinvf(Vector a) { + return (Vector){-a.x, -a.y}; +} +static inline +Vector vperpendicularf(Vector a) { + return (Vector){a.y, -a.x}; +} +static inline +float vmagnitudef(Vector a) { + if(veqf(a, ZeroVector)) return 0.f; + a.x = fabsf(a.x); + a.y = fabsf(a.y); + return sqrtf(a.x*a.x + a.y*a.y); +} +static inline +float vsqrmagnitudef(Vector a) { + a.x = fabsf(a.x); + a.y = fabsf(a.y); + return a.x*a.x + a.y*a.y; +} +static inline +Vector vnormalizedf(Vector a) { + if(veqf(a, ZeroVector)) return ZeroVector; + return vmulff(a, 1.0f/vmagnitudef(a)); +} +static inline +float vdotf(Vector a, Vector b) { + return (a.x*b.x) + (a.y*b.y); +} +static inline +float vdistancef(Vector a, Vector b) { + return vmagnitudef(vsubf(a, b)); +} +static inline +float vsqrdistf(Vector a, Vector b) { + return vsqrmagnitudef(vsubf(a, b)); +} +static inline +Vector vreciprocalf(Vector a) { + return (Vector){1.0f/a.x, 1.0f/a.y}; +} +static inline +Vector vrotatef(Vector a, float t) { + return (Vector){ + cosf(t) * a.x - sinf(t) * a.y, + sinf(t) * a.x + cosf(t) * a.y + }; +} +static inline +float vanglebetweenf(Vector a, Vector b) { + return vdotf(a, b) / (vmagnitudef(a) * vmagnitudef(b)); +} +static inline +Vector vprojectf(Vector onto, Vector from) { + float dot = vdotf(onto, from); + return vmulff(onto, dot); +} +static inline +Vector vlerpf(Vector start, Vector end, float t) { + if(veqf(start, end)) + return end; + t = fminf(fmaxf(0.0f, t), 1.0f); + return vaddf(start, vmulff(vsubf(end, start), t)); +} +static inline +Vector vmovetowardsf(Vector start, Vector end, float delta) { + return vlerpf(start, end, delta / vdistancef(end, start)); +} +static inline +Vector vaveragef(Vector* array, size_t count) { + Vector acc = ZeroVector; + for(size_t i = 0; i < count; ++i) { + acc = vaddf(acc, array[i]); + } + return vmulff(acc, 1.0f/(float)count); +} + +static inline +int veqi(IVector a, IVector b) { + return a.x == b.x && a.y == b.y; +} + +static inline +IVector vaddi(IVector a, IVector b) { + return (IVector){a.x + b.x, a.y + b.y}; +} +static inline +IVector vsubi(IVector a, IVector b) { + return (IVector){a.x - b.x, a.y - b.y}; +} +static inline +IVector vmuli(IVector a, IVector b) { + return (IVector){a.x * b.x, a.y * b.y}; +} + +#endif // !_fencer_vmath_h