From 1337e4a7be1bc6db2bf820b1f5b55764c417b33a Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 30 Jul 2016 21:08:26 +0200 Subject: Initial commit --- .gitignore | 33 +++++++++++++ LICENSE | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..f805e810e5c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# 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 diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..65c5ca88a67c --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. -- cgit v1.2.3 From 7d6b295c9e3f2ae3279039fb50f9e3bb0478074c Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 30 Jul 2016 21:26:43 +0200 Subject: Initial commit of the library --- .gitignore | 1 + CMakeLists.txt | 25 ++++ README | 20 +++ include/dtree/dyn_err.h | 29 +++++ include/dtree/dyn_tree.h | 77 ++++++++++++ lib/dyn_tree.c | 319 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 471 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README create mode 100644 include/dtree/dyn_err.h create mode 100644 include/dtree/dyn_tree.h create mode 100644 lib/dyn_tree.c diff --git a/.gitignore b/.gitignore index f805e810e5c6..de789df8581b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ # Debug files *.dSYM/ *.su +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000000..6a90f6bb0d46 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +############################################ +# +# 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/dyn_tree.c) + +# Define our library in cmake +add_library(libdyntree STATIC ${DYN_TREE_SRC}) + +# 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 "") diff --git a/README b/README new file mode 100644 index 000000000000..c918f0edb17e --- /dev/null +++ b/README @@ -0,0 +1,20 @@ +# libdyntree + +:tree: A dynamic n-ary tree to store recursive structures, key-value stores and single fields in a fast and comprehensive C datastructure. + +## 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 C11 but should be able to run on C99 or older. + +```console +$> mkdir build; cd build +$> cmake .. +$> make -j 2 +``` + +This will create a `.a` file. If you require a shared object, you can change the linking behaviour in the `CMakeLists.txt` file. + + +## How to use + +Using libdyntree is straighforward with a comprehensive API. Everything resolves around `dtree` objects and providing fields to API functions. Every function is documented as outlined in the header files. \ No newline at end of file diff --git a/include/dtree/dyn_err.h b/include/dtree/dyn_err.h new file mode 100644 index 000000000000..23dae0f5e0ee --- /dev/null +++ b/include/dtree/dyn_err.h @@ -0,0 +1,29 @@ + +/* Make sure we're not included multiple times */ +#ifndef _DYN_ERR_H +#define _DYN_ERR_H + +/* 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 dt_err { + + /* General purpose error codes */ + FAILURE = -1, + SUCCESS = 0, + + INVALID_PARAMS, + MALLOC_FAILED, + INVALID_PAYLOAD + +} dt_err; + +const char *rdb_error_getmsg(dt_err *e); + +#ifdef __cplusplus +} +#endif +#endif /* _DYN_ERR_H */ \ No newline at end of file diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h new file mode 100644 index 000000000000..e1db1d64bb18 --- /dev/null +++ b/include/dtree/dyn_tree.h @@ -0,0 +1,77 @@ +/* + * 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 [recursive] + * child1 [recursive] + * key [literal] - "Username" + * value [literal] - "spacekookie" + * child2 [recursive] + * key [literal] - "Age" + * value [numerical] - 23 + * child3 + * subchild [recursive] + * ... + * + * Freeing the root node will free all children + */ + +#ifndef _DYNTREE_H_ +#define _DYNTREE_H_ + +#include "dyn_err.h" +#include + + +/* Also make sure we're _always_ interpreted as a C file */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + UNSET, LITERAL, NUMERAL, RECURSIVE, PAIR +} dt_uni_t; + +typedef struct dtree { + dt_uni_t type; + size_t size, used; + union { + char *literal; + int numeral; + struct dtree *(*recursive); + } payload; +} dtree; + +/** Malloc a new dtree object */ +dt_err dtree_malloc(dtree *(*data)); + +dt_err dtree_resettype(dtree *data); + +/** Set the data element to a literal and save it's length */ +dt_err dtree_addliteral(dtree *data, const char *literal, size_t length); + +/** Set the data element to a numeral */ +dt_err dtree_addnumeral(dtree *data, int numeral); + +/** Add two new elements as a PAIR node under an existing node */ +dt_err dtree_addpair(dtree *data, dtree *(*key), dtree *(*value)); + +/** Add a new data element to the resursive data store */ +dt_err dtree_addrecursive(dtree *data, dtree *(*new_data)); + +dt_err dtree_get(dtree *data, void *(*val)); + +const char *dtree_dtype(dt_uni_t type); + +/** Prints*/ +void dtree_print(dtree *data); + +/** Will free all memory allocated by this element and it's children */ +dt_err dtree_free(dtree *data); + +#ifdef __cplusplus +} +#endif +#endif //_DYNTREE_H_ diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c new file mode 100644 index 000000000000..466533256ff2 --- /dev/null +++ b/lib/dyn_tree.c @@ -0,0 +1,319 @@ +// Include our header file +#include + +// Runtime includes +#include +#include +#include +#include + +#define RDB_REC_DEF_SIZE 2 +#define RDB_REC_MULTIPLY 2 +#define REAL_STRLEN(str) (strlen(str) + 1) + +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 == RECURSIVE || 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.recursive[i]); + if(err) return err; + } + } + + /* Set the data type to unset */ + data->type = UNSET; + data->size = 0; + data->used = 0; + + return SUCCESS; +} + +dt_err dtree_addliteral(dtree *data, const char *literal, size_t length) +{ + /* Make sure we are a literal or unset data object */ + if(data->type != UNSET) + if(data->type != LITERAL) return INVALID_PAYLOAD; + + /* 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_addnumeral(dtree *data, int numeral) +{ + /* Make sure we are a literal or unset data object */ + if(data->type != UNSET) + if(data->type != NUMERAL) return INVALID_PAYLOAD; + + data->payload.numeral = numeral; + data->type = NUMERAL; + data->size = sizeof(int); + data->used = sizeof(int); + return SUCCESS; +} + +dt_err dtree_addrecursive(dtree *data, dtree *(*new_data)) +{ + /* Make sure we are a literal or unset data object */ + if(data->type != UNSET) + if(data->type != RECURSIVE) 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.recursive, sizeof(dtree*) * data->used); + + /* Free the list WITHOUT the children! */ + free(data->payload.recursive); + data->payload.recursive = tmp; + } + + /* This means the data object is new */ + } else { + dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); + data->payload.recursive = tmp; + data->type = RECURSIVE; + 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.recursive[data->used] = *new_data; + data->used++; + + 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.recursive = tmp; + + { /* Assign data to new array */ + data->payload.recursive[data->used] = *key; + data->used++; + data->payload.recursive[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; +} + +void recursive_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 NUMERAL: + printf("%s[%d]\n", offset, data->payload.numeral); + break; + case PAIR: + { + dt_uni_t k_type = data->payload.recursive[0]->type; + dt_uni_t v_type = data->payload.recursive[1]->type; + + if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.recursive[0]->payload.literal); + if(k_type == NUMERAL) printf("%s[%d]", offset, data->payload.recursive[0]->payload.numeral); + + char new_offset[REAL_STRLEN(offset) + 2]; + strcpy(new_offset, offset); + strcat(new_offset, " "); + + if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); + + /* Print the value now */ + + if(k_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); + if(k_type == NUMERAL) printf(" => [%d]\n", data->payload.recursive[1]->payload.numeral); + + if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); + } + break; + + case RECURSIVE: + { + int i; + printf("%s[RECURSIVE]\n", offset); + for(i = 0; i < data->used; i++) { + dt_uni_t type = data->payload.recursive[i]->type; + + + char new_offset[REAL_STRLEN(offset) + 2]; + strcpy(new_offset, offset); + strcat(new_offset, " "); + + if(type == LITERAL || type == NUMERAL) { + recursive_print(data->payload.recursive[i], new_offset); + continue; + } + + if(type == RECURSIVE) + { + recursive_print(data->payload.recursive[i], new_offset); + continue; + } + + if(type == PAIR) { + printf("%s[PAIR] <==> ", new_offset); + recursive_print(data->payload.recursive[i], new_offset); + } + } + break; + } + + default: + break; + + } +} + +void dtree_print(dtree *data) +{ + recursive_print(data, ""); +} + +dt_err dtree_get(dtree *data, void *(*val)) +{ + if(data->type == LITERAL) *val = (char*) data->payload.literal; + if(data->type == NUMERAL) *val = (int*) &data->payload.numeral; + if(data->type == RECURSIVE || data->type == PAIR) + *val = (dtree*) data->payload.recursive; + + return SUCCESS; +} + +dt_err dtree_free(dtree *data) +{ + if(data == NULL) return SUCCESS; + + if(data->type == LITERAL) { + if(data->payload.literal) free(data->payload.literal); + } else if(data->type == RECURSIVE || data->type == PAIR) { + int i; + dt_err err; + for(i = 0; i < data->size; i++) { + err = dtree_free(data->payload.recursive[i]); + if(err) return err; + } + + free(data->payload.recursive); + } + + free(data); + return SUCCESS; +} + +const char *dtree_dtype(dt_uni_t type) +{ + switch(type) { + case LITERAL: return "Literal"; + case NUMERAL: return "Numeral"; + case RECURSIVE: return "Recursive"; + default: return "Unknown"; + } +} + +/**************** PRIVATE UTILITY FUNCTIONS ******************/ + + +/** + * 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 == RECURSIVE) + { + 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; +} -- cgit v1.2.3 From 6715214825f7c4b555227cc48e65c8af860f4010 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 30 Jul 2016 21:31:17 +0200 Subject: Quickly updating README --- README | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 1 deletion(-) diff --git a/README b/README index c918f0edb17e..3c2da293db34 100644 --- a/README +++ b/README @@ -17,4 +17,154 @@ This will create a `.a` file. If you require a shared object, you can change the ## How to use -Using libdyntree is straighforward with a comprehensive API. Everything resolves around `dtree` objects and providing fields to API functions. Every function is documented as outlined in the header files. \ No newline at end of file +Using libdyntree is straighforward with a comprehensive API. Everything resolves around `dtree` objects and providing fields to API functions. Every function is documented as outlined in the header files. + + + +Looking at this piece of code. What do you think it does? + +```C + void main(void) { + rdb_data *root; + err = rdb_data_malloc(&root); + + rdb_data_addnumeral(root, 1337); + } + +``` + +It creates a new rdb_data object which gets allocated and initialised by the `rdb_data_malloc` function. Then you pass a pointer to the allocated object and afterwards assigns the number "1337" to it. + +Note how now the type of the rdb_data node is now `NUMERICAL`. We can no longer store any other type of data in it unless we call `rdb_data_resettype(root)`. + +The reedb data storage API is meant to give you an easy recursive and flexible storage system for whatever type of data you might require in C. It was originally written to allow for write and read operations on the C API of libreedb. But I feel that it has uses outside of this project as well. So please use it if you think it is useful. + +### Node Types + +In total there are five different node types in rdb_data. + + - UNSET + - LITERAL + - NUMERICAL + - RECURSIVE + - PAIR + +The name `recursive` might be a bit misleading. What it means is that the node contains a list of data - potentially a list of other nodes - and can thus be recursive. +A pair is simply a mapping of a key to a value. It takes up one node. So to implement a traditional key-value store you take a RECURSIVE node and you store PAIR nodes inside it. + +The API is very straightforward. + +```C + + rdb_data *root; + err = rdb_data_malloc(&root); + + rdb_data *pair1, *pair2; + + /* Add the two nodes to the recursive list */ + rdb_data_mallocrecursive(root, &pair1); + rdb_data_mallocrecursive(root, &pair2); + + /* This code makes 4 new nodes and assigns them as pair nodes */ + rdb_data *pair1_key, *pair1_val, *pair2_key, *pair2_val + rdb_data_mallocpair(pair1, &pair1_key, &pair1_val); + rdb_data_mallocpair(pair2, &pair2_key, &pair2_val); +``` + +At this point the structure of our rdb_data set would look somewhat like this: + +``` + [root] + [pair1] => [ [pair1_key] => [pair1_val] ] + [pair2] => [ [pair2_key] => [pair2_val] ] +``` + +### A more complex example + +From here on out you can then normally put values into the key and value items. You could even have a PAIR or RECURSIVE element as a key or value! The options are limitless. + +Also...don't be afraid of reusing your pointers: you don't need to keep them. rdb_data allocates the fields and keeps a reference to each field. This means that a single call can free an entire nested structure. And you don't need to keep coming up with variable names. + +You just need one pointer somewhere as a buffer to work on. + +```C + rdb_data *root; + err = rdb_data_malloc(&root); + + rdb_data *pair, *key, *val; + + rdb_data_mallocrecursive(root, &pair); + rdb_data_mallocpair(pair, &key, &val); + + // ... Assign data to key and val + + /* Remove the pointers */ + pair = key = val = NULL; + + /* Start again */ + rdb_data_mallocrecursive(root, &pair); + rdb_data_mallocpair(pair, &key, &val); + + // ... Assign data to key and val +``` + +I hope you like this library and can do awesome stuff with rdb_data. If you find any bugs, please report them on the libreedb github repository. This project might become it's own repository at some point but until then, we shall see :) + +Below there is a slightly more complicated example, including several nested types and printing the structure. + + +``` +#include +#include + +#include + +int main(void) +{ + rdb_err_t err; + rdb_data *root; + err = rdb_data_malloc(&root); + printf("Malloc returned: %s\n", rdb_error_getmsg(&err)); + + rdb_data *lit, *num, *pair1, *pair2, *rec2; + rdb_data_mallocrecursive(root, &lit); + rdb_data_mallocrecursive(root, &num); + rdb_data_mallocrecursive(root, &rec2); + rdb_data_mallocrecursive(root, &pair1); + rdb_data_mallocrecursive(root, &pair2); + + rdb_data_addliteral(lit, "This is a string", REAL_STRLEN("This is a string")); + rdb_data_addnumeral(num, 1337); + + rdb_data *rec_d1, *rec_d2; + rdb_data_mallocrecursive(rec2, &rec_d1); + rdb_data_addliteral(rec_d1, "Reedb is awesome!", REAL_STRLEN("Reedb is awesome!")); + + rdb_data_mallocrecursive(rec2, &rec_d2); + rdb_data_addnumeral(rec_d2, 666); + + rdb_data *pair1_key, *pair1_val; + rdb_data_mallocpair(pair1, &pair1_key, &pair1_val); + + rdb_data_addliteral(pair1_key, "Username", REAL_STRLEN("Username")); + rdb_data_addliteral(pair1_val, "spacekookie", REAL_STRLEN("spacekookie")); + + rdb_data *pair2_key, *pair2_val; + rdb_data_mallocpair(pair2, &pair2_key, &pair2_val); + + rdb_data_addliteral(pair2_key, "Website", REAL_STRLEN("Website")); + rdb_data_addliteral(pair2_val, "www.spacekookie.de", REAL_STRLEN("www.spacekookie.de")); + + + /* Print our structure */ + rdb_data_print(root); + + /* Free everything */ + rdb_data_free(root); + + return 0; +} +``` +## License + +The library is licensed under LGPL-3 (the same as it's origin project [libreedb](https://github.com/reepass/libreedb)). Have fun <3s \ No newline at end of file -- cgit v1.2.3 From 2af583b513ea34726dbcdaa4d1cb7468b8122128 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 30 Jul 2016 21:35:00 +0200 Subject: Update README.md --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 3c2da293db34..16b4457443d9 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ # libdyntree -:tree: A dynamic n-ary tree to store recursive structures, key-value stores and single fields in a fast and comprehensive C datastructure. + :herb: A dynamic n-ary tree to store recursive structures, key-value stores and single fields in a fast and comprehensive C data structure. ## How to build @@ -167,4 +167,4 @@ int main(void) ``` ## License -The library is licensed under LGPL-3 (the same as it's origin project [libreedb](https://github.com/reepass/libreedb)). Have fun <3s \ No newline at end of file +The library is licensed under LGPL-3 (the same as it's origin project [libreedb](https://github.com/reepass/libreedb)). Have fun <3s -- cgit v1.2.3 From dfad7481625d61780b4e3223321b53fb0468ba97 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 30 Jul 2016 21:35:27 +0200 Subject: Update README.md --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 16b4457443d9..4c30befdddf2 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ -# libdyntree +# libdyntree :herb: - :herb: A dynamic n-ary tree to store recursive structures, key-value stores and single fields in a fast and comprehensive C data structure. +A dynamic n-ary tree to store recursive structures, key-value stores and single fields in a fast and comprehensive C data structure. ## How to build -- cgit v1.2.3 From 58045e8a341b066a1957cc2472cb91b0d39b28ef Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 30 Jul 2016 23:28:36 +0200 Subject: Changing the APi slightly and adding better comments for the api functions. - Also adding the ability to store custom pointers in a node --- include/dtree/dyn_tree.h | 125 +++++++++++++++++++++++++++++++++++++++++++---- lib/dyn_tree.c | 55 ++++++++++++++++++--- 2 files changed, 164 insertions(+), 16 deletions(-) diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h index e1db1d64bb18..cfd723923940 100644 --- a/include/dtree/dyn_tree.h +++ b/include/dtree/dyn_tree.h @@ -30,8 +30,9 @@ extern "C" { #endif +/* Type that determines what data is stored inside a tree-node */ typedef enum { - UNSET, LITERAL, NUMERAL, RECURSIVE, PAIR + UNSET, LITERAL, NUMERAL, RECURSIVE, PAIR, POINTER } dt_uni_t; typedef struct dtree { @@ -40,35 +41,139 @@ typedef struct dtree { union { char *literal; int numeral; - struct dtree *(*recursive); + struct dtree *(*recursive); + void *pointer; } payload; } dtree; -/** Malloc a new dtree object */ + +/** + * 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 */ + +/** + * Set the data element to a literal and save it's length + * + * @param data Reference to a dtree object + * @param literal String to store + * @param length TRUE string length to use. + * @return + */ dt_err dtree_addliteral(dtree *data, const char *literal, size_t length); -/** Set the data element to a numeral */ + +/** + * 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, int numeral); -/** Add two new elements as a PAIR node under an existing node */ + +/** + * 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 */ + +/** + * 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_addrecursive(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); + + +/** + * 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 or leave it as a void* to let + * libdyntree do the casting for you. + * + * @param data Node reference to access + * @param val Reference pointer to write into + * @return + */ dt_err dtree_get(dtree *data, void *(*val)); -const char *dtree_dtype(dt_uni_t type); -/** Prints*/ +/** + * 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 all memory allocated by this element and it's children */ +/** + * 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 + * + * @param data + * @return + */ dt_err dtree_free(dtree *data); #ifdef __cplusplus diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c index 466533256ff2..095d379d9f45 100644 --- a/lib/dyn_tree.c +++ b/lib/dyn_tree.c @@ -74,6 +74,21 @@ dt_err dtree_addliteral(dtree *data, const char *literal, size_t 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, int numeral) { /* Make sure we are a literal or unset data object */ @@ -130,6 +145,7 @@ dt_err dtree_addrecursive(dtree *data, dtree *(*new_data)) return SUCCESS; } + dt_err dtree_addpair(dtree *data, dtree *(*key), dtree *(*value)) { /* Make sure we are a literal or unset data object */ @@ -265,6 +281,7 @@ dt_err dtree_free(dtree *data) if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); + } else if(data->type == RECURSIVE || data->type == PAIR) { int i; dt_err err; @@ -274,19 +291,45 @@ dt_err dtree_free(dtree *data) } free(data->payload.recursive); + + } else if(data->type == POINTER) { + if(data->payload.pointer) free(data->payload.pointer); } free(data); return SUCCESS; } -const char *dtree_dtype(dt_uni_t type) +dt_err dtree_free_shallow(dtree *data) { - switch(type) { - case LITERAL: return "Literal"; - case NUMERAL: return "Numeral"; - case RECURSIVE: return "Recursive"; - default: return "Unknown"; + if(data == NULL) return SUCCESS; + + if(data->type == LITERAL) { + if(data->payload.literal) free(data->payload.literal); + } else if(data->type == RECURSIVE || data->type == PAIR) { + int i; + dt_err err; + for(i = 0; i < data->size; i++) { + err = dtree_free(data->payload.recursive[i]); + if(err) return err; + } + + free(data->payload.recursive); + } + + free(data); + return SUCCESS; +} + +const char *dtree_dtype(dtree *data) +{ + switch(data->type) { + case LITERAL: return "Literal"; + case NUMERAL: return "Numeral"; + case RECURSIVE: return "Recursive"; + case PAIR: return "Pair"; + case POINTER: return "Pointer"; + default: return "Unknown"; } } -- cgit v1.2.3 From 2317004e19eadae8742645dc71ca18957c8f43c1 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 30 Jul 2016 23:47:55 +0200 Subject: Updating the README to reflect some of the changes made to the API and to be more explanatory :) --- README | 162 ++++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 91 insertions(+), 71 deletions(-) diff --git a/README b/README index 4c30befdddf2..b21dbd114ce2 100644 --- a/README +++ b/README @@ -19,82 +19,85 @@ This will create a `.a` file. If you require a shared object, you can change the Using libdyntree is straighforward with a comprehensive API. Everything resolves around `dtree` objects and providing fields to API functions. Every function is documented as outlined in the header files. - - -Looking at this piece of code. What do you think it does? +Generally, memory is managed for you by libdyntree. So allocating a new node is as easy as passing a reference pointer to a malloc function: ```C - void main(void) { - rdb_data *root; - err = rdb_data_malloc(&root); +dtree *data; +dtree_malloc(&data); +``` - rdb_data_addnumeral(root, 1337); - } +The above code will initialise an empty dtree node that can then be written into. While it's possible to edit the values in the struct yourself it is not recomended. -``` +Instead you should use the utility functions provided to you by libdyntree that will make sure that you don't accidentally leak any memory or try to write something invalid into your structures. + +**Possible Nodes** + - Unset + - Literal + - Numerical + - Recursive + - Pair + - Pointer -It creates a new rdb_data object which gets allocated and initialised by the `rdb_data_malloc` function. Then you pass a pointer to the allocated object and afterwards assigns the number "1337" to it. +```C -Note how now the type of the rdb_data node is now `NUMERICAL`. We can no longer store any other type of data in it unless we call `rdb_data_resettype(root)`. + dtree_addliteral(data, "My String", REAL_STRLEN("My String")); -The reedb data storage API is meant to give you an easy recursive and flexible storage system for whatever type of data you might require in C. It was originally written to allow for write and read operations on the C API of libreedb. But I feel that it has uses outside of this project as well. So please use it if you think it is useful. + dtree_addnumeral(data, 42); -### Node Types + dtree_addpair(data, &key, &value); -In total there are five different node types in rdb_data. + dtree_addrecursive(data, &sub_d); - - UNSET - - LITERAL - - NUMERICAL - - RECURSIVE - - PAIR + dtree_addpointer(data, my_ptr); +``` -The name `recursive` might be a bit misleading. What it means is that the node contains a list of data - potentially a list of other nodes - and can thus be recursive. -A pair is simply a mapping of a key to a value. It takes up one node. So to implement a traditional key-value store you take a RECURSIVE node and you store PAIR nodes inside it. +For more detail on how to use each individual function, please consult the libdyntree header file. Following will be a more complex example of how to represent. -The API is very straightforward. ```C - rdb_data *root; - err = rdb_data_malloc(&root); + dtree *root; + err = dtree_malloc(&root); - rdb_data *pair1, *pair2; + dtree *pair1, *pair2; /* Add the two nodes to the recursive list */ - rdb_data_mallocrecursive(root, &pair1); - rdb_data_mallocrecursive(root, &pair2); + dtree_addrecursive(root, &pair1); + dtree_addrecursive(root, &pair2); /* This code makes 4 new nodes and assigns them as pair nodes */ - rdb_data *pair1_key, *pair1_val, *pair2_key, *pair2_val - rdb_data_mallocpair(pair1, &pair1_key, &pair1_val); - rdb_data_mallocpair(pair2, &pair2_key, &pair2_val); + dtree *pair1_key, *pair1_val, *pair2_key, *pair2_val + dtree_addpair(pair1, &pair1_key, &pair1_val); + dtree_addpair(pair2, &pair2_key, &pair2_val); ``` -At this point the structure of our rdb_data set would look somewhat like this: +At this point the structure of our dtree set would look somewhat like this: ``` - [root] + +[root] [pair1] => [ [pair1_key] => [pair1_val] ] [pair2] => [ [pair2_key] => [pair2_val] ] + ``` -### A more complex example +### An even more complicated Example From here on out you can then normally put values into the key and value items. You could even have a PAIR or RECURSIVE element as a key or value! The options are limitless. -Also...don't be afraid of reusing your pointers: you don't need to keep them. rdb_data allocates the fields and keeps a reference to each field. This means that a single call can free an entire nested structure. And you don't need to keep coming up with variable names. +Also...don't be afraid of reusing your pointers: you don't need to keep them. dtree allocates the fields and keeps a reference to each field. This means that a single call can free an entire nested structure. And you don't need to keep coming up with variable names. You just need one pointer somewhere as a buffer to work on. ```C - rdb_data *root; - err = rdb_data_malloc(&root); - rdb_data *pair, *key, *val; + dtree *root; + err = dtree_malloc(&root); + + dtree *pair, *key, *val; - rdb_data_mallocrecursive(root, &pair); - rdb_data_mallocpair(pair, &key, &val); + dtree_addrecursive(root, &pair); + dtree_addpair(pair, &key, &val); // ... Assign data to key and val @@ -102,69 +105,86 @@ You just need one pointer somewhere as a buffer to work on. pair = key = val = NULL; /* Start again */ - rdb_data_mallocrecursive(root, &pair); - rdb_data_mallocpair(pair, &key, &val); + dtree_addrecursive(root, &pair); + dtree_addpair(pair, &key, &val); // ... Assign data to key and val ``` -I hope you like this library and can do awesome stuff with rdb_data. If you find any bugs, please report them on the libreedb github repository. This project might become it's own repository at some point but until then, we shall see :) +I hope you like this library and can do awesome stuff with dtree. If you find any bugs, please report them on the libreedb github repository. This project might become it's own repository at some point but until then, we shall see :) Below there is a slightly more complicated example, including several nested types and printing the structure. ``` -#include -#include - +#include #include int main(void) { rdb_err_t err; - rdb_data *root; - err = rdb_data_malloc(&root); + dtree *root; + err = dtree_malloc(&root); printf("Malloc returned: %s\n", rdb_error_getmsg(&err)); - rdb_data *lit, *num, *pair1, *pair2, *rec2; - rdb_data_mallocrecursive(root, &lit); - rdb_data_mallocrecursive(root, &num); - rdb_data_mallocrecursive(root, &rec2); - rdb_data_mallocrecursive(root, &pair1); - rdb_data_mallocrecursive(root, &pair2); + dtree *lit, *num, *pair1, *pair2, *rec2; + dtree_addrecursive(root, &lit); + dtree_addrecursive(root, &num); + dtree_addrecursive(root, &rec2); + dtree_addrecursive(root, &pair1); + dtree_addrecursive(root, &pair2); - rdb_data_addliteral(lit, "This is a string", REAL_STRLEN("This is a string")); - rdb_data_addnumeral(num, 1337); + dtree_addliteral(lit, "This is a string", REAL_STRLEN("This is a string")); + dtree_addnumeral(num, 1337); - rdb_data *rec_d1, *rec_d2; - rdb_data_mallocrecursive(rec2, &rec_d1); - rdb_data_addliteral(rec_d1, "Reedb is awesome!", REAL_STRLEN("Reedb is awesome!")); + dtree *rec_d1, *rec_d2; + dtree_addrecursive(rec2, &rec_d1); + dtree_addliteral(rec_d1, "Reedb is awesome!", REAL_STRLEN("Reedb is awesome!")); - rdb_data_mallocrecursive(rec2, &rec_d2); - rdb_data_addnumeral(rec_d2, 666); + dtree_addrecursive(rec2, &rec_d2); + dtree_addnumeral(rec_d2, 666); - rdb_data *pair1_key, *pair1_val; - rdb_data_mallocpair(pair1, &pair1_key, &pair1_val); + dtree *pair1_key, *pair1_val; + dtree_addpair(pair1, &pair1_key, &pair1_val); - rdb_data_addliteral(pair1_key, "Username", REAL_STRLEN("Username")); - rdb_data_addliteral(pair1_val, "spacekookie", REAL_STRLEN("spacekookie")); + dtree_addliteral(pair1_key, "Username", REAL_STRLEN("Username")); + dtree_addliteral(pair1_val, "spacekookie", REAL_STRLEN("spacekookie")); - rdb_data *pair2_key, *pair2_val; - rdb_data_mallocpair(pair2, &pair2_key, &pair2_val); + dtree *pair2_key, *pair2_val; + dtree_addpair(pair2, &pair2_key, &pair2_val); - rdb_data_addliteral(pair2_key, "Website", REAL_STRLEN("Website")); - rdb_data_addliteral(pair2_val, "www.spacekookie.de", REAL_STRLEN("www.spacekookie.de")); + dtree_addliteral(pair2_key, "Website", REAL_STRLEN("Website")); + dtree_addliteral(pair2_val, "www.spacekookie.de", REAL_STRLEN("www.spacekookie.de")); /* Print our structure */ - rdb_data_print(root); + dtree_print(root); /* Free everything */ - rdb_data_free(root); + dtree_free(root); return 0; } ``` + +The above program would have the following output: + +``` +[RECURSIVE] + ['This is a string'] + [1337] + [RECURSIVE] + ['Reedb is awesome!'] + [666] + [PAIR] <==> ['Username'] => ['spacekookie'] + [PAIR] <==> ['Website'] => ['www.spacekookie.de'] + +``` + ## License -The library is licensed under LGPL-3 (the same as it's origin project [libreedb](https://github.com/reepass/libreedb)). Have fun <3s +This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + +This library is part of the **Reepass Project**, namely [libreedb](https://github.com/reepass/libreedb). It was exported from the trunk to be useful for other people. + +I hope you enjoy :heart_decoration: \ No newline at end of file -- cgit v1.2.3 From 3c9a1ad3d80e8c3e89a18d779702b91803ca0982 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Fri, 19 Aug 2016 16:11:51 +0200 Subject: Small change --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a90f6bb0d46..85eb9af95fb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ project(libdyntree) set(DYN_TREE_SRC lib/dyn_tree.c) # Define our library in cmake -add_library(libdyntree STATIC ${DYN_TREE_SRC}) +add_library(libdyntree SHARED ${DYN_TREE_SRC}) # Include the subdirectories to search for headers target_include_directories(libdyntree PUBLIC "include") -- cgit v1.2.3 From cf97068884083c7d1d1bc95fb3099b113d7e8dad Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Aug 2016 12:12:59 +0200 Subject: Adding new functions to API - Encode a dyntree object hirarchy into json - Decode a json string to a dyntree object hirarchy - Split a tree in two (no longer related) sub-trees - Merge two non-related subtrees together --- include/dtree/dyn_err.h | 29 ------------- include/dtree/dyn_tree.h | 107 ++++++++++++++++++++++++++++++++++++++++++++++- lib/dyn_tree.c | 2 +- 3 files changed, 107 insertions(+), 31 deletions(-) delete mode 100644 include/dtree/dyn_err.h diff --git a/include/dtree/dyn_err.h b/include/dtree/dyn_err.h deleted file mode 100644 index 23dae0f5e0ee..000000000000 --- a/include/dtree/dyn_err.h +++ /dev/null @@ -1,29 +0,0 @@ - -/* Make sure we're not included multiple times */ -#ifndef _DYN_ERR_H -#define _DYN_ERR_H - -/* 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 dt_err { - - /* General purpose error codes */ - FAILURE = -1, - SUCCESS = 0, - - INVALID_PARAMS, - MALLOC_FAILED, - INVALID_PAYLOAD - -} dt_err; - -const char *rdb_error_getmsg(dt_err *e); - -#ifdef __cplusplus -} -#endif -#endif /* _DYN_ERR_H */ \ No newline at end of file diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h index cfd723923940..c91f48bce06d 100644 --- a/include/dtree/dyn_tree.h +++ b/include/dtree/dyn_tree.h @@ -21,20 +21,27 @@ #ifndef _DYNTREE_H_ #define _DYNTREE_H_ -#include "dyn_err.h" #include +/* A helpful macro that can take care of \0 termated strings! */ +#define REAL_STRLEN(str) (strlen(str) + 1) + +#define DYNTREE_ENCODE_NONE 0x0 +#define DYNTREE_JSON_MINIFIED 0xA +#define DYNTREE_JSON_HUMAN 0xB /* 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 { UNSET, LITERAL, NUMERAL, RECURSIVE, PAIR, POINTER } dt_uni_t; + typedef struct dtree { dt_uni_t type; size_t size, used; @@ -44,9 +51,24 @@ typedef struct dtree { struct dtree *(*recursive); void *pointer; } payload; + short encset; } 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, + MALLOC_FAILED, + INVALID_PAYLOAD + +} dt_err; + + /** * Malloc a new dtree object * @@ -125,6 +147,38 @@ dt_err dtree_addrecursive(dtree *data, dtree *(*new_data)); */ 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_addrecursive" + * 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 recursive. 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); /** * A retrieve function to get data back from a node that doesn't require @@ -176,6 +230,57 @@ dt_err dtree_free_shallow(dtree *data); */ 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 recursive 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(d_tree *(*data), const char *json_data); + + #ifdef __cplusplus } #endif diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c index 095d379d9f45..6cb456bdf0a3 100644 --- a/lib/dyn_tree.c +++ b/lib/dyn_tree.c @@ -9,7 +9,6 @@ #define RDB_REC_DEF_SIZE 2 #define RDB_REC_MULTIPLY 2 -#define REAL_STRLEN(str) (strlen(str) + 1) dt_err dtree_malloc(dtree *(*data)) { @@ -42,6 +41,7 @@ dt_err dtree_resettype(dtree *data) /* Set the data type to unset */ data->type = UNSET; + data->encset = DYNTREE_ENCODE_NONE; data->size = 0; data->used = 0; -- cgit v1.2.3 From a3022f0ba3dc26409eac2d9811dd94375346eb53 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Aug 2016 15:23:52 +0200 Subject: Adding split and merge features --- include/dtree/dyn_tree.h | 10 +++-- lib/dyn_tree.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 4 deletions(-) diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h index c91f48bce06d..d08d26195fd9 100644 --- a/include/dtree/dyn_tree.h +++ b/include/dtree/dyn_tree.h @@ -44,6 +44,7 @@ typedef enum { typedef struct dtree { dt_uni_t type; + short encset; size_t size, used; union { char *literal; @@ -51,7 +52,6 @@ typedef struct dtree { struct dtree *(*recursive); void *pointer; } payload; - short encset; } dtree; @@ -59,12 +59,14 @@ typedef struct dtree { typedef enum dt_err { /* General purpose error codes */ - FAILURE = -1, + FAILURE = -1, SUCCESS = 0, INVALID_PARAMS, MALLOC_FAILED, - INVALID_PAYLOAD + INVALID_PAYLOAD, + DATA_NOT_RELATED, + NODE_NOT_FOUND, } dt_err; @@ -278,7 +280,7 @@ dt_err dtree_encode_json(dtree *data, char *(*json_data)); * @param json_data Input json string * @return */ -dt_err dtree_decode_json(d_tree *(*data), const char *json_data); +dt_err dtree_decode_json(dtree *(*data), const char *json_data); #ifdef __cplusplus diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c index 6cb456bdf0a3..d72d1d5c676a 100644 --- a/lib/dyn_tree.c +++ b/lib/dyn_tree.c @@ -10,6 +10,13 @@ #define RDB_REC_DEF_SIZE 2 #define RDB_REC_MULTIPLY 2 +/***Forward declared functions ***/ + +int recursive_search(dtree**, dtree *, dtree *); + +/******/ + + dt_err dtree_malloc(dtree *(*data)) { (*data) = (dtree*) malloc(sizeof(dtree)); @@ -102,6 +109,7 @@ dt_err dtree_addnumeral(dtree *data, int numeral) return SUCCESS; } + dt_err dtree_addrecursive(dtree *data, dtree *(*new_data)) { /* Make sure we are a literal or unset data object */ @@ -186,6 +194,73 @@ dt_err dtree_addpair(dtree *data, dtree *(*key), dtree *(*value)) 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 = recursive_search(&dp, data, sp); + if(ret != 0) return DATA_NOT_RELATED; + if(dp == NULL) return NODE_NOT_FOUND; + + /* Find the exact recursive reference and remove it */ + int i; + for(i = 0; i < dp->used; i++) { + if(dp->payload.recursive[i] == NULL) continue; + + /* Manually remove the entry */ + if(dp->payload.recursive[i] == sp) { + dp->used--; + dp->payload.recursive[i] = NULL; + } + } + + return SUCCESS; +} + + +dt_err dtree_merge_trees(dtree *data, dtree *merge) +{ + /* REALLY make sure the type is correct */ + if(data->type == UNSET || + !(data->type == RECURSIVE || 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.recursive, sizeof(dtree*) * data->used); + + /* Free the list WITHOUT the children! */ + free(data->payload.recursive); + data->payload.recursive = tmp; + } + + /* This means the data object is new */ + } else { + dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); + data->payload.recursive = tmp; + data->type = RECURSIVE; + data->used = 0; + data->size = RDB_REC_DEF_SIZE; + } + + /* Reference the slot, assign it, then move our ctr */ + data->payload.recursive[data->used] = merge; + data->used++; + + return SUCCESS; +} + + void recursive_print(dtree *data, const char *offset) { dt_uni_t type = data->type; @@ -260,6 +335,7 @@ void recursive_print(dtree *data, const char *offset) } } + void dtree_print(dtree *data) { recursive_print(data, ""); @@ -275,6 +351,7 @@ dt_err dtree_get(dtree *data, void *(*val)) return SUCCESS; } + dt_err dtree_free(dtree *data) { if(data == NULL) return SUCCESS; @@ -300,6 +377,7 @@ dt_err dtree_free(dtree *data) return SUCCESS; } + dt_err dtree_free_shallow(dtree *data) { if(data == NULL) return SUCCESS; @@ -321,6 +399,7 @@ dt_err dtree_free_shallow(dtree *data) return SUCCESS; } + const char *dtree_dtype(dtree *data) { switch(data->type) { @@ -335,6 +414,39 @@ const char *dtree_dtype(dtree *data) /**************** PRIVATE UTILITY FUNCTIONS ******************/ +/** + * Steps down the recursive hirarchy of a dyntree node to + * find a sub-child target. Returns 0 if it can be found. + * + * @param data + * @param target + * @return + */ +int recursive_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 == RECURSIVE || data->type == PAIR) { + int i; + for(i = 0; i < data->used; i++) { + res = recursive_search(direct_parent, data->payload.recursive[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. -- cgit v1.2.3 From 9d3395a98ff79596444796ba694be0ed1c8a8248 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Aug 2016 16:56:43 +0200 Subject: Fixing an issue that could cause a SIGABRT --- lib/dyn_tree.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c index d72d1d5c676a..2cf100d3e119 100644 --- a/lib/dyn_tree.c +++ b/lib/dyn_tree.c @@ -225,8 +225,8 @@ dt_err dtree_split_trees(dtree *data, dtree *sp) dt_err dtree_merge_trees(dtree *data, dtree *merge) { /* REALLY make sure the type is correct */ - if(data->type == UNSET || - !(data->type == RECURSIVE || data->type == PAIR)) + if(data->type == UNSET) return INVALID_PARAMS; + if(!(data->type == RECURSIVE || data->type == PAIR)) return INVALID_PAYLOAD; /* This means elements already exist */ @@ -362,7 +362,7 @@ dt_err dtree_free(dtree *data) } else if(data->type == RECURSIVE || data->type == PAIR) { int i; dt_err err; - for(i = 0; i < data->size; i++) { + for(i = 0; i < data->used; i++) { err = dtree_free(data->payload.recursive[i]); if(err) return err; } -- cgit v1.2.3 From 98a9a31789203e051404b9bcf2faa29bafd9e4bf Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Aug 2016 16:57:32 +0200 Subject: Adding recursive search function --- include/dtree/dyn_tree.h | 18 ++++++++++++++++-- lib/dyn_tree.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h index d08d26195fd9..64b463b47654 100644 --- a/include/dtree/dyn_tree.h +++ b/include/dtree/dyn_tree.h @@ -37,7 +37,7 @@ extern "C" { /* Type that determines what data is stored inside a tree-node */ -typedef enum { +typedef enum dt_uni_t { UNSET, LITERAL, NUMERAL, RECURSIVE, PAIR, POINTER } dt_uni_t; @@ -48,7 +48,7 @@ typedef struct dtree { size_t size, used; union { char *literal; - int numeral; + long numeral; struct dtree *(*recursive); void *pointer; } payload; @@ -182,6 +182,20 @@ dt_err dtree_split_trees(dtree *data, dtree *sp); */ dt_err dtree_merge_trees(dtree *data, dtree *merge); + +/** + * 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); + /** * A retrieve function to get data back from a node that doesn't require * you to manually access parts of the struct. diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c index 2cf100d3e119..503584965f93 100644 --- a/lib/dyn_tree.c +++ b/lib/dyn_tree.c @@ -261,6 +261,51 @@ dt_err dtree_merge_trees(dtree *data, dtree *merge) } +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 == RECURSIVE|| data->type == PAIR) { + + int i; + for(i = 0; i < data->used; i++) { + dt_err err = dtree_search_payload(data->payload.recursive[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 NUMERAL: + if(data->payload.numeral == (long) 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; +} + + void recursive_print(dtree *data, const char *offset) { dt_uni_t type = data->type; -- cgit v1.2.3 From d1f9b84e6926e1f0f1259ec79091873fd221cf01 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Aug 2016 16:59:22 +0200 Subject: Adding test file with different test suites (as well as examples). This should hopefully make it easier to understand the API :) --- CMakeLists.txt | 10 ++++- lib/dyn_utils.c | 6 +++ test/main.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 lib/dyn_utils.c create mode 100644 test/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 85eb9af95fb1..5f6d416a515f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_BUILD_TYPE Debug) # Create our project for further reference project(libdyntree) -set(DYN_TREE_SRC lib/dyn_tree.c) +set(DYN_TREE_SRC lib/dyn_tree.c lib/dyn_utils.c) # Define our library in cmake add_library(libdyntree SHARED ${DYN_TREE_SRC}) @@ -23,3 +23,11 @@ target_include_directories(libdyntree PRIVATE "lib") # since the name starts with 'lib' dont add it again set_target_properties(libdyntree PROPERTIES PREFIX "") + +################### 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 diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c new file mode 100644 index 000000000000..81e6d1db2ed4 --- /dev/null +++ b/lib/dyn_utils.c @@ -0,0 +1,6 @@ +#include + +const char *rdb_error_getmsg(dt_err *e) +{ + +} \ No newline at end of file diff --git a/test/main.c b/test/main.c new file mode 100644 index 000000000000..51e626a656f7 --- /dev/null +++ b/test/main.c @@ -0,0 +1,113 @@ + +#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(); + +#define TEST(function) \ + printf("Running '%s'...", #function); \ + fflush(stdout); \ + err = function(); \ + printf(" %s\n", (err == 0) ? "OK!" : "FAILED!"); \ + if(err) goto end; + +int main(void) +{ + dt_err err; + printf("=== libdyntree test suite ===\n"); + + /* Search inside trees */ + TEST(search_for_payload) + + /* Split and merge trees */ + TEST(split_and_merge) + + 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_addrecursive(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, REAL_STRLEN(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_addrecursive(root, &a); + if(err) goto exit; + + err = dtree_addliteral(a, string, REAL_STRLEN(string)); + if(err) goto exit; + + err = dtree_addrecursive(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, NUMERAL); + if(err) goto exit; + + exit: + dtree_free(root); + return err; +} \ No newline at end of file -- cgit v1.2.3 From 40a3881a694a9e33104dba706e9ec15eba5e67d7 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Aug 2016 17:01:57 +0200 Subject: Adjusting gitignore to exclude some IDE files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index de789df8581b..739405323f07 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ *.dSYM/ *.su build/ + +.idea/ -- cgit v1.2.3 From 1fdedc5f41a36b774a7a691edb5c8722f2529f82 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Mon, 22 Aug 2016 11:15:16 +0200 Subject: First release candidate for version 1.0 - Adding preliminary json encode functionality - Adding new executable with test functions Code needs further testing but should be functional. --- include/dtree/dyn_tree.h | 2 +- lib/dyn_tree.c | 2 +- lib/dyn_utils.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++- test/main.c | 91 ++++++++++++++++- 4 files changed, 335 insertions(+), 7 deletions(-) diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h index 64b463b47654..4155af43ecb2 100644 --- a/include/dtree/dyn_tree.h +++ b/include/dtree/dyn_tree.h @@ -282,7 +282,7 @@ dt_err dtree_encode_set(dtree *data, short setting); * @param json_data * @return */ -dt_err dtree_encode_json(dtree *data, char *(*json_data)); +dt_err dtree_encode_json(dtree *data, char *json_data); /** diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c index 503584965f93..c01d9f6a2593 100644 --- a/lib/dyn_tree.c +++ b/lib/dyn_tree.c @@ -10,7 +10,7 @@ #define RDB_REC_DEF_SIZE 2 #define RDB_REC_MULTIPLY 2 -/***Forward declared functions ***/ +/*** Forward declared functions ***/ int recursive_search(dtree**, dtree *, dtree *); diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index 81e6d1db2ed4..8e24c3ff091b 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -1,6 +1,251 @@ #include +#include +#include + + +/*** Forward declared functions ***/ + +#define TRUE 1 +#define FALSE 0 + +static int json_len = 0; + +int human(short mm) { + if(mm == DYNTREE_JSON_HUMAN) return 1; + return 0; +} + +int parse_key_value(dtree *key, char *buffer, short mode) +{ + if(key->type != LITERAL) 5; + + size_t key_len = key->used; + int base = 3; + + /* Make an array that will survive function switch */ + char lit[key_len + base + 1]; + + strcpy(buffer, "\""); + strcat(buffer, key->payload.literal); + strcat(buffer, "\":"); + strcat(buffer, "\0"); + return 0; +} + +const char *parse_value_list(dtree *value, char *buffer, char *global, short mode, int depth, int last) +{ + if(value == NULL) return "[ERROR]"; + + /* The new offset we need (in \t) */ + + int no_len = depth + 1; + char new_offset[no_len]; + int i; + for(i = 0; i < depth + 1; i++) + strcat(new_offset, "\t"); + + int base; +// if(human(mode)) base = 4 + no_len; // "" + base = 2; + if(!last) base++; + + switch(value->type) { + case LITERAL: + { + size_t key_len = value->used; + + strcpy(buffer, "\""); + strcat(buffer, value->payload.literal); + strcat(buffer, "\""); + strcat(buffer, "\0"); + + if(last == 0) strcat(buffer, ","); + break; + } + + case NUMERAL: + { + char str[15]; + sprintf(str, "%ld", value->payload.numeral); + int val_len = (int) strlen((char*) str); + + strcat(buffer, str); + if(last == 0) strcat(buffer, ","); + strcat(buffer, "\0"); + + break; + } + + case RECURSIVE: + { + if(value->used > 0) { + + dt_uni_t test = value->payload.recursive[0]->type; + + if(test == LITERAL || test == NUMERAL) { + fflush(stdout); + + int j; + for(j = 0; j < value->used; j++) { + dtree *child = value->payload.recursive[j]; + + char vall[1024]; + parse_value_list(child, vall, global, mode, depth + 1, (i == value->used - 1) ? TRUE : FALSE); + fflush(stdout); + } + + fflush(stdout); + + } else if(test == PAIR) { + fflush(stdout); +// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); + append(global, "{"); + + int j; + for(j = 0; j < value->used; j++) { + dtree *child = value->payload.recursive[j]; + + if(child->type == PAIR) { + dtree *key = child->payload.recursive[0]; + dtree *val = child->payload.recursive[1]; + + char kkey[1024]; + + parse_key_value(key, kkey, mode); + fflush(stdout); +// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(kkey)); + append(global, kkey); + + char vval[1024]; + parse_value_list(val, vval, global, mode, 1, (j == child->used - 1) ? TRUE : FALSE); + fflush(stdout); + +// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(vval)); + append(global, vval); + } + } + + fflush(stdout); +// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); + append(global, "}"); + } + + } else { + fflush(stdout); + } + } + + default: INVALID_PAYLOAD; + } + + return ""; +} + +void append(char *buffer, char *message) +{ + int msg_len = (int) strlen(message); + sprintf(buffer + json_len, message); + json_len += msg_len; +} + +/******/ + const char *rdb_error_getmsg(dt_err *e) { -} \ No newline at end of file +} + +dt_err dtree_encode_set(dtree *data, short setting) +{ + if(data == NULL) return INVALID_PARAMS; + + /* Check if setting is valid */ + switch(setting) { + case DYNTREE_ENCODE_NONE: + case DYNTREE_JSON_MINIFIED: + case DYNTREE_JSON_HUMAN: + break; + + default: return INVALID_PARAMS; + } + + data->encset = setting; + return SUCCESS; +} + + +dt_err dtree_encode_json(dtree *data, char *json_data) +{ + if(data == NULL) return INVALID_PARAMS; + + json_len = 0; + + /* Check if setting is valid */ + switch(data->encset) { + case DYNTREE_JSON_MINIFIED: + case DYNTREE_JSON_HUMAN: + break; + + default: return INVALID_PARAMS; + } + + /* Assume mode for all children */ + short mode = data->encset; + + char *open = "{"; + char *close = "}"; + + if(data->type == RECURSIVE) { + + fflush(stdout); + append(json_data, open); + + /* Iterate through all it's children */ + int i; + for(i = 0; i < data->used; i++) { + dtree *child = data->payload.recursive[i]; + + if(child->type == PAIR) { + dtree *key = child->payload.recursive[0]; + dtree *val = child->payload.recursive[1]; + + char kkey[1024]; + parse_key_value(key, kkey, mode);; + fflush(stdout); + append(json_data, kkey); + memset(kkey, 0, 1024); + + char vval[1024]; + parse_value_list(val, vval, json_data, mode, 1, (i == data->used - 1) ? TRUE : FALSE); + fflush(stdout); + + append(json_data, vval); + memset(vval, 0, 1024); + + } else if(child->type == RECURSIVE) { + dt_err err = dtree_encode_json(child, json_data); + if(err) return err; + } + } + + } else { + return INVALID_PAYLOAD; + } + fflush(stdout); + + /* Add closing } and null terminator to finish */ + append(json_data, close); + append(json_data, "\0"); + + return SUCCESS; +} + + +dt_err dtree_decode_json(dtree *(*data), const char *json_data) +{ + +} + + +/**************** PRIVATE UTILITY FUNCTIONS ******************/ \ No newline at end of file diff --git a/test/main.c b/test/main.c index 51e626a656f7..f508247696b4 100644 --- a/test/main.c +++ b/test/main.c @@ -10,10 +10,12 @@ dt_err split_and_merge(); dt_err search_for_payload(); +dt_err json_encode(char *json); + #define TEST(function) \ printf("Running '%s'...", #function); \ fflush(stdout); \ - err = function(); \ + err = function; \ printf(" %s\n", (err == 0) ? "OK!" : "FAILED!"); \ if(err) goto end; @@ -23,12 +25,20 @@ int main(void) printf("=== libdyntree test suite ===\n"); /* Search inside trees */ - TEST(search_for_payload) + TEST(search_for_payload()) /* Split and merge trees */ - TEST(split_and_merge) + TEST(split_and_merge()) + + /* Try to encode a structure into json */ + char json[512]; // Provide a buffer that is big enough + TEST(json_encode(json)) + + printf("Json string: %s\n", json); + end: + exit: printf("==== done ====\n"); return err; } @@ -110,4 +120,77 @@ dt_err search_for_payload() exit: dtree_free(root); return err; -} \ No newline at end of file +} + +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_addrecursive(root, &a); + if(err) goto exit; + err = dtree_addrecursive(root, &b); + if(err) goto exit; + err = dtree_addrecursive(root, &c); + if(err) goto exit; + + err = dtree_addpair(a, &key, &val); + if(err) goto exit; + err = dtree_addliteral(key, "Server Address", REAL_STRLEN("Server Address")); + if(err) goto exit; + err = dtree_addliteral(val, "https://github.com", REAL_STRLEN("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", REAL_STRLEN("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", REAL_STRLEN("Users")); + if(err) goto exit; + + dtree *sbrec, *sbrec2; + err = dtree_addrecursive(val, &sbrec); + if(err) goto exit; + err = dtree_addrecursive(val, &sbrec2); + if(err) goto exit; + + dtree *subkey, *subval; + err = dtree_addpair(sbrec, &subkey, &subval); + if(err) goto exit; + err = dtree_addliteral(subkey, "spacekookie", REAL_STRLEN("spacekookie")); + if(err) goto exit; + err = dtree_addliteral(subval, "Admin", REAL_STRLEN("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", REAL_STRLEN("jane")); + if(err) goto exit; + err = dtree_addliteral(subval2, "normal", REAL_STRLEN("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; +} \ No newline at end of file -- cgit v1.2.3 From d57c4094e24554389f46bdf34b29c2d12380ad24 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Aug 2016 00:28:06 +0200 Subject: Adding some helper functions --- include/dtree/dyn_tree.h | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h index 4155af43ecb2..42aa37884b28 100644 --- a/include/dtree/dyn_tree.h +++ b/include/dtree/dyn_tree.h @@ -297,6 +297,85 @@ dt_err dtree_encode_json(dtree *data, char *json_data); dt_err dtree_decode_json(dtree *(*data), const char *json_data); + +/*** Some helper functions for more non-C oriented developers ***/ + +/** + * Shortcut function that allocates a new string node. Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param string + * @return + */ +static dtree *dtree_alloc_literal(const char *string) +{ + dtree *node; + dtree_malloc(&node); + dtree_addliteral(node, string, REAL_STRLEN(string)); + return node; +} + +/** + * Shortcut function that allocates a new numerical node. + * Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param num + * @return + */ +static dtree *dtree_alloc_numeral(const long num) +{ + dtree *node; + dtree_malloc(&node); + dtree_addnumeral(node, num); + return node; +} + +/** + * Shortcut function which creates two nodes as pair under a root + * node which is returned. Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + @param key Will be the key node + * @param val Will be the value node + * @return New root node with key-val children + */ +static dtree *dtree_allocpair_new(dtree **key, dtree **val) +{ + dtree *root; + dtree_malloc(&root); + dtree_addpair(root, key, val); + return root; +} + + +/** + * Shortcut function which allocates a list of nodes in a list under + * a root node recursively. + * + * WARNING: Return value is allocated on heap. MUST FREE MANUALLY! + * WARNING: THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param root + * @param count + * @return + */ +static dtree **dtree_alloc_reclist(dtree **root, unsigned int count) +{ + dtree **nodes = malloc(sizeof(dtree**) * count); + + dtree_malloc(root); + + int i; + for(i = 0; i < count; i++) + dtree_addrecursive(*root, &nodes[i]); + + return nodes; +} + #ifdef __cplusplus } #endif -- cgit v1.2.3 From 823f5b3d495cb0930a26fb0461da54cad78d46f4 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Aug 2016 00:28:34 +0200 Subject: Changing formatting --- lib/dyn_tree.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/dyn_tree.c b/lib/dyn_tree.c index c01d9f6a2593..49cfd780e8d4 100644 --- a/lib/dyn_tree.c +++ b/lib/dyn_tree.c @@ -318,55 +318,56 @@ void recursive_print(dtree *data, const char *offset) printf("%s['%s']\n", offset, data->payload.literal); break; case NUMERAL: - printf("%s[%d]\n", offset, data->payload.numeral); + printf("%s[%lu]\n", offset, data->payload.numeral); break; case PAIR: - { - dt_uni_t k_type = data->payload.recursive[0]->type; - dt_uni_t v_type = data->payload.recursive[1]->type; + { + dt_uni_t k_type = data->payload.recursive[0]->type; + dt_uni_t v_type = data->payload.recursive[1]->type; - if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.recursive[0]->payload.literal); - if(k_type == NUMERAL) printf("%s[%d]", offset, data->payload.recursive[0]->payload.numeral); + if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.recursive[0]->payload.literal); + if(k_type == NUMERAL) printf("%s[%lu]", offset, data->payload.recursive[0]->payload.numeral); - char new_offset[REAL_STRLEN(offset) + 2]; - strcpy(new_offset, offset); - strcat(new_offset, " "); + char new_offset[REAL_STRLEN(offset) + 2]; + strcpy(new_offset, offset); + strcat(new_offset, " "); - if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); + if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); - /* Print the value now */ + /* Print the value now */ - if(k_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); - if(k_type == NUMERAL) printf(" => [%d]\n", data->payload.recursive[1]->payload.numeral); + if(k_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); + if(k_type == NUMERAL) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); + + if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); - if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); - } break; + } case RECURSIVE: { int i; printf("%s[RECURSIVE]\n", offset); for(i = 0; i < data->used; i++) { - dt_uni_t type = data->payload.recursive[i]->type; + dt_uni_t t = data->payload.recursive[i]->type; char new_offset[REAL_STRLEN(offset) + 2]; strcpy(new_offset, offset); strcat(new_offset, " "); - if(type == LITERAL || type == NUMERAL) { + if(t == LITERAL || t == NUMERAL) { recursive_print(data->payload.recursive[i], new_offset); continue; } - if(type == RECURSIVE) + if(t == RECURSIVE) { recursive_print(data->payload.recursive[i], new_offset); continue; } - if(type == PAIR) { + if(t == PAIR) { printf("%s[PAIR] <==> ", new_offset); recursive_print(data->payload.recursive[i], new_offset); } -- cgit v1.2.3 From befcaae9d9606e585429932306d8ac7b76f6fbb8 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Aug 2016 00:30:22 +0200 Subject: Adding preliminary json parser functionality --- lib/dyn_utils.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 1 deletion(-) diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index 8e24c3ff091b..9e27ed0b2402 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -148,6 +148,13 @@ void append(char *buffer, char *message) json_len += msg_len; } +void append_char(char *buffer, int *ctr, char c) +{ + sprintf(buffer + (*ctr), "%c", c); + (*ctr)++; +} + + /******/ @@ -242,9 +249,177 @@ dt_err dtree_encode_json(dtree *data, char *json_data) } -dt_err dtree_decode_json(dtree *(*data), const char *json_data) +dt_err dtree_decode_json(dtree *(*data), const char *jd) { + /* Always create an empty root node */ + + int ctr = -1; + dtree *parents[32]; // Only support 32 deep for now + memset(parents, 0, sizeof(dtree*) * 32); + + +#define IN_KEY 5 +#define IN_VAL 6 +#define IN_HASH 7 +#define IN_LIST 8 +#define IN_STRING 9 +#define NEUTRAL 10 + + /** Parse stack */ + int in_str = 0; + char curr_key[512]; int key_inx = 0; + char curr_str[512]; int str_inx = 0; + + memset(curr_key, 0, 512); + memset(curr_str, 0, 512); + + int curr_num = 0; + int mode = 0; + + /* Get the first character of our json string */ + int jd_len = (int) REAL_STRLEN(jd); + int pos = 0; + char curr; + + for (; pos < jd_len && jd[pos] != '\0'; pos++) { + curr = jd[pos]; + + switch(curr) { + case '{': + { + dtree *new_root; + dtree_malloc(&new_root); + + if(ctr < 0) { + parents[0] = new_root; + ctr = 0; + } else { + dtree_addrecursive(parents[ctr], &new_root); + parents[++ctr] = new_root; + } + + if(in_str) break; // Ignore if we're in a string + + // Open a block +// if(VERBOSE) printf("Opening block for %s\n", curr_key); + break; + } + case '[': + { + if(in_str) break; // Ignore if we're in a string + + dtree *new_root; + dtree_addrecursive(parents[ctr], &new_root); + parents[++ctr] = new_root; + + // Open a block +// if(VERBOSE) printf("Opening block for '%s'\n", (key_inx > 0) ? curr_key : "ROOT"); + break; + } + + case '}': + case ']': + { + if(in_str) { +// if(VERBOSE) printf("Ending string %s\n", curr_str); + } else { + if(curr_key[0] != NULL) { + + dtree *key, *val; + dtree *rec_entry; + + dtree_addrecursive(parents[ctr], &rec_entry); + dtree_addpair(rec_entry, &key, &val); + dtree_addliteral(key, curr_key, REAL_STRLEN(curr_key)); + dtree_addliteral(val, curr_str, REAL_STRLEN(curr_str)); + + /* Clear the pointer reference */ + rec_entry = key = val = NULL; + + memset(curr_key, 0, (size_t) key_inx); + memset(curr_str, 0, (size_t) str_inx); + key_inx = 0; + str_inx = 0; + } + + if(ctr > 0) parents[ctr--] = NULL; // Remove current parent again + } + + + // Close a block +// if(VERBOSE) printf("Closing block for %c\n", curr); + break; + } + + case '"': + { + in_str = (in_str) ? FALSE : TRUE; + mode = (in_str) ? IN_STRING : NEUTRAL; + // Open/ Close a string (depending on mode) +// if(VERBOSE && !in_str) printf("String start\n"); +// if(VERBOSE && in_str) printf("Ending string: %s\n", curr_str); + break; + } + + case ',': + { + mode = NEUTRAL; +// printf("Ending entry '%s':'%s'\n", curr_key, curr_str); + dtree *key, *val; + dtree *rec_entry; + + dtree_addrecursive(parents[ctr], &rec_entry); + dtree_addpair(rec_entry, &key, &val); + dtree_addliteral(key, curr_key, REAL_STRLEN(curr_key)); + dtree_addliteral(val, curr_str, REAL_STRLEN(curr_str)); + + /* Clear the pointer reference */ + rec_entry = key = val = NULL; + + memset(curr_key, 0, (size_t) key_inx); + memset(curr_str, 0, (size_t) str_inx); + key_inx = 0; + str_inx = 0; + break; + } + + case ':': + { + if(in_str) break; // Ignore if we're in a string + + // End a key + // if(VERBOSE) printf("Ending key: %s\n", curr_str); + strcpy(curr_key, curr_str); + memset(curr_str, 0, (size_t) str_inx); + key_inx = str_inx; + str_inx = 0; + break; + } + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + append_char(curr_str, &str_inx, curr); + break; + } + + default: + { + if(in_str) { +// if(VERBOSE) printf("Saving letter: %c\n", curr); + append_char(curr_str, &str_inx, curr); + } + break; + } + } + } + + /* Allocate our first node */ + *data = parents[0]; + dtree_print(*data); + + return SUCCESS; } -- cgit v1.2.3 From 8eebd04a0a9ae58160878d72b5ef62ec5ed55605 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Aug 2016 00:30:36 +0200 Subject: Adjusting test functions to test json_decode --- test/main.c | 98 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/test/main.c b/test/main.c index f508247696b4..6090e9404dbb 100644 --- a/test/main.c +++ b/test/main.c @@ -12,6 +12,8 @@ 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); \ @@ -30,12 +32,17 @@ int main(void) /* Split and merge trees */ TEST(split_and_merge()) + /* Test shortcut functions */ + TEST(test_shortcut_functions()) + /* Try to encode a structure into json */ char json[512]; // Provide a buffer that is big enough TEST(json_encode(json)) - printf("Json string: %s\n", json); + printf("\n\nJson string: %s\n", json); + dtree *data; + dtree_decode_json(&data, json); end: exit: @@ -122,75 +129,98 @@ dt_err search_for_payload() return err; } -dt_err json_encode(char *json) -{ +dt_err json_encode(char *json) { dt_err err; dtree *root, *a, *b, *c, *found; err = dtree_malloc(&root); - if(err) goto exit; - + if (err) goto exit; + dtree *key, *val; err = dtree_addrecursive(root, &a); - if(err) goto exit; + if (err) goto exit; err = dtree_addrecursive(root, &b); - if(err) goto exit; + if (err) goto exit; err = dtree_addrecursive(root, &c); - if(err) goto exit; - + if (err) goto exit; + err = dtree_addpair(a, &key, &val); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(key, "Server Address", REAL_STRLEN("Server Address")); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(val, "https://github.com", REAL_STRLEN("https://github.com")); - if(err) goto exit; - + if (err) goto exit; + key = val = NULL; err = dtree_addpair(b, &key, &val); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(key, "Server Port", REAL_STRLEN("Server Port")); - if(err) goto exit; + if (err) goto exit; err = dtree_addnumeral(val, 8080); - if(err) goto exit; - + if (err) goto exit; + key = val = NULL; err = dtree_addpair(c, &key, &val); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(key, "Users", REAL_STRLEN("Users")); - if(err) goto exit; - + if (err) goto exit; + dtree *sbrec, *sbrec2; err = dtree_addrecursive(val, &sbrec); - if(err) goto exit; + if (err) goto exit; err = dtree_addrecursive(val, &sbrec2); - if(err) goto exit; - + if (err) goto exit; + dtree *subkey, *subval; err = dtree_addpair(sbrec, &subkey, &subval); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(subkey, "spacekookie", REAL_STRLEN("spacekookie")); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(subval, "Admin", REAL_STRLEN("Admin")); - if(err) goto exit; - + if (err) goto exit; + key = val = NULL; dtree *subkey2, *subval2; err = dtree_addpair(sbrec2, &subkey2, &subval2); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(subkey2, "jane", REAL_STRLEN("jane")); - if(err) goto exit; + if (err) goto exit; err = dtree_addliteral(subval2, "normal", REAL_STRLEN("normal")); - if(err) goto exit; - + if (err) goto exit; + err = dtree_encode_set(root, DYNTREE_JSON_MINIFIED); - if(err) goto exit; + if (err) goto exit; err = dtree_encode_json(root, json); - if(err) goto exit; + if (err) goto exit; exit: dtree_free(root); return err; -} \ No newline at end of file +} + +dt_err test_shortcut_functions() +{ + dtree *key, *val; + dtree *root = dtree_allocpair_new(&key, &val); + + dtree_addliteral(key, "Address", REAL_STRLEN("Address")); +// dtree_addliteral(val, "Berlin 10555", REAL_STRLEN("Berlin 10555")); + + dtree *list_root; + dtree **list = dtree_alloc_reclist(&list_root, 4); // Allocate 4 nodes + + int i; + for(i = 0; i < 4; i++){ + char message[32]; + sprintf(message, "Message no.%d", i); + dtree_addliteral(list[i], message, REAL_STRLEN(message)); + } + + dtree_merge_trees(val, list_root); + + dtree_free(root); + return SUCCESS; // We think +} \ No newline at end of file -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 From 9a934b2b39062f60c310e7cfa350c56210434474 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Aug 2016 11:31:53 +0200 Subject: Updating README --- LICENSE | 827 +++++++++++++++++++++++++++++++++++++++++++++++++++------------- README | 194 +++------------ 2 files changed, 703 insertions(+), 318 deletions(-) diff --git a/LICENSE b/LICENSE index 65c5ca88a67c..94a9ed024d38 100644 --- a/LICENSE +++ b/LICENSE @@ -1,165 +1,674 @@ - GNU LESSER GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README b/README index b21dbd114ce2..45c958732852 100644 --- a/README +++ b/README @@ -1,10 +1,17 @@ -# libdyntree :herb: +libdtree +======== -A dynamic n-ary tree to store recursive structures, key-value stores and single fields in a fast and comprehensive C data structure. +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. -## How to build +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 C11 but should be able to run on C99 or older. +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. ```console $> mkdir build; cd build @@ -12,25 +19,34 @@ $> cmake .. $> make -j 2 ``` -This will create a `.a` file. If you require a shared object, you can change the linking behaviour in the `CMakeLists.txt` file. - +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 -Using libdyntree is straighforward with a comprehensive API. Everything resolves around `dtree` objects and providing fields to API functions. Every function is documented as outlined in the header files. - -Generally, memory is managed for you by libdyntree. So allocating a new node is as easy as passing a reference pointer to a malloc function: +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; -dtree_malloc(&data); +err = dtree_malloc(&data); ``` -The above code will initialise an empty dtree node that can then be written into. While it's possible to edit the values in the struct yourself it is not recomended. +Alternatively you can use the shortcut alloc functions provided: -Instead you should use the utility functions provided to you by libdyntree that will make sure that you don't accidentally leak any memory or try to write something invalid into your structures. +```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: -**Possible Nodes** - Unset - Literal - Numerical @@ -38,153 +54,13 @@ Instead you should use the utility functions provided to you by libdyntree that - Pair - Pointer -```C - - dtree_addliteral(data, "My String", REAL_STRLEN("My String")); - - dtree_addnumeral(data, 42); - - dtree_addpair(data, &key, &value); - - dtree_addrecursive(data, &sub_d); - - dtree_addpointer(data, my_ptr); -``` - -For more detail on how to use each individual function, please consult the libdyntree header file. Following will be a more complex example of how to represent. - - -```C - - dtree *root; - err = dtree_malloc(&root); - - dtree *pair1, *pair2; - - /* Add the two nodes to the recursive list */ - dtree_addrecursive(root, &pair1); - dtree_addrecursive(root, &pair2); - - /* This code makes 4 new nodes and assigns them as pair nodes */ - dtree *pair1_key, *pair1_val, *pair2_key, *pair2_val - dtree_addpair(pair1, &pair1_key, &pair1_val); - dtree_addpair(pair2, &pair2_key, &pair2_val); -``` - -At this point the structure of our dtree set would look somewhat like this: - -``` - -[root] - [pair1] => [ [pair1_key] => [pair1_val] ] - [pair2] => [ [pair2_key] => [pair2_val] ] - -``` - -### An even more complicated Example - -From here on out you can then normally put values into the key and value items. You could even have a PAIR or RECURSIVE element as a key or value! The options are limitless. - -Also...don't be afraid of reusing your pointers: you don't need to keep them. dtree allocates the fields and keeps a reference to each field. This means that a single call can free an entire nested structure. And you don't need to keep coming up with variable names. - -You just need one pointer somewhere as a buffer to work on. - -```C - - dtree *root; - err = dtree_malloc(&root); - - dtree *pair, *key, *val; - - dtree_addrecursive(root, &pair); - dtree_addpair(pair, &key, &val); - - // ... Assign data to key and val - - /* Remove the pointers */ - pair = key = val = NULL; - - /* Start again */ - dtree_addrecursive(root, &pair); - dtree_addpair(pair, &key, &val); - - // ... Assign data to key and val -``` - -I hope you like this library and can do awesome stuff with dtree. If you find any bugs, please report them on the libreedb github repository. This project might become it's own repository at some point but until then, we shall see :) - -Below there is a slightly more complicated example, including several nested types and printing the structure. - - -``` -#include -#include - -int main(void) -{ - rdb_err_t err; - dtree *root; - err = dtree_malloc(&root); - printf("Malloc returned: %s\n", rdb_error_getmsg(&err)); - - dtree *lit, *num, *pair1, *pair2, *rec2; - dtree_addrecursive(root, &lit); - dtree_addrecursive(root, &num); - dtree_addrecursive(root, &rec2); - dtree_addrecursive(root, &pair1); - dtree_addrecursive(root, &pair2); - - dtree_addliteral(lit, "This is a string", REAL_STRLEN("This is a string")); - dtree_addnumeral(num, 1337); - - dtree *rec_d1, *rec_d2; - dtree_addrecursive(rec2, &rec_d1); - dtree_addliteral(rec_d1, "Reedb is awesome!", REAL_STRLEN("Reedb is awesome!")); - - dtree_addrecursive(rec2, &rec_d2); - dtree_addnumeral(rec_d2, 666); - - dtree *pair1_key, *pair1_val; - dtree_addpair(pair1, &pair1_key, &pair1_val); - - dtree_addliteral(pair1_key, "Username", REAL_STRLEN("Username")); - dtree_addliteral(pair1_val, "spacekookie", REAL_STRLEN("spacekookie")); - - dtree *pair2_key, *pair2_val; - dtree_addpair(pair2, &pair2_key, &pair2_val); - - dtree_addliteral(pair2_key, "Website", REAL_STRLEN("Website")); - dtree_addliteral(pair2_val, "www.spacekookie.de", REAL_STRLEN("www.spacekookie.de")); - - - /* Print our structure */ - dtree_print(root); - - /* Free everything */ - dtree_free(root); - - return 0; -} -``` - -The above program would have the following output: - -``` -[RECURSIVE] - ['This is a string'] - [1337] - [RECURSIVE] - ['Reedb is awesome!'] - [666] - [PAIR] <==> ['Username'] => ['spacekookie'] - [PAIR] <==> ['Website'] => ['www.spacekookie.de'] - -``` +You can find more usage examples in the wiki ## License -This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - -This library is part of the **Reepass Project**, namely [libreedb](https://github.com/reepass/libreedb). It was exported from the trunk to be useful for other people. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at +your option) any later version. -I hope you enjoy :heart_decoration: \ No newline at end of file +I hope you enjoy ♥ -- cgit v1.2.3 From b3acb65150078e93a64263324dbf432e7b7d2442 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Aug 2016 11:45:25 +0200 Subject: Update README.md --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 45c958732852..c6967bfd42a6 100644 --- a/README +++ b/README @@ -54,7 +54,7 @@ below: - Pair - Pointer -You can find more usage examples in the wiki +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 -- cgit v1.2.3 From caad27814fab468f66de0a3138e3900342d3acde Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 27 Aug 2016 01:07:35 +0200 Subject: API Changes. - Renaming the recursive field to "list" functions - Renaming main file to dtree for less confusion - Allowing for long values to be stored - not just integers - Adjusting quick access functions to use new API --- include/dtree/dtree.h | 414 +++++++++++++++++++++++++++++++++ include/dtree/dyn_tree.h | 382 ------------------------------- lib/dtree.c | 581 +++++++++++++++++++++++++++++++++++++++++++++++ lib/dyn_tree.c | 520 ------------------------------------------ 4 files changed, 995 insertions(+), 902 deletions(-) create mode 100644 include/dtree/dtree.h delete mode 100644 include/dtree/dyn_tree.h create mode 100644 lib/dtree.c delete mode 100644 lib/dyn_tree.c diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h new file mode 100644 index 000000000000..b46b000c3ece --- /dev/null +++ b/include/dtree/dtree.h @@ -0,0 +1,414 @@ +/* + * 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 [recursive] + * child1 [recursive] + * key [literal] - "Username" + * value [literal] - "spacekookie" + * child2 [recursive] + * key [literal] - "Age" + * value [numerical] - 23 + * child3 + * subchild [recursive] + * ... + * + * Freeing the root node will free all children + */ + +#ifndef _DYNTREE_H_ +#define _DYNTREE_H_ + +#include + +/* A helpful macro that can take care of \0 termated strings! */ +#define REAL_STRLEN(str) (strlen(str) + 1) + +#define DYNTREE_ENCODE_NONE 0x0 +#define DYNTREE_JSON_MINIFIED 0xA +#define DYNTREE_JSON_HUMAN 0xB + +/* 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, NUMERAL, RECURSIVE, PAIR, POINTER +} dt_uni_t; + + +typedef struct dtree { + dt_uni_t type; + short encset; + size_t size, used; + short copy; + union { + char *literal; + long numeral; + struct dtree *(*recursive); + void *pointer; + } 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, + MALLOC_FAILED, + INVALID_PAYLOAD, + DATA_NOT_RELATED, + NODE_NOT_FOUND, + +} 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 + * @param length TRUE string length to use. + * @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); + + +/** + * 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_addrecursive" + * 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 recursive. 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); + + +/** + * 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); + + +/** + * 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_deep_copy(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 or leave it as a void* to let + * libdyntree do the casting for you. + * + * @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 + * + * @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 recursive 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); + + + +/*** Some helper functions for more non-C oriented developers ***/ + +/** + * Shortcut function that allocates a new string node. Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param string + * @return + */ +static dtree *dtree_alloc_literal(const char *string) +{ + dtree *node; + dtree_malloc(&node); + dtree_addliteral(node, string); + return node; +} + +/** + * Shortcut function that allocates a new numerical node. + * Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param num + * @return + */ +static dtree *dtree_alloc_numeral(const long num) +{ + dtree *node; + dtree_malloc(&node); + dtree_addnumeral(node, num); + return node; +} + +/** + * Shortcut function which creates two nodes as pair under a root + * node which is returned. Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + @param key Will be the key node + * @param val Will be the value node + * @return New root node with key-val children + */ +static dtree *dtree_allocpair_new(dtree **key, dtree **val) +{ + dtree *root; + dtree_malloc(&root); + dtree_addpair(root, key, val); + return root; +} + + +/** + * Shortcut function which allocates a list of nodes in a list under + * a root node recursively. + * + * WARNING: Return value is allocated on heap. MUST FREE MANUALLY! + * WARNING: THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param root + * @param count + * @return + */ +static dtree **dtree_alloc_listlist(dtree **root, unsigned int count) +{ + dtree **nodes = malloc(sizeof(dtree**) * count); + + dtree_malloc(root); + + int i; + for(i = 0; i < count; i++) + dtree_addlist(*root, &nodes[i]); + + return nodes; +} + +#ifdef __cplusplus +} +#endif +#endif //_DYNTREE_H_ diff --git a/include/dtree/dyn_tree.h b/include/dtree/dyn_tree.h deleted file mode 100644 index 42aa37884b28..000000000000 --- a/include/dtree/dyn_tree.h +++ /dev/null @@ -1,382 +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 [recursive] - * child1 [recursive] - * key [literal] - "Username" - * value [literal] - "spacekookie" - * child2 [recursive] - * key [literal] - "Age" - * value [numerical] - 23 - * child3 - * subchild [recursive] - * ... - * - * Freeing the root node will free all children - */ - -#ifndef _DYNTREE_H_ -#define _DYNTREE_H_ - -#include - -/* A helpful macro that can take care of \0 termated strings! */ -#define REAL_STRLEN(str) (strlen(str) + 1) - -#define DYNTREE_ENCODE_NONE 0x0 -#define DYNTREE_JSON_MINIFIED 0xA -#define DYNTREE_JSON_HUMAN 0xB - -/* 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, NUMERAL, RECURSIVE, PAIR, POINTER -} dt_uni_t; - - -typedef struct dtree { - dt_uni_t type; - short encset; - size_t size, used; - union { - char *literal; - long numeral; - struct dtree *(*recursive); - void *pointer; - } 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, - MALLOC_FAILED, - INVALID_PAYLOAD, - DATA_NOT_RELATED, - NODE_NOT_FOUND, - -} 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 - * @param length TRUE string length to use. - * @return - */ -dt_err dtree_addliteral(dtree *data, const char *literal, size_t length); - - -/** - * 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, int numeral); - - -/** - * 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_addrecursive(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_addrecursive" - * 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 recursive. 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); - - -/** - * 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); - -/** - * 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 or leave it as a void* to let - * libdyntree do the casting for you. - * - * @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 - * - * @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 recursive 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); - - - -/*** Some helper functions for more non-C oriented developers ***/ - -/** - * Shortcut function that allocates a new string node. Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - * @param string - * @return - */ -static dtree *dtree_alloc_literal(const char *string) -{ - dtree *node; - dtree_malloc(&node); - dtree_addliteral(node, string, REAL_STRLEN(string)); - return node; -} - -/** - * Shortcut function that allocates a new numerical node. - * Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - * @param num - * @return - */ -static dtree *dtree_alloc_numeral(const long num) -{ - dtree *node; - dtree_malloc(&node); - dtree_addnumeral(node, num); - return node; -} - -/** - * Shortcut function which creates two nodes as pair under a root - * node which is returned. Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - @param key Will be the key node - * @param val Will be the value node - * @return New root node with key-val children - */ -static dtree *dtree_allocpair_new(dtree **key, dtree **val) -{ - dtree *root; - dtree_malloc(&root); - dtree_addpair(root, key, val); - return root; -} - - -/** - * Shortcut function which allocates a list of nodes in a list under - * a root node recursively. - * - * WARNING: Return value is allocated on heap. MUST FREE MANUALLY! - * WARNING: THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - * @param root - * @param count - * @return - */ -static dtree **dtree_alloc_reclist(dtree **root, unsigned int count) -{ - dtree **nodes = malloc(sizeof(dtree**) * count); - - dtree_malloc(root); - - int i; - for(i = 0; i < count; i++) - dtree_addrecursive(*root, &nodes[i]); - - return nodes; -} - -#ifdef __cplusplus -} -#endif -#endif //_DYNTREE_H_ diff --git a/lib/dtree.c b/lib/dtree.c new file mode 100644 index 000000000000..746ca13d52f4 --- /dev/null +++ b/lib/dtree.c @@ -0,0 +1,581 @@ +// 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 recursive_search(dtree**, dtree *, dtree *); + +/******/ + + +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 == RECURSIVE || 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.recursive[i]); + if(err) return err; + } + } + + /* Set the data type to unset */ + data->type = UNSET; + data->encset = DYNTREE_ENCODE_NONE; + data->size = 0; + data->used = 0; + + 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 != NUMERAL) return INVALID_PAYLOAD; + + data->payload.numeral = numeral; + data->type = NUMERAL; + data->size = sizeof(int); + data->used = sizeof(int); + 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 != RECURSIVE) 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.recursive, sizeof(dtree*) * data->used); + + /* Free the list WITHOUT the children! */ + free(data->payload.recursive); + data->payload.recursive = tmp; + } + + /* This means the data object is new */ + } else { + dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); + data->payload.recursive = tmp; + data->type = RECURSIVE; + 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.recursive[data->used] = *new_data; + data->used++; + + 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.recursive = tmp; + + { /* Assign data to new array */ + data->payload.recursive[data->used] = *key; + data->used++; + data->payload.recursive[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 = recursive_search(&dp, data, sp); + if(ret != 0) return DATA_NOT_RELATED; + if(dp == NULL) return NODE_NOT_FOUND; + + /* Find the exact recursive reference and remove it */ + int i; + for(i = 0; i < dp->used; i++) { + if(dp->payload.recursive[i] == NULL) continue; + + /* Manually remove the entry */ + if(dp->payload.recursive[i] == sp) { + dp->used--; + dp->payload.recursive[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 == RECURSIVE || 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.recursive, sizeof(dtree*) * data->used); + + /* Free the list WITHOUT the children! */ + free(data->payload.recursive); + data->payload.recursive = tmp; + } + + /* This means the data object is new */ + } else { + dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); + data->payload.recursive = tmp; + data->type = RECURSIVE; + data->used = 0; + data->size = RDB_REC_DEF_SIZE; + } + + /* Reference the slot, assign it, then move our ctr */ + data->payload.recursive[data->used] = merge; + data->used++; + + return SUCCESS; +} + + +dt_err dtree_deep_copy(dtree *data, dtree *(*copy)) +{ + if(data == NULL) return INVALID_PARAMS; + + return SUCCESS; +} + +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 NUMERAL: + err = dtree_addnumeral(*copy, data->payload.numeral); + break; + + case RECURSIVE: + (*copy)->type = RECURSIVE; + (*copy)->payload.recursive = (dtree**) malloc(sizeof(dtree*) * data->size); + memcpy((*copy)->payload.recursive, data->payload.recursive, sizeof(dtree*) * data->used); + break; + + case PAIR: + (*copy)->type = PAIR; + (*copy)->payload.recursive = (dtree**) malloc(sizeof(dtree*) * data->size); + memcpy((*copy)->payload.recursive, data->payload.recursive, 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 == RECURSIVE|| data->type == PAIR) { + + int i; + for(i = 0; i < data->used; i++) { + dt_err err = dtree_search_payload(data->payload.recursive[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 NUMERAL: + if(data->payload.numeral == (long) 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; +} + + +void recursive_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 NUMERAL: + printf("%s[%lu]\n", offset, data->payload.numeral); + break; + case PAIR: + { + dt_uni_t k_type = data->payload.recursive[0]->type; + dt_uni_t v_type = data->payload.recursive[1]->type; + + if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.recursive[0]->payload.literal); + if(k_type == NUMERAL) printf("%s[%lu]", offset, data->payload.recursive[0]->payload.numeral); + + char new_offset[REAL_STRLEN(offset) + 2]; + strcpy(new_offset, offset); + strcat(new_offset, " "); + + if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); + + /* Print the value now */ + + if(k_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); + if(k_type == NUMERAL) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); + + if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); + + break; + } + + case RECURSIVE: + { + int i; + printf("%s[RECURSIVE]\n", offset); + for(i = 0; i < data->used; i++) { + dt_uni_t t = data->payload.recursive[i]->type; + + + char new_offset[REAL_STRLEN(offset) + 2]; + strcpy(new_offset, offset); + strcat(new_offset, " "); + + if(t == LITERAL || t == NUMERAL) { + recursive_print(data->payload.recursive[i], new_offset); + continue; + } + + if(t == RECURSIVE) + { + recursive_print(data->payload.recursive[i], new_offset); + continue; + } + + if(t == PAIR) { + printf("%s[PAIR] <==> ", new_offset); + recursive_print(data->payload.recursive[i], new_offset); + } + } + break; + } + + default: + break; + + } +} + + +void dtree_print(dtree *data) +{ + recursive_print(data, ""); +} + +dt_err dtree_get(dtree *data, void *(*val)) +{ + if(data->type == LITERAL) *val = (char*) data->payload.literal; + if(data->type == NUMERAL) *val = (int*) &data->payload.numeral; + if(data->type == RECURSIVE || data->type == PAIR) + *val = (dtree*) data->payload.recursive; + + return SUCCESS; +} + + +dt_err dtree_free(dtree *data) +{ + if(data == NULL) return SUCCESS; + + if(data->type == LITERAL) { + if(data->payload.literal) free(data->payload.literal); + + } else if(data->type == RECURSIVE || data->type == PAIR) { + int i; + dt_err err; + for(i = 0; i < data->used; i++) { + err = dtree_free(data->payload.recursive[i]); + if(err) return err; + } + + free(data->payload.recursive); + + } else if(data->type == POINTER) { + if(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 == RECURSIVE || data->type == PAIR) { + int i; + dt_err err; + for(i = 0; i < data->size; i++) { + err = dtree_free(data->payload.recursive[i]); + if(err) return err; + } + + free(data->payload.recursive); + } + + free(data); + return SUCCESS; +} + + +const char *dtree_dtype(dtree *data) +{ + switch(data->type) { + case LITERAL: return "Literal"; + case NUMERAL: return "Numeral"; + case RECURSIVE: return "Recursive"; + case PAIR: return "Pair"; + case POINTER: return "Pointer"; + default: return "Unknown"; + } +} + +/**************** PRIVATE UTILITY FUNCTIONS ******************/ + +/** + * Steps down the recursive hirarchy of a dyntree node to + * find a sub-child target. Returns 0 if it can be found. + * + * @param data + * @param target + * @return + */ +int recursive_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 == RECURSIVE || data->type == PAIR) { + int i; + for(i = 0; i < data->used; i++) { + res = recursive_search(direct_parent, data->payload.recursive[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 == RECURSIVE) + { + 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/dyn_tree.c b/lib/dyn_tree.c deleted file mode 100644 index 49cfd780e8d4..000000000000 --- a/lib/dyn_tree.c +++ /dev/null @@ -1,520 +0,0 @@ -// Include our header file -#include - -// Runtime includes -#include -#include -#include -#include - -#define RDB_REC_DEF_SIZE 2 -#define RDB_REC_MULTIPLY 2 - -/*** Forward declared functions ***/ - -int recursive_search(dtree**, dtree *, dtree *); - -/******/ - - -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 == RECURSIVE || 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.recursive[i]); - if(err) return err; - } - } - - /* Set the data type to unset */ - data->type = UNSET; - data->encset = DYNTREE_ENCODE_NONE; - data->size = 0; - data->used = 0; - - return SUCCESS; -} - -dt_err dtree_addliteral(dtree *data, const char *literal, size_t length) -{ - /* Make sure we are a literal or unset data object */ - if(data->type != UNSET) - if(data->type != LITERAL) return INVALID_PAYLOAD; - - /* 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, int numeral) -{ - /* Make sure we are a literal or unset data object */ - if(data->type != UNSET) - if(data->type != NUMERAL) return INVALID_PAYLOAD; - - data->payload.numeral = numeral; - data->type = NUMERAL; - data->size = sizeof(int); - data->used = sizeof(int); - return SUCCESS; -} - - -dt_err dtree_addrecursive(dtree *data, dtree *(*new_data)) -{ - /* Make sure we are a literal or unset data object */ - if(data->type != UNSET) - if(data->type != RECURSIVE) 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.recursive, sizeof(dtree*) * data->used); - - /* Free the list WITHOUT the children! */ - free(data->payload.recursive); - data->payload.recursive = tmp; - } - - /* This means the data object is new */ - } else { - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); - data->payload.recursive = tmp; - data->type = RECURSIVE; - 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.recursive[data->used] = *new_data; - data->used++; - - 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.recursive = tmp; - - { /* Assign data to new array */ - data->payload.recursive[data->used] = *key; - data->used++; - data->payload.recursive[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 = recursive_search(&dp, data, sp); - if(ret != 0) return DATA_NOT_RELATED; - if(dp == NULL) return NODE_NOT_FOUND; - - /* Find the exact recursive reference and remove it */ - int i; - for(i = 0; i < dp->used; i++) { - if(dp->payload.recursive[i] == NULL) continue; - - /* Manually remove the entry */ - if(dp->payload.recursive[i] == sp) { - dp->used--; - dp->payload.recursive[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 == RECURSIVE || 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.recursive, sizeof(dtree*) * data->used); - - /* Free the list WITHOUT the children! */ - free(data->payload.recursive); - data->payload.recursive = tmp; - } - - /* This means the data object is new */ - } else { - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); - data->payload.recursive = tmp; - data->type = RECURSIVE; - data->used = 0; - data->size = RDB_REC_DEF_SIZE; - } - - /* Reference the slot, assign it, then move our ctr */ - data->payload.recursive[data->used] = merge; - data->used++; - - return SUCCESS; -} - - -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 == RECURSIVE|| data->type == PAIR) { - - int i; - for(i = 0; i < data->used; i++) { - dt_err err = dtree_search_payload(data->payload.recursive[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 NUMERAL: - if(data->payload.numeral == (long) 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; -} - - -void recursive_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 NUMERAL: - printf("%s[%lu]\n", offset, data->payload.numeral); - break; - case PAIR: - { - dt_uni_t k_type = data->payload.recursive[0]->type; - dt_uni_t v_type = data->payload.recursive[1]->type; - - if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.recursive[0]->payload.literal); - if(k_type == NUMERAL) printf("%s[%lu]", offset, data->payload.recursive[0]->payload.numeral); - - char new_offset[REAL_STRLEN(offset) + 2]; - strcpy(new_offset, offset); - strcat(new_offset, " "); - - if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); - - /* Print the value now */ - - if(k_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); - if(k_type == NUMERAL) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); - - if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); - - break; - } - - case RECURSIVE: - { - int i; - printf("%s[RECURSIVE]\n", offset); - for(i = 0; i < data->used; i++) { - dt_uni_t t = data->payload.recursive[i]->type; - - - char new_offset[REAL_STRLEN(offset) + 2]; - strcpy(new_offset, offset); - strcat(new_offset, " "); - - if(t == LITERAL || t == NUMERAL) { - recursive_print(data->payload.recursive[i], new_offset); - continue; - } - - if(t == RECURSIVE) - { - recursive_print(data->payload.recursive[i], new_offset); - continue; - } - - if(t == PAIR) { - printf("%s[PAIR] <==> ", new_offset); - recursive_print(data->payload.recursive[i], new_offset); - } - } - break; - } - - default: - break; - - } -} - - -void dtree_print(dtree *data) -{ - recursive_print(data, ""); -} - -dt_err dtree_get(dtree *data, void *(*val)) -{ - if(data->type == LITERAL) *val = (char*) data->payload.literal; - if(data->type == NUMERAL) *val = (int*) &data->payload.numeral; - if(data->type == RECURSIVE || data->type == PAIR) - *val = (dtree*) data->payload.recursive; - - return SUCCESS; -} - - -dt_err dtree_free(dtree *data) -{ - if(data == NULL) return SUCCESS; - - if(data->type == LITERAL) { - if(data->payload.literal) free(data->payload.literal); - - } else if(data->type == RECURSIVE || data->type == PAIR) { - int i; - dt_err err; - for(i = 0; i < data->used; i++) { - err = dtree_free(data->payload.recursive[i]); - if(err) return err; - } - - free(data->payload.recursive); - - } else if(data->type == POINTER) { - if(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 == RECURSIVE || data->type == PAIR) { - int i; - dt_err err; - for(i = 0; i < data->size; i++) { - err = dtree_free(data->payload.recursive[i]); - if(err) return err; - } - - free(data->payload.recursive); - } - - free(data); - return SUCCESS; -} - - -const char *dtree_dtype(dtree *data) -{ - switch(data->type) { - case LITERAL: return "Literal"; - case NUMERAL: return "Numeral"; - case RECURSIVE: return "Recursive"; - case PAIR: return "Pair"; - case POINTER: return "Pointer"; - default: return "Unknown"; - } -} - -/**************** PRIVATE UTILITY FUNCTIONS ******************/ - -/** - * Steps down the recursive hirarchy of a dyntree node to - * find a sub-child target. Returns 0 if it can be found. - * - * @param data - * @param target - * @return - */ -int recursive_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 == RECURSIVE || data->type == PAIR) { - int i; - for(i = 0; i < data->used; i++) { - res = recursive_search(direct_parent, data->payload.recursive[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 == RECURSIVE) - { - 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; -} -- cgit v1.2.3 From ab7dbf81bc03b0e01074a7fd1e25bcd18292907d Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 27 Aug 2016 01:08:08 +0200 Subject: Updating API part 2 --- CMakeLists.txt | 2 +- lib/dyn_utils.c | 18 +++++++++--------- test/main.c | 58 +++++++++++++++++++++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f6d416a515f..e738040e2011 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_BUILD_TYPE Debug) # Create our project for further reference project(libdyntree) -set(DYN_TREE_SRC lib/dyn_tree.c lib/dyn_utils.c) +set(DYN_TREE_SRC lib/dtree.c lib/dyn_utils.c) # Define our library in cmake add_library(libdyntree SHARED ${DYN_TREE_SRC}) diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index 9e27ed0b2402..ae00bf997e54 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -294,7 +294,7 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) parents[0] = new_root; ctr = 0; } else { - dtree_addrecursive(parents[ctr], &new_root); + dtree_addlist(parents[ctr], &new_root); parents[++ctr] = new_root; } @@ -309,7 +309,7 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) if(in_str) break; // Ignore if we're in a string dtree *new_root; - dtree_addrecursive(parents[ctr], &new_root); + dtree_addlist(parents[ctr], &new_root); parents[++ctr] = new_root; // Open a block @@ -328,10 +328,10 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) dtree *key, *val; dtree *rec_entry; - dtree_addrecursive(parents[ctr], &rec_entry); + dtree_addlist(parents[ctr], &rec_entry); dtree_addpair(rec_entry, &key, &val); - dtree_addliteral(key, curr_key, REAL_STRLEN(curr_key)); - dtree_addliteral(val, curr_str, REAL_STRLEN(curr_str)); + dtree_addliteral(key, curr_key); + dtree_addliteral(val, curr_str); /* Clear the pointer reference */ rec_entry = key = val = NULL; @@ -369,10 +369,10 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) dtree *key, *val; dtree *rec_entry; - dtree_addrecursive(parents[ctr], &rec_entry); + dtree_addlist(parents[ctr], &rec_entry); dtree_addpair(rec_entry, &key, &val); - dtree_addliteral(key, curr_key, REAL_STRLEN(curr_key)); - dtree_addliteral(val, curr_str, REAL_STRLEN(curr_str)); + dtree_addliteral(key, curr_key); + dtree_addliteral(val, curr_str); /* Clear the pointer reference */ rec_entry = key = val = NULL; diff --git a/test/main.c b/test/main.c index 6090e9404dbb..240de6c9ccab 100644 --- a/test/main.c +++ b/test/main.c @@ -1,6 +1,6 @@ #include -#include +#include /** * A small test that creates a tree, splits the nodes @@ -44,6 +44,20 @@ int main(void) dtree *data; dtree_decode_json(&data, json); + /* Copy structures */ + + dtree *key, *val; + dtree *root = dtree_allocpair_new(&key, &val); + + dtree_addliteral(key, "Core Data"); + dtree_addliteral(val, "dtree is cool!"); + + dtree *root_copy; + dtree_copy(root, &root_copy); + + printf("Copy complete?\n"); + + end: exit: printf("==== done ====\n"); @@ -64,7 +78,7 @@ dt_err split_and_merge() /* Add child as a recursive node to root */ dtree *child; - err = dtree_addrecursive(root, &child); + err = dtree_addlist(root, &child); if(err) goto exit; /* Make child a literal node containing the works of shakespeare */ @@ -78,7 +92,7 @@ dt_err split_and_merge() "That flesh is heir to, 'tis a consummation\n" "Devoutly to be wish'd. To die, to sleep;"; - err = dtree_addliteral(child, hamlet, REAL_STRLEN(hamlet)); + err = dtree_addliteral(child, hamlet); if(err) goto exit; /* Split our tree into two single-nodes */ @@ -104,13 +118,13 @@ dt_err search_for_payload() if(err) goto exit; const char *string = "This is some data!"; - err = dtree_addrecursive(root, &a); + err = dtree_addlist(root, &a); if(err) goto exit; - err = dtree_addliteral(a, string, REAL_STRLEN(string)); + err = dtree_addliteral(a, string); if(err) goto exit; - err = dtree_addrecursive(root, &b); + err = dtree_addlist(root, &b); if(err) goto exit; err = dtree_addnumeral(b, 1337); @@ -137,25 +151,25 @@ dt_err json_encode(char *json) { if (err) goto exit; dtree *key, *val; - err = dtree_addrecursive(root, &a); + err = dtree_addlist(root, &a); if (err) goto exit; - err = dtree_addrecursive(root, &b); + err = dtree_addlist(root, &b); if (err) goto exit; - err = dtree_addrecursive(root, &c); + 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", REAL_STRLEN("Server Address")); + err = dtree_addliteral(key, "Server Address"); if (err) goto exit; - err = dtree_addliteral(val, "https://github.com", REAL_STRLEN("https://github.com")); + 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", REAL_STRLEN("Server Port")); + err = dtree_addliteral(key, "Server Port"); if (err) goto exit; err = dtree_addnumeral(val, 8080); if (err) goto exit; @@ -164,21 +178,21 @@ dt_err json_encode(char *json) { err = dtree_addpair(c, &key, &val); if (err) goto exit; - err = dtree_addliteral(key, "Users", REAL_STRLEN("Users")); + err = dtree_addliteral(key, "Users"); if (err) goto exit; dtree *sbrec, *sbrec2; - err = dtree_addrecursive(val, &sbrec); + err = dtree_addlist(val, &sbrec); if (err) goto exit; - err = dtree_addrecursive(val, &sbrec2); + 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", REAL_STRLEN("spacekookie")); + err = dtree_addliteral(subkey, "spacekookie"); if (err) goto exit; - err = dtree_addliteral(subval, "Admin", REAL_STRLEN("Admin")); + err = dtree_addliteral(subval, "Admin"); if (err) goto exit; key = val = NULL; @@ -186,9 +200,9 @@ dt_err json_encode(char *json) { dtree *subkey2, *subval2; err = dtree_addpair(sbrec2, &subkey2, &subval2); if (err) goto exit; - err = dtree_addliteral(subkey2, "jane", REAL_STRLEN("jane")); + err = dtree_addliteral(subkey2, "jane"); if (err) goto exit; - err = dtree_addliteral(subval2, "normal", REAL_STRLEN("normal")); + err = dtree_addliteral(subval2, "normal"); if (err) goto exit; err = dtree_encode_set(root, DYNTREE_JSON_MINIFIED); @@ -206,17 +220,17 @@ dt_err test_shortcut_functions() dtree *key, *val; dtree *root = dtree_allocpair_new(&key, &val); - dtree_addliteral(key, "Address", REAL_STRLEN("Address")); + dtree_addliteral(key, "Address"); // dtree_addliteral(val, "Berlin 10555", REAL_STRLEN("Berlin 10555")); dtree *list_root; - dtree **list = dtree_alloc_reclist(&list_root, 4); // Allocate 4 nodes + dtree **list = dtree_alloc_listlist(&list_root, 4); // Allocate 4 nodes int i; for(i = 0; i < 4; i++){ char message[32]; sprintf(message, "Message no.%d", i); - dtree_addliteral(list[i], message, REAL_STRLEN(message)); + dtree_addliteral(list[i], message); } dtree_merge_trees(val, list_root); -- cgit v1.2.3 From 582bb41d6f01906887ccff239d80c5c54563e4e1 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 27 Aug 2016 16:46:09 +0200 Subject: More API changes deep_copy is now copy_deep --- include/dtree/dtree.h | 2 +- lib/dtree.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index b46b000c3ece..2a62ee7a9621 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -208,7 +208,7 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ * @param copy * @return */ -dt_err dtree_deep_copy(dtree *data, dtree *(*copy)); +dt_err dtree_copy_deep(dtree *data, dtree *(*copy)); /** diff --git a/lib/dtree.c b/lib/dtree.c index 746ca13d52f4..1cf8beec2f88 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -267,7 +267,7 @@ dt_err dtree_merge_trees(dtree *data, dtree *merge) } -dt_err dtree_deep_copy(dtree *data, dtree *(*copy)) +dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) { if(data == NULL) return INVALID_PARAMS; -- cgit v1.2.3 From ff7eacd5d62c0591dd293622e45cb4a6683cec1e Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 27 Aug 2016 17:38:32 +0200 Subject: Cleaning up the json decoder. Fixing an error in the print function which would sometimes yield incorrect results --- lib/dtree.c | 38 ++++--- lib/dyn_utils.c | 348 +++++++++++++++++++++++++++++--------------------------- 2 files changed, 201 insertions(+), 185 deletions(-) diff --git a/lib/dtree.c b/lib/dtree.c index 1cf8beec2f88..97ca14cb489e 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -396,11 +396,10 @@ void recursive_print(dtree *data, const char *offset) if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); /* Print the value now */ + if(v_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); + if(v_type== NUMERAL) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); - if(k_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); - if(k_type == NUMERAL) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); - - if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); + if(v_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); break; } @@ -408,29 +407,32 @@ void recursive_print(dtree *data, const char *offset) case RECURSIVE: { int i; - printf("%s[RECURSIVE]\n", offset); + printf("%s[LIST]\n", offset); for(i = 0; i < data->used; i++) { dt_uni_t t = data->payload.recursive[i]->type; - + /* Calculate the new offset */ char new_offset[REAL_STRLEN(offset) + 2]; strcpy(new_offset, offset); strcat(new_offset, " "); - if(t == LITERAL || t == NUMERAL) { - recursive_print(data->payload.recursive[i], new_offset); - continue; - } + switch(t) { + case LITERAL: + case NUMERAL: + recursive_print(data->payload.recursive[i], new_offset); + continue; - if(t == RECURSIVE) - { - recursive_print(data->payload.recursive[i], new_offset); - continue; - } + case RECURSIVE: + recursive_print(data->payload.recursive[i], new_offset); + continue; + + case PAIR: + printf("%s[PAIR] <==> ", new_offset); + recursive_print(data->payload.recursive[i], new_offset); + continue; - if(t == PAIR) { - printf("%s[PAIR] <==> ", new_offset); - recursive_print(data->payload.recursive[i], new_offset); + default: + break; } } break; diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index ae00bf997e54..84f34465a2b4 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -10,153 +10,24 @@ static int json_len = 0; -int human(short mm) { - if(mm == DYNTREE_JSON_HUMAN) return 1; - return 0; -} - -int parse_key_value(dtree *key, char *buffer, short mode) -{ - if(key->type != LITERAL) 5; - - size_t key_len = key->used; - int base = 3; - - /* Make an array that will survive function switch */ - char lit[key_len + base + 1]; - - strcpy(buffer, "\""); - strcat(buffer, key->payload.literal); - strcat(buffer, "\":"); - strcat(buffer, "\0"); - return 0; -} - -const char *parse_value_list(dtree *value, char *buffer, char *global, short mode, int depth, int last) -{ - if(value == NULL) return "[ERROR]"; - - /* The new offset we need (in \t) */ - - int no_len = depth + 1; - char new_offset[no_len]; - int i; - for(i = 0; i < depth + 1; i++) - strcat(new_offset, "\t"); - - int base; -// if(human(mode)) base = 4 + no_len; // "" - base = 2; - if(!last) base++; - - switch(value->type) { - case LITERAL: - { - size_t key_len = value->used; - - strcpy(buffer, "\""); - strcat(buffer, value->payload.literal); - strcat(buffer, "\""); - strcat(buffer, "\0"); - - if(last == 0) strcat(buffer, ","); - break; - } - - case NUMERAL: - { - char str[15]; - sprintf(str, "%ld", value->payload.numeral); - int val_len = (int) strlen((char*) str); - - strcat(buffer, str); - if(last == 0) strcat(buffer, ","); - strcat(buffer, "\0"); - - break; - } - - case RECURSIVE: - { - if(value->used > 0) { - - dt_uni_t test = value->payload.recursive[0]->type; - - if(test == LITERAL || test == NUMERAL) { - fflush(stdout); - - int j; - for(j = 0; j < value->used; j++) { - dtree *child = value->payload.recursive[j]; - - char vall[1024]; - parse_value_list(child, vall, global, mode, depth + 1, (i == value->used - 1) ? TRUE : FALSE); - fflush(stdout); - } - - fflush(stdout); - - } else if(test == PAIR) { - fflush(stdout); -// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); - append(global, "{"); - - int j; - for(j = 0; j < value->used; j++) { - dtree *child = value->payload.recursive[j]; - - if(child->type == PAIR) { - dtree *key = child->payload.recursive[0]; - dtree *val = child->payload.recursive[1]; - - char kkey[1024]; - - parse_key_value(key, kkey, mode); - fflush(stdout); -// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(kkey)); - append(global, kkey); - - char vval[1024]; - parse_value_list(val, vval, global, mode, 1, (j == child->used - 1) ? TRUE : FALSE); - fflush(stdout); - -// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(vval)); - append(global, vval); - } - } +/************* Forward Function Declarations *************/ - fflush(stdout); -// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); - append(global, "}"); - } +// Functions required by encoder +int human(short ); - } else { - fflush(stdout); - } - } +void append(char *, char *); - default: INVALID_PAYLOAD; - } +int parse_key_value(dtree *, char *, short ); - return ""; -} +const char *parse_value_list(dtree *, char *, char *, short , int , int ); -void append(char *buffer, char *message) -{ - int msg_len = (int) strlen(message); - sprintf(buffer + json_len, message); - json_len += msg_len; -} - -void append_char(char *buffer, int *ctr, char c) -{ - sprintf(buffer + (*ctr), "%c", c); - (*ctr)++; -} +// Functions required by decoder +void append_char(char *, int *, char ); -/******/ +long to_long(char *); +/*********************************************************/ const char *rdb_error_getmsg(dt_err *e) { @@ -269,11 +140,11 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) int in_str = 0; char curr_key[512]; int key_inx = 0; char curr_str[512]; int str_inx = 0; + char curr_num[512]; int num_inx = 0; memset(curr_key, 0, 512); memset(curr_str, 0, 512); - - int curr_num = 0; + memset(curr_num, 0, 512); int mode = 0; /* Get the first character of our json string */ @@ -298,10 +169,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) parents[++ctr] = new_root; } - if(in_str) break; // Ignore if we're in a string - - // Open a block -// if(VERBOSE) printf("Opening block for %s\n", curr_key); break; } case '[': @@ -311,9 +178,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) dtree *new_root; dtree_addlist(parents[ctr], &new_root); parents[++ctr] = new_root; - - // Open a block -// if(VERBOSE) printf("Opening block for '%s'\n", (key_inx > 0) ? curr_key : "ROOT"); break; } @@ -321,7 +185,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) case ']': { if(in_str) { -// if(VERBOSE) printf("Ending string %s\n", curr_str); } else { if(curr_key[0] != NULL) { @@ -344,43 +207,43 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) if(ctr > 0) parents[ctr--] = NULL; // Remove current parent again } - - - // Close a block -// if(VERBOSE) printf("Closing block for %c\n", curr); break; } case '"': { in_str = (in_str) ? FALSE : TRUE; - mode = (in_str) ? IN_STRING : NEUTRAL; - - // Open/ Close a string (depending on mode) -// if(VERBOSE && !in_str) printf("String start\n"); -// if(VERBOSE && in_str) printf("Ending string: %s\n", curr_str); + mode = (in_str) ? IN_STRING : NEUTRAL; // TODO: Work with the mode ? break; } case ',': { mode = NEUTRAL; -// printf("Ending entry '%s':'%s'\n", curr_key, curr_str); dtree *key, *val; dtree *rec_entry; + /* Add a new pair as a list item */ dtree_addlist(parents[ctr], &rec_entry); dtree_addpair(rec_entry, &key, &val); dtree_addliteral(key, curr_key); - dtree_addliteral(val, curr_str); + + /* Either make it a literal or number node */ + if(num_inx > 0) + dtree_addnumeral(val, to_long(curr_num)); + else + dtree_addliteral(val, curr_str); /* Clear the pointer reference */ rec_entry = key = val = NULL; + /* Reset the key/ value status */ memset(curr_key, 0, (size_t) key_inx); memset(curr_str, 0, (size_t) str_inx); + memset(curr_num, 0, (size_t) num_inx); key_inx = 0; str_inx = 0; + num_inx = 0; break; } @@ -389,7 +252,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) if(in_str) break; // Ignore if we're in a string // End a key - // if(VERBOSE) printf("Ending key: %s\n", curr_str); strcpy(curr_key, curr_str); memset(curr_str, 0, (size_t) str_inx); key_inx = str_inx; @@ -400,16 +262,17 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { - append_char(curr_str, &str_inx, curr); + if(in_str) { + append_char(curr_str, &str_inx, curr); + } else { + append_char(curr_num, &num_inx, curr); + } break; } default: { - if(in_str) { -// if(VERBOSE) printf("Saving letter: %c\n", curr); - append_char(curr_str, &str_inx, curr); - } + if(in_str) append_char(curr_str, &str_inx, curr); break; } } @@ -423,4 +286,155 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) } -/**************** PRIVATE UTILITY FUNCTIONS ******************/ \ No newline at end of file +/**************** ENCODER UTILITY FUNCTIONS ******************/ + +int human(short mode) { + if(mode == DYNTREE_JSON_HUMAN) return 1; + return 0; +} + +int parse_key_value(dtree *key, char *buffer, short mode) +{ + if(key->type != LITERAL) 5; + + size_t key_len = key->used; + int base = 3; + + /* Make an array that will survive function switch */ + char lit[key_len + base + 1]; + + strcpy(buffer, "\""); + strcat(buffer, key->payload.literal); + strcat(buffer, "\":"); + strcat(buffer, "\0"); + return 0; +} + +const char *parse_value_list(dtree *value, char *buffer, char *global, short mode, int depth, int last) +{ + if(value == NULL) return "[ERROR]"; + + /* The new offset we need (in \t) */ + + int no_len = depth + 1; + char new_offset[no_len]; + int i; + for(i = 0; i < depth + 1; i++) + strcat(new_offset, "\t"); + + int base; +// if(human(mode)) base = 4 + no_len; // "" + base = 2; + if(!last) base++; + + switch(value->type) { + case LITERAL: + { + size_t key_len = value->used; + + strcpy(buffer, "\""); + strcat(buffer, value->payload.literal); + strcat(buffer, "\""); + strcat(buffer, "\0"); + + if(last == 0) strcat(buffer, ","); + break; + } + + case NUMERAL: + { + char str[15]; + sprintf(str, "%ld", value->payload.numeral); + int val_len = (int) strlen((char*) str); + + strcat(buffer, str); + if(last == 0) strcat(buffer, ","); + strcat(buffer, "\0"); + + break; + } + + case RECURSIVE: + { + if(value->used > 0) { + + dt_uni_t test = value->payload.recursive[0]->type; + + if(test == LITERAL || test == NUMERAL) { + fflush(stdout); + + int j; + for(j = 0; j < value->used; j++) { + dtree *child = value->payload.recursive[j]; + + char vall[1024]; + parse_value_list(child, vall, global, mode, depth + 1, (i == value->used - 1) ? TRUE : FALSE); + fflush(stdout); + } + + fflush(stdout); + + } else if(test == PAIR) { + fflush(stdout); +// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); + append(global, "{"); + + int j; + for(j = 0; j < value->used; j++) { + dtree *child = value->payload.recursive[j]; + + if(child->type == PAIR) { + dtree *key = child->payload.recursive[0]; + dtree *val = child->payload.recursive[1]; + + char kkey[1024]; + + parse_key_value(key, kkey, mode); + fflush(stdout); +// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(kkey)); + append(global, kkey); + + char vval[1024]; + parse_value_list(val, vval, global, mode, 1, (j == child->used - 1) ? TRUE : FALSE); + fflush(stdout); + +// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(vval)); + append(global, vval); + } + } + + fflush(stdout); +// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); + append(global, "}"); + } + + } else { + fflush(stdout); + } + } + + default: INVALID_PAYLOAD; + } + + return ""; +} + +void append(char *buffer, char *message) +{ + int msg_len = (int) strlen(message); + sprintf(buffer + json_len, message); + json_len += msg_len; +} + +/**************** DECODER UTILITY FUNCTIONS ******************/ + +void append_char(char *buffer, int *ctr, char c) +{ + sprintf(buffer + (*ctr), "%c", c); + (*ctr)++; +} + +long to_long(char *buffer) +{ + return atol(buffer); +} -- cgit v1.2.3 From df049ef1715c2b16667f9a26d8a9fd26be24977a Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 27 Aug 2016 17:41:49 +0200 Subject: API refactor part 3: Recursive elements are now called "List" elements, Numeral elements are now called "Numeric" elements. --- include/dtree/dtree.h | 2 +- lib/dtree.c | 56 +++++++++++++++++++++++++-------------------------- lib/dyn_utils.c | 10 ++++----- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index 2a62ee7a9621..912030a7a3b4 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -38,7 +38,7 @@ extern "C" { /* Type that determines what data is stored inside a tree-node */ typedef enum dt_uni_t { - UNSET, LITERAL, NUMERAL, RECURSIVE, PAIR, POINTER + UNSET, LITERAL, NUMERIC, LIST, PAIR, POINTER } dt_uni_t; diff --git a/lib/dtree.c b/lib/dtree.c index 97ca14cb489e..b2ea03e29ed1 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -39,7 +39,7 @@ dt_err dtree_resettype(dtree *data) { if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); - } else if(data->type == RECURSIVE || data->type == PAIR) { + } else if(data->type == LIST || data->type == PAIR) { /* Iterate over all children and clear them */ int i; @@ -106,10 +106,10 @@ 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 != NUMERAL) return INVALID_PAYLOAD; + if(data->type != NUMERIC) return INVALID_PAYLOAD; data->payload.numeral = numeral; - data->type = NUMERAL; + data->type = NUMERIC; data->size = sizeof(int); data->used = sizeof(int); return SUCCESS; @@ -120,7 +120,7 @@ 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 != RECURSIVE) return INVALID_PAYLOAD; + if(data->type != LIST) return INVALID_PAYLOAD; dt_err err; @@ -144,7 +144,7 @@ dt_err dtree_addlist(dtree *data, dtree *(*new_data)) } else { dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); data->payload.recursive = tmp; - data->type = RECURSIVE; + data->type = LIST; data->used = 0; data->size = RDB_REC_DEF_SIZE; } @@ -232,7 +232,7 @@ 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 == RECURSIVE || data->type == PAIR)) + if(!(data->type == LIST || data->type == PAIR)) return INVALID_PAYLOAD; /* This means elements already exist */ @@ -254,7 +254,7 @@ dt_err dtree_merge_trees(dtree *data, dtree *merge) } else { dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); data->payload.recursive = tmp; - data->type = RECURSIVE; + data->type = LIST; data->used = 0; data->size = RDB_REC_DEF_SIZE; } @@ -292,12 +292,12 @@ dt_err dtree_copy(dtree *data, dtree *(*copy)) err = dtree_addliteral(*copy, data->payload.literal); break; - case NUMERAL: + case NUMERIC: err = dtree_addnumeral(*copy, data->payload.numeral); break; - case RECURSIVE: - (*copy)->type = RECURSIVE; + case LIST: + (*copy)->type = LIST; (*copy)->payload.recursive = (dtree**) malloc(sizeof(dtree*) * data->size); memcpy((*copy)->payload.recursive, data->payload.recursive, sizeof(dtree*) * data->used); break; @@ -329,7 +329,7 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ /* Make sure our pointer is clean */ *found = NULL; - if(data->type == RECURSIVE|| data->type == PAIR) { + if(data->type == LIST|| data->type == PAIR) { int i; for(i = 0; i < data->used; i++) { @@ -348,7 +348,7 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ *found = data; break; - case NUMERAL: + case NUMERIC: if(data->payload.numeral == (long) payload) *found = data; break; @@ -378,7 +378,7 @@ void recursive_print(dtree *data, const char *offset) case LITERAL: printf("%s['%s']\n", offset, data->payload.literal); break; - case NUMERAL: + case NUMERIC: printf("%s[%lu]\n", offset, data->payload.numeral); break; case PAIR: @@ -387,24 +387,24 @@ void recursive_print(dtree *data, const char *offset) dt_uni_t v_type = data->payload.recursive[1]->type; if(k_type == LITERAL) printf("%s['%s']", offset, data->payload.recursive[0]->payload.literal); - if(k_type == NUMERAL) printf("%s[%lu]", offset, data->payload.recursive[0]->payload.numeral); + if(k_type == NUMERIC) printf("%s[%lu]", offset, data->payload.recursive[0]->payload.numeral); char new_offset[REAL_STRLEN(offset) + 2]; strcpy(new_offset, offset); strcat(new_offset, " "); - if(k_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); + if(k_type == LIST || k_type == PAIR) recursive_print(data->payload.recursive[0], new_offset); /* Print the value now */ if(v_type == LITERAL) printf(" => ['%s']\n", data->payload.recursive[1]->payload.literal); - if(v_type== NUMERAL) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); + if(v_type== NUMERIC) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); - if(v_type == RECURSIVE || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); + if(v_type == LIST || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); break; } - case RECURSIVE: + case LIST: { int i; printf("%s[LIST]\n", offset); @@ -418,11 +418,11 @@ void recursive_print(dtree *data, const char *offset) switch(t) { case LITERAL: - case NUMERAL: + case NUMERIC: recursive_print(data->payload.recursive[i], new_offset); continue; - case RECURSIVE: + case LIST: recursive_print(data->payload.recursive[i], new_offset); continue; @@ -453,8 +453,8 @@ void dtree_print(dtree *data) dt_err dtree_get(dtree *data, void *(*val)) { if(data->type == LITERAL) *val = (char*) data->payload.literal; - if(data->type == NUMERAL) *val = (int*) &data->payload.numeral; - if(data->type == RECURSIVE || data->type == PAIR) + if(data->type == NUMERIC) *val = (int*) &data->payload.numeral; + if(data->type == LIST || data->type == PAIR) *val = (dtree*) data->payload.recursive; return SUCCESS; @@ -468,7 +468,7 @@ dt_err dtree_free(dtree *data) if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); - } else if(data->type == RECURSIVE || data->type == PAIR) { + } else if(data->type == LIST || data->type == PAIR) { int i; dt_err err; for(i = 0; i < data->used; i++) { @@ -493,7 +493,7 @@ dt_err dtree_free_shallow(dtree *data) if(data->type == LITERAL) { if(data->payload.literal) free(data->payload.literal); - } else if(data->type == RECURSIVE || data->type == PAIR) { + } else if(data->type == LIST || data->type == PAIR) { int i; dt_err err; for(i = 0; i < data->size; i++) { @@ -513,8 +513,8 @@ const char *dtree_dtype(dtree *data) { switch(data->type) { case LITERAL: return "Literal"; - case NUMERAL: return "Numeral"; - case RECURSIVE: return "Recursive"; + case NUMERIC: return "Numeral"; + case LIST: return "Recursive"; case PAIR: return "Pair"; case POINTER: return "Pointer"; default: return "Unknown"; @@ -540,7 +540,7 @@ int recursive_search(dtree **direct_parent, dtree *data, dtree *target) if(data == target) return 0; int res = 1; - if(data->type == RECURSIVE || data->type == PAIR) { + if(data->type == LIST || data->type == PAIR) { int i; for(i = 0; i < data->used; i++) { res = recursive_search(direct_parent, data->payload.recursive[i], target); @@ -567,7 +567,7 @@ int recursive_search(dtree **direct_parent, dtree *data, dtree *target) dt_err data_check(dtree *data) { /* Check if the data block has children */ - if(data->type == RECURSIVE) + if(data->type == LIST) { printf("Won't override heap payload with data!"); return INVALID_PAYLOAD; diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index 84f34465a2b4..055623ba36d2 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -74,7 +74,7 @@ dt_err dtree_encode_json(dtree *data, char *json_data) char *open = "{"; char *close = "}"; - if(data->type == RECURSIVE) { + if(data->type == LIST) { fflush(stdout); append(json_data, open); @@ -101,7 +101,7 @@ dt_err dtree_encode_json(dtree *data, char *json_data) append(json_data, vval); memset(vval, 0, 1024); - } else if(child->type == RECURSIVE) { + } else if(child->type == LIST) { dt_err err = dtree_encode_json(child, json_data); if(err) return err; } @@ -341,7 +341,7 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod break; } - case NUMERAL: + case NUMERIC: { char str[15]; sprintf(str, "%ld", value->payload.numeral); @@ -354,13 +354,13 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod break; } - case RECURSIVE: + case LIST: { if(value->used > 0) { dt_uni_t test = value->payload.recursive[0]->type; - if(test == LITERAL || test == NUMERAL) { + if(test == LITERAL || test == NUMERIC) { fflush(stdout); int j; -- cgit v1.2.3 From cb73654ecc93d8efed08ab337812bd0d63170241 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 28 Aug 2016 13:58:50 +0200 Subject: Adding the ability to copy nodes in shallow or deep mode --- include/dtree/dtree.h | 22 ++++++++++++-- lib/dtree.c | 84 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index 912030a7a3b4..e4ced5b2df7a 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -200,6 +200,20 @@ dt_err dtree_merge_trees(dtree *data, dtree *merge); 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. @@ -225,14 +239,15 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)); */ 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 or leave it as a void* to let - * libdyntree do the casting for you. + * 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 @@ -270,7 +285,8 @@ dt_err dtree_free_shallow(dtree *data); /** * Like #{dtree_free_shallow} but will also remove structs that - * weren't allocated by libdyntree + * weren't allocated by libdyntree. Will throw warnings when trying + * to free payloads from shallow copy nodes * * @param data * @return diff --git a/lib/dtree.c b/lib/dtree.c index b2ea03e29ed1..30d8c9ac2c1b 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -18,6 +18,8 @@ int recursive_search(dtree**, dtree *, dtree *); +void recursive_print(dtree *data, const char *offset); + /******/ @@ -96,7 +98,7 @@ dt_err dtree_addpointer(dtree *data, void *ptr) data->payload.pointer = ptr; data->type = POINTER; data->size = sizeof(ptr); - data->used = sizeof(*ptr); + data->used = sizeof(ptr); return SUCCESS; } @@ -270,8 +272,64 @@ dt_err dtree_merge_trees(dtree *data, dtree *merge) dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) { if(data == NULL) return INVALID_PARAMS; + dt_err err = SUCCESS; - return 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 LIST: + { + int i; + int num = (int) data->used; + + for(i = 0; i < num; i++) { + dtree *node = data->payload.recursive[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.recursive[0]; + dtree *orig_val = data->payload.recursive[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_copy(dtree *data, dtree *(*copy)) @@ -375,12 +433,15 @@ void recursive_print(dtree *data, const char *offset) 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 PAIR: { dt_uni_t k_type = data->payload.recursive[0]->type; @@ -452,11 +513,9 @@ void dtree_print(dtree *data) dt_err dtree_get(dtree *data, void *(*val)) { - if(data->type == LITERAL) *val = (char*) data->payload.literal; - if(data->type == NUMERIC) *val = (int*) &data->payload.numeral; - if(data->type == LIST || data->type == PAIR) - *val = (dtree*) data->payload.recursive; - + if(data->type == LITERAL) *val = data->payload.literal; + if(data->type == NUMERIC) *val = &data->payload.numeral; + if(data->type == LIST || data->type == PAIR) *val = (dtree*) data->payload.recursive; return SUCCESS; } @@ -472,6 +531,8 @@ dt_err dtree_free(dtree *data) int i; dt_err err; for(i = 0; i < data->used; i++) { + if(data->copy == SHALLOW) continue; + err = dtree_free(data->payload.recursive[i]); if(err) return err; } @@ -479,7 +540,8 @@ dt_err dtree_free(dtree *data) free(data->payload.recursive); } else if(data->type == POINTER) { - if(data->payload.pointer) free(data->payload.pointer); + if(data->copy != SHALLOW && data->payload.pointer) + free(data->payload.pointer); } free(data); @@ -513,16 +575,18 @@ const char *dtree_dtype(dtree *data) { switch(data->type) { case LITERAL: return "Literal"; - case NUMERIC: return "Numeral"; - case LIST: return "Recursive"; + case NUMERIC: return "Numeric"; + case LIST: return "List"; case PAIR: return "Pair"; case POINTER: return "Pointer"; default: return "Unknown"; } } + /**************** PRIVATE UTILITY FUNCTIONS ******************/ + /** * Steps down the recursive hirarchy of a dyntree node to * find a sub-child target. Returns 0 if it can be found. -- cgit v1.2.3 From bcf48a0e4bd5bbf97c22cf9f7c9494b54d1f22ff Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 28 Aug 2016 14:05:15 +0200 Subject: Moving utility functions into new file with a new API namespace --- CMakeLists.txt | 2 +- include/dtree/dtree.h | 98 +++++------------------------------------------ include/dtree/eztree.h | 87 +++++++++++++++++++++++++++++++++++++++++ lib/dtree.c | 102 ++++++++++++++++++++++++------------------------- 4 files changed, 148 insertions(+), 141 deletions(-) create mode 100644 include/dtree/eztree.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e738040e2011..2fd3b8e18902 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ set_target_properties(libdyntree PROPERTIES PREFIX "") ################### TESTING CODE BELOW ################### -set(TEST_SRC test/main.c) +set(TEST_SRC test/main.c include/dtree/eztree.h) add_executable(dyntree_test ${TEST_SRC}) # Library dependencies for the http extention diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index e4ced5b2df7a..e9acf1a6f01a 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -4,15 +4,15 @@ * * With the API you can easily create structures like the following: * - * root [recursive] - * child1 [recursive] + * root [list] + * child1 [list] * key [literal] - "Username" * value [literal] - "spacekookie" - * child2 [recursive] + * child2 [list] * key [literal] - "Age" * value [numerical] - 23 * child3 - * subchild [recursive] + * subchild [list] * ... * * Freeing the root node will free all children @@ -50,7 +50,7 @@ typedef struct dtree { union { char *literal; long numeral; - struct dtree *(*recursive); + struct dtree *(*list); void *pointer; } payload; } dtree; @@ -169,11 +169,11 @@ dt_err dtree_split_trees(dtree *data, dtree *sp); /** - * This function is very simmilar to dt_err "dtree_addrecursive" + * 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 recursive. It will + * 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. * @@ -200,7 +200,6 @@ dt_err dtree_merge_trees(dtree *data, dtree *merge); 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 @@ -214,6 +213,7 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ */ 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. @@ -320,7 +320,7 @@ const char *dtree_err_getmsg(dt_err *e); dt_err dtree_encode_set(dtree *data, short setting); /** - * A simple recursive node walker that encodes a dyn_tree node hirarchy + * 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. * @@ -344,86 +344,6 @@ dt_err dtree_encode_json(dtree *data, char *json_data); */ dt_err dtree_decode_json(dtree *(*data), const char *json_data); - - -/*** Some helper functions for more non-C oriented developers ***/ - -/** - * Shortcut function that allocates a new string node. Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - * @param string - * @return - */ -static dtree *dtree_alloc_literal(const char *string) -{ - dtree *node; - dtree_malloc(&node); - dtree_addliteral(node, string); - return node; -} - -/** - * Shortcut function that allocates a new numerical node. - * Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - * @param num - * @return - */ -static dtree *dtree_alloc_numeral(const long num) -{ - dtree *node; - dtree_malloc(&node); - dtree_addnumeral(node, num); - return node; -} - -/** - * Shortcut function which creates two nodes as pair under a root - * node which is returned. Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - @param key Will be the key node - * @param val Will be the value node - * @return New root node with key-val children - */ -static dtree *dtree_allocpair_new(dtree **key, dtree **val) -{ - dtree *root; - dtree_malloc(&root); - dtree_addpair(root, key, val); - return root; -} - - -/** - * Shortcut function which allocates a list of nodes in a list under - * a root node recursively. - * - * WARNING: Return value is allocated on heap. MUST FREE MANUALLY! - * WARNING: THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * - * @param root - * @param count - * @return - */ -static dtree **dtree_alloc_listlist(dtree **root, unsigned int count) -{ - dtree **nodes = malloc(sizeof(dtree**) * count); - - dtree_malloc(root); - - int i; - for(i = 0; i < count; i++) - dtree_addlist(*root, &nodes[i]); - - return nodes; -} - #ifdef __cplusplus } #endif diff --git a/include/dtree/eztree.h b/include/dtree/eztree.h new file mode 100644 index 000000000000..8b138743c472 --- /dev/null +++ b/include/dtree/eztree.h @@ -0,0 +1,87 @@ +// +// Created by spacekookie on 28/08/16. +// + +#ifndef LIBDYNTREE_EZTREE_H +#define LIBDYNTREE_EZTREE_H + +#include +#include + +/** + * Shortcut function that allocates a new string node. Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param string + * @return + */ +static dtree *dtree_alloc_literal(const char *string) +{ + dtree *node; + dtree_malloc(&node); + dtree_addliteral(node, string); + return node; +} + +/** + * Shortcut function that allocates a new numerical node. + * Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param num + * @return + */ +static dtree *dtree_alloc_numeral(const long num) +{ + dtree *node; + dtree_malloc(&node); + dtree_addnumeral(node, num); + return node; +} + +/** + * Shortcut function which creates two nodes as pair under a root + * node which is returned. Beware however: + * + * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + @param key Will be the key node + * @param val Will be the value node + * @return New root node with key-val children + */ +static dtree *dtree_allocpair_new(dtree **key, dtree **val) +{ + dtree *root; + dtree_malloc(&root); + dtree_addpair(root, key, val); + return root; +} + + +/** + * Shortcut function which allocates a list of nodes in a list under + * a root node listly. + * + * WARNING: Return value is allocated on heap. MUST FREE MANUALLY! + * WARNING: THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * + * @param root + * @param count + * @return + */ +static dtree **dtree_alloc_listlist(dtree **root, unsigned int count) +{ + dtree **nodes = malloc(sizeof(dtree**) * count); + + dtree_malloc(root); + + int i; + for(i = 0; i < count; i++) + dtree_addlist(*root, &nodes[i]); + + return nodes; +} + +#endif //LIBDYNTREE_EZTREE_H diff --git a/lib/dtree.c b/lib/dtree.c index 30d8c9ac2c1b..cd9b1f2b69c3 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -16,9 +16,9 @@ /*** Forward declared functions ***/ -int recursive_search(dtree**, dtree *, dtree *); +int list_search(dtree**, dtree *, dtree *); -void recursive_print(dtree *data, const char *offset); +void list_print(dtree *data, const char *offset); /******/ @@ -47,7 +47,7 @@ dt_err dtree_resettype(dtree *data) int i; dt_err err; for(i = 0; i < data->size; i++) { - err = dtree_free(data->payload.recursive[i]); + err = dtree_free(data->payload.list[i]); if(err) return err; } } @@ -135,17 +135,17 @@ dt_err dtree_addlist(dtree *data, dtree *(*new_data)) // TODO Use Realloc dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy(tmp, data->payload.recursive, sizeof(dtree*) * data->used); + memcpy(tmp, data->payload.list, sizeof(dtree*) * data->used); /* Free the list WITHOUT the children! */ - free(data->payload.recursive); - data->payload.recursive = tmp; + 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.recursive = tmp; + data->payload.list = tmp; data->type = LIST; data->used = 0; data->size = RDB_REC_DEF_SIZE; @@ -155,7 +155,7 @@ dt_err dtree_addlist(dtree *data, dtree *(*new_data)) if(err) return err; /* Reference the slot, assign it, then move our ctr */ - data->payload.recursive[data->used] = *new_data; + data->payload.list[data->used] = *new_data; data->used++; return SUCCESS; @@ -181,12 +181,12 @@ dt_err dtree_addpair(dtree *data, dtree *(*key), dtree *(*value)) dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); if(!tmp) goto cleanup; - data->payload.recursive = tmp; + data->payload.list = tmp; { /* Assign data to new array */ - data->payload.recursive[data->used] = *key; + data->payload.list[data->used] = *key; data->used++; - data->payload.recursive[data->used] = *value; + data->payload.list[data->used] = *value; data->used++; } @@ -210,19 +210,19 @@ dt_err dtree_split_trees(dtree *data, dtree *sp) /* Check that sp is really a child of data */ dtree *dp; - int ret = recursive_search(&dp, data, sp); + int ret = list_search(&dp, data, sp); if(ret != 0) return DATA_NOT_RELATED; if(dp == NULL) return NODE_NOT_FOUND; - /* Find the exact recursive reference and remove it */ + /* Find the exact list reference and remove it */ int i; for(i = 0; i < dp->used; i++) { - if(dp->payload.recursive[i] == NULL) continue; + if(dp->payload.list[i] == NULL) continue; /* Manually remove the entry */ - if(dp->payload.recursive[i] == sp) { + if(dp->payload.list[i] == sp) { dp->used--; - dp->payload.recursive[i] = NULL; + dp->payload.list[i] = NULL; } } @@ -245,24 +245,24 @@ dt_err dtree_merge_trees(dtree *data, dtree *merge) data->size += RDB_REC_MULTIPLY; dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy(tmp, data->payload.recursive, sizeof(dtree*) * data->used); + memcpy(tmp, data->payload.list, sizeof(dtree*) * data->used); /* Free the list WITHOUT the children! */ - free(data->payload.recursive); - data->payload.recursive = tmp; + 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.recursive = tmp; + 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.recursive[data->used] = merge; + data->payload.list[data->used] = merge; data->used++; return SUCCESS; @@ -296,7 +296,7 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) int num = (int) data->used; for(i = 0; i < num; i++) { - dtree *node = data->payload.recursive[i]; + dtree *node = data->payload.list[i]; dtree *new; dtree_addlist((*copy), &new); @@ -311,8 +311,8 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) dtree *key, *val; dtree_addpair((*copy), &key, &val); - dtree *orig_key = data->payload.recursive[0]; - dtree *orig_val = data->payload.recursive[1]; + 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); @@ -356,14 +356,14 @@ dt_err dtree_copy(dtree *data, dtree *(*copy)) case LIST: (*copy)->type = LIST; - (*copy)->payload.recursive = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy((*copy)->payload.recursive, data->payload.recursive, sizeof(dtree*) * data->used); + (*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.recursive = (dtree**) malloc(sizeof(dtree*) * data->size); - memcpy((*copy)->payload.recursive, data->payload.recursive, sizeof(dtree*) * data->used); + (*copy)->payload.list = (dtree**) malloc(sizeof(dtree*) * data->size); + memcpy((*copy)->payload.list, data->payload.list, sizeof(dtree*) * data->used); break; case POINTER: @@ -391,7 +391,7 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ int i; for(i = 0; i < data->used; i++) { - dt_err err = dtree_search_payload(data->payload.recursive[i], found, payload, type); + dt_err err = dtree_search_payload(data->payload.list[i], found, payload, type); if(err == SUCCESS) return SUCCESS; } @@ -425,7 +425,7 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ } -void recursive_print(dtree *data, const char *offset) +void list_print(dtree *data, const char *offset) { dt_uni_t type = data->type; @@ -444,23 +444,23 @@ void recursive_print(dtree *data, const char *offset) case PAIR: { - dt_uni_t k_type = data->payload.recursive[0]->type; - dt_uni_t v_type = data->payload.recursive[1]->type; + 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.recursive[0]->payload.literal); - if(k_type == NUMERIC) printf("%s[%lu]", offset, data->payload.recursive[0]->payload.numeral); + 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); char new_offset[REAL_STRLEN(offset) + 2]; strcpy(new_offset, offset); strcat(new_offset, " "); - if(k_type == LIST || k_type == PAIR) recursive_print(data->payload.recursive[0], 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.recursive[1]->payload.literal); - if(v_type== NUMERIC) printf(" => [%lu]\n", data->payload.recursive[1]->payload.numeral); + 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 == LIST || k_type == PAIR) recursive_print(data->payload.recursive[1], new_offset); + if(v_type == LIST || k_type == PAIR) list_print(data->payload.list[1], new_offset); break; } @@ -470,7 +470,7 @@ void recursive_print(dtree *data, const char *offset) int i; printf("%s[LIST]\n", offset); for(i = 0; i < data->used; i++) { - dt_uni_t t = data->payload.recursive[i]->type; + dt_uni_t t = data->payload.list[i]->type; /* Calculate the new offset */ char new_offset[REAL_STRLEN(offset) + 2]; @@ -480,16 +480,16 @@ void recursive_print(dtree *data, const char *offset) switch(t) { case LITERAL: case NUMERIC: - recursive_print(data->payload.recursive[i], new_offset); + list_print(data->payload.list[i], new_offset); continue; case LIST: - recursive_print(data->payload.recursive[i], new_offset); + list_print(data->payload.list[i], new_offset); continue; case PAIR: printf("%s[PAIR] <==> ", new_offset); - recursive_print(data->payload.recursive[i], new_offset); + list_print(data->payload.list[i], new_offset); continue; default: @@ -508,14 +508,14 @@ void recursive_print(dtree *data, const char *offset) void dtree_print(dtree *data) { - recursive_print(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 == LIST || data->type == PAIR) *val = (dtree*) data->payload.recursive; + if(data->type == LIST || data->type == PAIR) *val = (dtree*) data->payload.list; return SUCCESS; } @@ -533,11 +533,11 @@ dt_err dtree_free(dtree *data) for(i = 0; i < data->used; i++) { if(data->copy == SHALLOW) continue; - err = dtree_free(data->payload.recursive[i]); + err = dtree_free(data->payload.list[i]); if(err) return err; } - free(data->payload.recursive); + free(data->payload.list); } else if(data->type == POINTER) { if(data->copy != SHALLOW && data->payload.pointer) @@ -559,11 +559,11 @@ dt_err dtree_free_shallow(dtree *data) int i; dt_err err; for(i = 0; i < data->size; i++) { - err = dtree_free(data->payload.recursive[i]); + err = dtree_free(data->payload.list[i]); if(err) return err; } - free(data->payload.recursive); + free(data->payload.list); } free(data); @@ -588,14 +588,14 @@ const char *dtree_dtype(dtree *data) /** - * Steps down the recursive hirarchy of a dyntree node to + * 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 recursive_search(dtree **direct_parent, dtree *data, dtree *target) +int list_search(dtree **direct_parent, dtree *data, dtree *target) { /* Check if data is actually valid */ if(data == NULL) return 1; @@ -607,7 +607,7 @@ int recursive_search(dtree **direct_parent, dtree *data, dtree *target) if(data->type == LIST || data->type == PAIR) { int i; for(i = 0; i < data->used; i++) { - res = recursive_search(direct_parent, data->payload.recursive[i], target); + res = list_search(direct_parent, data->payload.list[i], target); if(res == 0) { /* Save the node that contains our child for later */ -- cgit v1.2.3 From 75481c8cb77067cda4c9fb6499d72764f7e84a24 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 28 Aug 2016 14:06:22 +0200 Subject: Fixing some errors and finishing more refactoring --- lib/dyn_utils.c | 18 ++++++------ test/main.c | 89 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index 055623ba36d2..c6e5ac06474e 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -82,11 +82,11 @@ dt_err dtree_encode_json(dtree *data, char *json_data) /* Iterate through all it's children */ int i; for(i = 0; i < data->used; i++) { - dtree *child = data->payload.recursive[i]; + dtree *child = data->payload.list[i]; if(child->type == PAIR) { - dtree *key = child->payload.recursive[0]; - dtree *val = child->payload.recursive[1]; + dtree *key = child->payload.list[0]; + dtree *val = child->payload.list[1]; char kkey[1024]; parse_key_value(key, kkey, mode);; @@ -186,7 +186,7 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) { if(in_str) { } else { - if(curr_key[0] != NULL) { + if(curr_key[0] != '\0') { dtree *key, *val; dtree *rec_entry; @@ -358,14 +358,14 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod { if(value->used > 0) { - dt_uni_t test = value->payload.recursive[0]->type; + dt_uni_t test = value->payload.list[0]->type; if(test == LITERAL || test == NUMERIC) { fflush(stdout); int j; for(j = 0; j < value->used; j++) { - dtree *child = value->payload.recursive[j]; + dtree *child = value->payload.list[j]; char vall[1024]; parse_value_list(child, vall, global, mode, depth + 1, (i == value->used - 1) ? TRUE : FALSE); @@ -381,11 +381,11 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod int j; for(j = 0; j < value->used; j++) { - dtree *child = value->payload.recursive[j]; + dtree *child = value->payload.list[j]; if(child->type == PAIR) { - dtree *key = child->payload.recursive[0]; - dtree *val = child->payload.recursive[1]; + dtree *key = child->payload.list[0]; + dtree *val = child->payload.list[1]; char kkey[1024]; diff --git a/test/main.c b/test/main.c index 240de6c9ccab..e551d4f3432c 100644 --- a/test/main.c +++ b/test/main.c @@ -1,6 +1,8 @@ #include + #include +#include /** * A small test that creates a tree, splits the nodes @@ -26,37 +28,34 @@ int main(void) dt_err err; printf("=== libdyntree test suite ===\n"); - /* Search inside trees */ - TEST(search_for_payload()) - - /* Split and merge trees */ - TEST(split_and_merge()) - - /* Test shortcut functions */ - TEST(test_shortcut_functions()) - - /* Try to encode a structure into json */ - char json[512]; // Provide a buffer that is big enough - TEST(json_encode(json)) - - printf("\n\nJson string: %s\n", json); - - dtree *data; - dtree_decode_json(&data, json); - - /* Copy structures */ - - dtree *key, *val; - dtree *root = dtree_allocpair_new(&key, &val); - - dtree_addliteral(key, "Core Data"); - dtree_addliteral(val, "dtree is cool!"); - - dtree *root_copy; - dtree_copy(root, &root_copy); - - printf("Copy complete?\n"); - +// /* Search inside trees */ +// TEST(search_for_payload()) +// +// /* Split and merge trees */ +// TEST(split_and_merge()) +// +// /* Test shortcut functions */ +// TEST(test_shortcut_functions()) +// +// /* Try to encode a structure into json */ +// char json[512]; // Provide a buffer that is big enough +// TEST(json_encode(json)) +// +// printf("\n\nJson string: %s\n", json); +// +// dtree *data; +// dtree_decode_json(&data, json); +// dtree_free(data); + + dtree *root, *child, *key, *val; + dtree_malloc(&root); + dtree_addlist(root, &child); + dtree_addpair(child, &key, &val); + dtree_addliteral(key, "server"); + dtree_addliteral(val, "github.com"); + + dtree *copy; + dtree_copy_deep(root, ©); end: exit: @@ -135,7 +134,7 @@ dt_err search_for_payload() err = dtree_search_payload(root, &found, (void*) string, LITERAL); if(err) goto exit; - err = dtree_search_payload(root, &found, (void*) 1337, NUMERAL); + err = dtree_search_payload(root, &found, (void*) 1337, NUMERIC); if(err) goto exit; exit: @@ -221,19 +220,19 @@ dt_err test_shortcut_functions() dtree *root = dtree_allocpair_new(&key, &val); dtree_addliteral(key, "Address"); -// dtree_addliteral(val, "Berlin 10555", REAL_STRLEN("Berlin 10555")); - - dtree *list_root; - dtree **list = dtree_alloc_listlist(&list_root, 4); // Allocate 4 nodes - - int i; - for(i = 0; i < 4; i++){ - char message[32]; - sprintf(message, "Message no.%d", i); - dtree_addliteral(list[i], message); - } - - dtree_merge_trees(val, list_root); + dtree_addliteral(val, "Berlin 10555"); + +// dtree *list_root; +// dtree **list = dtree_alloc_listlist(&list_root, 4); // Allocate 4 nodes +// +// int i; +// for(i = 0; i < 4; i++){ +// char message[32]; +// sprintf(message, "Message no.%d", i); +// dtree_addliteral(list[i], message); +// } +// +// dtree_merge_trees(val, list_root); dtree_free(root); return SUCCESS; // We think -- cgit v1.2.3 From a0f0220726f84dc62f49b593481a00f005e3443e Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 28 Aug 2016 16:31:16 +0200 Subject: First version of the "eztree" api. It provides more straight-forward ways to use libdyntree (in a less safe way however!) eztree_* functions do not provide error chec --- CMakeLists.txt | 7 ++-- include/dtree/eztree.h | 89 +++++++++++++++++++------------------------------- lib/eztree.c | 77 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 57 deletions(-) create mode 100644 lib/eztree.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fd3b8e18902..57359472ca5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,10 @@ set(CMAKE_BUILD_TYPE Debug) # Create our project for further reference project(libdyntree) -set(DYN_TREE_SRC lib/dtree.c lib/dyn_utils.c) +set(DYN_TREE_SRC + lib/dtree.c + lib/eztree.c + lib/dyn_utils.c) # Define our library in cmake add_library(libdyntree SHARED ${DYN_TREE_SRC}) @@ -26,7 +29,7 @@ set_target_properties(libdyntree PROPERTIES PREFIX "") ################### TESTING CODE BELOW ################### -set(TEST_SRC test/main.c include/dtree/eztree.h) +set(TEST_SRC test/main.c) add_executable(dyntree_test ${TEST_SRC}) # Library dependencies for the http extention diff --git a/include/dtree/eztree.h b/include/dtree/eztree.h index 8b138743c472..a5388319fdd0 100644 --- a/include/dtree/eztree.h +++ b/include/dtree/eztree.h @@ -1,87 +1,66 @@ -// -// Created by spacekookie on 28/08/16. -// - #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 + + /** - * Shortcut function that allocates a new string node. Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * An quick create function for a literal node * * @param string * @return */ -static dtree *dtree_alloc_literal(const char *string) -{ - dtree *node; - dtree_malloc(&node); - dtree_addliteral(node, string); - return node; -} +dtree *eztree_new_literal(const char *string); + /** - * Shortcut function that allocates a new numerical node. - * Beware however: - * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! - * + * A quick create function for a number (numeric) node * @param num * @return */ -static dtree *dtree_alloc_numeral(const long num) -{ - dtree *node; - dtree_malloc(&node); - dtree_addnumeral(node, num); - return node; -} +dtree *eztree_new_numeric(const long num); + /** - * Shortcut function which creates two nodes as pair under a root - * node which is returned. Beware however: + * A quick create function for a string key and an arbitrary type value. + * The value needs to be marked properly or errors might occur. * - * THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * 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 Will be the key node - * @param val Will be the value node - * @return New root node with key-val children + * @param key + * @param val + * @param type + * @return */ -static dtree *dtree_allocpair_new(dtree **key, dtree **val) -{ - dtree *root; - dtree_malloc(&root); - dtree_addpair(root, key, val); - return root; -} +dtree *eztree_new_pair(const char *key, void *val, short type); /** - * Shortcut function which allocates a list of nodes in a list under - * a root node listly. + * 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. * - * WARNING: Return value is allocated on heap. MUST FREE MANUALLY! - * WARNING: THIS FUNCTION DOES NOT RETURN WARNINGS OR ERROR CODES! + * Provided size needs to be size of the child-buffer or else errors might occur! * - * @param root - * @param count + * @param list + * @param size * @return */ -static dtree **dtree_alloc_listlist(dtree **root, unsigned int count) -{ - dtree **nodes = malloc(sizeof(dtree**) * count); - - dtree_malloc(root); - - int i; - for(i = 0; i < count; i++) - dtree_addlist(*root, &nodes[i]); +dtree *eztree_new_list(dtree **list, size_t size); - return nodes; +#ifdef __cplusplus } +#endif #endif //LIBDYNTREE_EZTREE_H diff --git a/lib/eztree.c b/lib/eztree.c new file mode 100644 index 000000000000..ba8506afd883 --- /dev/null +++ b/lib/eztree.c @@ -0,0 +1,77 @@ +// 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 -- cgit v1.2.3 From 8d3da701c0cb3410a947a84d5147368744c774ec Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 28 Aug 2016 23:57:07 +0200 Subject: Adding a new error code and commenting the existing ones :) --- include/dtree/dtree.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index e9acf1a6f01a..988b8b41ed88 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -63,11 +63,12 @@ typedef enum dt_err { FAILURE = -1, SUCCESS = 0, - INVALID_PARAMS, - MALLOC_FAILED, - INVALID_PAYLOAD, - DATA_NOT_RELATED, - NODE_NOT_FOUND, + 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 } dt_err; -- cgit v1.2.3 From b7f57ab908d97b6980b5f90fbb8122a0d57cc451 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 28 Aug 2016 23:57:20 +0200 Subject: No longer allow to free shallow copies of nodes --- lib/dtree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/dtree.c b/lib/dtree.c index cd9b1f2b69c3..cb2578d00e55 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -41,6 +41,7 @@ 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 */ @@ -48,6 +49,7 @@ dt_err dtree_resettype(dtree *data) dt_err err; for(i = 0; i < data->size; i++) { err = dtree_free(data->payload.list[i]); + memset(data->payload.list[i], 0, sizeof(data->payload.list[i])); if(err) return err; } } @@ -523,6 +525,7 @@ dt_err dtree_get(dtree *data, void *(*val)) 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); @@ -531,7 +534,6 @@ dt_err dtree_free(dtree *data) int i; dt_err err; for(i = 0; i < data->used; i++) { - if(data->copy == SHALLOW) continue; err = dtree_free(data->payload.list[i]); if(err) return err; -- cgit v1.2.3 From a13d78cbd5a7aba4d5ce3f9c81602e6449decd7e Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Mon, 29 Aug 2016 11:03:04 +0200 Subject: - Fixing an issue where the list malloc was of size 0 - Now properly cleaning payload space after type reset - Minor tweaks and improvements --- lib/dtree.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/dtree.c b/lib/dtree.c index cb2578d00e55..43213d0573d1 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -49,7 +49,6 @@ dt_err dtree_resettype(dtree *data) dt_err err; for(i = 0; i < data->size; i++) { err = dtree_free(data->payload.list[i]); - memset(data->payload.list[i], 0, sizeof(data->payload.list[i])); if(err) return err; } } @@ -60,6 +59,9 @@ dt_err dtree_resettype(dtree *data) data->size = 0; data->used = 0; + /* Forcibly clean union memory to avoid bleeding data */ + memset(&data->payload, 0, sizeof(data->payload)); + return SUCCESS; } @@ -146,7 +148,7 @@ dt_err dtree_addlist(dtree *data, dtree *(*new_data)) /* This means the data object is new */ } else { - dtree **tmp = (dtree**) malloc(sizeof(dtree*) * data->size); + dtree **tmp = (dtree**) malloc(sizeof(dtree*) * RDB_REC_DEF_SIZE); data->payload.list = tmp; data->type = LIST; data->used = 0; @@ -157,8 +159,7 @@ dt_err dtree_addlist(dtree *data, dtree *(*new_data)) if(err) return err; /* Reference the slot, assign it, then move our ctr */ - data->payload.list[data->used] = *new_data; - data->used++; + data->payload.list[data->used++] = *new_data; return SUCCESS; } -- cgit v1.2.3 From 21215483984d374780aee46f23b9ed6d4909adf5 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Mon, 29 Aug 2016 11:03:47 +0200 Subject: Cleaning up json encoder and decoder - Removing the requirement to set an encoder setting - Only supporting minified json (for now) --- lib/dyn_utils.c | 65 +++++++++++++++------------------------------------------ 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index c6e5ac06474e..b614634f9f4c 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -17,9 +17,9 @@ int human(short ); void append(char *, char *); -int parse_key_value(dtree *, char *, short ); +int parse_key_value(dtree *, char *); -const char *parse_value_list(dtree *, char *, char *, short , int , int ); +const char *parse_value_list(dtree *, char *, char *, int ); // Functions required by decoder @@ -57,19 +57,17 @@ dt_err dtree_encode_json(dtree *data, char *json_data) { if(data == NULL) return INVALID_PARAMS; - json_len = 0; - /* Check if setting is valid */ - switch(data->encset) { - case DYNTREE_JSON_MINIFIED: - case DYNTREE_JSON_HUMAN: - break; - - default: return INVALID_PARAMS; - } + // switch(data->encset) { + // case DYNTREE_JSON_MINIFIED: + // case DYNTREE_JSON_HUMAN: + // break; + // + // default: return INVALID_PARAMS; + // } /* Assume mode for all children */ - short mode = data->encset; + json_len = 0; char *open = "{"; char *close = "}"; @@ -89,13 +87,13 @@ dt_err dtree_encode_json(dtree *data, char *json_data) dtree *val = child->payload.list[1]; char kkey[1024]; - parse_key_value(key, kkey, mode);; + parse_key_value(key, kkey); fflush(stdout); append(json_data, kkey); memset(kkey, 0, 1024); char vval[1024]; - parse_value_list(val, vval, json_data, mode, 1, (i == data->used - 1) ? TRUE : FALSE); + parse_value_list(val, vval, json_data, (i == data->used - 1) ? TRUE : FALSE); fflush(stdout); append(json_data, vval); @@ -110,7 +108,6 @@ dt_err dtree_encode_json(dtree *data, char *json_data) } else { return INVALID_PAYLOAD; } - fflush(stdout); /* Add closing } and null terminator to finish */ append(json_data, close); @@ -128,11 +125,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) dtree *parents[32]; // Only support 32 deep for now memset(parents, 0, sizeof(dtree*) * 32); - -#define IN_KEY 5 -#define IN_VAL 6 -#define IN_HASH 7 -#define IN_LIST 8 #define IN_STRING 9 #define NEUTRAL 10 @@ -145,7 +137,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) memset(curr_key, 0, 512); memset(curr_str, 0, 512); memset(curr_num, 0, 512); - int mode = 0; /* Get the first character of our json string */ int jd_len = (int) REAL_STRLEN(jd); @@ -213,13 +204,11 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) case '"': { in_str = (in_str) ? FALSE : TRUE; - mode = (in_str) ? IN_STRING : NEUTRAL; // TODO: Work with the mode ? break; } case ',': { - mode = NEUTRAL; dtree *key, *val; dtree *rec_entry; @@ -288,12 +277,7 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) /**************** ENCODER UTILITY FUNCTIONS ******************/ -int human(short mode) { - if(mode == DYNTREE_JSON_HUMAN) return 1; - return 0; -} - -int parse_key_value(dtree *key, char *buffer, short mode) +int parse_key_value(dtree *key, char *buffer) { if(key->type != LITERAL) 5; @@ -310,22 +294,12 @@ int parse_key_value(dtree *key, char *buffer, short mode) return 0; } -const char *parse_value_list(dtree *value, char *buffer, char *global, short mode, int depth, int last) +const char *parse_value_list(dtree *value, char *buffer, char *global, int last) { if(value == NULL) return "[ERROR]"; /* The new offset we need (in \t) */ - - int no_len = depth + 1; - char new_offset[no_len]; int i; - for(i = 0; i < depth + 1; i++) - strcat(new_offset, "\t"); - - int base; -// if(human(mode)) base = 4 + no_len; // "" - base = 2; - if(!last) base++; switch(value->type) { case LITERAL: @@ -345,7 +319,6 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod { char str[15]; sprintf(str, "%ld", value->payload.numeral); - int val_len = (int) strlen((char*) str); strcat(buffer, str); if(last == 0) strcat(buffer, ","); @@ -368,7 +341,7 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod dtree *child = value->payload.list[j]; char vall[1024]; - parse_value_list(child, vall, global, mode, depth + 1, (i == value->used - 1) ? TRUE : FALSE); + parse_value_list(child, vall, global, (j == value->used - 1) ? TRUE : FALSE); fflush(stdout); } @@ -376,7 +349,6 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod } else if(test == PAIR) { fflush(stdout); -// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); append(global, "{"); int j; @@ -389,22 +361,19 @@ const char *parse_value_list(dtree *value, char *buffer, char *global, short mod char kkey[1024]; - parse_key_value(key, kkey, mode); + parse_key_value(key, kkey); fflush(stdout); -// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(kkey)); append(global, kkey); char vval[1024]; - parse_value_list(val, vval, global, mode, 1, (j == child->used - 1) ? TRUE : FALSE); + parse_value_list(val, vval, global, (j == child->used - 1) ? TRUE : FALSE); fflush(stdout); -// *global = realloc(*global, sizeof(char) * strlen(*global) + strlen(vval)); append(global, vval); } } fflush(stdout); -// *global = realloc(*global, sizeof(char) * strlen(*global) + 1); append(global, "}"); } -- cgit v1.2.3 From 5310505396621abfa9d457d7e6c97ba85dce2e78 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 30 Aug 2016 22:09:52 +0200 Subject: Adding new error return for limited recursive queries --- include/dtree/dtree.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index 988b8b41ed88..766241203769 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -69,6 +69,7 @@ typedef enum dt_err { 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; -- cgit v1.2.3 From 60441671a612552d47b23841b44135593806468a Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 30 Aug 2016 22:10:35 +0200 Subject: Adding new recursive key search function --- lib/dtree.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/lib/dtree.c b/lib/dtree.c index 43213d0573d1..96ed3475a2d6 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -427,6 +427,81 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ return (*found == NULL) ? NODE_NOT_FOUND : SUCCESS; } +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(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 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) { -- cgit v1.2.3 From ecc9349e6ea6b2b779c30ef41f8f2354d1ad8566 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 30 Aug 2016 22:10:54 +0200 Subject: Updating the examples a little --- lib/dyn_utils.c | 3 +-- test/main.c | 65 ++++++++++----------------------------------------------- 2 files changed, 12 insertions(+), 56 deletions(-) diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c index b614634f9f4c..6912dd239036 100644 --- a/lib/dyn_utils.c +++ b/lib/dyn_utils.c @@ -153,8 +153,7 @@ dt_err dtree_decode_json(dtree *(*data), const char *jd) dtree_malloc(&new_root); if(ctr < 0) { - parents[0] = new_root; - ctr = 0; + parents[ctr = 0] = new_root; } else { dtree_addlist(parents[ctr], &new_root); parents[++ctr] = new_root; diff --git a/test/main.c b/test/main.c index e551d4f3432c..02e871b576d9 100644 --- a/test/main.c +++ b/test/main.c @@ -25,40 +25,21 @@ dt_err test_shortcut_functions(); int main(void) { - dt_err err; + dt_err err = SUCCESS; printf("=== libdyntree test suite ===\n"); -// /* Search inside trees */ -// TEST(search_for_payload()) -// -// /* Split and merge trees */ -// TEST(split_and_merge()) -// -// /* Test shortcut functions */ -// TEST(test_shortcut_functions()) -// -// /* Try to encode a structure into json */ -// char json[512]; // Provide a buffer that is big enough -// TEST(json_encode(json)) -// -// printf("\n\nJson string: %s\n", json); -// -// dtree *data; -// dtree_decode_json(&data, json); -// dtree_free(data); - - dtree *root, *child, *key, *val; - dtree_malloc(&root); - dtree_addlist(root, &child); - dtree_addpair(child, &key, &val); - dtree_addliteral(key, "server"); - dtree_addliteral(val, "github.com"); - - dtree *copy; - dtree_copy_deep(root, ©); + 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); end: - exit: printf("==== done ====\n"); return err; } @@ -213,27 +194,3 @@ dt_err json_encode(char *json) { dtree_free(root); return err; } - -dt_err test_shortcut_functions() -{ - dtree *key, *val; - dtree *root = dtree_allocpair_new(&key, &val); - - dtree_addliteral(key, "Address"); - dtree_addliteral(val, "Berlin 10555"); - -// dtree *list_root; -// dtree **list = dtree_alloc_listlist(&list_root, 4); // Allocate 4 nodes -// -// int i; -// for(i = 0; i < 4; i++){ -// char message[32]; -// sprintf(message, "Message no.%d", i); -// dtree_addliteral(list[i], message); -// } -// -// dtree_merge_trees(val, list_root); - - dtree_free(root); - return SUCCESS; // We think -} \ No newline at end of file -- cgit v1.2.3 From e3fe9401e4593c317ca967738d4c14e638a6ccd6 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 9 Oct 2016 10:27:09 +0200 Subject: Updating lots of code to make finalisation of vault creations easier --- lib/dtree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtree.c b/lib/dtree.c index 96ed3475a2d6..466c8c0cb513 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -451,7 +451,7 @@ dt_err dtree_search_keypayload(dtree *data, dtree *(*found), void *payload, dt_u hit = 0; } - if(hit == 0) *found = data->payload.list[1]: + if(hit == 0) *found = data->payload.list[1]; } else if(data->type == LIST) { -- cgit v1.2.3 From a4b6f559da2af7f1c035446a1ad8f01fd4d9dbf7 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 11 Dec 2016 03:09:14 +0100 Subject: Adding new function that gets the parent of a current node in a recursive structure --- include/dtree/dtree.h | 13 ++ lib/dtree.c | 42 ++++++ lib/dtree_utils.c | 408 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/dyn_utils.c | 408 -------------------------------------------------- 4 files changed, 463 insertions(+), 408 deletions(-) create mode 100644 lib/dtree_utils.c delete mode 100644 lib/dyn_utils.c diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index 766241203769..a3150144d373 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -188,6 +188,19 @@ dt_err dtree_split_trees(dtree *data, dtree *sp); 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 diff --git a/lib/dtree.c b/lib/dtree.c index 466c8c0cb513..e45d453c8ce0 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -335,6 +335,48 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) 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(data->type) { + + /* Dead-end data stores automatically return @{NODE_NOT_FOUND} */ + case POINTER: + case LITERAL: + 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; diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c new file mode 100644 index 000000000000..6912dd239036 --- /dev/null +++ b/lib/dtree_utils.c @@ -0,0 +1,408 @@ +#include +#include +#include + + +/*** Forward declared functions ***/ + +#define TRUE 1 +#define FALSE 0 + +static int json_len = 0; + +/************* Forward Function Declarations *************/ + +// Functions required by encoder +int human(short ); + +void append(char *, char *); + +int parse_key_value(dtree *, char *); + +const char *parse_value_list(dtree *, char *, char *, int ); + +// Functions required by decoder + +void append_char(char *, int *, char ); + +long to_long(char *); + +/*********************************************************/ + +const char *rdb_error_getmsg(dt_err *e) +{ + +} + +dt_err dtree_encode_set(dtree *data, short setting) +{ + if(data == NULL) return INVALID_PARAMS; + + /* Check if setting is valid */ + switch(setting) { + case DYNTREE_ENCODE_NONE: + case DYNTREE_JSON_MINIFIED: + case DYNTREE_JSON_HUMAN: + break; + + default: return INVALID_PARAMS; + } + + data->encset = setting; + return SUCCESS; +} + + +dt_err dtree_encode_json(dtree *data, char *json_data) +{ + if(data == NULL) return INVALID_PARAMS; + + /* Check if setting is valid */ + // switch(data->encset) { + // case DYNTREE_JSON_MINIFIED: + // case DYNTREE_JSON_HUMAN: + // break; + // + // default: return INVALID_PARAMS; + // } + + /* Assume mode for all children */ + json_len = 0; + + char *open = "{"; + char *close = "}"; + + if(data->type == LIST) { + + fflush(stdout); + append(json_data, open); + + /* Iterate through all it's children */ + int i; + for(i = 0; i < data->used; i++) { + dtree *child = data->payload.list[i]; + + if(child->type == PAIR) { + dtree *key = child->payload.list[0]; + dtree *val = child->payload.list[1]; + + char kkey[1024]; + parse_key_value(key, kkey); + fflush(stdout); + append(json_data, kkey); + memset(kkey, 0, 1024); + + char vval[1024]; + parse_value_list(val, vval, json_data, (i == data->used - 1) ? TRUE : FALSE); + fflush(stdout); + + append(json_data, vval); + memset(vval, 0, 1024); + + } else if(child->type == LIST) { + dt_err err = dtree_encode_json(child, json_data); + if(err) return err; + } + } + + } else { + return INVALID_PAYLOAD; + } + + /* Add closing } and null terminator to finish */ + append(json_data, close); + append(json_data, "\0"); + + return SUCCESS; +} + + +dt_err dtree_decode_json(dtree *(*data), const char *jd) +{ + /* Always create an empty root node */ + + int ctr = -1; + dtree *parents[32]; // Only support 32 deep for now + memset(parents, 0, sizeof(dtree*) * 32); + +#define IN_STRING 9 +#define NEUTRAL 10 + + /** Parse stack */ + int in_str = 0; + char curr_key[512]; int key_inx = 0; + char curr_str[512]; int str_inx = 0; + char curr_num[512]; int num_inx = 0; + + memset(curr_key, 0, 512); + memset(curr_str, 0, 512); + memset(curr_num, 0, 512); + + /* Get the first character of our json string */ + int jd_len = (int) REAL_STRLEN(jd); + int pos = 0; + char curr; + + for (; pos < jd_len && jd[pos] != '\0'; pos++) { + curr = jd[pos]; + + switch(curr) { + case '{': + { + dtree *new_root; + dtree_malloc(&new_root); + + if(ctr < 0) { + parents[ctr = 0] = new_root; + } else { + dtree_addlist(parents[ctr], &new_root); + parents[++ctr] = new_root; + } + + break; + } + case '[': + { + if(in_str) break; // Ignore if we're in a string + + dtree *new_root; + dtree_addlist(parents[ctr], &new_root); + parents[++ctr] = new_root; + break; + } + + case '}': + case ']': + { + if(in_str) { + } else { + if(curr_key[0] != '\0') { + + dtree *key, *val; + dtree *rec_entry; + + dtree_addlist(parents[ctr], &rec_entry); + dtree_addpair(rec_entry, &key, &val); + dtree_addliteral(key, curr_key); + dtree_addliteral(val, curr_str); + + /* Clear the pointer reference */ + rec_entry = key = val = NULL; + + memset(curr_key, 0, (size_t) key_inx); + memset(curr_str, 0, (size_t) str_inx); + key_inx = 0; + str_inx = 0; + } + + if(ctr > 0) parents[ctr--] = NULL; // Remove current parent again + } + break; + } + + case '"': + { + in_str = (in_str) ? FALSE : TRUE; + break; + } + + case ',': + { + dtree *key, *val; + dtree *rec_entry; + + /* Add a new pair as a list item */ + dtree_addlist(parents[ctr], &rec_entry); + dtree_addpair(rec_entry, &key, &val); + dtree_addliteral(key, curr_key); + + /* Either make it a literal or number node */ + if(num_inx > 0) + dtree_addnumeral(val, to_long(curr_num)); + else + dtree_addliteral(val, curr_str); + + /* Clear the pointer reference */ + rec_entry = key = val = NULL; + + /* Reset the key/ value status */ + memset(curr_key, 0, (size_t) key_inx); + memset(curr_str, 0, (size_t) str_inx); + memset(curr_num, 0, (size_t) num_inx); + key_inx = 0; + str_inx = 0; + num_inx = 0; + break; + } + + case ':': + { + if(in_str) break; // Ignore if we're in a string + + // End a key + strcpy(curr_key, curr_str); + memset(curr_str, 0, (size_t) str_inx); + key_inx = str_inx; + str_inx = 0; + break; + } + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + if(in_str) { + append_char(curr_str, &str_inx, curr); + } else { + append_char(curr_num, &num_inx, curr); + } + break; + } + + default: + { + if(in_str) append_char(curr_str, &str_inx, curr); + break; + } + } + } + + /* Allocate our first node */ + *data = parents[0]; + dtree_print(*data); + + return SUCCESS; +} + + +/**************** ENCODER UTILITY FUNCTIONS ******************/ + +int parse_key_value(dtree *key, char *buffer) +{ + if(key->type != LITERAL) 5; + + size_t key_len = key->used; + int base = 3; + + /* Make an array that will survive function switch */ + char lit[key_len + base + 1]; + + strcpy(buffer, "\""); + strcat(buffer, key->payload.literal); + strcat(buffer, "\":"); + strcat(buffer, "\0"); + return 0; +} + +const char *parse_value_list(dtree *value, char *buffer, char *global, int last) +{ + if(value == NULL) return "[ERROR]"; + + /* The new offset we need (in \t) */ + int i; + + switch(value->type) { + case LITERAL: + { + size_t key_len = value->used; + + strcpy(buffer, "\""); + strcat(buffer, value->payload.literal); + strcat(buffer, "\""); + strcat(buffer, "\0"); + + if(last == 0) strcat(buffer, ","); + break; + } + + case NUMERIC: + { + char str[15]; + sprintf(str, "%ld", value->payload.numeral); + + strcat(buffer, str); + if(last == 0) strcat(buffer, ","); + strcat(buffer, "\0"); + + break; + } + + case LIST: + { + if(value->used > 0) { + + dt_uni_t test = value->payload.list[0]->type; + + if(test == LITERAL || test == NUMERIC) { + fflush(stdout); + + int j; + for(j = 0; j < value->used; j++) { + dtree *child = value->payload.list[j]; + + char vall[1024]; + parse_value_list(child, vall, global, (j == value->used - 1) ? TRUE : FALSE); + fflush(stdout); + } + + fflush(stdout); + + } else if(test == PAIR) { + fflush(stdout); + append(global, "{"); + + int j; + for(j = 0; j < value->used; j++) { + dtree *child = value->payload.list[j]; + + if(child->type == PAIR) { + dtree *key = child->payload.list[0]; + dtree *val = child->payload.list[1]; + + char kkey[1024]; + + parse_key_value(key, kkey); + fflush(stdout); + append(global, kkey); + + char vval[1024]; + parse_value_list(val, vval, global, (j == child->used - 1) ? TRUE : FALSE); + fflush(stdout); + + append(global, vval); + } + } + + fflush(stdout); + append(global, "}"); + } + + } else { + fflush(stdout); + } + } + + default: INVALID_PAYLOAD; + } + + return ""; +} + +void append(char *buffer, char *message) +{ + int msg_len = (int) strlen(message); + sprintf(buffer + json_len, message); + json_len += msg_len; +} + +/**************** DECODER UTILITY FUNCTIONS ******************/ + +void append_char(char *buffer, int *ctr, char c) +{ + sprintf(buffer + (*ctr), "%c", c); + (*ctr)++; +} + +long to_long(char *buffer) +{ + return atol(buffer); +} diff --git a/lib/dyn_utils.c b/lib/dyn_utils.c deleted file mode 100644 index 6912dd239036..000000000000 --- a/lib/dyn_utils.c +++ /dev/null @@ -1,408 +0,0 @@ -#include -#include -#include - - -/*** Forward declared functions ***/ - -#define TRUE 1 -#define FALSE 0 - -static int json_len = 0; - -/************* Forward Function Declarations *************/ - -// Functions required by encoder -int human(short ); - -void append(char *, char *); - -int parse_key_value(dtree *, char *); - -const char *parse_value_list(dtree *, char *, char *, int ); - -// Functions required by decoder - -void append_char(char *, int *, char ); - -long to_long(char *); - -/*********************************************************/ - -const char *rdb_error_getmsg(dt_err *e) -{ - -} - -dt_err dtree_encode_set(dtree *data, short setting) -{ - if(data == NULL) return INVALID_PARAMS; - - /* Check if setting is valid */ - switch(setting) { - case DYNTREE_ENCODE_NONE: - case DYNTREE_JSON_MINIFIED: - case DYNTREE_JSON_HUMAN: - break; - - default: return INVALID_PARAMS; - } - - data->encset = setting; - return SUCCESS; -} - - -dt_err dtree_encode_json(dtree *data, char *json_data) -{ - if(data == NULL) return INVALID_PARAMS; - - /* Check if setting is valid */ - // switch(data->encset) { - // case DYNTREE_JSON_MINIFIED: - // case DYNTREE_JSON_HUMAN: - // break; - // - // default: return INVALID_PARAMS; - // } - - /* Assume mode for all children */ - json_len = 0; - - char *open = "{"; - char *close = "}"; - - if(data->type == LIST) { - - fflush(stdout); - append(json_data, open); - - /* Iterate through all it's children */ - int i; - for(i = 0; i < data->used; i++) { - dtree *child = data->payload.list[i]; - - if(child->type == PAIR) { - dtree *key = child->payload.list[0]; - dtree *val = child->payload.list[1]; - - char kkey[1024]; - parse_key_value(key, kkey); - fflush(stdout); - append(json_data, kkey); - memset(kkey, 0, 1024); - - char vval[1024]; - parse_value_list(val, vval, json_data, (i == data->used - 1) ? TRUE : FALSE); - fflush(stdout); - - append(json_data, vval); - memset(vval, 0, 1024); - - } else if(child->type == LIST) { - dt_err err = dtree_encode_json(child, json_data); - if(err) return err; - } - } - - } else { - return INVALID_PAYLOAD; - } - - /* Add closing } and null terminator to finish */ - append(json_data, close); - append(json_data, "\0"); - - return SUCCESS; -} - - -dt_err dtree_decode_json(dtree *(*data), const char *jd) -{ - /* Always create an empty root node */ - - int ctr = -1; - dtree *parents[32]; // Only support 32 deep for now - memset(parents, 0, sizeof(dtree*) * 32); - -#define IN_STRING 9 -#define NEUTRAL 10 - - /** Parse stack */ - int in_str = 0; - char curr_key[512]; int key_inx = 0; - char curr_str[512]; int str_inx = 0; - char curr_num[512]; int num_inx = 0; - - memset(curr_key, 0, 512); - memset(curr_str, 0, 512); - memset(curr_num, 0, 512); - - /* Get the first character of our json string */ - int jd_len = (int) REAL_STRLEN(jd); - int pos = 0; - char curr; - - for (; pos < jd_len && jd[pos] != '\0'; pos++) { - curr = jd[pos]; - - switch(curr) { - case '{': - { - dtree *new_root; - dtree_malloc(&new_root); - - if(ctr < 0) { - parents[ctr = 0] = new_root; - } else { - dtree_addlist(parents[ctr], &new_root); - parents[++ctr] = new_root; - } - - break; - } - case '[': - { - if(in_str) break; // Ignore if we're in a string - - dtree *new_root; - dtree_addlist(parents[ctr], &new_root); - parents[++ctr] = new_root; - break; - } - - case '}': - case ']': - { - if(in_str) { - } else { - if(curr_key[0] != '\0') { - - dtree *key, *val; - dtree *rec_entry; - - dtree_addlist(parents[ctr], &rec_entry); - dtree_addpair(rec_entry, &key, &val); - dtree_addliteral(key, curr_key); - dtree_addliteral(val, curr_str); - - /* Clear the pointer reference */ - rec_entry = key = val = NULL; - - memset(curr_key, 0, (size_t) key_inx); - memset(curr_str, 0, (size_t) str_inx); - key_inx = 0; - str_inx = 0; - } - - if(ctr > 0) parents[ctr--] = NULL; // Remove current parent again - } - break; - } - - case '"': - { - in_str = (in_str) ? FALSE : TRUE; - break; - } - - case ',': - { - dtree *key, *val; - dtree *rec_entry; - - /* Add a new pair as a list item */ - dtree_addlist(parents[ctr], &rec_entry); - dtree_addpair(rec_entry, &key, &val); - dtree_addliteral(key, curr_key); - - /* Either make it a literal or number node */ - if(num_inx > 0) - dtree_addnumeral(val, to_long(curr_num)); - else - dtree_addliteral(val, curr_str); - - /* Clear the pointer reference */ - rec_entry = key = val = NULL; - - /* Reset the key/ value status */ - memset(curr_key, 0, (size_t) key_inx); - memset(curr_str, 0, (size_t) str_inx); - memset(curr_num, 0, (size_t) num_inx); - key_inx = 0; - str_inx = 0; - num_inx = 0; - break; - } - - case ':': - { - if(in_str) break; // Ignore if we're in a string - - // End a key - strcpy(curr_key, curr_str); - memset(curr_str, 0, (size_t) str_inx); - key_inx = str_inx; - str_inx = 0; - break; - } - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - if(in_str) { - append_char(curr_str, &str_inx, curr); - } else { - append_char(curr_num, &num_inx, curr); - } - break; - } - - default: - { - if(in_str) append_char(curr_str, &str_inx, curr); - break; - } - } - } - - /* Allocate our first node */ - *data = parents[0]; - dtree_print(*data); - - return SUCCESS; -} - - -/**************** ENCODER UTILITY FUNCTIONS ******************/ - -int parse_key_value(dtree *key, char *buffer) -{ - if(key->type != LITERAL) 5; - - size_t key_len = key->used; - int base = 3; - - /* Make an array that will survive function switch */ - char lit[key_len + base + 1]; - - strcpy(buffer, "\""); - strcat(buffer, key->payload.literal); - strcat(buffer, "\":"); - strcat(buffer, "\0"); - return 0; -} - -const char *parse_value_list(dtree *value, char *buffer, char *global, int last) -{ - if(value == NULL) return "[ERROR]"; - - /* The new offset we need (in \t) */ - int i; - - switch(value->type) { - case LITERAL: - { - size_t key_len = value->used; - - strcpy(buffer, "\""); - strcat(buffer, value->payload.literal); - strcat(buffer, "\""); - strcat(buffer, "\0"); - - if(last == 0) strcat(buffer, ","); - break; - } - - case NUMERIC: - { - char str[15]; - sprintf(str, "%ld", value->payload.numeral); - - strcat(buffer, str); - if(last == 0) strcat(buffer, ","); - strcat(buffer, "\0"); - - break; - } - - case LIST: - { - if(value->used > 0) { - - dt_uni_t test = value->payload.list[0]->type; - - if(test == LITERAL || test == NUMERIC) { - fflush(stdout); - - int j; - for(j = 0; j < value->used; j++) { - dtree *child = value->payload.list[j]; - - char vall[1024]; - parse_value_list(child, vall, global, (j == value->used - 1) ? TRUE : FALSE); - fflush(stdout); - } - - fflush(stdout); - - } else if(test == PAIR) { - fflush(stdout); - append(global, "{"); - - int j; - for(j = 0; j < value->used; j++) { - dtree *child = value->payload.list[j]; - - if(child->type == PAIR) { - dtree *key = child->payload.list[0]; - dtree *val = child->payload.list[1]; - - char kkey[1024]; - - parse_key_value(key, kkey); - fflush(stdout); - append(global, kkey); - - char vval[1024]; - parse_value_list(val, vval, global, (j == child->used - 1) ? TRUE : FALSE); - fflush(stdout); - - append(global, vval); - } - } - - fflush(stdout); - append(global, "}"); - } - - } else { - fflush(stdout); - } - } - - default: INVALID_PAYLOAD; - } - - return ""; -} - -void append(char *buffer, char *message) -{ - int msg_len = (int) strlen(message); - sprintf(buffer + json_len, message); - json_len += msg_len; -} - -/**************** DECODER UTILITY FUNCTIONS ******************/ - -void append_char(char *buffer, int *ctr, char c) -{ - sprintf(buffer + (*ctr), "%c", c); - (*ctr)++; -} - -long to_long(char *buffer) -{ - return atol(buffer); -} -- cgit v1.2.3 From b37e6ac63cd8bf607b734a7b030640782e7442fd Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 11 Dec 2016 03:09:55 +0100 Subject: Adding work-in-progress new json parser --- CMakeLists.txt | 2 +- lib/dtree_utils.c | 449 +++++++++++------------------------------------------- 2 files changed, 86 insertions(+), 365 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57359472ca5a..1ceec9148d30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ project(libdyntree) set(DYN_TREE_SRC lib/dtree.c lib/eztree.c - lib/dyn_utils.c) + lib/dtree_utils.c) # Define our library in cmake add_library(libdyntree SHARED ${DYN_TREE_SRC}) diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c index 6912dd239036..082ece3d87bb 100644 --- a/lib/dtree_utils.c +++ b/lib/dtree_utils.c @@ -2,407 +2,128 @@ #include #include +void pk_string_trim(char *src, char *dst); -/*** Forward declared functions ***/ - -#define TRUE 1 -#define FALSE 0 - -static int json_len = 0; - -/************* Forward Function Declarations *************/ - -// Functions required by encoder -int human(short ); - -void append(char *, char *); - -int parse_key_value(dtree *, char *); - -const char *parse_value_list(dtree *, char *, char *, int ); - -// Functions required by decoder - -void append_char(char *, int *, char ); - -long to_long(char *); - -/*********************************************************/ - -const char *rdb_error_getmsg(dt_err *e) -{ - -} - -dt_err dtree_encode_set(dtree *data, short setting) +dt_err dtree_decode_json(dtree *(*data), const char *json_data) { - if(data == NULL) return INVALID_PARAMS; - - /* Check if setting is valid */ - switch(setting) { - case DYNTREE_ENCODE_NONE: - case DYNTREE_JSON_MINIFIED: - case DYNTREE_JSON_HUMAN: - break; - - default: return INVALID_PARAMS; - } - - data->encset = setting; - return SUCCESS; -} + enum parse_state { + VALUE, HASH, LIST, WAITING + }; +#define BUF_SIZE 256 -dt_err dtree_encode_json(dtree *data, char *json_data) -{ - if(data == NULL) return INVALID_PARAMS; + /* Save some space for a token */ + const char *delims = ",:"; - /* Check if setting is valid */ - // switch(data->encset) { - // case DYNTREE_JSON_MINIFIED: - // case DYNTREE_JSON_HUMAN: - // break; - // - // default: return INVALID_PARAMS; - // } + char *parse; + char curr_key[BUF_SIZE]; + char curr_val[BUF_SIZE]; + dtree *root, *curr_root; + enum parse_state state = WAITING; - /* Assume mode for all children */ - json_len = 0; + /* Prepare environment for parsing */ + char json_buf[REAL_STRLEN(json_data)]; + strcpy(json_buf, json_data); - char *open = "{"; - char *close = "}"; + /* Setup root dtree node */ + dtree_malloc(&root); + curr_root = root; - if(data->type == LIST) { + /* Read in the first token */ + parse = strtok(json_buf, delims); - fflush(stdout); - append(json_data, open); + while(parse != NULL) { - /* Iterate through all it's children */ - int i; - for(i = 0; i < data->used; i++) { - dtree *child = data->payload.list[i]; + char tok[strlen(parse) + 1]; + memset(tok, 0, strlen(parse) + 1); - if(child->type == PAIR) { - dtree *key = child->payload.list[0]; - dtree *val = child->payload.list[1]; + pk_string_trim(parse, tok); - char kkey[1024]; - parse_key_value(key, kkey); - fflush(stdout); - append(json_data, kkey); - memset(kkey, 0, 1024); + /* Open a new hash context */ + if(tok[0] == '{') { - char vval[1024]; - parse_value_list(val, vval, json_data, (i == data->used - 1) ? TRUE : FALSE); - fflush(stdout); + dtree *new_root; + dtree_addlist(curr_root, &new_root); - append(json_data, vval); - memset(vval, 0, 1024); + curr_root = new_root; - } else if(child->type == LIST) { - dt_err err = dtree_encode_json(child, json_data); - if(err) return err; - } + printf("Creating new hash context...\n"); + state = HASH; } - } else { - return INVALID_PAYLOAD; - } - - /* Add closing } and null terminator to finish */ - append(json_data, close); - append(json_data, "\0"); - - return SUCCESS; -} - - -dt_err dtree_decode_json(dtree *(*data), const char *jd) -{ - /* Always create an empty root node */ - - int ctr = -1; - dtree *parents[32]; // Only support 32 deep for now - memset(parents, 0, sizeof(dtree*) * 32); - -#define IN_STRING 9 -#define NEUTRAL 10 - - /** Parse stack */ - int in_str = 0; - char curr_key[512]; int key_inx = 0; - char curr_str[512]; int str_inx = 0; - char curr_num[512]; int num_inx = 0; - - memset(curr_key, 0, 512); - memset(curr_str, 0, 512); - memset(curr_num, 0, 512); - - /* Get the first character of our json string */ - int jd_len = (int) REAL_STRLEN(jd); - int pos = 0; - char curr; - - for (; pos < jd_len && jd[pos] != '\0'; pos++) { - curr = jd[pos]; - - switch(curr) { - case '{': - { - dtree *new_root; - dtree_malloc(&new_root); - - if(ctr < 0) { - parents[ctr = 0] = new_root; - } else { - dtree_addlist(parents[ctr], &new_root); - parents[++ctr] = new_root; - } - - break; - } - case '[': - { - if(in_str) break; // Ignore if we're in a string - - dtree *new_root; - dtree_addlist(parents[ctr], &new_root); - parents[++ctr] = new_root; - break; - } - - case '}': - case ']': - { - if(in_str) { - } else { - if(curr_key[0] != '\0') { - - dtree *key, *val; - dtree *rec_entry; - - dtree_addlist(parents[ctr], &rec_entry); - dtree_addpair(rec_entry, &key, &val); - dtree_addliteral(key, curr_key); - dtree_addliteral(val, curr_str); - - /* Clear the pointer reference */ - rec_entry = key = val = NULL; - - memset(curr_key, 0, (size_t) key_inx); - memset(curr_str, 0, (size_t) str_inx); - key_inx = 0; - str_inx = 0; - } - - if(ctr > 0) parents[ctr--] = NULL; // Remove current parent again - } - break; - } - - case '"': - { - in_str = (in_str) ? FALSE : TRUE; - break; - } - - case ',': - { - dtree *key, *val; - dtree *rec_entry; - - /* Add a new pair as a list item */ - dtree_addlist(parents[ctr], &rec_entry); - dtree_addpair(rec_entry, &key, &val); - dtree_addliteral(key, curr_key); - - /* Either make it a literal or number node */ - if(num_inx > 0) - dtree_addnumeral(val, to_long(curr_num)); - else - dtree_addliteral(val, curr_str); - - /* Clear the pointer reference */ - rec_entry = key = val = NULL; - - /* Reset the key/ value status */ - memset(curr_key, 0, (size_t) key_inx); - memset(curr_str, 0, (size_t) str_inx); - memset(curr_num, 0, (size_t) num_inx); - key_inx = 0; - str_inx = 0; - num_inx = 0; - break; - } - - case ':': - { - if(in_str) break; // Ignore if we're in a string - - // End a key - strcpy(curr_key, curr_str); - memset(curr_str, 0, (size_t) str_inx); - key_inx = str_inx; - str_inx = 0; - break; - } - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - if(in_str) { - append_char(curr_str, &str_inx, curr); - } else { - append_char(curr_num, &num_inx, curr); - } - break; - } - - default: - { - if(in_str) append_char(curr_str, &str_inx, curr); - break; - } + /* Open a new list context - finishing a PAIR - Back to waiting */ + if(tok[0] == '[') { + printf("Creating new hash context...\n"); + state = LIST; } - } - - /* Allocate our first node */ - *data = parents[0]; - dtree_print(*data); - - return SUCCESS; -} - - -/**************** ENCODER UTILITY FUNCTIONS ******************/ - -int parse_key_value(dtree *key, char *buffer) -{ - if(key->type != LITERAL) 5; - - size_t key_len = key->used; - int base = 3; - - /* Make an array that will survive function switch */ - char lit[key_len + base + 1]; - - strcpy(buffer, "\""); - strcat(buffer, key->payload.literal); - strcat(buffer, "\":"); - strcat(buffer, "\0"); - return 0; -} - -const char *parse_value_list(dtree *value, char *buffer, char *global, int last) -{ - if(value == NULL) return "[ERROR]"; - - /* The new offset we need (in \t) */ - int i; - switch(value->type) { - case LITERAL: - { - size_t key_len = value->used; + /* If we're in a hash & waiting for a key */ + if(state == HASH) { + if(tok[0] == '{') strcpy(curr_key, tok + 1); + else strcpy(curr_key, tok); - strcpy(buffer, "\""); - strcat(buffer, value->payload.literal); - strcat(buffer, "\""); - strcat(buffer, "\0"); - - if(last == 0) strcat(buffer, ","); - break; - } - - case NUMERIC: - { - char str[15]; - sprintf(str, "%ld", value->payload.numeral); - - strcat(buffer, str); - if(last == 0) strcat(buffer, ","); - strcat(buffer, "\0"); - - break; + printf("Current Key: %s\n", curr_key); + state = VALUE; + goto END; } - case LIST: - { - if(value->used > 0) { - - dt_uni_t test = value->payload.list[0]->type; - - if(test == LITERAL || test == NUMERIC) { - fflush(stdout); - - int j; - for(j = 0; j < value->used; j++) { - dtree *child = value->payload.list[j]; - - char vall[1024]; - parse_value_list(child, vall, global, (j == value->used - 1) ? TRUE : FALSE); - fflush(stdout); - } - - fflush(stdout); + /* We already had a key - finishing up the pair */ + if(state == VALUE) { + strcpy(curr_val, tok); + printf("Current Val: %s\n", curr_val); - } else if(test == PAIR) { - fflush(stdout); - append(global, "{"); + /* Copy pair into dtree structure */ + dtree *parent, *key, *val; + dtree_addlist(curr_root, &parent); - int j; - for(j = 0; j < value->used; j++) { - dtree *child = value->payload.list[j]; + /* Make the "parent" node into the pair parent */ + dtree_addpair(parent, &key, &val); + dtree_addliteral(key, curr_key); + dtree_addliteral(val, curr_val); - if(child->type == PAIR) { - dtree *key = child->payload.list[0]; - dtree *val = child->payload.list[1]; + /* Add the parent */ - char kkey[1024]; + /* Blank current pair data */ + memset(curr_key, 0, BUF_SIZE); + memset(curr_val, 0, BUF_SIZE); - parse_key_value(key, kkey); - fflush(stdout); - append(global, kkey); - - char vval[1024]; - parse_value_list(val, vval, global, (j == child->used - 1) ? TRUE : FALSE); - fflush(stdout); + state = HASH; + goto END; + } - append(global, vval); - } - } + if(state == LIST) { + dtree *child; + dtree_addlist(curr_root, &child); + dtree_addliteral(child, tok); - fflush(stdout); - append(global, "}"); - } + size_t chs = strlen(tok); + dtree *parent; - } else { - fflush(stdout); + dtree_parent(root, curr_root, &parent); + if(tok[chs] == ']') { + curr_root = parent; + state = HASH; } } - default: INVALID_PAYLOAD; + printf(" Recognised token: %s\n", tok); + END: + parse = strtok(NULL, delims); } - return ""; -} - -void append(char *buffer, char *message) -{ - int msg_len = (int) strlen(message); - sprintf(buffer + json_len, message); - json_len += msg_len; + return SUCCESS; } -/**************** DECODER UTILITY FUNCTIONS ******************/ -void append_char(char *buffer, int *ctr, char c) -{ - sprintf(buffer + (*ctr), "%c", c); - (*ctr)++; -} +/************************************************************/ -long to_long(char *buffer) +void pk_string_trim(char *src, char *dst) { - return atol(buffer); -} + int s, d=0; + for (s=0; src[s] != 0; s++) + if (src[s] != ' ') { + dst[d] = src[s]; + d++; + } + dst[d] = 0; +} \ No newline at end of file -- cgit v1.2.3 From 379046562d8f0c24d50168a5c9dad6a3e4c4ab91 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 11 Dec 2016 16:37:51 +0100 Subject: Adding a static copy of the jasmine json tokeniser --- lib/jsmn.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/jsmn.h | 98 ++++++++++++++++++ 2 files changed, 437 insertions(+) create mode 100644 lib/jsmn.c create mode 100644 lib/jsmn.h diff --git a/lib/jsmn.c b/lib/jsmn.c new file mode 100644 index 000000000000..a006d609f5b3 --- /dev/null +++ b/lib/jsmn.c @@ -0,0 +1,339 @@ +/* + * 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 new file mode 100644 index 000000000000..2f588e863c78 --- /dev/null +++ b/lib/jsmn.h @@ -0,0 +1,98 @@ +/* + * 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_ */ -- cgit v1.2.3 From 3a24d54a98effc87a0c415d83ef774e02f3a75cc Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 11 Dec 2016 16:38:56 +0100 Subject: Changing parser API slightly and adding compile support to build system --- CMakeLists.txt | 5 ++++- include/dtree/dtree.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ceec9148d30..fc96527e86e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,10 @@ project(libdyntree) set(DYN_TREE_SRC lib/dtree.c lib/eztree.c - lib/dtree_utils.c) + lib/dtree_utils.c + + # External files compiled in + lib/jsmn.c) # Define our library in cmake add_library(libdyntree SHARED ${DYN_TREE_SRC}) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index a3150144d373..c0ab82490e5b 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -357,7 +357,7 @@ dt_err dtree_encode_json(dtree *data, char *json_data); * @param json_data Input json string * @return */ -dt_err dtree_decode_json(dtree *(*data), const char *json_data); +dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len); #ifdef __cplusplus } -- cgit v1.2.3 From 877f10e3f6618fd761b71bbb5e839b4a4f98760f Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 11 Dec 2016 16:39:13 +0100 Subject: Fixing a bug where parent searches were always false --- lib/dtree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtree.c b/lib/dtree.c index e45d453c8ce0..cc2fa91ec92d 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -343,7 +343,7 @@ dt_err dtree_parent(dtree *root, dtree *data, dtree **parent) /* Blank the search pointer for easy error checking */ (*parent) = NULL; - switch(data->type) { + switch(root->type) { /* Dead-end data stores automatically return @{NODE_NOT_FOUND} */ case POINTER: -- cgit v1.2.3 From e8e503ed6ef043f0ca9d540290adc5ca61b8551e Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 11 Dec 2016 16:39:43 +0100 Subject: Impementing a functional json parser on top of the tokeniser --- .gitignore | 1 + lib/dtree_utils.c | 264 +++++++++++++++++++++++++++--------------- test/main.c | 341 ++++++++++++++++++++++++++++++------------------------ 3 files changed, 361 insertions(+), 245 deletions(-) diff --git a/.gitignore b/.gitignore index 739405323f07..1212a6347158 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ build/ .idea/ +cmake-build-debug diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c index 082ece3d87bb..3d1c39441af8 100644 --- a/lib/dtree_utils.c +++ b/lib/dtree_utils.c @@ -1,129 +1,207 @@ #include #include +#include #include -void pk_string_trim(char *src, char *dst); +#include "jsmn.h" -dt_err dtree_decode_json(dtree *(*data), const char *json_data) -{ - enum parse_state { - VALUE, HASH, LIST, WAITING - }; - -#define BUF_SIZE 256 - - /* Save some space for a token */ - const char *delims = ",:"; - char *parse; - char curr_key[BUF_SIZE]; - char curr_val[BUF_SIZE]; - dtree *root, *curr_root; - enum parse_state state = WAITING; +#define DTREE_TOK_BOOLEAN (1 << 1) +#define DTREE_TOK_NUMERICAL (1 << 2) +#define DTREE_TOK_LITERAL (1 << 3) - /* Prepare environment for parsing */ - char json_buf[REAL_STRLEN(json_data)]; - strcpy(json_buf, json_data); - /* Setup root dtree node */ - dtree_malloc(&root); - curr_root = root; +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"; + } +} - /* Read in the first token */ - parse = strtok(json_buf, delims); - while(parse != NULL) { +int digest_payload(const char *token) +{ + char* end; + size_t len = strlen(token); - char tok[strlen(parse) + 1]; - memset(tok, 0, strlen(parse) + 1); + if(len == strlen("true") || len == strlen("false")) + if(strcmp(token, "true") == 0 || strcmp(token, "false") == 0) + return DTREE_TOK_BOOLEAN; - pk_string_trim(parse, tok); + /* It could still be a number! */ + strtol(token, &end, 10); + if (!*end) return DTREE_TOK_NUMERICAL; - /* Open a new hash context */ - if(tok[0] == '{') { + return DTREE_TOK_LITERAL; +} - dtree *new_root; - dtree_addlist(curr_root, &new_root); - curr_root = new_root; +dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) +{ + jsmn_parser parse; + jsmn_init(&parse); - printf("Creating new hash context...\n"); - state = HASH; - } + // FIXME: Variable amount of tokens? + jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * len); + memset(tokens, 0, sizeof(jsmntok_t) * len); - /* Open a new list context - finishing a PAIR - Back to waiting */ - if(tok[0] == '[') { - printf("Creating new hash context...\n"); - state = LIST; - } + int ret = jsmn_parse(&parse, json_data, strlen(json_data), tokens, sizeof(tokens) / sizeof(tokens[0])); - /* If we're in a hash & waiting for a key */ - if(state == HASH) { - if(tok[0] == '{') strcpy(curr_key, tok + 1); - else strcpy(curr_key, tok); + jsmntok_t tok; + unsigned int idx = 0; - printf("Current Key: %s\n", curr_key); - state = VALUE; - goto END; - } + /** Prepare dtree nodes */ + dtree *root, *curr; + dtree_malloc(&root); + curr = root; - /* We already had a key - finishing up the pair */ - if(state == VALUE) { - strcpy(curr_val, tok); - printf("Current Val: %s\n", curr_val); + struct bounds { + int low, high; + }; - /* Copy pair into dtree structure */ - dtree *parent, *key, *val; - dtree_addlist(curr_root, &parent); + struct pair { + short state; +#define TOK_PAIR_KEYED 1 +#define TOK_PAIR_VALUED 2 + char key[1024]; + union value { + char string[1024]; + unsigned long num; + } value; + }; - /* Make the "parent" node into the pair parent */ - dtree_addpair(parent, &key, &val); - dtree_addliteral(key, curr_key); - dtree_addliteral(val, curr_val); + /* Save some space to store token bounds */ + struct bounds *bounds = malloc(sizeof(struct bounds) * len); + memset(bounds, 0, sizeof(struct bounds) * len); + int focused = -1; - /* Add the parent */ + struct pair c_pair; + memset(&c_pair, 0, sizeof(struct pair)); - /* Blank current pair data */ - memset(curr_key, 0, BUF_SIZE); - memset(curr_val, 0, BUF_SIZE); + while(tok = tokens[idx++], tok.type != NULL) { - state = HASH; - goto END; - } + size_t tok_len = (size_t) tok.end - tok.start; + char token[tok_len]; + memset(token, 0, tok_len); + memcpy(token, json_data + tok.start, tok_len); - if(state == LIST) { - dtree *child; - dtree_addlist(curr_root, &child); - dtree_addliteral(child, tok); + /** Check if we need to move the boundry scope (again) */ + if(focused > 0 && tok.end >= bounds[focused].high) { + focused--; - size_t chs = strlen(tok); - dtree *parent; + /* Because of how our root node is a VALUE node, we need the parents parent */ + dtree *parent, *pair_parent; + dtree_parent(root, curr, &parent); + dtree_parent(root, parent, &pair_parent); - dtree_parent(root, curr_root, &parent); - if(tok[chs] == ']') { - curr_root = parent; - state = HASH; - } + /* Assign the new root node - old scope restored */ + curr = pair_parent; } - printf(" Recognised token: %s\n", tok); - END: - parse = strtok(NULL, delims); - } + 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_OBJECT: + { + focused++; + bounds[focused].low = tok.start; + bounds[focused].high = tok.end; + + /** + * 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; + } - return SUCCESS; -} + case JSMN_STRING: + { + /** + * 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; + + default: continue; + } + + /* Blank c_pair data for next tokens */ + memset(&c_pair, 0, sizeof(struct pair)); + } + + /* Skip to next token */ + continue; + } -/************************************************************/ -void pk_string_trim(char *src, char *dst) -{ - int s, d=0; - for (s=0; src[s] != 0; s++) - if (src[s] != ' ') { - dst[d] = src[s]; - d++; + default: + continue; } - dst[d] = 0; + } + + /* Switch over data pointer and return */ + (*data) = root; + return SUCCESS; } \ No newline at end of file diff --git a/test/main.c b/test/main.c index 02e871b576d9..63ff52109900 100644 --- a/test/main.c +++ b/test/main.c @@ -3,6 +3,8 @@ #include #include +#include +#include /** * A small test that creates a tree, splits the nodes @@ -23,174 +25,209 @@ dt_err test_shortcut_functions(); printf(" %s\n", (err == 0) ? "OK!" : "FAILED!"); \ if(err) goto end; -int main(void) +int main(int argn, char **argv) { dt_err err = SUCCESS; printf("=== libdyntree test suite ===\n"); - TEST(split_and_merge()) +// 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); - TEST(search_for_payload()) + struct timeval t1, t2; + double elapsedTime; - char json[1024]; - TEST(json_encode(json)) + // start timer + gettimeofday(&t1, NULL); - dtree *recover; - dtree_decode_json(&recover, json); - dtree_free(recover); +#define PATH "/home/spacekookie/Downloads/generated.json" - end: - printf("==== done ====\n"); - return err; -} - - -/*************** TEST IMPLEMENTATIONS ****************/ + /* 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); -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; -} + /* 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); -dt_err search_for_payload() -{ - dt_err err; + dtree *recov; + dtree_decode_json(&recov, json, file_size); - dtree *root, *a, *b, *found; - err = dtree_malloc(&root); - if(err) goto exit; + dtree_print(recov); + dtree_free(recov); - const char *string = "This is some data!"; - err = dtree_addlist(root, &a); - if(err) goto exit; + gettimeofday(&t2, NULL); - err = dtree_addliteral(a, string); - if(err) goto exit; + // 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 - err = dtree_addlist(root, &b); - if(err) goto exit; + printf("Program took %fms to run\n", elapsedTime); - 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); +end: + printf("==== done ====\n"); 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; -} + +/*************** 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 From ced46bac3291118e136f5e3d50bc898b018276fc Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 13 Dec 2016 00:50:23 +0100 Subject: Updating the dtree API to contain a boolean value and be comfigurable to support long long numerals --- include/dtree/dtree.h | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index c0ab82490e5b..b74ea338f083 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -26,10 +26,6 @@ /* A helpful macro that can take care of \0 termated strings! */ #define REAL_STRLEN(str) (strlen(str) + 1) -#define DYNTREE_ENCODE_NONE 0x0 -#define DYNTREE_JSON_MINIFIED 0xA -#define DYNTREE_JSON_HUMAN 0xB - /* Also make sure we're _always_ interpreted as a C file */ #ifdef __cplusplus extern "C" { @@ -49,9 +45,15 @@ typedef struct dtree { short copy; union { char *literal; - long numeral; + bool bool; struct dtree *(*list); void *pointer; +#ifdef __LONG_LONG_SUPPORT__ + long long numeral; +#else + long numeral; +#endif + } payload; } dtree; @@ -97,7 +99,6 @@ dt_err dtree_resettype(dtree *data); * * @param data Reference to a dtree object * @param literal String to store - * @param length TRUE string length to use. * @return */ dt_err dtree_addliteral(dtree *data, const char *literal); @@ -113,6 +114,17 @@ dt_err dtree_addliteral(dtree *data, const char *literal); 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 * -- cgit v1.2.3 From 69fc0d982de64a7d2fdad063fd24a44b16fcab11 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 13 Dec 2016 01:43:18 +0100 Subject: Fixing a few parser issues that prevented it from working properly. Includes: - Now NULL terminates all inputs pre-emptively - Can now properly parse numerical values - Calculates heap memory field size for tokens arrayproperly --- lib/dtree_utils.c | 17 ++++++++++------- test/main.c | 6 +++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c index 3d1c39441af8..fcd543a20253 100644 --- a/lib/dtree_utils.c +++ b/lib/dtree_utils.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "jsmn.h" @@ -10,6 +11,8 @@ #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) { @@ -47,10 +50,11 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) jsmn_init(&parse); // FIXME: Variable amount of tokens? - jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * len); - memset(tokens, 0, sizeof(jsmntok_t) * len); + 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, sizeof(tokens) / sizeof(tokens[0])); + int ret = jsmn_parse(&parse, json_data, strlen(json_data), tokens, no_tokens); jsmntok_t tok; unsigned int idx = 0; @@ -66,8 +70,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) struct pair { short state; -#define TOK_PAIR_KEYED 1 -#define TOK_PAIR_VALUED 2 char key[1024]; union value { char string[1024]; @@ -86,8 +88,8 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) while(tok = tokens[idx++], tok.type != NULL) { size_t tok_len = (size_t) tok.end - tok.start; - char token[tok_len]; - memset(token, 0, tok_len); + 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) */ @@ -150,6 +152,7 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) continue; } + case JSMN_PRIMITIVE: case JSMN_STRING: { /** diff --git a/test/main.c b/test/main.c index 63ff52109900..3a4412955ddc 100644 --- a/test/main.c +++ b/test/main.c @@ -47,7 +47,7 @@ int main(int argn, char **argv) // start timer gettimeofday(&t1, NULL); -#define PATH "/home/spacekookie/Downloads/generated.json" +#define PATH "/home/spacekookie/Downloads/MOCK_DATA.json" /* Open the file and seek through it for length */ FILE *f = fopen(PATH, "r"); @@ -61,6 +61,10 @@ int main(int argn, char **argv) 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); -- cgit v1.2.3 From 1662652061ed68fcddf3789bf5f20087510b101c Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 13 Dec 2016 02:03:04 +0100 Subject: Adding support for boolean nodes as well as long long supports via compile options --- include/dtree/dtree.h | 5 +++-- lib/dtree.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/include/dtree/dtree.h b/include/dtree/dtree.h index b74ea338f083..5193abac3771 100644 --- a/include/dtree/dtree.h +++ b/include/dtree/dtree.h @@ -22,6 +22,7 @@ #define _DYNTREE_H_ #include +#include /* A helpful macro that can take care of \0 termated strings! */ #define REAL_STRLEN(str) (strlen(str) + 1) @@ -34,7 +35,7 @@ extern "C" { /* Type that determines what data is stored inside a tree-node */ typedef enum dt_uni_t { - UNSET, LITERAL, NUMERIC, LIST, PAIR, POINTER + UNSET, LITERAL, NUMERIC, LONG_NUMERIC, BOOLEAN, LIST, PAIR, POINTER } dt_uni_t; @@ -45,7 +46,7 @@ typedef struct dtree { short copy; union { char *literal; - bool bool; + bool boolean; struct dtree *(*list); void *pointer; #ifdef __LONG_LONG_SUPPORT__ diff --git a/lib/dtree.c b/lib/dtree.c index cc2fa91ec92d..32adb0c10789 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -55,7 +55,7 @@ dt_err dtree_resettype(dtree *data) /* Set the data type to unset */ data->type = UNSET; - data->encset = DYNTREE_ENCODE_NONE; + data->encset = 0; data->size = 0; data->used = 0; @@ -122,6 +122,20 @@ dt_err dtree_addnumeral(dtree *data, long numeral) } +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 */ @@ -286,11 +300,15 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) switch(type) { case LITERAL: - dtree_addliteral((*copy), data->payload.literal); + dtree_addliteral(*copy, data->payload.literal); break; case NUMERIC: - dtree_addnumeral((*copy), data->payload.numeral); + dtree_addnumeral(*copy, data->payload.numeral); + break; + + case BOOLEAN: + dtree_addboolean(*copy, data->payload.boolean); break; case LIST: @@ -302,7 +320,7 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) dtree *node = data->payload.list[i]; dtree *new; - dtree_addlist((*copy), &new); + dtree_addlist(*copy, &new); dtree_copy_deep(node, &new); } @@ -312,7 +330,7 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) case PAIR: { dtree *key, *val; - dtree_addpair((*copy), &key, &val); + dtree_addpair(*copy, &key, &val); dtree *orig_key = data->payload.list[0]; dtree *orig_val = data->payload.list[1]; @@ -324,7 +342,7 @@ dt_err dtree_copy_deep(dtree *data, dtree *(*copy)) } case POINTER: - dtree_addpointer((*copy), data->payload.pointer); + dtree_addpointer(*copy, data->payload.pointer); break; default: @@ -348,6 +366,7 @@ dt_err dtree_parent(dtree *root, dtree *data, dtree **parent) /* Dead-end data stores automatically return @{NODE_NOT_FOUND} */ case POINTER: case LITERAL: + case BOOLEAN: case NUMERIC: return NODE_NOT_FOUND; @@ -399,6 +418,10 @@ dt_err dtree_copy(dtree *data, dtree *(*copy)) 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); @@ -456,6 +479,11 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ *found = data; break; + case BOOLEAN: + if(data->payload.boolean == (bool) payload) + *found = data; + break; + case POINTER: if(data->payload.pointer == payload) *found = data; @@ -469,6 +497,7 @@ dt_err dtree_search_payload(dtree *data, dtree *(*found), void *payload, dt_uni_ 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) { @@ -484,15 +513,22 @@ dt_err dtree_search_keypayload(dtree *data, dtree *(*found), void *payload, dt_u 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) { @@ -531,6 +567,10 @@ dt_err dtree_search_keypayload(dtree *data, dtree *(*found), void *payload, dt_u *found = data; break; + case BOOLEAN: + if(data->payload.boolean == (bool) payload) + *found = data; + case POINTER: if(data->payload.pointer == payload) *found = data; @@ -562,6 +602,10 @@ void list_print(dtree *data, const char *offset) 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; @@ -569,6 +613,7 @@ void list_print(dtree *data, const char *offset) 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); @@ -579,6 +624,7 @@ void list_print(dtree *data, const char *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(k_type == BOOLEAN) printf("%s[%s]", offset, (data->payload.list[1]->payload.boolean) ? "TRUE" : "FALSE"); if(v_type == LIST || k_type == PAIR) list_print(data->payload.list[1], new_offset); @@ -599,6 +645,7 @@ void list_print(dtree *data, const char *offset) switch(t) { case LITERAL: + case BOOLEAN: case NUMERIC: list_print(data->payload.list[i], new_offset); continue; @@ -635,6 +682,7 @@ 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; } @@ -696,6 +744,7 @@ 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"; -- cgit v1.2.3 From e7ab531686126ebfc8b7e46996447b15f3ad447a Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 13 Dec 2016 02:05:04 +0100 Subject: Adding boolean support to the json parser --- lib/dtree_utils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c index fcd543a20253..d968d00e0f4a 100644 --- a/lib/dtree_utils.c +++ b/lib/dtree_utils.c @@ -186,6 +186,10 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) dtree_addnumeral(val, atol(token)); break; + case DTREE_TOK_BOOLEAN: + dtree_addboolean(val, (strcpy(token, "true") == 0) ? true : false); + break; + default: continue; } -- cgit v1.2.3 From eaf61dbb340fd537162d2e88079a2aa9b9ad0bdc Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 13 Dec 2016 02:17:57 +0100 Subject: Fixed an issue where printing boolean values in a tree wouldn't work --- lib/dtree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtree.c b/lib/dtree.c index 32adb0c10789..11090d8366fc 100644 --- a/lib/dtree.c +++ b/lib/dtree.c @@ -624,7 +624,7 @@ void list_print(dtree *data, const char *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(k_type == BOOLEAN) printf("%s[%s]", offset, (data->payload.list[1]->payload.boolean) ? "TRUE" : "FALSE"); + 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); -- cgit v1.2.3 From 45025166f3299f32d63f34db0375adb7d81ff3db Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 13 Dec 2016 11:44:03 +0100 Subject: Added support for array parsing --- lib/dtree_utils.c | 112 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 33 deletions(-) diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c index d968d00e0f4a..f8110ecdbb6f 100644 --- a/lib/dtree_utils.c +++ b/lib/dtree_utils.c @@ -56,8 +56,8 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) int ret = jsmn_parse(&parse, json_data, strlen(json_data), tokens, no_tokens); - jsmntok_t tok; unsigned int idx = 0; + jsmntok_t tok; /** Prepare dtree nodes */ dtree *root, *curr; @@ -80,6 +80,12 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) /* 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(bounds, 0, sizeof(bool) * len); + + /* Set the currently focused node */ int focused = -1; struct pair c_pair; @@ -118,12 +124,18 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) * 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; + + + /** * 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 @@ -145,7 +157,6 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) /* Blank c_pair data for next tokens */ memset(&c_pair, 0, sizeof(struct pair)); - } /* Skip to next token */ @@ -155,47 +166,82 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) case JSMN_PRIMITIVE: case JSMN_STRING: { + /** - * 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 + * 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(c_pair.state == 0) { - memcpy(c_pair.key, json_data + tok.start, (size_t) tok.end - tok.start); - c_pair.state = TOK_PAIR_KEYED; + if(is_array[focused]) { - } else if(c_pair.state == TOK_PAIR_KEYED){ + dtree *val; + dtree_addlist(curr, &val); - /** Create a PAIR node under current root */ - dtree *pair, *key, *val; - dtree_addlist(curr, &pair); - dtree_addpair(pair, &key, &val); + /* Parse payload and asign to value node */ + switch(digest_payload(token)) { + case DTREE_TOK_LITERAL: + dtree_addliteral(val, token); + break; - /* Key is always literal */ - dtree_addliteral(key, c_pair.key); + case DTREE_TOK_NUMERICAL: + dtree_addnumeral(val, atol(token)); + break; - /* Parse payload and asign to value node */ - switch(digest_payload(token)) { - case DTREE_TOK_LITERAL: - dtree_addliteral(val, token); - break; + case DTREE_TOK_BOOLEAN: + dtree_addboolean(val, (strcpy(token, "true") == 0) ? true : false); + break; - case DTREE_TOK_NUMERICAL: - dtree_addnumeral(val, atol(token)); - break; + default: continue; + } - case DTREE_TOK_BOOLEAN: - dtree_addboolean(val, (strcpy(token, "true") == 0) ? true : false); - break; + /* Blank c_pair data for next tokens */ + memset(&c_pair, 0, sizeof(struct pair)); - default: continue; - } + } else { - /* Blank c_pair data for next tokens */ - memset(&c_pair, 0, sizeof(struct pair)); - } + /** + * 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; -- cgit v1.2.3 From 805ea6f7be9c34fef416236a6aa9064e484c6714 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 13 Dec 2016 12:21:07 +0100 Subject: Adding support for list roots: [{'id': 5, ...}, {'id': 6, ...}] Which results in [LIST] [PAIR] <==> ['id'] => [5] ... structures. Advanced json parsing support is still experimental! --- lib/dtree_utils.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/dtree_utils.c b/lib/dtree_utils.c index f8110ecdbb6f..b163236139b1 100644 --- a/lib/dtree_utils.c +++ b/lib/dtree_utils.c @@ -83,7 +83,7 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) /* Have a structure to record array types in the tree */ bool *is_array = malloc(sizeof(bool) * len); - memset(bounds, 0, sizeof(bool) * len); + memset(is_array, 0, sizeof(bool) * len); /* Set the currently focused node */ int focused = -1; @@ -102,13 +102,21 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) if(focused > 0 && tok.end >= bounds[focused].high) { focused--; - /* Because of how our root node is a VALUE node, we need the parents parent */ + /** + * 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); - dtree_parent(root, parent, &pair_parent); + + if(parent->type == PAIR){ + dtree_parent(root, parent, &parent); // Override the PARENT variable + } /* Assign the new root node - old scope restored */ - curr = pair_parent; + curr = parent; } switch(tok.type) { @@ -134,7 +142,17 @@ dt_err dtree_decode_json(dtree *(*data), const char *json_data, size_t len) /* 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 -- cgit v1.2.3 From a81e5628bd1c855289a6919822cc612a6871b039 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 4 Jun 2019 20:24:02 +0200 Subject: Updating some formatting issues in the README --- README | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README b/README index c6967bfd42a6..8e48facf84d1 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ features are still experimental and the API might change at any point so be aware. 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 @@ -22,7 +22,8 @@ $> 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 +How to use +---------- Everything resolves around `dtree` objects and providing fields to API functions. Generally, memory is managed for you by libdtree: @@ -54,13 +55,16 @@ below: - 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. +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 +License +------- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. -I hope you enjoy ♥ +I hope you enjoy ❤ -- cgit v1.2.3 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 1212a6347158..de789df8581b 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 fc96527e86e3..c68025a2d176 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 000000000000..0f43db2b1c39 --- /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 000000000000..88811c470cb3 --- /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 5193abac3771..000000000000 --- 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 a5388319fdd0..000000000000 --- 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 11090d8366fc..000000000000 --- 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 b163236139b1..000000000000 --- 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 ba8506afd883..000000000000 --- 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 a006d609f5b3..000000000000 --- 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 2f588e863c78..000000000000 --- 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 000000000000..5fa7c8b81656 --- /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 3a4412955ddc..000000000000 --- 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 From d0cca976be6fb90f3724911c8a124bce56b3c5f9 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 9 Jun 2019 09:07:48 +0200 Subject: Restructuring the main API and project This commit rewrites pretty much the entire library. It is now much smaller and more maintainable (split over multiple files). It will now also support more features (that aren't implemented yet). Adding two examples to show how to use the new API. Also changing the name of the library everywhere. --- .gitignore | 34 +-- CMakeLists.txt | 13 +- README | 51 +--- array.c | 120 ++++++++ array.h | 27 ++ bowl.c | 828 +++++--------------------------------------------------- bowl.h | 413 +++++----------------------- data.c | 81 ++++++ examples/list.c | 23 ++ examples/tree.c | 46 ++++ main.c | 11 - utils.c | 46 ++++ utils.h | 19 ++ 13 files changed, 512 insertions(+), 1200 deletions(-) create mode 100644 array.c create mode 100644 array.h create mode 100644 data.c create mode 100644 examples/list.c create mode 100644 examples/tree.c delete mode 100644 main.c create mode 100644 utils.c create mode 100644 utils.h diff --git a/.gitignore b/.gitignore index de789df8581b..6f31401f7879 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 c68025a2d176..5ab2f46c755f 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 8e48facf84d1..eadb8ec88c69 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 000000000000..b0db3f5819b7 --- /dev/null +++ b/array.c @@ -0,0 +1,120 @@ +#include "bowl.h" +#include "array.h" +#include "utils.h" + +#include + + +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 000000000000..d35a1f80730d --- /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 0f43db2b1c39..4eeeb59982e7 100644 --- a/bowl.c +++ b/bowl.c @@ -1,815 +1,123 @@ -// Include our header file #include "bowl.h" +#include "array.h" -// Runtime includes #include -#include #include #include -#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 88811c470cb3..63149d200fa4 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 #include -/* 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 000000000000..9077d9b25082 --- /dev/null +++ b/data.c @@ -0,0 +1,81 @@ +#include "bowl.h" + +#include +#include +#include +#include + +/// 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 000000000000..a09588b1d91a --- /dev/null +++ b/examples/list.c @@ -0,0 +1,23 @@ +#include + +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 000000000000..e55acb900b86 --- /dev/null +++ b/examples/tree.c @@ -0,0 +1,46 @@ +#include + +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 5fa7c8b81656..000000000000 --- a/main.c +++ /dev/null @@ -1,11 +0,0 @@ -// Some static tests - -#include -#include -// #include - -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 000000000000..9e5a02799dac --- /dev/null +++ b/utils.c @@ -0,0 +1,46 @@ +#include +#include +#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 000000000000..c7c2178dfb17 --- /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 -- cgit v1.2.3 From 9130e47b171c5182ffe6c14eb710fdcb73943de4 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 13 Jul 2019 06:40:05 +0100 Subject: Adding initial support for HASH nodes This PR adds initial support for HASH data nodes in libbowl. This allows a performant key-value store lookup in a node tree. The hashing code implements the "murmur" hash, which has shown good performance over at [`libcuckoo`]. Currently there is no extended hashing strategy, which should definitely be changed. [`libcuckoo`]: https://github.com/qaul/libcuckoo (currently a collision will cause a recursive re-alloc) Some of the type-level hacks also begs the question if a PAIR data node might be warranted, even though it would break the simple design around bowl->data. --- CMakeLists.txt | 9 +++---- bowl.c | 19 ++++++++++++--- bowl.h | 19 ++++++++------- examples/list.c | 8 +++---- examples/tree.c | 10 ++++---- hash.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hash.h | 11 +++++++++ utils.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.h | 4 +++- 9 files changed, 198 insertions(+), 25 deletions(-) create mode 100644 hash.c create mode 100644 hash.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ab2f46c755f..abe82d30c181 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,11 @@ 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 array.c + bowl.c data.c - utils.c - array.c) + hash.c + utils.c) target_include_directories(bowl PUBLIC ".") @@ -15,4 +16,4 @@ add_executable(ex_tree examples/tree.c) target_link_libraries(ex_tree bowl) add_executable(ex_list examples/list.c) -target_link_libraries(ex_list bowl) \ No newline at end of file +target_link_libraries(ex_list bowl) diff --git a/bowl.c b/bowl.c index 4eeeb59982e7..3cb36b95cb2c 100644 --- a/bowl.c +++ b/bowl.c @@ -1,5 +1,6 @@ #include "bowl.h" #include "array.h" +#include "hash.h" #include #include @@ -37,7 +38,7 @@ err_t bowl_malloc(struct bowl **ptr, bowl_t type) return OK; } -err_t bowl_insert(struct bowl *self, struct bowl *new) +err_t bowl_append(struct bowl *self, struct bowl *new) { CHECK(self, INVALID_PARAMS) CHECK(new, INVALID_PARAMS) @@ -52,7 +53,19 @@ err_t bowl_insert(struct bowl *self, struct bowl *new) return OK; } -err_t bowl_insert_key(struct bowl *self, size_t idx, struct bowl *child) +err_t bowl_insert_key(struct bowl *self, char *key, struct bowl *child) +{ + CHECK(self, INVALID_PARAMS) + CHECK(child, INVALID_PARAMS) + CHECK(key, INVALID_PARAMS) + + switch(self->type) { + case HASH: return hash_insert_key(self, key, child); + default: return INVALID_PARAMS; + } +} + +err_t bowl_insert_idx(struct bowl *self, size_t idx, struct bowl *child) { CHECK(self, INVALID_PARAMS) CHECK(child, INVALID_PARAMS) @@ -66,7 +79,7 @@ err_t bowl_insert_key(struct bowl *self, size_t idx, struct bowl *child) } } -err_t bowl_swap_key(struct bowl *self, size_t idx, struct bowl *child, struct bowl **prev) +err_t bowl_swap_idx(struct bowl *self, size_t idx, struct bowl *child, struct bowl **prev) { CHECK(self, INVALID_PARAMS) CHECK(child, INVALID_PARAMS) diff --git a/bowl.h b/bowl.h index 63149d200fa4..ae7efe3f5651 100644 --- a/bowl.h +++ b/bowl.h @@ -12,7 +12,7 @@ extern "C" { #define CHECK(ptr, err) if(!ptr) return err; /** Define some generic error codes first that we can propagate **/ -typedef enum err_t { +typedef enum { OK = 0, ERR = -1, NOT_IMPLEMENTED = 1, // A runtime error for missing features @@ -22,7 +22,7 @@ typedef enum err_t { INVALID_STATE = 4, // Calling an operation on an invalid state } err_t; -typedef enum data_t { +typedef enum { LITERAL, // Any string that is \0 terminated INTEGER, // Integer value REAL, // Floating point value @@ -30,7 +30,7 @@ typedef enum data_t { RAW // A platform-length pointer } data_t; -typedef enum bowl_t { +typedef enum { LEAF = 1, ARRAY, HASH, LINKED } bowl_t; @@ -67,14 +67,17 @@ struct b_ptr { /// 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 *); +/// Insert one node at the end of another +err_t bowl_append(struct bowl *, struct bowl *); + +/// Insert a node under a specific key (relevant for HASH nodes) +err_t bowl_insert_key(struct bowl *, char *key, struct bowl *); /// Insert a node into a specific index of another node -err_t bowl_insert_key(struct bowl *, size_t idx, struct bowl *); +err_t bowl_insert_idx(struct bowl *, size_t idx, struct bowl *); /// 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 **); +err_t bowl_swap_idx(struct bowl *, size_t idx, struct bowl *, struct bowl **); /// Remove a bowl node by it's pointer reference err_t bowl_remove(struct bowl *, struct bowl *); @@ -94,4 +97,4 @@ err_t data_free(struct data *); #ifdef __cplusplus } #endif -#endif // _BOWL_H_ \ No newline at end of file +#endif // _BOWL_H_ diff --git a/examples/list.c b/examples/list.c index a09588b1d91a..84205e283f07 100644 --- a/examples/list.c +++ b/examples/list.c @@ -9,15 +9,15 @@ int main() struct bowl *a; data_malloc(&a, LITERAL, "Destroy capitalism"); - bowl_insert(root, a); + bowl_append(root, a); struct bowl *b; data_malloc(&b, INTEGER, 1312); - bowl_insert(root, b); + bowl_append(root, b); struct bowl *c; data_malloc(&c, LITERAL, "Alerta, Antifascista!"); - bowl_insert(root, c); + bowl_append(root, c); return bowl_free(root); -} \ No newline at end of file +} diff --git a/examples/tree.c b/examples/tree.c index e55acb900b86..eb1b33a8d42f 100644 --- a/examples/tree.c +++ b/examples/tree.c @@ -30,17 +30,17 @@ int main() if(e) return e; // Add the d node to c - e = bowl_insert(c, d); + e = bowl_append(c, d); if(e) e; // Add other nodes to root - e = bowl_insert(root, a); + e = bowl_append(root, a); if(e) return e; - e = bowl_insert(root, b); + e = bowl_append(root, b); if(e) return e; - e = bowl_insert(root, c); + e = bowl_append(root, c); if(e) return e; e = bowl_free(root); return e; -} \ No newline at end of file +} diff --git a/hash.c b/hash.c new file mode 100644 index 000000000000..689bf185dbe6 --- /dev/null +++ b/hash.c @@ -0,0 +1,73 @@ +#include "bowl.h" +#include "array.h" +#include "hash.h" +#include "utils.h" + +#include + +typedef struct { + char *key; + struct bowl *data; +} hash_item; + +err_t hash_insert_key(struct bowl *self, char *key, struct bowl *child) +{ + CHECK(self, INVALID_STATE) + CHECK(child, INVALID_STATE) + CHECK(key, INVALID_STATE) + + // Even though we're a HASH node, we use an array under the hood + struct bowl_arr *arr = self->_pl.array; + CHECK(arr, INVALID_STATE) + + size_t idx; + err_t e = _hash(key, arr->size, &idx); + if(e) return e; + + // If the index is used, rescale and try again + if(arr->ptr[idx] != NULL) { + struct bowl *prev = self; + + // Allocate new array + e = array_malloc(self, arr->size * 2); + if(e) return e; + + for(int i = 0; i < arr->size; i++) { + if(arr->ptr[i] != NULL) { + // FIXME: This is horrible + hash_item *item = (hash_item*) arr->ptr[i]; + e = hash_insert_key(self, item->key, item->data); + if(e) return e; + } + } + + e = array_free(prev); + if(e) return e; + + e = hash_insert_key(self, key, child); + if(e) return e; + + } else { + hash_item *item = malloc(sizeof(hash_item)); + CHECK(item, MALLOC_FAILED) + + item->key = calloc(sizeof(char), REAL_STRLEN(key)); + CHECK(item->key, MALLOC_FAILED) + strcpy(item->key, key); + + item->data = child; + + arr->ptr[idx] = (struct bowl*) item; + } + + return OK; +} + +err_t hash_remove_key(struct bowl *self, char *key, struct bowl **child) +{ + CHECK(self, INVALID_STATE) + CHECK(key, INVALID_STATE) + + return OK; +} + diff --git a/hash.h b/hash.h new file mode 100644 index 000000000000..a5d763deb2a3 --- /dev/null +++ b/hash.h @@ -0,0 +1,11 @@ +#ifndef HASH_H_ +#define HASH_H_ + +#include "bowl.h" + +err_t hash_insert_key(struct bowl *, char *key, struct bowl *); + +err_t hash_remove_key(struct bowl *, char *key, struct bowl **); + +#endif // HASH_H_ + diff --git a/utils.c b/utils.c index 9e5a02799dac..f08c670d67e5 100644 --- a/utils.c +++ b/utils.c @@ -1,4 +1,5 @@ #include +#include #include #include "utils.h" @@ -44,3 +45,72 @@ err_t _array_remove(void **ptr, size_t idx, size_t len, void **out) return OK; } + +err_t _hash(char *str, size_t len, size_t *out) +{ + CHECK(str, INVALID_PARAMS) + CHECK((len < 0), INVALID_PARAMS) + + // Implements the "murmur" non-cryptographic hash + +#define C1 0xcc9e2d51 +#define C2 0x1b873593 +#define N 0xe6546b64 +#define M 5 +#define C3 0x85ebca6b +#define C4 0xc2b2ae35 +#define R1 15 +#define R2 13 +#define R3 16 +#define R4 13 +#define ROTL32(v, shift) ( ((v) << (shift)) | ( (v) >> (32 - (shift))) ) + + size_t key_len = strlen(str); + int l = (int) key_len / 4; + uint32_t seed = 0xAF172FE; + int i = 0; + + uint32_t k = 0; + uint32_t h = seed; + uint8_t *d = (uint8_t *) str; + const uint32_t *chunks = (const uint32_t *)(d + l * 4); + const uint8_t *tail = (const uint8_t *)(d + l * 4); + + for (i = -l; i != 0; ++i) { + k = chunks[i]; + k *= C1; + k = ROTL32(k, R1); + k *= C2; + h ^= k; + h = ROTL32(k, R2); + h = h * M + N; + } + + k = 0; + switch(key_len & 3) { + case 3: + k ^= (tail[2] << 16); + case 2: + k ^= (tail[1] << 8); + case 1: + k ^= tail[0]; + k *= C1; + k = ROTL32(k, R1); + k *= C2; + h ^= k; + default: break; + } + + /* Finalised hash */ + h ^= key_len; + h ^= (h >> R3); + h *= C3; + h ^= (h >> R4); + h *= C4; + h ^= (h >> R3); + + /* Finalise ✨ */ + h %= len; + *out = (size_t) h; + return OK; +} diff --git a/utils.h b/utils.h index c7c2178dfb17..4fa7a901317c 100644 --- a/utils.h +++ b/utils.h @@ -13,7 +13,9 @@ 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); +err_t _hash(char *str, size_t len, size_t *out); + #ifdef __cplusplus } #endif -#endif // _UTIL_H_ \ No newline at end of file +#endif // _UTIL_H_ -- cgit v1.2.3 From 42cd7d8cd86c39af525f28c8c6c28f1a342c697b Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 13 Jul 2019 13:29:36 +0100 Subject: Conditionally compiling example binaries --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index abe82d30c181..4097a7804548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 2.8.11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c11") set(CMAKE_BUILD_TYPE Debug) +set(BUILD_EXAMPLES 0 CACHE BOOL "Build the included examples projects") project(bowl) add_library(bowl SHARED array.c @@ -12,8 +13,11 @@ add_library(bowl SHARED array.c target_include_directories(bowl PUBLIC ".") ################### EXAMPLES ################### -add_executable(ex_tree examples/tree.c) -target_link_libraries(ex_tree bowl) -add_executable(ex_list examples/list.c) -target_link_libraries(ex_list bowl) +if(BUILD_EXAMPLES) + add_executable(ex_tree examples/tree.c) + target_link_libraries(ex_tree bowl) + + add_executable(ex_list examples/list.c) + target_link_libraries(ex_list bowl) +endif() -- cgit v1.2.3 From c4fcb1b7fbbd62adac128726dea116ce95801875 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 13 Jul 2019 13:52:04 +0100 Subject: Fixing simple HASH node memory leak --- bowl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bowl.c b/bowl.c index 3cb36b95cb2c..c1271a2b3dfe 100644 --- a/bowl.c +++ b/bowl.c @@ -126,7 +126,7 @@ err_t bowl_free(struct bowl *self) err_t e; switch(self->type) { case LEAF: e = data_free(self->_pl.data); break; - case ARRAY: e = array_free(self); break; + case ARRAY | HASH: e = array_free(self); break; default: e = INVALID_STATE; break; } if(e) return e; -- cgit v1.2.3 From 6c9c0502798681d7eaa91c81858d3c113e124676 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 13 Jul 2019 13:57:17 +0100 Subject: Adjusting gitignore to capture in-source cmake artefacts --- .gitignore | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6f31401f7879..a35b02021dcd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,63 @@ -build/ -.vscode/ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# 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 +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf -- cgit v1.2.3 From c312e74f95fcfe9bd1e91c092a2d987cb067a075 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 14 Jul 2019 02:00:32 +0100 Subject: Implementing HASH node `remove_key` function --- bowl.c | 15 ++++++++++++++- bowl.h | 5 ++++- hash.c | 12 ++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/bowl.c b/bowl.c index c1271a2b3dfe..e515f6b9b587 100644 --- a/bowl.c +++ b/bowl.c @@ -106,7 +106,20 @@ err_t bowl_remove(struct bowl *self, struct bowl *child) } } -err_t bowl_remove_key(struct bowl *self, size_t idx, struct bowl **prev) +err_t bowl_remove_key(struct bowl *self, char *key, struct bowl **prev) +{ + CHECK(self, INVALID_PARAMS) + CHECK(key, INVALID_PARAMS) + + switch(self->type) { + case LEAF: return INVALID_PARAMS; + case HASH: return hash_remove_key(self, key, prev); + + default: return INVALID_STATE; + } +} + +err_t bowl_remove_idx(struct bowl *self, size_t idx, struct bowl **prev) { CHECK(self, INVALID_PARAMS) CHECK((idx >= 0), INVALID_PARAMS) diff --git a/bowl.h b/bowl.h index ae7efe3f5651..ae1321e4189d 100644 --- a/bowl.h +++ b/bowl.h @@ -82,8 +82,11 @@ err_t bowl_swap_idx(struct bowl *, size_t idx, struct bowl *, struct bowl **); /// Remove a bowl node by it's pointer reference err_t bowl_remove(struct bowl *, struct bowl *); +/// Remove a specific key (relevant for HASH nodes) +err_t bowl_remove_key(struct bowl *, char *key, struct bowl **); + /// Removing a bowl node with a key -err_t bowl_remove_key(struct bowl *, size_t idx, struct bowl **); +err_t bowl_remove_idx(struct bowl *, size_t idx, struct bowl **); /// Cascade-free memory from a bowl node err_t bowl_free(struct bowl *); diff --git a/hash.c b/hash.c index 689bf185dbe6..5ee4f9059379 100644 --- a/hash.c +++ b/hash.c @@ -65,9 +65,21 @@ err_t hash_insert_key(struct bowl *self, char *key, struct bowl *child) err_t hash_remove_key(struct bowl *self, char *key, struct bowl **child) { + CHECK(self, INVALID_STATE) + CHECK(child, INVALID_STATE) CHECK(key, INVALID_STATE) + // Even though we're a HASH node, we use an array under the hood + struct bowl_arr *arr = self->_pl.array; + CHECK(arr, INVALID_STATE) + + size_t idx; + err_t e = _hash(key, arr->size, &idx); + if(e) return e; + + (*child) = arr->ptr[idx]; + arr->ptr[idx] = NULL; return OK; } -- cgit v1.2.3 From 98852464c7d4cd3a6a08d0208378506fdce822d6 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 14 Jul 2019 02:13:13 +0100 Subject: Adding a hashmap example --- CMakeLists.txt | 11 +++++++---- examples/hash.c | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 examples/hash.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4097a7804548..3dd2f151d497 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,12 @@ target_include_directories(bowl PUBLIC ".") ################### EXAMPLES ################### if(BUILD_EXAMPLES) - add_executable(ex_tree examples/tree.c) - target_link_libraries(ex_tree bowl) + add_executable(example_tree examples/tree.c) + target_link_libraries(example_tree bowl) - add_executable(ex_list examples/list.c) - target_link_libraries(ex_list bowl) + add_executable(example_list examples/list.c) + target_link_libraries(example_list bowl) + + add_executable(example_hash examples/hash.c) + target_link_libraries(example_hash bowl) endif() diff --git a/examples/hash.c b/examples/hash.c new file mode 100644 index 000000000000..7a06bbe78687 --- /dev/null +++ b/examples/hash.c @@ -0,0 +1,23 @@ +#include + +int main() +{ + + // Root node which contains a list + struct bowl *root; + bowl_malloc(&root, HASH); + + struct bowl *a; + data_malloc(&a, LITERAL, "Destroy capitalism"); + bowl_insert_key(root, "a", a); + + struct bowl *b; + data_malloc(&b, INTEGER, 1312); + bowl_insert_key(root, "b", b); + + struct bowl *c; + data_malloc(&c, LITERAL, "Alerta, Antifascista!"); + bowl_insert_key(root, "c", c); + + return bowl_free(root); +} -- cgit v1.2.3 From f6e83e64abbf76bbfddc90f885f7c3be379ecf2c Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 14 Jul 2019 18:41:07 +0100 Subject: Various improvements around HASH nodes The example previously would lead to corrupt memory when running for items that needed to re-hash the hash array. This has been fixed. Secondly, all HASH node memory leaks are now fixed, that resulted from badly tracking objects through the resize process. A new function `hash_free_shallow` was added to help with this. The function `array_free_shallow` is unused but might become useful in the future and so was not removed. The hash strategy is still garbage but this can be fixed later :) --- array.c | 15 ++++++++++++--- array.h | 4 +++- bowl.c | 5 +++-- hash.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++------- hash.h | 4 ++++ utils.c | 2 +- 6 files changed, 71 insertions(+), 14 deletions(-) diff --git a/array.c b/array.c index b0db3f5819b7..78317e9a0fb6 100644 --- a/array.c +++ b/array.c @@ -12,7 +12,7 @@ err_t array_malloc(struct bowl *self, size_t size) struct bowl_arr *arr = malloc(sizeof(struct bowl_arr)); CHECK(arr, MALLOC_FAILED) - arr->size = 2; + arr->size = size; arr->used = 0; struct bowl **ptr = calloc(sizeof(struct bowl *), arr->size); @@ -109,12 +109,21 @@ err_t array_free(struct bowl *self) struct bowl_arr *arr = self->_pl.array; CHECK(arr, INVALID_STATE) + err_t e; for(int i = 0; i < arr->used; i++) { - err_t e = bowl_free(arr->ptr[i]); + e = bowl_free(arr->ptr[i]); if(e) break; } + free(arr->ptr); + free(arr); + return e; +} + +err_t array_free_shallow(struct bowl_arr *arr) +{ + CHECK(arr, INVALID_STATE) free(arr->ptr); free(arr); return OK; -} \ No newline at end of file +} diff --git a/array.h b/array.h index d35a1f80730d..a1ac8c06df79 100644 --- a/array.h +++ b/array.h @@ -21,7 +21,9 @@ err_t array_remove_key(struct bowl *, size_t, struct bowl **); err_t array_free(struct bowl *); +err_t array_free_shallow(struct bowl_arr *); + #ifdef __cplusplus } #endif -#endif // _ARRAY_H_ \ No newline at end of file +#endif // _ARRAY_H_ diff --git a/bowl.c b/bowl.c index e515f6b9b587..248ac37a124f 100644 --- a/bowl.c +++ b/bowl.c @@ -31,7 +31,7 @@ err_t bowl_malloc(struct bowl **ptr, bowl_t type) switch((*ptr)->type) { case LEAF: return OK; // No further allocation needed - case ARRAY: return array_malloc(*ptr, ARRAY_START_SIZE); + case ARRAY | HASH: return array_malloc(*ptr, ARRAY_START_SIZE); default: return INVALID_STATE; } @@ -139,7 +139,8 @@ err_t bowl_free(struct bowl *self) err_t e; switch(self->type) { case LEAF: e = data_free(self->_pl.data); break; - case ARRAY | HASH: e = array_free(self); break; + case ARRAY: e = array_free(self); break; + case HASH: e = hash_free(self); break; default: e = INVALID_STATE; break; } if(e) return e; diff --git a/hash.c b/hash.c index 5ee4f9059379..f1abfb34ecaf 100644 --- a/hash.c +++ b/hash.c @@ -26,9 +26,9 @@ err_t hash_insert_key(struct bowl *self, char *key, struct bowl *child) // If the index is used, rescale and try again if(arr->ptr[idx] != NULL) { - struct bowl *prev = self; + struct bowl_arr *prev = arr; - // Allocate new array + // Allocate new hash array e = array_malloc(self, arr->size * 2); if(e) return e; @@ -41,12 +41,11 @@ err_t hash_insert_key(struct bowl *self, char *key, struct bowl *child) } } - e = array_free(prev); - if(e) return e; - e = hash_insert_key(self, key, child); if(e) return e; + hash_free_shallow(prev); + } else { hash_item *item = malloc(sizeof(hash_item)); CHECK(item, MALLOC_FAILED) @@ -58,6 +57,7 @@ err_t hash_insert_key(struct bowl *self, char *key, struct bowl *child) item->data = child; arr->ptr[idx] = (struct bowl*) item; + arr->used += 1; } return OK; @@ -65,7 +65,6 @@ err_t hash_insert_key(struct bowl *self, char *key, struct bowl *child) err_t hash_remove_key(struct bowl *self, char *key, struct bowl **child) { - CHECK(self, INVALID_STATE) CHECK(child, INVALID_STATE) CHECK(key, INVALID_STATE) @@ -79,7 +78,49 @@ err_t hash_remove_key(struct bowl *self, char *key, struct bowl **child) if(e) return e; (*child) = arr->ptr[idx]; + arr->used -= 1; arr->ptr[idx] = NULL; return OK; } - + +err_t hash_free(struct bowl *self) +{ + CHECK(self, INVALID_PARAMS) + struct bowl_arr *arr = self->_pl.array; + CHECK(arr, INVALID_PARAMS) + + err_t e = OK; + for(int i = 0; i < arr->size; i++) { + hash_item *item = (hash_item *) arr->ptr[i]; + if(item != NULL) { + CHECK(item->data, INVALID_STATE) + CHECK(item->key, INVALID_STATE) + + e = bowl_free(item->data); + if(e) break; + + free(item->key); + free(item); + } + } + + free(arr->ptr); + free(arr); + return e; +} + +err_t hash_free_shallow(struct bowl_arr *arr) +{ + CHECK(arr, INVALID_PARAMS); + for(int i = 0; i < arr->size; i++) { + hash_item *item = (hash_item *) arr->ptr[i]; + if(item != NULL) { + free(item->key); + free(item); + } + } + + free(arr->ptr); + free(arr); + return OK; +} diff --git a/hash.h b/hash.h index a5d763deb2a3..369d8495e1af 100644 --- a/hash.h +++ b/hash.h @@ -7,5 +7,9 @@ err_t hash_insert_key(struct bowl *, char *key, struct bowl *); err_t hash_remove_key(struct bowl *, char *key, struct bowl **); +err_t hash_free(struct bowl *); + +err_t hash_free_shallow(struct bowl_arr *); + #endif // HASH_H_ diff --git a/utils.c b/utils.c index f08c670d67e5..0979d07bd632 100644 --- a/utils.c +++ b/utils.c @@ -49,7 +49,7 @@ err_t _array_remove(void **ptr, size_t idx, size_t len, void **out) err_t _hash(char *str, size_t len, size_t *out) { CHECK(str, INVALID_PARAMS) - CHECK((len < 0), INVALID_PARAMS) + CHECK((len > 0), INVALID_PARAMS) // Implements the "murmur" non-cryptographic hash -- cgit v1.2.3 From 0a97a1613bf42dc4ecaffbed1426d3495f5f9160 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 14 Jul 2019 18:59:39 +0100 Subject: Updating README with sr.ht issue tracker --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index eadb8ec88c69..e2ce73937cbb 100644 --- a/README +++ b/README @@ -4,6 +4,8 @@ libbowl The last C datastructure library you will use. Provides a versatile structure that can act as lists, sets and more! +Issues are being tracked here: https://todo.sr.ht/~spacekookie/libbowl + How to build ------------ -- cgit v1.2.3 From 7e59c771569e6162f2ef96b1d88c85f05cc0e552 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Mon, 15 Jul 2019 10:52:13 +0100 Subject: Fixing a memory allocation regression In a previous commit, adding HASH node support, this code was added that would let both ARRAY and HASH nodes allocate an array. The syntax was however wrong, never allocating memory and thus failing with an INVALID_STATE when freeing. Additionally, this begs the question how a cleanup of INVALID_STATE could be handled (i.e. with a `goto fail` in the `bowl_free` function) --- bowl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bowl.c b/bowl.c index 248ac37a124f..9d12c69e81b6 100644 --- a/bowl.c +++ b/bowl.c @@ -31,7 +31,8 @@ err_t bowl_malloc(struct bowl **ptr, bowl_t type) switch((*ptr)->type) { case LEAF: return OK; // No further allocation needed - case ARRAY | HASH: return array_malloc(*ptr, ARRAY_START_SIZE); + case ARRAY: + case HASH: return array_malloc(*ptr, ARRAY_START_SIZE); default: return INVALID_STATE; } -- cgit v1.2.3 From d84c98eecaf08bfe46915ef38b46d27b019f6a78 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Jul 2019 14:11:02 +0200 Subject: Adding a CI manifest --- .build.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .build.yml diff --git a/.build.yml b/.build.yml new file mode 100644 index 000000000000..bd2f7d78606d --- /dev/null +++ b/.build.yml @@ -0,0 +1,11 @@ +image: archlinux +packages: + - cmake + - gcc +sources: + - https://git.sr.ht/~spacekookie/libbowl +tasks: + - build: | + cd libbowl + cmake . + make -- cgit v1.2.3 From 06b906165ff3f640639c90ffe150e41ba5d224aa Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 21 Jul 2019 15:40:06 +0200 Subject: Changing README to markdown and adding a build badge --- README | 33 --------------------------------- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 33 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index e2ce73937cbb..000000000000 --- a/README +++ /dev/null @@ -1,33 +0,0 @@ -libbowl -======= - -The last C datastructure library you will use. Provides a versatile -structure that can act as lists, sets and more! - -Issues are being tracked here: https://todo.sr.ht/~spacekookie/libbowl - - -How to build ------------- - -An out-of-source build is recommended. - -``` -$> mkdir build; cd build -$> cmake .. -$> 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. - - -License -------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or (at -your option) any later version. - -I hope you enjoy ❤ diff --git a/README.md b/README.md new file mode 100644 index 000000000000..46443320eb3b --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +libbowl [![][badge]][badge-link] +======= + +The last C datastructure library you will ever use. Provides a +versalite API to build nested, serial and hashed structure nodes. + +Check out the [issue tracker][issues] + +I hope you enjoy ❤ + +[badge]: https://builds.sr.ht/~spacekookie/libbowl.svg +[badge-link]: https://builds.sr.ht/~spacekookie/libbowl? +[issues]: https://todo.sr.ht/~spacekookie/libbowl + + +How to build +------------ + +Build dependencies + +- gcc (`4.0+`) +- cmake (`2.18+`) + +An out-of-source build is recommended. You can specify the linking +behaviour with `-DLINK_DYNAMIC=1`. Optionally you can disable tests +with `-DRUN_TESTS=0`. + +``` +$ mkdir build; cd build +$ cmake .. -DLINK_DYNAMIC=1 -DRUN_TESTS=1 +$ make +``` + +License +------- + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at +your option) any later version. -- cgit v1.2.3 From 27814bbae0854adb916d39f82c29590e903c3584 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Jul 2019 12:36:50 +0200 Subject: Switching default build to C99 --- CMakeLists.txt | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dd2f151d497..93e5d15cec8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,20 @@ cmake_minimum_required(VERSION 2.8.11) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c11") -set(CMAKE_BUILD_TYPE Debug) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c99") set(BUILD_EXAMPLES 0 CACHE BOOL "Build the included examples projects") +set(LINK_DYNAMIC 0 CACHE BOOL "Set the default linking behaviour to `dynamic`") project(bowl) -add_library(bowl SHARED array.c - bowl.c - data.c - hash.c - utils.c) +set(SRC array.c + bowl.c + data.c + hash.c + utils.c) + +if(LINK_DYNAMIC) + add_library(bowl SHARED ${SRC}) +else() + add_library(bowl STATIC ${SRC}) +endif() target_include_directories(bowl PUBLIC ".") -- cgit v1.2.3 From e234a2baa210df4c4376e0b378f0b2cbbf79d84b Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Tue, 23 Jul 2019 12:42:43 +0200 Subject: Adding some examples to the README --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 46443320eb3b..bd9ac7bddb0a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,39 @@ I hope you enjoy ❤ [badge-link]: https://builds.sr.ht/~spacekookie/libbowl? [issues]: https://todo.sr.ht/~spacekookie/libbowl +Exampple +-------- + +The root structure in `libbowl` is a `bowl`. It can either be a leaf +node, containing some data (`bowl_data`, which can be many types), +or be a structure node, either in `ARRAY`, `LINK` or `HASH` mode. + +Each `libbowl` function returns a status code and uses reverse-input +pointers to communicate results. A convenience `DEBUG` macro is included. + +```C +bowl *list; +int r = bowl_malloc(&list, ARRAY); +if(r) { printf("Failed to malloc!\n"); exit(2); } + +// `DEBUG` does the same thing as the above `if` +bowl *number; +DEBUG (data_malloc(&number, INTEGER, 1312)) + +// Finally append data into list +DEBUG (bowl_append(list, number)) +``` + +Generally, `data` prefixed functions are convenience wrappers around +allocating a `bowl`, setting it's type to `LEAF`, and inserting some +data into it (performing at least 2 allocation calls). + +Freeing a `bowl` structure is as easy as calling free on the top node. + +```C +// ... +DEBUG (bowl_free(list)) +``` How to build ------------ -- cgit v1.2.3