aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKatharina Fey <kookie@spacekookie.de>2019-06-09 09:07:48 +0200
committerKatharina Fey <kookie@spacekookie.de>2019-06-09 09:12:52 +0200
commitd0cca976be6fb90f3724911c8a124bce56b3c5f9 (patch)
tree96c92dc695f81b39227fb5f52e4c2ac469fea06b
parent7df71001ef1a9ea271ae3c409a367d6c2dd628b7 (diff)
Restructuring the main API and project
This commit rewrites pretty much the entire library. It is now much smaller and more maintainable (split over multiple files). It will now also support more features (that aren't implemented yet). Adding two examples to show how to use the new API. Also changing the name of the library everywhere.
-rw-r--r--.gitignore34
-rw-r--r--CMakeLists.txt13
-rw-r--r--README51
-rw-r--r--array.c120
-rw-r--r--array.h27
-rw-r--r--bowl.c828
-rw-r--r--bowl.h413
-rw-r--r--data.c81
-rw-r--r--examples/list.c23
-rw-r--r--examples/tree.c46
-rw-r--r--main.c11
-rw-r--r--utils.c46
-rw-r--r--utils.h19
13 files changed, 512 insertions, 1200 deletions
diff --git a/.gitignore b/.gitignore
index de789df..6f31401 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,34 +1,2 @@
-# Object files
-*.o
-*.ko
-*.obj
-*.elf
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Libraries
-*.lib
-*.a
-*.la
-*.lo
-
-# Shared objects (inc. Windows DLLs)
-*.dll
-*.so
-*.so.*
-*.dylib
-
-# Executables
-*.exe
-*.out
-*.app
-*.i*86
-*.x86_64
-*.hex
-
-# Debug files
-*.dSYM/
-*.su
build/
+.vscode/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c68025a..5ab2f46 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,11 +3,16 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c11")
set(CMAKE_BUILD_TYPE Debug)
project(bowl)
-add_library(bowl SHARED bowl.c)
+add_library(bowl SHARED bowl.c
+ data.c
+ utils.c
+ array.c)
target_include_directories(bowl PUBLIC ".")
-################### TESTING CODE BELOW ###################
+################### EXAMPLES ###################
+add_executable(ex_tree examples/tree.c)
+target_link_libraries(ex_tree bowl)
-add_executable(bowl_test main.c)
-target_link_libraries(bowl_test bowl) \ No newline at end of file
+add_executable(ex_list examples/list.c)
+target_link_libraries(ex_list bowl) \ No newline at end of file
diff --git a/README b/README
index 8e48fac..eadb8ec 100644
--- a/README
+++ b/README
@@ -1,19 +1,16 @@
-libdtree
-========
+libbowl
+=======
The last C datastructure library you will use. Provides a versatile
-tree-like structure that can act as lists, sets and more! Many
-features are still experimental and the API might change at any point
-so be aware.
+structure that can act as lists, sets and more!
+
How to build
------------
-libdyntree is built with cmake. It has no external dependencies and
-compilation has been tested with gcc 6+ on Linx systems. It was tested
-with C99 but shouldbe able to compile with ANSI C as well.
+An out-of-source build is recommended.
-```console
+```
$> mkdir build; cd build
$> cmake ..
$> make -j 2
@@ -22,42 +19,6 @@ $> make -j 2
This will create a `.so` file. If you require a static object, you can
change the linking behaviour in the `CMakeLists.txt` file.
-How to use
-----------
-
-Everything resolves around `dtree` objects and providing fields to API
-functions. Generally, memory is managed for you by libdtree:
-
-```C
-dt_err err;
-dtree *data;
-err = dtree_malloc(&data);
-```
-
-Alternatively you can use the shortcut alloc functions provided:
-
-```C
-dtree *str_node = dtree_alloc_literal("My string in this node!");
-dtree *num_node = dtree_alloc_numeral(1337);
-
-dtree *key, *val;
-dtree *pair_node = dtree *dtree_allocpair_new(&key, &val);
-```
-
-Nodes can change their type, provided they get reset before assigning
-a different type of data to them first. The available types are listed
-below:
-
- - Unset
- - Literal
- - Numerical
- - Recursive
- - Pair
- - Pointer
-
-Some more advanced functions include a getter, a search, a keyed
-search as well as tree merge and split functions. Please consult the
-wiki for details on how to use some of these functions.
License
-------
diff --git a/array.c b/array.c
new file mode 100644
index 0000000..b0db3f5
--- /dev/null
+++ b/array.c
@@ -0,0 +1,120 @@
+#include "bowl.h"
+#include "array.h"
+#include "utils.h"
+
+#include <malloc.h>
+
+
+err_t array_malloc(struct bowl *self, size_t size)
+{
+ CHECK(self, INVALID_STATE)
+ CHECK((size > 0), INVALID_PARAMS)
+
+ struct bowl_arr *arr = malloc(sizeof(struct bowl_arr));
+ CHECK(arr, MALLOC_FAILED)
+ arr->size = 2;
+ arr->used = 0;
+
+ struct bowl **ptr = calloc(sizeof(struct bowl *), arr->size);
+ CHECK(ptr, MALLOC_FAILED)
+ arr->ptr = ptr;
+
+ self->_pl.array = arr;
+ return OK;
+}
+
+err_t array_insert(struct bowl *self, struct bowl *new)
+{
+ CHECK(self, INVALID_PARAMS)
+ CHECK(new, INVALID_PARAMS)
+
+ struct bowl_arr *arr = self->_pl.array;
+ CHECK(arr, INVALID_STATE)
+
+ err_t e = _array_rescale((void ***) &arr->ptr, &arr->size, arr->used);
+ if(e) return e;
+
+ arr->ptr[arr->used++] = new;
+ return OK;
+}
+
+err_t array_insert_key(struct bowl *self, size_t idx, struct bowl *new)
+{
+ CHECK(self, INVALID_PARAMS)
+ CHECK(new, INVALID_PARAMS)
+
+ struct bowl_arr *arr = self->_pl.array;
+ CHECK(arr, INVALID_STATE)
+ CHECK((idx < arr->used), INVALID_PARAMS)
+
+ err_t e = _array_rescale((void ***) &arr->ptr, &arr->size, arr->used);
+ if(e) return e;
+
+ for(int i = idx + 1; idx < arr->used; i++)
+ arr->ptr[i] = arr->ptr[i - 1];
+
+ arr->ptr[idx] = new;
+ arr->used++;
+
+ return OK;
+}
+
+err_t array_swap_key(struct bowl *self, size_t idx, struct bowl *new, struct bowl **old)
+{
+ CHECK(self, INVALID_PARAMS)
+ CHECK(new, INVALID_PARAMS)
+
+ struct bowl_arr *arr = self->_pl.array;
+ CHECK(arr, INVALID_STATE)
+
+ (*old) = NULL; // Explicitly set to NULL if no such key
+ CHECK((idx < arr->used), INVALID_PARAMS)
+
+ (*old) = arr->ptr[idx];
+ arr->ptr[idx] = new;
+
+ return OK;
+}
+
+err_t array_remove(struct bowl *self, struct bowl *to_remove)
+{
+ CHECK(self, INVALID_PARAMS)
+ CHECK(to_remove, INVALID_PARAMS)
+
+ struct bowl_arr *arr = self->_pl.array;
+ CHECK(arr, INVALID_STATE)
+
+ size_t idx;
+ err_t e = _array_search((void **) arr->ptr, arr->size, &idx, to_remove);
+ if(e) return e;
+
+ e = _array_remove((void **) arr->ptr, idx, arr->size, NULL);
+ return e;
+}
+
+err_t array_remove_key(struct bowl *self, size_t idx, struct bowl **out)
+{
+ CHECK(self, INVALID_PARAMS);
+ struct bowl_arr *arr = self->_pl.array;
+ CHECK(arr, INVALID_STATE)
+ CHECK((idx < arr->used), INVALID_PARAMS)
+
+ err_t e = _array_remove((void **) arr->ptr, idx, arr->size, (void **) out);
+ return e;
+}
+
+err_t array_free(struct bowl *self)
+{
+ CHECK(self, INVALID_PARAMS)
+ struct bowl_arr *arr = self->_pl.array;
+ CHECK(arr, INVALID_STATE)
+
+ for(int i = 0; i < arr->used; i++) {
+ err_t e = bowl_free(arr->ptr[i]);
+ if(e) break;
+ }
+
+ free(arr->ptr);
+ free(arr);
+ return OK;
+} \ No newline at end of file
diff --git a/array.h b/array.h
new file mode 100644
index 0000000..d35a1f8
--- /dev/null
+++ b/array.h
@@ -0,0 +1,27 @@
+#ifndef _ARRAY_H_
+#define _ARRAY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "bowl.h"
+
+err_t array_malloc(struct bowl *, size_t);
+
+err_t array_insert(struct bowl *, struct bowl *);
+
+err_t array_insert_key(struct bowl *, size_t idx, struct bowl *);
+
+err_t array_swap_key(struct bowl *, size_t idx, struct bowl *, struct bowl **);
+
+err_t array_remove(struct bowl *, struct bowl *);
+
+err_t array_remove_key(struct bowl *, size_t, struct bowl **);
+
+err_t array_free(struct bowl *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // _ARRAY_H_ \ No newline at end of file
diff --git a/bowl.c b/bowl.c
index 0f43db2..4eeeb59 100644
--- a/bowl.c
+++ b/bowl.c
@@ -1,815 +1,123 @@
-// Include our header file
#include "bowl.h"
+#include "array.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 ARRAY_START_SIZE 2
+#define HASH_START_SIZE 24
-#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)
+void *_get_data(struct bowl *ptr)
{
- 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;
+ switch(ptr->type) {
+ case ARRAY | HASH: return ptr->_pl.array;
+ case LINKED: return ptr->_pl.linked;
+ case LEAF: return ptr->_pl.data;
+ default: return NULL;
}
-
- return NODE_NOT_FOUND;
}
-
-dt_err bowl_copy(struct bowl *data, struct bowl *(*copy))
+err_t bowl_malloc(struct bowl **ptr, bowl_t type)
{
- 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;
+ CHECK((type != 0), INVALID_PARAMS)
- case NUMERIC:
- err = bowl_addnumeral(*copy, data->payload.numeral);
- break;
+ (*ptr) = malloc(sizeof(struct bowl));
+ CHECK(*ptr, MALLOC_FAILED)
- case BOOLEAN:
- err = bowl_addboolean(*copy, data->payload.boolean);
- break;
+ memset(*ptr, 0, sizeof(struct bowl));
+ (*ptr)->type = type;
- 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;
+ switch((*ptr)->type) {
+ case LEAF: return OK; // No further allocation needed
+ case ARRAY: return array_malloc(*ptr, ARRAY_START_SIZE);
+ default: return INVALID_STATE;
}
- exit:
- return err;
+ return OK;
}
-
-dt_err bowl_search_payload(struct bowl *data, struct bowl *(*found), void *payload, bowl_t type)
+err_t bowl_insert(struct bowl *self, struct bowl *new)
{
- 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;
+ CHECK(self, INVALID_PARAMS)
+ CHECK(new, INVALID_PARAMS)
- 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;
- }
+ switch(self->type) {
+ case LEAF: return INVALID_PARAMS;
+ case ARRAY: return array_insert(self, new);
+ default: return INVALID_STATE;
}
- return (*found == NULL) ? NODE_NOT_FOUND : SUCCESS;
+ return OK;
}
-
-void list_print(struct bowl *data, const char *offset)
+err_t bowl_insert_key(struct bowl *self, size_t idx, struct bowl *child)
{
- 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;
- }
+ CHECK(self, INVALID_PARAMS)
+ CHECK(child, INVALID_PARAMS)
+ CHECK((idx >= 0), INVALID_PARAMS)
- default:
- break;
+ switch(self->type) {
+ case LEAF: return INVALID_PARAMS;
+ case ARRAY: return array_insert_key(self, idx, child);
+ default: return INVALID_STATE;
}
}
-
-void bowl_print(struct bowl *data)
+err_t bowl_swap_key(struct bowl *self, size_t idx, struct bowl *child, struct bowl **prev)
{
- list_print(data, "");
-}
+ CHECK(self, INVALID_PARAMS)
+ CHECK(child, INVALID_PARAMS)
+ CHECK((idx >= 0), INVALID_PARAMS)
-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;
+ switch(self->type) {
+ case LEAF: return INVALID_PARAMS;
+ case ARRAY: return array_swap_key(self, idx, child, prev);
- 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);
+ default: return INVALID_STATE;
}
-
- free(data);
- return SUCCESS;
}
-
-dt_err bowl_free_shallow(struct bowl *data)
+err_t bowl_remove(struct bowl *self, struct bowl *child)
{
- if(data == NULL) return SUCCESS;
+ CHECK(self, INVALID_PARAMS)
+ CHECK(child, INVALID_PARAMS)
- 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;
- }
+ switch(self->type) {
+ case LEAF: return INVALID_PARAMS;
+ case ARRAY: return array_remove(self, child);
- free(data->payload.list);
+ default: return INVALID_STATE;
}
-
- free(data);
- return SUCCESS;
}
-
-const char *bowl_dtype(struct bowl *data)
+err_t bowl_remove_key(struct bowl *self, size_t idx, struct bowl **prev)
{
- 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";
- }
-}
+ CHECK(self, INVALID_PARAMS)
+ CHECK((idx >= 0), INVALID_PARAMS)
+ switch(self->type) {
+ case LEAF: return INVALID_PARAMS;
+ case ARRAY: return array_remove_key(self, idx, prev);
-/**************** 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;
- }
- }
+ default: return INVALID_STATE;
}
-
- 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)
+err_t bowl_free(struct bowl *self)
{
- /* Check if the data block has children */
- if(data->type == LIST)
- {
- printf("Won't override heap payload with data!");
- return INVALID_PAYLOAD;
- }
+ CHECK(self, INVALID_PARAMS)
- /* Free the existing string */
- if(data->type == LITERAL)
- {
- if(data->payload.literal) free(data->payload.literal);
+ err_t e;
+ switch(self->type) {
+ case LEAF: e = data_free(self->_pl.data); break;
+ case ARRAY: e = array_free(self); break;
+ default: e = INVALID_STATE; break;
}
+ if(e) return e;
- return SUCCESS;
+ free(self);
+ return OK;
}
diff --git a/bowl.h b/bowl.h
index 88811c4..63149d2 100644
--- a/bowl.h
+++ b/bowl.h
@@ -1,378 +1,97 @@
-/*
- * Please use the API to create and destroy objects as only this way
- * memory-safety and memory leaks can be guaranteed.
- *
- * With the API you can easily create structures like the following:
- *
- * root [list]
- * child1 [list]
- * key [literal] - "Username"
- * value [literal] - "spacekookie"
- * child2 [list]
- * key [literal] - "Age"
- * value [numerical] - 23
- * child3
- * subchild [list]
- * ...
- *
- * Freeing the root node will free all children
- */
-
#ifndef _BOWL_H_
#define _BOWL_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <memory.h>
#include <stdbool.h>
-/* A helpful macro that can take care of \0 termated strings! */
#define REAL_STRLEN(str) (strlen(str) + 1)
+#define CHECK(ptr, err) if(!ptr) return err;
-/* Also make sure we're _always_ interpreted as a C file */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+/** Define some generic error codes first that we can propagate **/
+typedef enum err_t {
+ OK = 0,
+ ERR = -1,
+ NOT_IMPLEMENTED = 1, // A runtime error for missing features
+
+ INVALID_PARAMS = 2, // A function didn't get the required parameters
+ MALLOC_FAILED = 3, // A memory allocation failed
+ INVALID_STATE = 4, // Calling an operation on an invalid state
+} err_t;
+
+typedef enum data_t {
+ LITERAL, // Any string that is \0 terminated
+ INTEGER, // Integer value
+ REAL, // Floating point value
+ BOOLEAN, // A 1-byte boolean
+ RAW // A platform-length pointer
+} data_t;
-/* Type that determines what data is stored inside a tree-node */
typedef enum bowl_t {
- UNSET, LITERAL, NUMERIC, LONG_NUMERIC, BOOLEAN, LIST, PAIR, POINTER
+ LEAF = 1, ARRAY, HASH, LINKED
} bowl_t;
-
struct bowl {
- bowl_t type;
- short encset;
- size_t size, used;
- short copy;
+ bowl_t type;
union {
- char *literal;
- bool boolean;
- struct bowl *(*list);
- void *pointer;
-#ifdef __LONG_LONG_SUPPORT__
- long long numeral;
-#else
- long numeral;
-#endif
-
- } payload;
+ struct data *data;
+ struct bowl_arr *array;
+ struct b_ptr *linked;
+ } _pl;
};
+struct data {
+ data_t type;
+ union {
+ char *literal;
+ bool boolean;
+ long integer;
+ double real;
+ void *raw;
+ } _pl;
+};
-/** Define some generic error codes first that we can propagate **/
-typedef enum dt_err {
-
- /* General purpose error codes */
- FAILURE = -1,
- SUCCESS = 0,
-
- INVALID_PARAMS, // A function didn't get the required parameters
- MALLOC_FAILED, // A memory allocation failed
- INVALID_PAYLOAD, // The payload of a node is invalid
- DATA_NOT_RELATED, // Tried to split non-related trees
- NODE_NOT_FOUND, // The sought after node was not found
- NODE_NOT_ORIGINAL, // Tried to free a node which was a shallow copy
- QUERY_TOO_DEEP,
-
-} dt_err;
-
-
-/**
- * Malloc a new bowl object
- *
- * @param data Reference pointer to bowl element
- * @return
- */
-dt_err bowl_malloc(struct bowl *(*data));
-
-
-/**
- * Reset the type of a node and free child data
- *
- * @param data
- * @return
- */
-dt_err bowl_resettype(struct bowl *data);
-
-
-/**
- * Set the data element to a literal and save it's length
- *
- * @param data Reference to a bowl object
- * @param literal String to store
- * @return
- */
-dt_err bowl_addliteral(struct bowl *data, const char *literal);
-
-
-/**
- * Set the data element to a numeral
- *
- * @param data Reference to a bowl object
- * @param numeral Number to store
- * @return
- */
-dt_err bowl_addnumeral(struct bowl *data, long numeral);
-
-
-/**
- * Set the data element to a boolean. Do you really
- * need this? Consider using @bowl_add_numeral instead.
- *
- * @param data Reference to a bowl object
- * @param b boolean value (true, false)
- * @return
- */
-dt_err bowl_addboolean(struct bowl *data, bool b);
-
-
-/**
- * Add two new elements as a PAIR node under an existing node
- *
- * @param data bowl node to become the sub-root
- * @param key Reference pointer to the key node
- * @param value Reference pointer to the value node
- * @return
- */
-dt_err bowl_addpair(struct bowl *data, struct bowl *(*key), struct bowl *(*value));
-
-
-/**
- * Add a new data element to the resursive data store
- *
- * @param data Root reference
- * @param new_data Reference pointer to a new bowl node
- * @return
- */
-dt_err bowl_addlist(struct bowl *data, struct bowl *(*new_data));
-
-
-/**
- * This function enables you to store your own structures in a node. It however
- * also requires you to do some of your own memory management.
- *
- * WARNING: Can leak memory if pointer is previously set!
- *
- * To make sure that this function CAN NOT leak memory you should run
- * "bowl_resettype" on the root element to remove the pointer.
- *
- * Also make sure that no other part of your application will use the
- * pointer at a later date!
- *
- * @param data Root reference
- * @param ptr A pointer to store in this node
- * @return
- */
-dt_err bowl_addpointer(struct bowl *data, void *ptr);
-
-
-/**
- * This function takes two nodes as arguments. The nodes MUST be
- * related or an error will be thrown. Both nodes will still
- * be accessable after this operation but no longer be related to
- * each other.
- *
- * The second node will be removed from the tree of the root node.
- *
- *
- *
- * @param data Root reference
- * @param sp Subtree node related to root to split off
- * @return
- */
-dt_err bowl_split_trees(struct bowl *data, struct bowl *sp);
-
-
-/**
- * This function is very simmilar to dt_err "bowl_addlist"
- * with the difference that it doesn't allocate new memory but instead
- * works with existing nodes.
- *
- * You need to provide a ROOT node which is of type list. It will
- * procede to add the second (merge) node into the child-list of the
- * root data node - essentially making them related.
- *
- * This allows for very efficient tree merging.
- *
- * @param data Root reference
- * @param merge Second root reference to merge
- * @return
- */
-dt_err bowl_merge_trees(struct bowl *data, struct bowl *merge);
-
-
-/**
- * You can use this function to search the structure of a root node to find the
- * parent of the node you provide as "data". It will leave the search pointer
- * blanked if the node can't be found in the structure.
- *
- * @param root Root reference to search
- * @param data The node we are searching for
- * @param parent The node parent we are interested in
- * @return
- */
-dt_err bowl_parent(struct bowl *root, struct bowl *data, struct bowl **parent);
-
-
-/**
- * Recursive tree search function that will return the first occurence match
- * to a provided payload (with an exact type). If you have data duplication
- * in your tree this _might_ return some false positives.
- *
- * @param data Root reference to search
- * @param found Empty pointer to put found node into
- * @param payload What should be found
- * @param type What type of data should be found
- * @return
- */
-dt_err bowl_search_payload(struct bowl *data, struct bowl *(*found), void *payload, bowl_t type);
-
-
-/**
- * Much like #{bowl_search_payload} but limiting it's search to keys in a list structure of certain depth.
- * This means that in a key-value store structure only top-level items can be searched or the entire
- * depth of the tree (or any vaue in between)
- *
- * @param data
- * @param found
- * @param payload
- * @param type
- * @return
- */
-dt_err bowl_search_keypayload(struct bowl *data, struct bowl *(*found), void *payload, bowl_t type, int depth);
-
-
-/**
- * Performs a deep copy of a data node hirarchy. Does not copy externally
- * pointed structures. Does garuantee safety of hirarchy.
- *
- * @param data
- * @param copy
- * @return
- */
-dt_err bowl_copy_deep(struct bowl *data, struct bowl *(*copy));
-
-
-/**
- * Performs a copy operation on a single node. Copies the payload on a pointer
- * level which means that strings and numbers will be duplicated whereas external
- * pointers and lists will only be references to the original content.
- *
- * Freeing the copy has no effect on the original payloads stored in other
- * nodes.
- *
- * @param data
- * @param copy
- * @return
- */
-dt_err bowl_copy(struct bowl *data, struct bowl *(*copy));
-
-
-/**
- * A retrieve function to get data back from a node that doesn't require
- * you to manually access parts of the struct.
- *
- * Needs to be provided with a reference to a pointer that can then be
- * written to. You can make the reference type specific if you know
- * what kind of data you're expecting. Please however note that compiler
- * errors might occur if you provide a wrong pointer type.
- *
- * @param data Node reference to access
- * @param val Reference pointer to write into
- * @return
- */
-dt_err bowl_get(struct bowl *data, void *(*val));
-
-
-/**
- * Return the type of a node as plain text
- *
- * @param data
- * @return
- */
-const char *bowl_dtype(struct bowl *data);
-
-
-/**
- * Prints the data bowl object and all of its children
- *
- * @param data
- */
-void bowl_print(struct bowl *data);
-
-
-/**
- * Will free the data reference and all of it's children. It will however NOT
- * touch pointers to objects that weren't allocated by libdyntree!
- *
- * @param data
- * @return
- */
-dt_err bowl_free_shallow(struct bowl *data);
+struct bowl_arr {
+ size_t size, used;
+ struct bowl **ptr;
+};
+struct b_ptr {
+ struct bowl *self;
+ struct b_ptr *next;
+};
-/**
- * Like #{bowl_free_shallow} but will also remove structs that
- * weren't allocated by libdyntree. Will throw warnings when trying
- * to free payloads from shallow copy nodes
- *
- * @param data
- * @return
- */
-dt_err bowl_free(struct bowl *data);
+/// Allocate memory for an new, empty bowl node
+err_t bowl_malloc(struct bowl **, bowl_t);
+/// Insert one node into another
+err_t bowl_insert(struct bowl *, struct bowl *);
-/**************************
- *
- * Error handling functions
- *
- **************************/
+/// Insert a node into a specific index of another node
+err_t bowl_insert_key(struct bowl *, size_t idx, struct bowl *);
-const char *bowl_err_getmsg(dt_err *e);
+/// Insert and swap a key in place, returning the old one
+err_t bowl_swap_key(struct bowl *, size_t idx, struct bowl *, struct bowl **);
-/***************************
- *
- * Encoding/ Decoding support hooks
- *
- ***************************/
+/// Remove a bowl node by it's pointer reference
+err_t bowl_remove(struct bowl *, struct bowl *);
-/**
- * This function sets the wanted encoding setting on a
- * root node (and assumes for children). Without setting flags via
- * this function first the encode will fail.
- *
- * @param data Root reference
- * @param setting Look at DYNTREE_JSON flags for options
- * @return
- */
-dt_err bowl_encode_set(struct bowl *data, short setting);
+/// Removing a bowl node with a key
+err_t bowl_remove_key(struct bowl *, size_t idx, struct bowl **);
-/**
- * A simple list node walker that encodes a dyn_tree node hirarchy
- * into a json string. Requires the encoding setting to be set on the
- * root node in order to successfully encode.
- *
- * Can throw errors and initialise NULL return string.
- *
- * @param data
- * @param json_data
- * @return
- */
-dt_err bowl_encode_json(struct bowl *data, char *json_data);
+/// Cascade-free memory from a bowl node
+err_t bowl_free(struct bowl *);
+/// Allocate memory for a new data node
+err_t data_malloc(struct bowl **, data_t, ...);
-/**
- * Decodes a json string into a dyn_tree node hirarchy while providing
- * memory safety and error checking. Will gracefully return errors
- * if the provided json string is invalid or contains errors.
- *
- * @param data New root reference
- * @param json_data Input json string
- * @return
- */
-dt_err bowl_decode_json(struct bowl *(*data), const char *json_data, size_t len);
+/// Free all data node memory
+err_t data_free(struct data *);
#ifdef __cplusplus
}
#endif
-#endif //_DYNTREE_H_
+#endif // _BOWL_H_ \ No newline at end of file
diff --git a/data.c b/data.c
new file mode 100644
index 0000000..9077d9b
--- /dev/null
+++ b/data.c
@@ -0,0 +1,81 @@
+#include "bowl.h"
+
+#include <stdlib.h>
+#include <memory.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+/// Allocate memory for a new data node
+err_t data_malloc(struct bowl **self, data_t type, ...)
+{
+ err_t e;
+ va_list args;
+
+ struct data *d = calloc(sizeof(struct data), 1);
+ CHECK(d, MALLOC_FAILED)
+ d->type = type;
+
+ va_start(args, type);
+ switch(type) {
+ case LITERAL: {
+ char *lit = va_arg(args, char *);
+ size_t len = REAL_STRLEN(lit);
+
+ d->_pl.literal = calloc(sizeof(char), len);
+ if(!d->_pl.literal) {
+ e = MALLOC_FAILED;
+ goto fail;
+ }
+
+ strcpy(d->_pl.literal, lit);
+ break;
+ };
+ case INTEGER: {
+ int i = va_arg(args, long);
+ d->_pl.integer = i;
+ break;
+ }
+ case REAL: {
+ int r = va_arg(args, double);
+ d->_pl.real = r;
+ break;
+ }
+ case BOOLEAN: {
+ int b = va_arg(args, int);
+ d->_pl.boolean = (bool) b;
+ break;
+ }
+ case RAW: {
+ void *raw = va_arg(args, void *);
+ d->_pl.raw = raw;
+ break;
+ }
+ default: e = INVALID_PARAMS; goto fail;
+ }
+ va_end(args);
+
+ e = bowl_malloc(self, LEAF);
+ if(e) goto fail;
+
+ (*self)->_pl.data = d;
+ return OK;
+
+fail:
+ if(d->type == LITERAL && d->_pl.literal) free(d->_pl.literal);
+ if(*self) bowl_free(*self);
+ if(d) free(d);
+ return e;
+}
+
+/// Free all data node memory
+err_t data_free(struct data *self)
+{
+ CHECK(self, INVALID_PARAMS)
+ switch(self->type) {
+ case LITERAL: free(self->_pl.literal); break;
+ default: break;
+ }
+
+ free(self);
+ return OK;
+}
diff --git a/examples/list.c b/examples/list.c
new file mode 100644
index 0000000..a09588b
--- /dev/null
+++ b/examples/list.c
@@ -0,0 +1,23 @@
+#include <bowl.h>
+
+int main()
+{
+
+ // Root node which contains a list
+ struct bowl *root;
+ bowl_malloc(&root, ARRAY);
+
+ struct bowl *a;
+ data_malloc(&a, LITERAL, "Destroy capitalism");
+ bowl_insert(root, a);
+
+ struct bowl *b;
+ data_malloc(&b, INTEGER, 1312);
+ bowl_insert(root, b);
+
+ struct bowl *c;
+ data_malloc(&c, LITERAL, "Alerta, Antifascista!");
+ bowl_insert(root, c);
+
+ return bowl_free(root);
+} \ No newline at end of file
diff --git a/examples/tree.c b/examples/tree.c
new file mode 100644
index 0000000..e55acb9
--- /dev/null
+++ b/examples/tree.c
@@ -0,0 +1,46 @@
+#include <bowl.h>
+
+int main()
+{
+ err_t e;
+
+ // Initialise a root node
+ struct bowl *root;
+ e = bowl_malloc(&root, ARRAY);
+ if(e) return e;
+
+ // First Node
+ struct bowl *a;
+ e = data_malloc(&a, LITERAL, "Destroy capitalism");
+ if(e) return e;
+
+ // Second Node
+ struct bowl *b;
+ e = data_malloc(&b, INTEGER, 1312);
+ if(e) return e;
+
+ // Third node is another ARRAY
+ struct bowl *c;
+ e = bowl_malloc(&c, ARRAY);
+ if(e) return e;
+
+ // Fourth node is another string
+ struct bowl *d;
+ e = data_malloc(&d, LITERAL, "Alerta, Antifascista!");
+ if(e) return e;
+
+ // Add the d node to c
+ e = bowl_insert(c, d);
+ if(e) e;
+
+ // Add other nodes to root
+ e = bowl_insert(root, a);
+ if(e) return e;
+ e = bowl_insert(root, b);
+ if(e) return e;
+ e = bowl_insert(root, c);
+ if(e) return e;
+
+ e = bowl_free(root);
+ return e;
+} \ No newline at end of file
diff --git a/main.c b/main.c
deleted file mode 100644
index 5fa7c8b..0000000
--- a/main.c
+++ /dev/null
@@ -1,11 +0,0 @@
-// Some static tests
-
-#include <stdio.h>
-#include <string.h>
-// #include <bowl.h>
-
-int main(int argn, char **argv)
-{
-
- return 0;
-} \ No newline at end of file
diff --git a/utils.c b/utils.c
new file mode 100644
index 0000000..9e5a027
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,46 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utils.h"
+
+#define DELTA 0x2
+#define OVERSHOOT 0x8
+
+err_t _array_rescale(void ***ptr, size_t *len, size_t use)
+{
+ CHECK(len, INVALID_PARAMS)
+ CHECK((use <= *len), INVALID_PARAMS)
+ CHECK(ptr, INVALID_PARAMS)
+
+ // This is a simple linear scale strategy
+ if ((int) (*len - OVERSHOOT) >= (int) (use + DELTA)) *len -= OVERSHOOT;
+ if (use + DELTA >= *len) *len += DELTA;
+
+ *ptr = realloc(*ptr, *len * sizeof(void *));
+ return OK;
+}
+
+err_t _array_search(void **ptr, size_t len, size_t *out, void *in)
+{
+ CHECK(ptr, INVALID_PARAMS)
+ CHECK(in, INVALID_PARAMS)
+ CHECK((len > 0), INVALID_PARAMS)
+ (*out) = -1;
+
+ for(int i = 0; i < len; i++)
+ if((*ptr) == in) (*out) = i;
+
+ return OK;
+}
+
+err_t _array_remove(void **ptr, size_t idx, size_t len, void **out)
+{
+ CHECK(ptr, INVALID_PARAMS)
+ CHECK((idx >= 0), INVALID_PARAMS)
+ CHECK((len > idx), INVALID_PARAMS)
+
+ if(out != NULL) (*out) = ptr[idx];
+ for(int i = idx + 1; idx < len; i++)
+ ptr[i - 1] = ptr[i];
+
+ return OK;
+}
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..c7c2178
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,19 @@
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "bowl.h"
+
+err_t _array_rescale(void ***, size_t *len, size_t use);
+
+err_t _array_search(void **, size_t, size_t *out, void *in);
+
+err_t _array_remove(void **, size_t idx, size_t len, void **out);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // _UTIL_H_ \ No newline at end of file