feat: added code
parent
8957fdd9bc
commit
aee9aec828
|
@ -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
|
|
@ -0,0 +1,69 @@
|
||||||
|
#ifndef _fencer_debug_h
|
||||||
|
#define _fencer_debug_h
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <assert.h>
|
||||||
|
#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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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: "<content>"
|
||||||
|
// 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(<x>, <y>)
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,181 @@
|
||||||
|
#ifndef _fencer_vmath_h
|
||||||
|
#define _fencer_vmath_h
|
||||||
|
|
||||||
|
#include "stddef.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#if defined _WIN32 && ! defined isnanf
|
||||||
|
# define isnanf(x) _isnanf(x)
|
||||||
|
# define HAVE_ISNANF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if VMATH_SDL == 1
|
||||||
|
|
||||||
|
#include <SDL2/SDL_rect.h>
|
||||||
|
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
|
Loading…
Reference in New Issue