aboutsummaryrefslogtreecommitdiff
path: root/bowl.c
diff options
context:
space:
mode:
authorKatharina Fey <kookie@spacekookie.de>2019-06-04 23:51:12 +0200
committerKatharina Fey <kookie@spacekookie.de>2019-06-04 23:51:12 +0200
commit7df71001ef1a9ea271ae3c409a367d6c2dd628b7 (patch)
treedfd86f523da1d1a8b4e08532a025355d505bc8a3 /bowl.c
parenta81e5628bd1c855289a6919822cc612a6871b039 (diff)
Changing library name and project structure
Diffstat (limited to 'bowl.c')
-rw-r--r--bowl.c815
1 files changed, 815 insertions, 0 deletions
diff --git a/bowl.c b/bowl.c
new file mode 100644
index 0000000..0f43db2
--- /dev/null
+++ b/bowl.c
@@ -0,0 +1,815 @@
+// Include our header file
+#include "bowl.h"
+
+// Runtime includes
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <stdbool.h>
+
+#define RDB_REC_DEF_SIZE 2
+#define RDB_REC_MULTIPLY 2
+
+#define ORIGINAL (short) 0
+#define SHALLOW (short) 1
+#define DEEP (short) 2
+
+/*** Forward declared functions ***/
+
+int list_search(struct bowl**, struct bowl*, struct bowl*);
+
+void list_print(struct bowl *data, const char *offset);
+
+/******/
+
+dt_err bowl_malloc(struct bowl *(*data))
+{
+ (*data) = (struct bowl*) malloc(sizeof(struct bowl));
+ if(*data == NULL) {
+ printf("Creating bowl object FAILED");
+ return MALLOC_FAILED;
+ }
+
+ memset(*data, 0, sizeof(struct bowl));
+
+ (*data)->type = UNSET;
+ return SUCCESS;
+}
+
+dt_err bowl_resettype(struct bowl *data)
+{
+ if(data->type == LITERAL) {
+ if(data->payload.literal) free(data->payload.literal);
+
+ } else if(data->type == LIST || data->type == PAIR) {
+
+ /* Iterate over all children and clear them */
+ int i;
+ dt_err err;
+ for(i = 0; i < data->size; i++) {
+ err = bowl_free(data->payload.list[i]);
+ if(err) return err;
+ }
+ }
+
+ /* Set the data type to unset */
+ data->type = UNSET;
+ data->encset = 0;
+ data->size = 0;
+ data->used = 0;
+
+ /* Forcibly clean union memory to avoid bleeding data */
+ memset(&data->payload, 0, sizeof(data->payload));
+
+ return SUCCESS;
+}
+
+dt_err bowl_addliteral(struct bowl *data, const char *literal)
+{
+ /* Make sure we are a literal or unset data object */
+ if(data->type != UNSET)
+ if(data->type != LITERAL) return INVALID_PAYLOAD;
+
+ size_t length = REAL_STRLEN(literal);
+
+ /* Get rid of previous data */
+ if(data->payload.literal) free(data->payload.literal);
+
+ /* Allocate space for the data */
+ char *tmp = (char *) malloc(sizeof(char) * length);
+ if(tmp == NULL) {
+ printf("Allocating space for literal data FAILED");
+ return MALLOC_FAILED;
+ }
+
+ /* Copy the string over and store it in the union */
+ strcpy(tmp, literal);
+ data->payload.literal = tmp;
+ data->type = LITERAL;
+ data->size = length;
+ data->used = length;
+
+ return SUCCESS;
+}
+
+
+dt_err bowl_addpointer(struct bowl *data, void *ptr)
+{
+ if(data->type != UNSET)
+ if(data->type != POINTER) return INVALID_PAYLOAD;
+
+ data->payload.pointer = ptr;
+ data->type = POINTER;
+ data->size = sizeof(ptr);
+ data->used = sizeof(ptr);
+
+ return SUCCESS;
+}
+
+
+dt_err bowl_addnumeral(struct bowl *data, long numeral)
+{
+ /* Make sure we are a literal or unset data object */
+ if(data->type != UNSET)
+ if(data->type != NUMERIC) return INVALID_PAYLOAD;
+
+ data->payload.numeral = numeral;
+ data->type = NUMERIC;
+ data->size = sizeof(int);
+ data->used = sizeof(int);
+ return SUCCESS;
+}
+
+
+dt_err bowl_addboolean(struct bowl *data, bool b)
+{
+ /* Make sure we are a literal or unset data object */
+ if(data->type != UNSET)
+ if(data->type != BOOLEAN) return INVALID_PAYLOAD;
+
+ data->payload.boolean = b;
+ data->type = BOOLEAN;
+ data->size = sizeof(bool);
+ data->used = sizeof(bool);
+ return SUCCESS;
+}
+
+
+dt_err bowl_addlist(struct bowl *data, struct bowl *(*new_data))
+{
+ /* Make sure we are a literal or unset data object */
+ if(data->type != UNSET)
+ if(data->type != LIST) return INVALID_PAYLOAD;
+
+ dt_err err;
+
+ /* This means elements already exist */
+ if(data->size > 0) {
+
+ /* Used should never > size */
+ if(data->used >= data->size) {
+ data->size += RDB_REC_MULTIPLY;
+
+ // TODO Use Realloc
+ struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size);
+ memcpy(tmp, data->payload.list, sizeof(struct bowl*) * data->used);
+
+ /* Free the list WITHOUT the children! */
+ free(data->payload.list);
+ data->payload.list = tmp;
+ }
+
+ /* This means the data object is new */
+ } else {
+ struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * RDB_REC_DEF_SIZE);
+ data->payload.list = tmp;
+ data->type = LIST;
+ data->used = 0;
+ data->size = RDB_REC_DEF_SIZE;
+ }
+
+ err = bowl_malloc(new_data);
+ if(err) return err;
+
+ /* Reference the slot, assign it, then move our ctr */
+ data->payload.list[data->used++] = *new_data;
+
+ return SUCCESS;
+}
+
+
+dt_err bowl_addpair(struct bowl *data, struct bowl *(*key), struct bowl *(*value))
+{
+ /* Make sure we are a literal or unset data object */
+ if(data->type != UNSET) return INVALID_PAYLOAD;
+
+ dt_err err;
+
+ /* Malloc two nodes */
+ err = bowl_malloc(key);
+ if(err) goto cleanup;
+
+ err = bowl_malloc(value);
+ if(err) goto cleanup;
+
+ /** Malloc space for PAIR */
+ data->size = 2;
+ struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size);
+ if(!tmp) goto cleanup;
+
+ data->payload.list = tmp;
+
+ { /* Assign data to new array */
+ data->payload.list[data->used] = *key;
+ data->used++;
+ data->payload.list[data->used] = *value;
+ data->used++;
+ }
+
+ /* Assign our new type and return */
+ data->type = PAIR;
+ return SUCCESS;
+
+ /* Code we run when we can't allocate structs anymore */
+ cleanup:
+ free(*key);
+ free(*value);
+ free(tmp);
+ return MALLOC_FAILED;
+}
+
+
+dt_err bowl_split_trees(struct bowl *data, struct bowl *sp)
+{
+ /* Make sure we are a literal or unset data object */
+ if(data->type == UNSET) return INVALID_PAYLOAD;
+
+ /* Check that sp is really a child of data */
+ struct bowl *dp;
+ int ret = list_search(&dp, data, sp);
+ if(ret != 0) return DATA_NOT_RELATED;
+ if(dp == NULL) return NODE_NOT_FOUND;
+
+ /* Find the exact list reference and remove it */
+ int i;
+ for(i = 0; i < dp->used; i++) {
+ if(dp->payload.list[i] == NULL) continue;
+
+ /* Manually remove the entry */
+ if(dp->payload.list[i] == sp) {
+ dp->used--;
+ dp->payload.list[i] = NULL;
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+dt_err bowl_merge_trees(struct bowl *data, struct bowl *merge)
+{
+ /* REALLY make sure the type is correct */
+ if(data->type == UNSET) return INVALID_PARAMS;
+ if(!(data->type == LIST || data->type == PAIR))
+ return INVALID_PAYLOAD;
+
+ /* This means elements already exist */
+ if(data->size > 0) {
+
+ /* Used should never > size */
+ if(data->used >= data->size) {
+ data->size += RDB_REC_MULTIPLY;
+
+ struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size);
+ memcpy(tmp, data->payload.list, sizeof(struct bowl*) * data->used);
+
+ /* Free the list WITHOUT the children! */
+ free(data->payload.list);
+ data->payload.list = tmp;
+ }
+
+ /* This means the data object is new */
+ } else {
+ struct bowl **tmp = (struct bowl**) malloc(sizeof(struct bowl*) * data->size);
+ data->payload.list = tmp;
+ data->type = LIST;
+ data->used = 0;
+ data->size = RDB_REC_DEF_SIZE;
+ }
+
+ /* Reference the slot, assign it, then move our ctr */
+ data->payload.list[data->used] = merge;
+ data->used++;
+
+ return SUCCESS;
+}
+
+
+dt_err bowl_copy_deep(struct bowl *data, struct bowl *(*copy))
+{
+ if(data == NULL) return INVALID_PARAMS;
+ dt_err err = SUCCESS;
+
+ int it_type = -1;
+ bowl_t type = data->type;
+
+ /* Check if we're the first call */
+ if((*copy) == NULL) bowl_malloc(copy);
+ (*copy)->copy = DEEP;
+
+ switch(type) {
+ case LITERAL:
+ bowl_addliteral(*copy, data->payload.literal);
+ break;
+
+ case NUMERIC:
+ bowl_addnumeral(*copy, data->payload.numeral);
+ break;
+
+ case BOOLEAN:
+ bowl_addboolean(*copy, data->payload.boolean);
+ break;
+
+ case LIST:
+ {
+ int i;
+ int num = (int) data->used;
+
+ for(i = 0; i < num; i++) {
+ struct bowl *node = data->payload.list[i];
+
+ struct bowl *new;
+ bowl_addlist(*copy, &new);
+ bowl_copy_deep(node, &new);
+ }
+
+ break;
+ }
+
+ case PAIR:
+ {
+ struct bowl *key, *val;
+ bowl_addpair(*copy, &key, &val);
+
+ struct bowl *orig_key = data->payload.list[0];
+ struct bowl *orig_val = data->payload.list[1];
+
+ bowl_copy_deep(orig_key, &key);
+ bowl_copy_deep(orig_val, &val);
+
+ break;
+ }
+
+ case POINTER:
+ bowl_addpointer(*copy, data->payload.pointer);
+ break;
+
+ default:
+ err = INVALID_PAYLOAD;
+ break;
+ }
+
+ return err;
+}
+
+
+dt_err bowl_parent(struct bowl *root, struct bowl *data, struct bowl **parent)
+{
+ if(root == NULL || data == NULL) return INVALID_PARAMS;
+
+ /* Blank the search pointer for easy error checking */
+ (*parent) = NULL;
+
+ switch(root->type) {
+
+ /* Dead-end data stores automatically return @{NODE_NOT_FOUND} */
+ case POINTER:
+ case LITERAL:
+ case BOOLEAN:
+ case NUMERIC:
+ return NODE_NOT_FOUND;
+
+ case PAIR:
+ case LIST:
+ {
+ int i;
+ for(i = 0; i < root->used; i++) {
+
+ /* Check if the node we're looking at is what we're searching for */
+ if(root->payload.list[i] == data) {
+ (*parent) = root;
+ return SUCCESS;
+ }
+
+ dt_err err = bowl_parent(root->payload.list[i], data, parent);
+ if(err == SUCCESS) return SUCCESS;
+ }
+ }
+ break;
+
+ default:
+ return INVALID_PAYLOAD;
+ }
+
+ return NODE_NOT_FOUND;
+}
+
+
+dt_err bowl_copy(struct bowl *data, struct bowl *(*copy))
+{
+ if(data == NULL) return INVALID_PARAMS;
+ dt_err err = SUCCESS;
+
+ /* Allocate a new node */
+ err = bowl_malloc(copy);
+ if(err) goto exit;
+
+ /* Mark as shallow copy */
+ (*copy)->copy = SHALLOW;
+
+ /* Find out how to handle specific payloads */
+ switch(data->type) {
+ case LITERAL:
+ err = bowl_addliteral(*copy, data->payload.literal);
+ break;
+
+ case NUMERIC:
+ err = bowl_addnumeral(*copy, data->payload.numeral);
+ break;
+
+ case BOOLEAN:
+ err = bowl_addboolean(*copy, data->payload.boolean);
+ break;
+
+ case LIST:
+ (*copy)->type = LIST;
+ (*copy)->payload.list = (struct bowl**) malloc(sizeof(struct bowl*) * data->size);
+ memcpy((*copy)->payload.list, data->payload.list, sizeof(struct bowl*) * data->used);
+ break;
+
+ case PAIR:
+ (*copy)->type = PAIR;
+ (*copy)->payload.list = (struct bowl**) malloc(sizeof(struct bowl*) * data->size);
+ memcpy((*copy)->payload.list, data->payload.list, sizeof(struct bowl*) * data->used);
+ break;
+
+ case POINTER:
+ (*copy)->type = POINTER;
+ memcpy((*copy)->payload.pointer, data->payload.pointer, sizeof(void*));
+ break;
+
+ default:
+ return INVALID_PAYLOAD;
+ }
+
+ exit:
+ return err;
+}
+
+
+dt_err bowl_search_payload(struct bowl *data, struct bowl *(*found), void *payload, bowl_t type)
+{
+ if(data == NULL) return INVALID_PARAMS;
+
+ /* Make sure our pointer is clean */
+ *found = NULL;
+
+ if(data->type == LIST|| data->type == PAIR) {
+
+ int i;
+ for(i = 0; i < data->used; i++) {
+ dt_err err = bowl_search_payload(data->payload.list[i], found, payload, type);
+ if(err == SUCCESS) return SUCCESS;
+ }
+
+ } else {
+
+ /* Check the type aligns */
+ if(data->type != type) return NODE_NOT_FOUND;
+
+ switch(type) {
+ case LITERAL:
+ if(strcmp(data->payload.literal, (char*) payload) == 0)
+ *found = data;
+ break;
+
+ case NUMERIC:
+ if(data->payload.numeral == (long) payload)
+ *found = data;
+ break;
+
+ case BOOLEAN:
+ if(data->payload.boolean == (bool) payload)
+ *found = data;
+ break;
+
+ case POINTER:
+ if(data->payload.pointer == payload)
+ *found = data;
+ break;
+
+ default: return NODE_NOT_FOUND;
+ }
+
+ }
+
+ return (*found == NULL) ? NODE_NOT_FOUND : SUCCESS;
+}
+
+// FIXME: This is horrible. Do via context?
+static int reached = 0;
+dt_err bowl_search_keypayload(struct bowl *data, struct bowl *(*found), void *payload, bowl_t type, int depth)
+{
+ if(data == NULL) return INVALID_PARAMS;
+ if(reached++ >= depth) return QUERY_TOO_DEEP;
+
+ /* Make sure our pointer is clean */
+ *found = NULL;
+
+ /* We can only search LISTed values or PAIRs */
+ if(data->type == PAIR) {
+ struct bowl *key = data->payload.list[0];
+
+ bowl_t tt;
+ int hit = -1;
+
+ if(strcmp(key->payload.literal, (char*) payload) == 0) {
+ tt = LITERAL;
+ hit = 0;
+ }
+
+ if(key->payload.numeral == (long) payload) {
+ tt = NUMERIC;
+ hit = 0;
+ }
+
+ if(key->payload.boolean == (bool) payload) {
+ tt = BOOLEAN;
+ hit = 0;
+ }
+
+ if(hit == 0) *found = data->payload.list[1];
+
+ } else if(data->type == LIST) {
+
+ int i;
+ for(i = 0; i < data->used; i++) {
+ bowl_search_keypayload(data->payload.list[i], found, payload, type, depth);
+ }
+
+ } else {
+
+
+ }
+
+ if(data->type == LIST|| data->type == PAIR) {
+
+ int i;
+ for(i = 0; i < data->used; i++) {
+ dt_err err = bowl_search_payload(data->payload.list[i], found, payload, type);
+ if(err == SUCCESS) return SUCCESS;
+ }
+
+ } else {
+
+ /* Check the type aligns */
+ if(data->type != type) return NODE_NOT_FOUND;
+
+ switch(type) {
+ case LITERAL:
+ if(strcmp(data->payload.literal, (char*) payload) == 0)
+ *found = data;
+ break;
+
+ case NUMERIC:
+ if(data->payload.numeral == (long) payload)
+ *found = data;
+ break;
+
+ case BOOLEAN:
+ if(data->payload.boolean == (bool) payload)
+ *found = data;
+
+ case POINTER:
+ if(data->payload.pointer == payload)
+ *found = data;
+ break;
+
+ default: return NODE_NOT_FOUND;
+ }
+
+ }
+
+ return (*found == NULL) ? NODE_NOT_FOUND : SUCCESS;
+}
+
+
+void list_print(struct bowl *data, const char *offset)
+{
+ bowl_t type = data->type;
+
+ switch(type) {
+ case UNSET:
+ printf("[NULL]\n");
+ break;
+
+ case LITERAL:
+ printf("%s['%s']\n", offset, data->payload.literal);
+ break;
+
+ case NUMERIC:
+ printf("%s[%lu]\n", offset, data->payload.numeral);
+ break;
+
+ case BOOLEAN:
+ printf("%s['%s']\n", offset, (data->payload.boolean) ? "TRUE" : "FALSE");
+ break;
+
+ case PAIR:
+ {
+ bowl_t k_type = data->payload.list[0]->type;
+ bowl_t v_type = data->payload.list[1]->type;
+
+ if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.list[0]->payload.literal);
+ if(k_type == NUMERIC) printf("%s[%lu]", offset, data->payload.list[0]->payload.numeral);
+ if(k_type == BOOLEAN) printf("%s[%s]", offset, (data->payload.list[0]->payload.boolean) ? "TRUE" : "FALSE");
+
+ char new_offset[REAL_STRLEN(offset) + 2];
+ strcpy(new_offset, offset);
+ strcat(new_offset, " ");
+
+ if(k_type == LIST || k_type == PAIR) list_print(data->payload.list[0], new_offset);
+
+ /* Print the value now */
+ if(v_type == LITERAL) printf(" => ['%s']\n", data->payload.list[1]->payload.literal);
+ if(v_type== NUMERIC) printf(" => [%lu]\n", data->payload.list[1]->payload.numeral);
+ if(v_type == BOOLEAN) printf(" => [%s]\n", (data->payload.list[1]->payload.boolean) ? "TRUE" : "FALSE");
+
+ if(v_type == LIST || k_type == PAIR) list_print(data->payload.list[1], new_offset);
+
+ break;
+ }
+
+ case LIST:
+ {
+ int i;
+ printf("%s[LIST]\n", offset);
+ for(i = 0; i < data->used; i++) {
+ bowl_t t = data->payload.list[i]->type;
+
+ /* Calculate the new offset */
+ char new_offset[REAL_STRLEN(offset) + 2];
+ strcpy(new_offset, offset);
+ strcat(new_offset, " ");
+
+ switch(t) {
+ case LITERAL:
+ case BOOLEAN:
+ case NUMERIC:
+ list_print(data->payload.list[i], new_offset);
+ continue;
+
+ case LIST:
+ list_print(data->payload.list[i], new_offset);
+ continue;
+
+ case PAIR:
+ printf("%s[PAIR] <==> ", new_offset);
+ list_print(data->payload.list[i], new_offset);
+ continue;
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+
+ }
+}
+
+
+void bowl_print(struct bowl *data)
+{
+ list_print(data, "");
+}
+
+dt_err bowl_get(struct bowl *data, void *(*val))
+{
+ if(data->type == LITERAL) *val = data->payload.literal;
+ if(data->type == NUMERIC) *val = &data->payload.numeral;
+ if(data->type == BOOLEAN) *val = &data->payload.boolean;
+ if(data->type == LIST || data->type == PAIR) *val = (struct bowl*) data->payload.list;
+ return SUCCESS;
+}
+
+
+dt_err bowl_free(struct bowl *data)
+{
+ if(data == NULL) return SUCCESS;
+ if(data->copy == SHALLOW) return NODE_NOT_ORIGINAL;
+
+ if(data->type == LITERAL) {
+ if(data->payload.literal) free(data->payload.literal);
+
+ } else if(data->type == LIST || data->type == PAIR) {
+ int i;
+ dt_err err;
+ for(i = 0; i < data->used; i++) {
+
+ err = bowl_free(data->payload.list[i]);
+ if(err) return err;
+ }
+
+ free(data->payload.list);
+
+ } else if(data->type == POINTER) {
+ if(data->copy != SHALLOW && data->payload.pointer)
+ free(data->payload.pointer);
+ }
+
+ free(data);
+ return SUCCESS;
+}
+
+
+dt_err bowl_free_shallow(struct bowl *data)
+{
+ if(data == NULL) return SUCCESS;
+
+ if(data->type == LITERAL) {
+ if(data->payload.literal) free(data->payload.literal);
+ } else if(data->type == LIST || data->type == PAIR) {
+ int i;
+ dt_err err;
+ for(i = 0; i < data->size; i++) {
+ err = bowl_free(data->payload.list[i]);
+ if(err) return err;
+ }
+
+ free(data->payload.list);
+ }
+
+ free(data);
+ return SUCCESS;
+}
+
+
+const char *bowl_dtype(struct bowl *data)
+{
+ switch(data->type) {
+ case LITERAL: return "Literal";
+ case NUMERIC: return "Numeric";
+ case BOOLEAN: return "Boolean";
+ case LIST: return "List";
+ case PAIR: return "Pair";
+ case POINTER: return "Pointer";
+ default: return "Unknown";
+ }
+}
+
+
+/**************** PRIVATE UTILITY FUNCTIONS ******************/
+
+
+/**
+ * Steps down the list hirarchy of a dyntree node to
+ * find a sub-child target. Returns 0 if it can be found.
+ *
+ * @param data
+ * @param target
+ * @return
+ */
+int list_search(struct bowl **direct_parent, struct bowl *data, struct bowl *target)
+{
+ /* Check if data is actually valid */
+ if(data == NULL) return 1;
+
+ /* Compare the pointers :) */
+ if(data == target) return 0;
+
+ int res = 1;
+ if(data->type == LIST || data->type == PAIR) {
+ int i;
+ for(i = 0; i < data->used; i++) {
+ res = list_search(direct_parent, data->payload.list[i], target);
+ if(res == 0) {
+
+ /* Save the node that contains our child for later */
+ (*direct_parent) = data;
+ return res;
+ }
+ }
+ }
+
+ return res;
+}
+
+
+/**
+ * Small utility function that checks if a datablock is valid to write into.
+ * Potentially releases previously owned memory to prevent memory leaks
+ *
+ * @param data The bowl object to check
+ * @return
+ */
+dt_err data_check(struct bowl *data)
+{
+ /* Check if the data block has children */
+ if(data->type == LIST)
+ {
+ printf("Won't override heap payload with data!");
+ return INVALID_PAYLOAD;
+ }
+
+ /* Free the existing string */
+ if(data->type == LITERAL)
+ {
+ if(data->payload.literal) free(data->payload.literal);
+ }
+
+ return SUCCESS;
+}