aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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