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