From 7df71001ef1a9ea271ae3c409a367d6c2dd628b7 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 4 Jun 2019 23:51:12 +0200 Subject: Changing library name and project structure --- .gitignore | 3 - CMakeLists.txt | 36 +-- bowl.c | 815 ++++++++++++++++++++++++++++++++++++++++++++++++ bowl.h | 378 +++++++++++++++++++++++ include/dtree/dtree.h | 378 ----------------------- include/dtree/eztree.h | 66 ---- lib/dtree.c | 816 ------------------------------------------------- lib/dtree_utils.c | 278 ----------------- lib/eztree.c | 77 ----- lib/jsmn.c | 339 -------------------- lib/jsmn.h | 98 ------ main.c | 11 + test/main.c | 237 -------------- 13 files changed, 1209 insertions(+), 2323 deletions(-) create mode 100644 bowl.c create mode 100644 bowl.h delete mode 100644 include/dtree/dtree.h delete mode 100644 include/dtree/eztree.h delete mode 100644 lib/dtree.c delete mode 100644 lib/dtree_utils.c delete mode 100644 lib/eztree.c delete mode 100644 lib/jsmn.c delete mode 100644 lib/jsmn.h create mode 100644 main.c delete mode 100644 test/main.c diff --git a/.gitignore b/.gitignore index 1212a63..de789df 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,3 @@ *.dSYM/ *.su build/ - -.idea/ -cmake-build-debug diff --git a/CMakeLists.txt b/CMakeLists.txt index fc96527..c68025a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,39 +1,13 @@ -############################################ -# -# libdyntree uses cmake because it's simple -# Build instructions can be found in the README -# -############################################ - -# Set some flags cmake_minimum_required(VERSION 2.8.11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c11") set(CMAKE_BUILD_TYPE Debug) -# Create our project for further reference -project(libdyntree) -set(DYN_TREE_SRC - lib/dtree.c - lib/eztree.c - lib/dtree_utils.c - - # External files compiled in - lib/jsmn.c) - -# Define our library in cmake -add_library(libdyntree SHARED ${DYN_TREE_SRC}) +project(bowl) +add_library(bowl SHARED bowl.c) -# Include the subdirectories to search for headers -target_include_directories(libdyntree PUBLIC "include") -target_include_directories(libdyntree PRIVATE "lib") - -# since the name starts with 'lib' dont add it again -set_target_properties(libdyntree PROPERTIES PREFIX "") +target_include_directories(bowl PUBLIC ".") ################### TESTING CODE BELOW ################### -set(TEST_SRC test/main.c) -add_executable(dyntree_test ${TEST_SRC}) - -# Library dependencies for the http extention -target_link_libraries(dyntree_test libdyntree) \ No newline at end of file +add_executable(bowl_test main.c) +target_link_libraries(bowl_test bowl) \ No newline at end of file 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 +#include +#include +#include + +#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; +} diff --git a/bowl.h b/bowl.h new file mode 100644 index 0000000..88811c4 --- /dev/null +++ b/bowl.h @@ -0,0 +1,378 @@ +/* + * 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_ + +#include +#include + +/* A helpful macro that can take care of \0 termated strings! */ +#define REAL_STRLEN(str) (strlen(str) + 1) + +/* Also make sure we're _always_ interpreted as a C file */ +#ifdef __cplusplus +extern "C" { +#endif + + +/* Type that determines what data is stored inside a tree-node */ +typedef enum bowl_t { + UNSET, LITERAL, NUMERIC, LONG_NUMERIC, BOOLEAN, LIST, PAIR, POINTER +} bowl_t; + + +struct bowl { + bowl_t type; + short encset; + size_t size, used; + short copy; + union { + char *literal; + bool boolean; + struct bowl *(*list); + void *pointer; +#ifdef __LONG_LONG_SUPPORT__ + long long numeral; +#else + long numeral; +#endif + + } payload; +}; + + +/** 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); + + +/** + * 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); + + +/************************** + * + * Error handling functions + * + **************************/ + +const char *bowl_err_getmsg(dt_err *e); + +/*************************** + * + * Encoding/ Decoding support hooks + * + ***************************/ + +/** + * 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); + +/** + * 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); + + +/** + * 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); + +#ifdef __cplusplus +} +#endif +#endif //_DYNTREE_H_ diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h deleted file mode 100644 index 5193aba..0000000 --- a/include/dtree/dtree.h +++ /dev/null @@ -1,378 +0,0 @@ -/* - * 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 _DYNTREE_H_ -#define _DYNTREE_H_ - -#include -#include - -/* A helpful macro that can take care of \0 termated strings! */ -#define REAL_STRLEN(str) (strlen(str) + 1) - -/* Also make sure we're _always_ interpreted as a C file */ -#ifdef __cplusplus -extern "C" { -#endif - - -/* Type that determines what data is stored inside a tree-node */ -typedef enum dt_uni_t { - UNSET, LITERAL, NUMERIC, LONG_NUMERIC, BOOLEAN, LIST, PAIR, POINTER -} dt_uni_t; - - -typedef struct dtree { - dt_uni_t type; - short encset; - size_t size, used; - short copy; - union { - char *literal; - bool boolean; - struct dtree *(*list); - void *pointer; -#ifdef __LONG_LONG_SUPPORT__ - long long numeral; -#else - long numeral; -#endif - - } payload; -} dtree; - - -/** 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 dtree object - * - * @param data Reference pointer to dtree element - * @return - */ -dt_err dtree_malloc(dtree *(*data)); - - -/** - * Reset the type of a node and free child data - * - * @param data - * @return - */ -dt_err dtree_resettype(dtree *data); - - -/** - * Set the data element to a literal and save it's length - * - * @param data Reference to a dtree object - * @param literal String to store - * @return - */ -dt_err dtree_addliteral(dtree *data, const char *literal); - - -/** - * Set the data element to a numeral - * - * @param data Reference to a dtree object - * @param numeral Number to store - * @return - */ -dt_err dtree_addnumeral(dtree *data, long numeral); - - -/** - * Set the data element to a boolean. Do you really - * need this? Consider using @dtree_add_numeral instead. - * - * @param data Reference to a dtree object - * @param b boolean value (true, false) - * @return - */ -dt_err dtree_addboolean(dtree *data, bool b); - - -/** - * Add two new elements as a PAIR node under an existing node - * - * @param data dtree 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 dtree_addpair(dtree *data, dtree *(*key), dtree *(*value)); - - -/** - * Add a new data element to the resursive data store - * - * @param data Root reference - * @param new_data Reference pointer to a new dtree node - * @return - */ -dt_err dtree_addlist(dtree *data, dtree *(*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 - * "dtree_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 dtree_addpointer(dtree *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 dtree_split_trees(dtree *data, dtree *sp); - - -/** - * This function is very simmilar to dt_err "dtree_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 dtree_merge_trees(dtree *data, dtree *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 dtree_parent(dtree *root, dtree *data, dtree **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 dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_t type); - - -/** - * Much like #{dtree_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 dtree_search_keypayload(dtree *data, dtree *(*found), void *payload, dt_uni_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 dtree_copy_deep(dtree *data, dtree *(*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 dtree_copy(dtree *data, dtree *(*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 dtree_get(dtree *data, void *(*val)); - - -/** - * Return the type of a node as plain text - * - * @param data - * @return - */ -const char *dtree_dtype(dtree *data); - - -/** - * Prints the data dtree object and all of its children - * - * @param data - */ -void dtree_print(dtree *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 dtree_free_shallow(dtree *data); - - -/** - * Like #{dtree_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 dtree_free(dtree *data); - - -/************************** - * - * Error handling functions - * - **************************/ - -const char *dtree_err_getmsg(dt_err *e); - -/*************************** - * - * Encoding/ Decoding support hooks - * - ***************************/ - -/** - * 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 dtree_encode_set(dtree *data, short setting); - -/** - * 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 dtree_encode_json(dtree *data, char *json_data); - - -/** - * 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 dtree_decode_json(dtree *(*data), const char *json_data, size_t len); - -#ifdef __cplusplus -} -#endif -#endif //_DYNTREE_H_ diff --git a/include/dtree/eztree.h b/include/dtree/eztree.h deleted file mode 100644 index a538831..0000000 --- a/include/dtree/eztree.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef LIBDYNTREE_EZTREE_H -#define LIBDYNTREE_EZTREE_H - -#include -#include - -/* Also make sure we're _always_ interpreted as a C file */ -#ifdef __cplusplus -extern "C" { -#endif - -#define EZTREE_LITERAL 0xA -#define EZTREE_NUMERIC 0xB -#define EZTREE_NESTED 0xC - - -/** - * An quick create function for a literal node - * - * @param string - * @return - */ -dtree *eztree_new_literal(const char *string); - - -/** - * A quick create function for a number (numeric) node - * @param num - * @return - */ -dtree *eztree_new_numeric(const long num); - - -/** - * A quick create function for a string key and an arbitrary type value. - * The value needs to be marked properly or errors might occur. - * - * Nested nodes can be passed as values but need to be declared in before. This means - * that the tree needs to be built bottom-up. - * - * @param key - * @param val - * @param type - * @return - */ -dtree *eztree_new_pair(const char *key, void *val, short type); - - -/** - * A quick create function for a list node with a certain number of children - * ready to go. Children will be placed into a bufer that needs to be provided - * and the new parent will be returned from the function. - * - * Provided size needs to be size of the child-buffer or else errors might occur! - * - * @param list - * @param size - * @return - */ -dtree *eztree_new_list(dtree **list, size_t size); - -#ifdef __cplusplus -} -#endif - -#endif //LIBDYNTREE_EZTREE_H diff --git a/lib/dtree.c b/lib/dtree.c deleted file mode 100644 index 11090d8..0000000 --- a/lib/dtree.c +++ /dev/null @@ -1,816 +0,0 @@ -// Include our header file -#include - -// Runtime includes -#include -#include -#include -#include - -#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(dtree**, dtree *, dtree *); - -void list_print(dtree *data, const char *offset); - -/******/ - - -dt_err dtree_malloc(dtree *(*data)) -{ - (*data) = (dtree*) malloc(sizeof(dtree)); - if(*data == NULL) { - printf("Creating dtree object FAILED"); - return MALLOC_FAILED; - } - - memset(*data, 0, sizeof(dtree)); - - (*data)->type = UNSET; - return SUCCESS; -} - -dt_err dtree_resettype(dtree *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 = dtree_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 dtree_addliteral(dtree *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 dtree_addpointer(dtree *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 dtree_addnumeral(dtree *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 dtree_addboolean(dtree *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 dtree_addlist(dtree *data, dtree *(*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 - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy(tmp, data->payload.list, sizeof(dtree*) * data->used); - - /* Free the list WITHOUT the children! */ - free(data->payload.list); - data->payload.list = tmp; - } - - /* This means the data object is new */ - } else { - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * RDB_REC_DEF_SIZE); - data->payload.list = tmp; - data->type = LIST; - data->used = 0; - data->size = RDB_REC_DEF_SIZE; - } - - err = dtree_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 dtree_addpair(dtree *data, dtree *(*key), dtree *(*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 = dtree_malloc(key); - if(err) goto cleanup; - - err = dtree_malloc(value); - if(err) goto cleanup; - - /** Malloc space for PAIR */ - data->size = 2; - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * 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 dtree_split_trees(dtree *data, dtree *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 */ - dtree *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 dtree_merge_trees(dtree *data, dtree *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; - - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy(tmp, data->payload.list, sizeof(dtree*) * data->used); - - /* Free the list WITHOUT the children! */ - free(data->payload.list); - data->payload.list = tmp; - } - - /* This means the data object is new */ - } else { - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * 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 dtree_copy_deep(dtree *data, dtree *(*copy)) -{ - if(data == NULL) return INVALID_PARAMS; - dt_err err = SUCCESS; - - int it_type = -1; - dt_uni_t type = data->type; - - /* Check if we're the first call */ - if((*copy) == NULL) dtree_malloc(copy); - (*copy)->copy = DEEP; - - switch(type) { - case LITERAL: - dtree_addliteral(*copy, data->payload.literal); - break; - - case NUMERIC: - dtree_addnumeral(*copy, data->payload.numeral); - break; - - case BOOLEAN: - dtree_addboolean(*copy, data->payload.boolean); - break; - - case LIST: - { - int i; - int num = (int) data->used; - - for(i = 0; i < num; i++) { - dtree *node = data->payload.list[i]; - - dtree *new; - dtree_addlist(*copy, &new); - dtree_copy_deep(node, &new); - } - - break; - } - - case PAIR: - { - dtree *key, *val; - dtree_addpair(*copy, &key, &val); - - dtree *orig_key = data->payload.list[0]; - dtree *orig_val = data->payload.list[1]; - - dtree_copy_deep(orig_key, &key); - dtree_copy_deep(orig_val, &val); - - break; - } - - case POINTER: - dtree_addpointer(*copy, data->payload.pointer); - break; - - default: - err = INVALID_PAYLOAD; - break; - } - - return err; -} - - -dt_err dtree_parent(dtree *root, dtree *data, dtree **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 = dtree_parent(root->payload.list[i], data, parent); - if(err == SUCCESS) return SUCCESS; - } - } - break; - - default: - return INVALID_PAYLOAD; - } - - return NODE_NOT_FOUND; -} - - -dt_err dtree_copy(dtree *data, dtree *(*copy)) -{ - if(data == NULL) return INVALID_PARAMS; - dt_err err = SUCCESS; - - /* Allocate a new node */ - err = dtree_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 = dtree_addliteral(*copy, data->payload.literal); - break; - - case NUMERIC: - err = dtree_addnumeral(*copy, data->payload.numeral); - break; - - case BOOLEAN: - err = dtree_addboolean(*copy, data->payload.boolean); - break; - - case LIST: - (*copy)->type = LIST; - (*copy)->payload.list = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy((*copy)->payload.list, data->payload.list, sizeof(dtree*) * data->used); - break; - - case PAIR: - (*copy)->type = PAIR; - (*copy)->payload.list = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy((*copy)->payload.list, data->payload.list, sizeof(dtree*) * 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 dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_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 = dtree_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 dtree_search_keypayload(dtree *data, dtree *(*found), void *payload, dt_uni_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) { - dtree *key = data->payload.list[0]; - - dt_uni_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++) { - dtree_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 = dtree_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(dtree *data, const char *offset) -{ - dt_uni_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: - { - dt_uni_t k_type = data->payload.list[0]->type; - dt_uni_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++) { - dt_uni_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 dtree_print(dtree *data) -{ - list_print(data, ""); -} - -dt_err dtree_get(dtree *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 = (dtree*) data->payload.list; - return SUCCESS; -} - - -dt_err dtree_free(dtree *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 = dtree_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 dtree_free_shallow(dtree *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 = dtree_free(data->payload.list[i]); - if(err) return err; - } - - free(data->payload.list); - } - - free(data); - return SUCCESS; -} - - -const char *dtree_dtype(dtree *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(dtree **direct_parent, dtree *data, dtree *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 dtree object to check - * @return - */ -dt_err data_check(dtree *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; -} diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c deleted file mode 100644 index b163236..0000000 --- a/lib/dtree_utils.c +++ /dev/null @@ -1,278 +0,0 @@ -#include -#include -#include -#include -#include - -#include "jsmn.h" - - -#define DTREE_TOK_BOOLEAN (1 << 1) -#define DTREE_TOK_NUMERICAL (1 << 2) -#define DTREE_TOK_LITERAL (1 << 3) - -#define TOK_PAIR_KEYED (1 << 11) - - -char *tok_to_str(jsmntype_t *tok) -{ - switch(*tok) { - case JSMN_UNDEFINED: return "UNDEFINED"; - case JSMN_OBJECT: return "OBJECT"; - case JSMN_ARRAY: return "ARRAY"; - case JSMN_STRING: return "STRING"; - case JSMN_PRIMITIVE: return "PRIMITIVE"; - default: return "UNKNOWN"; - } -} - - -int digest_payload(const char *token) -{ - char* end; - size_t len = strlen(token); - - if(len == strlen("true") || len == strlen("false")) - if(strcmp(token, "true") == 0 || strcmp(token, "false") == 0) - return DTREE_TOK_BOOLEAN; - - /* It could still be a number! */ - strtol(token, &end, 10); - if (!*end) return DTREE_TOK_NUMERICAL; - - return DTREE_TOK_LITERAL; -} - - -dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) -{ - jsmn_parser parse; - jsmn_init(&parse); - - // FIXME: Variable amount of tokens? - unsigned int no_tokens = 1024 * 32; - jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * no_tokens); - memset(tokens, 0, sizeof(jsmntok_t) * no_tokens); - - int ret = jsmn_parse(&parse, json_data, strlen(json_data), tokens, no_tokens); - - unsigned int idx = 0; - jsmntok_t tok; - - /** Prepare dtree nodes */ - dtree *root, *curr; - dtree_malloc(&root); - curr = root; - - struct bounds { - int low, high; - }; - - struct pair { - short state; - char key[1024]; - union value { - char string[1024]; - unsigned long num; - } value; - }; - - /* Save some space to store token bounds */ - struct bounds *bounds = malloc(sizeof(struct bounds) * len); - memset(bounds, 0, sizeof(struct bounds) * len); - - /* Have a structure to record array types in the tree */ - bool *is_array = malloc(sizeof(bool) * len); - memset(is_array, 0, sizeof(bool) * len); - - /* Set the currently focused node */ - int focused = -1; - - struct pair c_pair; - memset(&c_pair, 0, sizeof(struct pair)); - - while(tok = tokens[idx++], tok.type != NULL) { - - size_t tok_len = (size_t) tok.end - tok.start; - char token[tok_len + 1]; - memset(token, 0, tok_len + 1); - memcpy(token, json_data + tok.start, tok_len); - - /** Check if we need to move the boundry scope (again) */ - if(focused > 0 && tok.end >= bounds[focused].high) { - focused--; - - /** - * We need to check if our direct parent is a PAIR or LIST type - * - * If it is a PAIR, it means we need to extract 2-stage parents - * If it is a LIST, it means we can leave it at 1. - */ - dtree *parent, *pair_parent; - dtree_parent(root, curr, &parent); - - if(parent->type == PAIR){ - dtree_parent(root, parent, &parent); // Override the PARENT variable - } - - /* Assign the new root node - old scope restored */ - curr = parent; - } - - switch(tok.type) { - - /** - * When we encounter a new json object, shift our "focus" over by one so we can - * record in what range this object is going to accumilate tokens. - * - * We then create a new child node under the current root node and switch the - * curr root pointer over to that child. - * - * When we reach the end of the token scope, we need to re-reference the parent as - * current root and switch over our boundry scope as well. This is done before the - * parsing switch statement. - */ - case JSMN_ARRAY: - case JSMN_OBJECT: - { - focused++; - bounds[focused].low = tok.start; - bounds[focused].high = tok.end; - - /* This is not elegant at all! */ - if(tok.type == JSMN_ARRAY) is_array[focused] = true; - - /* Then we check if our parent is an array */ - if(focused - 1 >= 0 && is_array[focused - 1]) { - - /** If our parent is an array we need to add a new object to our parent (CURR) **/ - dtree *obj; - dtree_addlist(curr, &obj); - - /* Then switch over our current value */ - curr = obj; - - } - - /** - * Most of the time, we will create a new object under the key of - * a pair. This is the case, when the c_pair state buffer has been - * set to KEYED. In this case we allocate a new pair node for key - * and value and set that value to the new root. - */ - if(c_pair.state == TOK_PAIR_KEYED) { - - /* Create pair nodes & new_root which becomes curr */ - dtree *pair, *key, *val; - dtree_addlist(curr, &pair); - dtree_addpair(pair, &key, &val); - - /* Assign key and new_root as a value of the pair */ - dtree_addliteral(key, c_pair.key); - - /* Move curr root pointer */ - curr = val; - - /* Blank c_pair data for next tokens */ - memset(&c_pair, 0, sizeof(struct pair)); - } - - /* Skip to next token */ - continue; - } - - case JSMN_PRIMITIVE: - case JSMN_STRING: - { - - /** - * First check if we are currently dealing with an array. If we are - * the way that we create nodes changes. Every token is immediately added - * to the currently focused list node - */ - if(is_array[focused]) { - - dtree *val; - dtree_addlist(curr, &val); - - /* Parse payload and asign to value node */ - switch(digest_payload(token)) { - case DTREE_TOK_LITERAL: - dtree_addliteral(val, token); - break; - - case DTREE_TOK_NUMERICAL: - dtree_addnumeral(val, atol(token)); - break; - - case DTREE_TOK_BOOLEAN: - dtree_addboolean(val, (strcpy(token, "true") == 0) ? true : false); - break; - - default: continue; - } - - /* Blank c_pair data for next tokens */ - memset(&c_pair, 0, sizeof(struct pair)); - - } else { - - /** - * Here we need to check if we are adding a string as a key - * or as a value. This is simply done by checking for the existance - * of a key in the c_pair (current pair) variable. - * - * We know the token positions so we can manualy copy from the json stream - */ - if(c_pair.state == 0) { - memcpy(c_pair.key, json_data + tok.start, (size_t) tok.end - tok.start); - c_pair.state = TOK_PAIR_KEYED; - - } else if(c_pair.state == TOK_PAIR_KEYED){ - - /** Create a PAIR node under current root */ - dtree *pair, *key, *val; - dtree_addlist(curr, &pair); - dtree_addpair(pair, &key, &val); - - /* Key is always literal */ - dtree_addliteral(key, c_pair.key); - - /* Parse payload and asign to value node */ - switch(digest_payload(token)) { - case DTREE_TOK_LITERAL: - dtree_addliteral(val, token); - break; - - case DTREE_TOK_NUMERICAL: - dtree_addnumeral(val, atol(token)); - break; - - case DTREE_TOK_BOOLEAN: - dtree_addboolean(val, (strcpy(token, "true") == 0) ? true : false); - break; - - default: continue; - } - - /* Blank c_pair data for next tokens */ - memset(&c_pair, 0, sizeof(struct pair)); - } - - } - - /* Skip to next token */ - continue; - } - - - - default: - continue; - } - } - - /* Switch over data pointer and return */ - (*data) = root; - return SUCCESS; -} \ No newline at end of file diff --git a/lib/eztree.c b/lib/eztree.c deleted file mode 100644 index ba8506a..0000000 --- a/lib/eztree.c +++ /dev/null @@ -1,77 +0,0 @@ -// Include eztree header file -#include - -dtree *eztree_new_literal(const char *string) -{ - dtree *node; - dtree_malloc(&node); - dtree_addliteral(node, string); - return node; -} - - -dtree *eztree_new_numeric(const long num) -{ - dtree *node; - dtree_malloc(&node); - dtree_addnumeral(node, num); - return node; -} - - -dtree *eztree_new_pair(const char *kdata, void *vdata, short type) -{ - dtree *root, *key, *val; - - /* Allocate nodes */ - dtree_malloc(&root); - dtree_addpair(root, &key, &val); - - /* Fill the data */ - dtree_addliteral(key, kdata); - switch(type) { - case EZTREE_LITERAL: - dtree_addliteral(val, (char*) vdata); - break; - - case EZTREE_NUMERIC: - // FIXME: This might be dangerous on 32bit - dtree_addnumeral(val, (long) vdata); - break; - - case EZTREE_NESTED: - { - dtree *tmp; - dtree_addlist(val, &tmp); - - /* Manually override data */ - memcpy(val->payload.list[0], vdata, sizeof(vdata)); - break; - } - - default: break; - } - - return root; -} - -// -//dtree *eztree_new_list(dtree **list, size_t size) -//{ -// /* Prepare our buffer */ -// memset(list, 0, sizeof(dtree*) * size); -// -// /* Prepare root node */ -// dtree *root; -// dtree_malloc(&root); -// -// /* Add apropriate number of children to root node */ -// int i; -// for(i = 0; i < size; i++) { -// dtree *tmp; -// dtree_addlist(root, &tmp); -// list[i] = tmp; -// } -// -// return root; -//} \ No newline at end of file diff --git a/lib/jsmn.c b/lib/jsmn.c deleted file mode 100644 index a006d60..0000000 --- a/lib/jsmn.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "jsmn.h" - -/** - * Allocates a fresh unused token from the token pull. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *tok; - if (parser->toknext >= num_tokens) { - return NULL; - } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; -#ifdef JSMN_PARENT_LINKS - tok->parent = -1; -#endif - return tok; -} - -/** - * Fills token type and boundaries. - */ -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, - int start, int end) { - token->type = type; - token->start = start; - token->end = end; - token->size = 0; -} - -/** - * Fills next available token with JSON primitive. - */ -static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': -#endif - case '\t' : case '\r' : case '\n' : case ' ' : - case ',' : case ']' : case '}' : - goto found; - - default: break; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = (unsigned int) start; - return JSMN_ERROR_INVAL; - } - - } -#ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; -#endif - -found: - if (tokens == NULL) { - parser->pos--; - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = (unsigned int) start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - parser->pos--; - return 0; -} - -/** - * Fills next token with JSON string. - */ -static int jsmn_parse_string(jsmn_parser *parser, const char *js, - size_t len, jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - - int start = parser->pos; - - parser->pos++; - - /* Skip starting quote */ - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; - - /* Quote: end of string */ - if (c == '\"') { - if (tokens == NULL) { - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = (unsigned int) start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - return 0; - } - - /* Backslash: Quoted symbol expected */ - if (c == '\\' && parser->pos + 1 < len) { - int i; - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': case '/' : case '\\' : case 'b' : - case 'f' : case 'r' : case 'n' : case 't' : - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - parser->pos++; - for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { - /* If it isn't a hex character we have an error */ - if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ - (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ - (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ - parser->pos = (unsigned int) start; - return JSMN_ERROR_INVAL; - } - parser->pos++; - } - parser->pos--; - break; - /* Unexpected symbol */ - default: - parser->pos = (unsigned int) start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = (unsigned int) start; - return JSMN_ERROR_PART; -} - -/** - * Parse JSON string and fill tokens. - */ -int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, - jsmntok_t *tokens, unsigned int num_tokens) { - int r; - int i; - jsmntok_t *token; - int count = parser->toknext; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; - - c = js[parser->pos]; - switch (c) { - case '{': case '[': - count++; - if (tokens == NULL) { - break; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) - return JSMN_ERROR_NOMEM; - if (parser->toksuper != -1) { - tokens[parser->toksuper].size++; -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': case ']': - if (tokens == NULL) - break; - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); -#ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - if(token->type != type || parser->toksuper == -1) { - return JSMN_ERROR_INVAL; - } - break; - } - token = &tokens[token->parent]; - } -#else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) return JSMN_ERROR_INVAL; - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } -#endif - break; - case '\"': - r = jsmn_parse_string(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; - case '\t' : case '\r' : case '\n' : case ' ': - break; - case ':': - parser->toksuper = parser->toknext - 1; - break; - case ',': - if (tokens != NULL && parser->toksuper != -1 && - tokens[parser->toksuper].type != JSMN_ARRAY && - tokens[parser->toksuper].type != JSMN_OBJECT) { -#ifdef JSMN_PARENT_LINKS - parser->toksuper = tokens[parser->toksuper].parent; -#else - for (i = parser->toknext - 1; i >= 0; i--) { - if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { - if (tokens[i].start != -1 && tokens[i].end == -1) { - parser->toksuper = i; - break; - } - } - } -#endif - } - break; -#ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': case '0': case '1' : case '2': case '3' : case '4': - case '5': case '6': case '7' : case '8': case '9': - case 't': case 'f': case 'n' : - /* And they must not be keys of the object */ - if (tokens != NULL && parser->toksuper != -1) { - jsmntok_t *t = &tokens[parser->toksuper]; - if (t->type == JSMN_OBJECT || - (t->type == JSMN_STRING && t->size != 0)) { - return JSMN_ERROR_INVAL; - } - } -#else - /* In non-strict mode every unquoted value is a primitive */ - default: -#endif - r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); - if (r < 0) return r; - count++; - if (parser->toksuper != -1 && tokens != NULL) - tokens[parser->toksuper].size++; - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif - } - } - - if (tokens != NULL) { - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } - } - - return count; -} - -/** - * Creates a new parser based over a given buffer with an array of tokens - * available. - */ -void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} - diff --git a/lib/jsmn.h b/lib/jsmn.h deleted file mode 100644 index 2f588e8..0000000 --- a/lib/jsmn.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef __JSMN_H_ -#define __JSMN_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * JSON type identifier. Basic types are: - * o Object - * o Array - * o String - * o Other primitive: number, boolean (true/false) or null - */ -typedef enum { - JSMN_UNDEFINED = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3, - JSMN_PRIMITIVE = 4 -} jsmntype_t; - -enum jsmnerr { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3 -}; - -/** - * JSON token description. - * type type (object, array, string etc.) - * start start position in JSON data string - * end end position in JSON data string - */ -typedef struct { - jsmntype_t type; - int start; - int end; - int size; -#ifdef JSMN_PARENT_LINKS - int parent; -#endif -} jsmntok_t; - -/** - * JSON parser. Contains an array of token blocks available. Also stores - * the string being parsed now and current position in that string - */ -typedef struct { - unsigned int pos; /* offset in the JSON string */ - unsigned int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g parent object or array */ -} jsmn_parser; - -/** - * Create JSON parser over an array of tokens - */ -void jsmn_init(jsmn_parser *parser); - -/** - * Run JSON parser. It parses a JSON data string into and array of tokens, each describing - * a single JSON object. - */ -int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, - jsmntok_t *tokens, unsigned int num_tokens); - -#ifdef __cplusplus -} -#endif - -#endif /* __JSMN_H_ */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..5fa7c8b --- /dev/null +++ b/main.c @@ -0,0 +1,11 @@ +// Some static tests + +#include +#include +// #include + +int main(int argn, char **argv) +{ + + return 0; +} \ No newline at end of file diff --git a/test/main.c b/test/main.c deleted file mode 100644 index 3a44129..0000000 --- a/test/main.c +++ /dev/null @@ -1,237 +0,0 @@ - -#include - -#include -#include -#include -#include - -/** - * A small test that creates a tree, splits the nodes - * and then merges them again. - */ -dt_err split_and_merge(); - -dt_err search_for_payload(); - -dt_err json_encode(char *json); - -dt_err test_shortcut_functions(); - -#define TEST(function) \ - printf("Running '%s'...", #function); \ - fflush(stdout); \ - err = function; \ - printf(" %s\n", (err == 0) ? "OK!" : "FAILED!"); \ - if(err) goto end; - -int main(int argn, char **argv) -{ - dt_err err = SUCCESS; - printf("=== libdyntree test suite ===\n"); - -// TEST(split_and_merge()) -// -// TEST(search_for_payload()) -// -// char json[1024]; -// TEST(json_encode(json)) -// -// dtree *recover; -// dtree_decode_json(&recover, json); -// dtree_free(recover); - - struct timeval t1, t2; - double elapsedTime; - - // start timer - gettimeofday(&t1, NULL); - -#define PATH "/home/spacekookie/Downloads/MOCK_DATA.json" - - /* Open the file and seek through it for length */ - FILE *f = fopen(PATH, "r"); - fseek(f, 0, SEEK_END); - size_t file_size = (size_t) ftell(f); - fseek(f, 0, SEEK_SET); - - /* Create a buffer of the correct size */ - char *json = (char*) malloc(sizeof(char) * (file_size + 1)); - memset(json, 0, file_size + 1); - fread(json, file_size, 1, f); - fclose(f); - -// char *json = "{ \"some_key\": \"some_value\" }"; - - printf("Raw json data. %s", json); - - dtree *recov; - dtree_decode_json(&recov, json, file_size); - - dtree_print(recov); - dtree_free(recov); - - gettimeofday(&t2, NULL); - - // compute and print the elapsed time in millisec - elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms - elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms - - printf("Program took %fms to run\n", elapsedTime); - - -end: - printf("==== done ====\n"); - return err; -} - - -/*************** TEST IMPLEMENTATIONS ****************/ - -//dt_err split_and_merge() -//{ -// dt_err err; -// -// /* Allocate a node named root */ -// dtree *root; -// err = dtree_malloc(&root); -// if(err) goto exit; -// -// /* Add child as a recursive node to root */ -// dtree *child; -// err = dtree_addlist(root, &child); -// if(err) goto exit; -// -// /* Make child a literal node containing the works of shakespeare */ -// const char *hamlet = "To be, or not to be: that is the question:\n" -// "Whether 'tis nobler in the mind to suffer\n" -// "The slings and arrows of outrageous fortune,\n" -// "Or to take arms against a sea of troubles,\n" -// "And by opposing end them? To die: to sleep;\n" -// "No more; and by a sleep to say we end\n" -// "The heart-ache and the thousand natural shocks\n" -// "That flesh is heir to, 'tis a consummation\n" -// "Devoutly to be wish'd. To die, to sleep;"; -// -// err = dtree_addliteral(child, hamlet); -// if(err) goto exit; -// -// /* Split our tree into two single-nodes */ -// err = dtree_split_trees(root, child); -// if(err) goto exit; -// -// /* Re-merge because they miss each other */ -// err = dtree_merge_trees(root, child); -// if(err) goto exit; -// -// /* Cleanup */ -// exit: -// dtree_free(root); -// return err; -//} -// -//dt_err search_for_payload() -//{ -// dt_err err; -// -// dtree *root, *a, *b, *found; -// err = dtree_malloc(&root); -// if(err) goto exit; -// -// const char *string = "This is some data!"; -// err = dtree_addlist(root, &a); -// if(err) goto exit; -// -// err = dtree_addliteral(a, string); -// if(err) goto exit; -// -// err = dtree_addlist(root, &b); -// if(err) goto exit; -// -// err = dtree_addnumeral(b, 1337); -// if(err) goto exit; -// -// /* Try to find our data again */ -// -// err = dtree_search_payload(root, &found, (void*) string, LITERAL); -// if(err) goto exit; -// -// err = dtree_search_payload(root, &found, (void*) 1337, NUMERIC); -// if(err) goto exit; -// -// exit: -// dtree_free(root); -// return err; -//} -// -//dt_err json_encode(char *json) { -// dt_err err; -// -// dtree *root, *a, *b, *c, *found; -// err = dtree_malloc(&root); -// if (err) goto exit; -// -// dtree *key, *val; -// err = dtree_addlist(root, &a); -// if (err) goto exit; -// err = dtree_addlist(root, &b); -// if (err) goto exit; -// err = dtree_addlist(root, &c); -// if (err) goto exit; -// -// err = dtree_addpair(a, &key, &val); -// if (err) goto exit; -// err = dtree_addliteral(key, "Server Address"); -// if (err) goto exit; -// err = dtree_addliteral(val, "https://github.com"); -// if (err) goto exit; -// -// key = val = NULL; -// -// err = dtree_addpair(b, &key, &val); -// if (err) goto exit; -// err = dtree_addliteral(key, "Server Port"); -// if (err) goto exit; -// err = dtree_addnumeral(val, 8080); -// if (err) goto exit; -// -// key = val = NULL; -// -// err = dtree_addpair(c, &key, &val); -// if (err) goto exit; -// err = dtree_addliteral(key, "Users"); -// if (err) goto exit; -// -// dtree *sbrec, *sbrec2; -// err = dtree_addlist(val, &sbrec); -// if (err) goto exit; -// err = dtree_addlist(val, &sbrec2); -// if (err) goto exit; -// -// dtree *subkey, *subval; -// err = dtree_addpair(sbrec, &subkey, &subval); -// if (err) goto exit; -// err = dtree_addliteral(subkey, "spacekookie"); -// if (err) goto exit; -// err = dtree_addliteral(subval, "Admin"); -// if (err) goto exit; -// -// key = val = NULL; -// -// dtree *subkey2, *subval2; -// err = dtree_addpair(sbrec2, &subkey2, &subval2); -// if (err) goto exit; -// err = dtree_addliteral(subkey2, "jane"); -// if (err) goto exit; -// err = dtree_addliteral(subval2, "normal"); -// if (err) goto exit; -// -// err = dtree_encode_set(root, DYNTREE_JSON_MINIFIED); -// if (err) goto exit; -// err = dtree_encode_json(root, json); -// if (err) goto exit; -// -// exit: -// dtree_free(root); -// return err; -//} -- cgit v1.2.3