aboutsummaryrefslogtreecommitdiff
path: root/include/dtree/dtree.h
blob: b46b000c3ecedd31cacc5a880587a9e9b12c6f8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
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 <memory.h>

/* 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_