qLibc
qtreetbl.c
Go to the documentation of this file.
1 /******************************************************************************
2  * qLibc
3  *
4  * Copyright (c) 2010-2015 Seungyoung Kim.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *****************************************************************************/
28 
29 /**
30  * @file qtreetbl.c Tree Table container that implements "Left-Leaning Red-Black"
31  * BST algorithm.
32  *
33  * qtreetbl implements a binary search tree that allows efficient in-order traversal
34  * with O(log n) search time.
35  *
36  * The algorithm qtreetbl specifically implements is left-leaning red-black tree
37  * algorithm invented in 2008 by Robert Sedgewick. A left-leaning red–black tree
38  * is a type of self-balancing binary search tree and it is a variant of the
39  * red–black tree which was invented in 1972 by Rudolf Bayer.
40  *
41  * References:
42  * - http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf
43  * - http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf
44  * - http://en.wikipedia.org/wiki/Left-leaning_red-black_tree
45  *
46  * Following example code will construct the data structure shown in below
47  * diagram. This example is quoted from the inventor's presentation slide
48  * p24-p25, http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf and
49  * used in unit tests for verification purpose.
50  *
51  * (E)
52  * ______________|______________
53  * / \
54  * (C) (R)
55  * / \ / \
56  * (A,B) (D) (I,N) (S,X)
57  *
58  * <2-3-4 Tree Data Structure>
59  *
60  * E
61  * ______________|______________
62  * / \
63  * C R
64  * _______|_______ _______|_______
65  * / \ / \
66  * B D N X
67  * // // //
68  * A* I* S*
69  *
70  * Tree Info : tot=10, red=3, black=-7, root=E
71  * Nodes A, I and S are nodes with RED upper link. Others are BLACK
72  *
73  * <Left-Leaning Red-Black Tree Data Structure>
74  *
75  * Red-Black BST algorithm has been one of the famous BST algorithm especially
76  * for in-memory operation. It's been using in all around the computing area.
77  * I was very impressed about this variant, Left-Leaning version of Red-Black
78  * about how it improves the performance and reduces the overall complexity.
79  * Since it's relatively new algorithm, there's not many of practically functional
80  * working codes yet other than proof of concept kinds. Here's one of fully
81  * functional codes and I, Seungyoung Kim, would like to dedicate this code to
82  * the genius inventor Robert Sedgewick and to all the great qLibc users.
83  * Cheers!
84  *
85  * Unique features:
86  * - iterator. (have you ever seen iterator implementation in LLRB tree?)
87  * - find nearest key. (have you ever seen this as well? :)
88  * - iteration from given key.
89  * - find min/max key.
90  *
91  * @code
92  * qtreetbl_t *tbl = qtreetbl(QTREETBL_THREADSAFE);
93  *
94  * tbl->put(tbl, "key", "DATA", 4); // use put_by_obj() for binary keys.
95  * void *data = tbl->get(tbl, "key", false); // use get_by_obj() for binary keys.
96  * tbl->remove(tbl, "key"); // use remove_by_key() for binary keys.
97  *
98  * // iteration example
99  * tbl->lock(tbl);
100  * qtreetbl_obj_t obj = tbl->find_nearest(tbl, "k", 2, false);
101  * while (tbl->getnext(tbl, &obj, false) == true) {
102  * ...
103  * }
104  * tbl->unlock(tbl);
105  *
106  * tbl->set_compare(tbl, my_compare_func);
107  * size_t num = tbl->size(tbl);
108  * void *min = tbl->find_min(tbl, &keysize);
109  * qtree_clean();
110  *
111  * tbl->free(tbl);
112  * @endcode
113  */
114 
115 #include <stdio.h>
116 #include <stdlib.h>
117 #include <stdbool.h>
118 #include <string.h>
119 #include <stdarg.h>
120 #include <errno.h>
121 #include <assert.h>
122 #include "qinternal.h"
123 #include "utilities/qstring.h"
124 #include "containers/qtreetbl.h"
125 
126 #ifndef _DOXYGEN_SKIP
127 
128 /* internal functions */
129 static bool is_red(qtreetbl_obj_t *obj);
130 static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj);
131 static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj);
132 static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj);
133 static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj);
134 static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj);
135 static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj);
136 static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj);
137 static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj);
138 static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl, const void *name,
139  size_t namesize);
140 static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj);
141 static qtreetbl_obj_t *new_obj(bool red, const void *name, size_t namesize,
142  const void *data, size_t datasize);
143 static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
144  const void *name, size_t namesize,
145  const void *data, size_t datasize);
146 static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
147  const void *name, size_t namesize);
148 static void free_objs(qtreetbl_obj_t *obj);
149 static void free_obj(qtreetbl_obj_t *obj);
150 static uint8_t reset_iterator(qtreetbl_t *tbl);
151 
152 #endif
153 
154 /**
155  * Initialize a tree table.
156  *
157  * @param options combination of initialization options.
158  *
159  * @return a pointer of malloced qtreetbl_t, otherwise returns NULL.
160  * @retval errno will be set in error condition.
161  * - ENOMEM : Memory allocation failure.
162  *
163  * @code
164  * qtreetbl_t *tbl = qtreetbl(0);
165  * @endcode
166  *
167  * @note
168  * Available options:
169  * - QTREETBL_THREADSAFE - make it thread-safe.
170  */
171 qtreetbl_t *qtreetbl(int options) {
172  qtreetbl_t *tbl = (qtreetbl_t *) calloc(1, sizeof(qtreetbl_t));
173  if (tbl == NULL)
174  goto malloc_failure;
175 
176  // handle options.
177  if (options & QTREETBL_THREADSAFE) {
178  Q_MUTEX_NEW(tbl->qmutex, true);
179  if (tbl->qmutex == NULL)
180  goto malloc_failure;
181  }
182 
183  // assign methods
184  tbl->set_compare = qtreetbl_set_compare;
185 
186  tbl->put = qtreetbl_put;
187  tbl->putstr = qtreetbl_putstr;
188  tbl->putstrf = qtreetbl_putstrf;
189  tbl->put_by_obj = qtreetbl_put_by_obj;
190 
191  tbl->get = qtreetbl_get;
192  tbl->getstr = qtreetbl_getstr;
193  //tbl->getint = qtreetbl_getint;
194  tbl->get_by_obj = qtreetbl_get_by_obj;
195 
196  tbl->remove = qtreetbl_remove;
197  tbl->remove_by_obj = qtreetbl_remove_by_obj;
198 
199  tbl->getnext = qtreetbl_getnext;
200 
201  tbl->find_min = qtreetbl_find_min;
202  tbl->find_max = qtreetbl_find_max;
203  tbl->find_nearest = qtreetbl_find_nearest;
204 
205  tbl->size = qtreetbl_size;
206  tbl->clear = qtreetbl_clear;
207 
208  tbl->lock = qtreetbl_lock;
209  tbl->unlock = qtreetbl_unlock;
210 
211  tbl->free = qtreetbl_free;
212  tbl->debug = qtreetbl_debug;
213 
214  // Set default comparison function.
215  qtreetbl_set_compare(tbl, qtreetbl_byte_cmp);
216  reset_iterator(tbl);
217 
218  return tbl;
219 
220  malloc_failure:
221  errno = ENOMEM;
222  if (tbl) {
223  assert(tbl->qmutex == NULL);
224  qtreetbl_free(tbl);
225  }
226  return NULL;
227 }
228 
229 /**
230  * qtreetbl->set_compare(): Set user comparator.
231  *
232  * @param tbl qtreetbl_t container pointer.
233  * @param cmp a pointer to the user comparator function.
234  *
235  * @note
236  * By default, qtreetbl uses byte comparator that works for
237  * both binary type key and string type key. Please refer
238  * qtreetbl_byte_cmp() for your idea to make your own comparator,
239  */
241  qtreetbl_t *tbl,
242  int (*cmp)(const void *name1, size_t namesize1, const void *name2,
243  size_t namesize2)) {
244  tbl->compare = cmp;
245 }
246 
247 /**
248  * qtreetbl->put(): Put an object into this table with string type key.
249  *
250  * @param tbl qtreetbl_t container pointer.
251  * @param name key name
252  * @param data data object
253  * @param datasize size of data object
254  *
255  * @return true if successful, otherwise returns false
256  * @retval errno will be set in error condition.
257  * - EINVAL : Invalid argument.
258  * - ENOMEM : Memory allocation failure.
259  */
260 bool qtreetbl_put(qtreetbl_t *tbl, const char *name, const void *data,
261  size_t datasize) {
262  return qtreetbl_put_by_obj(tbl, name,
263  (name != NULL) ? (strlen(name) + 1) : 0, data,
264  datasize);
265 }
266 
267 /**
268  * qtreetbl->putstr(): Put a string into this table.
269  *
270  * @param tbl qtreetbl container pointer.
271  * @param name key name.
272  * @param str string data.
273  *
274  * @return true if successful, otherwise returns false.
275  * @retval errno will be set in error condition.
276  * - EINVAL : Invalid argument.
277  * - ENOMEM : Memory allocation failure.
278  */
279 bool qtreetbl_putstr(qtreetbl_t *tbl, const char *name, const char *str) {
280  return qtreetbl_put_by_obj(tbl, name,
281  (name != NULL) ? (strlen(name) + 1) : 0, str,
282  (str != NULL) ? (strlen(str) + 1) : 0);
283 }
284 
285 /**
286  * qtreetbl->putstrf(): Put a formatted string into this table.
287  *
288  * @param tbl qtreetbl_t container pointer.
289  * @param name key name.
290  * @param format formatted string data.
291  *
292  * @return true if successful, otherwise returns false.
293  * @retval errno will be set in error condition.
294  * - EINVAL : Invalid argument.
295  * - ENOMEM : Memory allocation failure.
296  */
297 bool qtreetbl_putstrf(qtreetbl_t *tbl, const char *name, const char *format,
298  ...) {
299  char *str;
300  DYNAMIC_VSPRINTF(str, format);
301  if (str == NULL) {
302  errno = ENOMEM;
303  return false;
304  }
305 
306  bool ret = qtreetbl_putstr(tbl, name, str);
307  free(str);
308  return ret;
309 }
310 
311 /**
312  * qtreetbl->put_by_obj(): Put an object data into this table with an object name.
313  *
314  * @param tbl qtreetbl_t container pointer.
315  * @param name key name
316  * @param namesize key size
317  * @param data data object
318  * @param datasize size of data object
319  *
320  * @return true if successful, otherwise returns false
321  * @retval errno will be set in error condition.
322  * - EINVAL : Invalid argument.
323  * - ENOMEM : Memory allocation failure.
324  *
325  * @note
326  * This is the underlying put function which all other put methods use.
327  */
328 bool qtreetbl_put_by_obj(qtreetbl_t *tbl, const void *name, size_t namesize,
329  const void *data, size_t datasize) {
330  if (name == NULL || namesize == 0 || data == NULL || datasize == 0) {
331  errno = EINVAL;
332  return false;
333  }
334 
335  qtreetbl_lock(tbl);
336  errno = 0;
337  qtreetbl_obj_t *root = put_obj(tbl, tbl->root, name, namesize, data,
338  datasize);
339  if (root == NULL || errno == ENOMEM) {
340  qtreetbl_unlock(tbl);
341  return false;
342  }
343  root->red = false;
344  tbl->root = root;
345  qtreetbl_unlock(tbl);
346 
347  return true;
348 }
349 
350 /**
351  * qtreetbl->get(): Get an object from this table.
352  *
353  * @param tbl qtreetbl_t container pointer.
354  * @param name key name.
355  * @param datasize if not NULL, oject size will be stored.
356  * @param newmem whether or not to allocate memory for the data.
357  *
358  * @return a pointer of data if the key is found, otherwise returns NULL.
359  * @retval errno will be set in error condition.
360  * - ENOENT : No such key found.
361  * - EINVAL : Invalid argument.
362  * - ENOMEM : Memory allocation failure.
363  *
364  * @code
365  * qtreetbl_t *tbl = qtreetbl(0);
366  * (...codes...)
367  *
368  * // with newmem flag unset
369  * size_t size;
370  * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, false);
371  *
372  * // with newmem flag set
373  * size_t size;
374  * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, true);
375  * free(data);
376  * @endcode
377  *
378  * @note
379  * If newmem flag is set, returned data will be malloced and should be
380  * deallocated by user. Otherwise returned pointer will point internal buffer
381  * directly and should not be de-allocated by user. In thread-safe mode,
382  * newmem flag must be set to true always.
383  */
384 void *qtreetbl_get(qtreetbl_t *tbl, const char *name, size_t *datasize,
385 bool newmem) {
386  return qtreetbl_get_by_obj(tbl, name,
387  (name != NULL) ? (strlen(name) + 1) : 0,
388  datasize, newmem);
389 }
390 
391 /**
392  * qtreetbl->getstr(): Finds an object and returns as string type.
393  *
394  * @param tbl qtreetbl_t container pointer.
395  * @param name key name
396  * @param newmem whether or not to allocate memory for the data.
397  *
398  * @return a pointer of data if the key is found, otherwise returns NULL.
399  * @retval errno will be set in error condition.
400  * - ENOENT : No such key found.
401  * - EINVAL : Invalid argument.
402  * - ENOMEM : Memory allocation failure.
403  *
404  * @note
405  * If newmem flag is set, returned data will be malloced and should be
406  * deallocated by user. Otherwise returned pointer will point internal buffer
407  * directly and should not be de-allocated by user. In thread-safe mode,
408  * newmem flag must be set to true always.
409  */
410 char *qtreetbl_getstr(qtreetbl_t *tbl, const char *name, const bool newmem) {
411  return qtreetbl_get_by_obj(tbl, name,
412  (name != NULL) ? (strlen(name) + 1) : 0, NULL,
413  newmem);
414 }
415 
416 /**
417  * qtreetbl->get_by_obj(): Get an object from this table with an object name.
418  *
419  * @param tbl qtreetbl_t container pointer.
420  * @param name key name
421  * @param namesize key size
422  * @param datasize if not NULL, oject size will be stored.
423  * @param newmem whether or not to allocate memory for the data.
424  *
425  * @return a pointer of data if the key is found, otherwise returns NULL.
426  * @retval errno will be set in error condition.
427  * - ENOENT : No such key found.
428  * - EINVAL : Invalid argument.
429  * - ENOMEM : Memory allocation failure.
430  *
431  * @note
432  * If newmem flag is set, returned data will be malloced and should be
433  * deallocated by user. Otherwise returned pointer will point internal buffer
434  * directly and should not be de-allocated by user. In thread-safe mode,
435  * newmem flag must be set to true always.
436  */
437 void *qtreetbl_get_by_obj(qtreetbl_t *tbl, const char *name, size_t namesize,
438  size_t *datasize, bool newmem) {
439  if (name == NULL || namesize == 0) {
440  errno = EINVAL;
441  return NULL;
442  }
443 
444  qtreetbl_lock(tbl);
445  qtreetbl_obj_t *obj = find_obj(tbl, name, namesize);
446  void *data = NULL;
447  if (obj != NULL) {
448  data = (newmem) ? qmemdup(obj->data, obj->datasize) : obj->data;
449  if (data != NULL && datasize != NULL) {
450  *datasize = obj->datasize;
451  }
452  }
453  qtreetbl_unlock(tbl);
454  return data;
455 }
456 
457 /**
458  * qtreetbl->remove(): Remove an object from this table.
459  *
460  * @param tbl qtreetbl_t container pointer.
461  * @param name key name
462  *
463  * @return true if successful, otherwise(not found) returns false
464  * @retval errno will be set in error condition.
465  * - ENOENT : No such key found.
466  * - EINVAL : Invalid argument.
467  */
468 bool qtreetbl_remove(qtreetbl_t *tbl, const char *name) {
469  return qtreetbl_remove_by_obj(tbl, name,
470  (name != NULL) ? strlen(name) + 1 : 0);
471 }
472 
473 /**
474  * qtreetbl->remove(): Remove an object from this table with an object name.
475  *
476  * @param tbl qtreetbl_t container pointer.
477  * @param name key name
478  * @param name key size
479  *
480  * @return true if successful, otherwise(not found) returns false
481  * @retval errno will be set in error condition.
482  * - ENOENT : No such key found.
483  * - EINVAL : Invalid argument.
484  */
485 bool qtreetbl_remove_by_obj(qtreetbl_t *tbl, const void *name, size_t namesize) {
486  if (name == NULL) {
487  errno = EINVAL;
488  return false;
489  }
490 
491  qtreetbl_lock(tbl);
492  errno = 0;
493  tbl->root = remove_obj(tbl, tbl->root, name, namesize);
494  if (tbl->root)
495  tbl->root->red = false;
496  bool removed = (errno != ENOENT) ? true : false;
497  qtreetbl_unlock(tbl);
498 
499  return removed;
500 }
501 
502 /**
503  * qhashtbl->getnext(): Get next element.
504  *
505  * @param tbl qhashtbl_t container pointer.
506  * @param obj found data will be stored in this object
507  * @param newmem whether or not to allocate memory for the data.
508  *
509  * @return true if found otherwise returns false
510  * @retval errno will be set in error condition.
511  * - ENOENT : No next element.
512  * - EINVAL : Invalid argument.
513  * - ENOMEM : Memory allocation failure.
514  *
515  * @code
516  * [Iteration example from the beginning]
517  * qtreetbl_obj_t obj;
518  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
519  * tbl->lock(tbl); // lock it when thread condition is expected
520  * while(tbl->getnext(tbl, &obj, false) == true) {
521  * //
522  * // obj.name : key data
523  * // obj.namesize : key size
524  * // obj.data : data
525  * // obj.datasize : data size
526  * //
527  * // Do free obj.name and obj.data if newmem is set to true;
528  * }
529  * tbl->unlock(tbl);
530  * @endcode
531  *
532  * @code
533  * [Iteration example from given point]
534  *
535  * qtreetbl_obj_t obj;
536  * memset((void*)&obj, 0, sizeof(obj));
537  * tbl->lock(tbl); // lock must be raised before find_nearest() call.
538  * qtreetbl_obj_t obj = tbl->find_nearest(tbl, "F", 2, false); // here we go!
539  * while (tbl->getnext(tbl, &obj, false) == true) { // newmem is false
540  * //If tree has 5 objects, A, C, E, G and I.
541  * //Iteration sequence from nearest "F" will be: E->G->I->A->C
542  * }
543  * tbl->unlock(tbl);
544  *
545  * @endcode
546  *
547  * @code
548  * [Removal example in iteration loop]
549  * qtreetbl_obj_t obj;
550  * memset((void*) &obj, 0, sizeof(obj)); // start from the minimum.
551  * tbl->lock(tbl);
552  * while (tbl->getnext(tbl, &obj, false) == true) {
553  * if (...condition...) {
554  * char *name = qmemdup(obj.name, obj.namesize); // keep the name
555  * size_t namesize = obj.namesize; // for removal argument
556  *
557  * tbl->remove_by_obj(tbl, obj.name, obj.namesize); // remove
558  *
559  * obj = tbl->find_nearest(tbl, name, namesize, false); // rewind one step back
560  *
561  * free(name); // clean up
562  * }
563  * }
564  * tbl->unlock(tbl);
565  * @endcode
566  *
567  * @note
568  * - locking must be provided on user code when thread condition is expected
569  * because entire traversal needs to be running under read-only mode.
570  * - Data insertion or deletion can be made during the traversal, but in that
571  * case iterator doesn't guarantee full sweep and possibly skip some visits.
572  * When deletion happens in getnext() loop, use find_nearest() to rewind the
573  * iterator one step back.
574  * - Object obj should be initialized with 0 by using memset() before first call.
575  * - If newmem flag is true, user should de-allocate obj.name and obj.data
576  * resources.
577  */
578 bool qtreetbl_getnext(qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem) {
579  if (obj == NULL) {
580  errno = EINVAL;
581  return NULL;
582  }
583 
584  uint8_t tid = obj->tid;
585  if (obj->next == NULL) { // first time call
586  if (tbl->root == NULL) {
587  return false;
588  }
589  // get a new iterator id
590  tid = reset_iterator(tbl);;
591  }
592 
593  qtreetbl_obj_t *cursor = ((obj->next != NULL) ? obj->next : tbl->root);
594  while (cursor != NULL) {
595  if (cursor->left && cursor->left->tid != tid) {
596  cursor->left->next = cursor;
597  cursor = cursor->left;
598  continue;
599  } else if (cursor->tid != tid) {
600  cursor->tid = tid;
601  *obj = *cursor;
602  if (newmem) {
603  obj->name = qmemdup(cursor->name, cursor->namesize);
604  obj->data = qmemdup(cursor->data, cursor->datasize);
605  }
606  obj->next = cursor; // store original address in tree for next iteration
607  return true;
608  } else if (cursor->right && cursor->right->tid != tid) {
609  cursor->right->next = cursor;
610  cursor = cursor->right;
611  continue;
612  }
613  cursor = cursor->next;
614  }
615 
616  // end of travel
617  reset_iterator(tbl); // to allow iteration start over directly from find_nearest()
618  return false;
619 }
620 
621 /**
622  * qtreetbl->find_min(): Find the name of very left object.
623  *
624  * @param tbl qtreetbl_t container pointer.
625  * @param namesize if not NULL, the size of key name will be stored.
626  *
627  * @return malloced memory copying the key name.
628  *
629  * @note
630  * It's user's responsibility to free the return.
631  */
632 void *qtreetbl_find_min(qtreetbl_t *tbl, size_t *namesize) {
633  qtreetbl_lock(tbl);
634  qtreetbl_obj_t *obj = find_min(tbl->root);
635  if (obj == NULL) {
636  errno = ENOENT;
637  qtreetbl_unlock(tbl);
638  return NULL;
639  }
640 
641  if (namesize != NULL) {
642  *namesize = obj->namesize;
643  }
644  void *name = qmemdup(obj->name, obj->namesize);
645  qtreetbl_unlock(tbl);
646  return name;
647 }
648 
649 /**
650  * qtreetbl->find_max(): Find the name of very right object.
651  *
652  * @param tbl qtreetbl_t container pointer.
653  * @param namesize if not NULL, the size of key name will be stored.
654  *
655  * @return malloced memory copying the key name.
656  *
657  * @note
658  * It's user's responsibility to free the return.
659  */
660 void *qtreetbl_find_max(qtreetbl_t *tbl, size_t *namesize) {
661  qtreetbl_lock(tbl);
662  qtreetbl_obj_t *obj = find_max(tbl->root);
663  if (obj == NULL) {
664  errno = ENOENT;
665  qtreetbl_unlock(tbl);
666  return NULL;
667  }
668 
669  if (namesize != NULL) {
670  *namesize = obj->namesize;
671  }
672  void *name = qmemdup(obj->name, obj->namesize);
673  qtreetbl_unlock(tbl);
674  return name;
675 }
676 
677 /**
678  * qtreetbl->find_nearest(): Find equal or nearest object.
679  *
680  * find_nearest() returns matching key or nearest key. If there's
681  * no keys in the table. It'll return empty qtreetbl_obj_t object
682  * with errno ENOENT.
683  *
684  * @param tbl qtreetbl_t container pointer.
685  * @param name key name
686  * @param namesize key size
687  * @param newmem whether or not to allocate memory for the data.
688  *
689  * @return qtreetbl_obj_t object.
690  *
691  * @retval errno will be set in error condition.
692  * - ENOENT : No next element.
693  * - EINVAL : Invalid argument.
694  * - ENOMEM : Memory allocation failure.
695  *
696  * @code
697  * [Some examples here]
698  * Data Set : A B C D E I N R S X
699  * find_nearest("0") => "A" // no smaller key available, so "A"
700  * find_nearest("C") => "C" // matching key found
701  * find_nearest("F") => "E" // "E" is nearest smaller key from "F"
702  * find_nearest("M") => "N" // "N" is nearest smaller key from "M"
703  * find_nearest("Z") => "X" // "X" is nearest smaller key from "Z"
704  * @endcode
705  *
706  * @note
707  * When there's no matching key it look for closest smaller key
708  * in the neighbors. The only exception when it returns bigger key
709  * than given search key is that when there's no smaller keys available
710  * in the table. In such case, it'll return the nearest bigger key.
711  */
712 qtreetbl_obj_t qtreetbl_find_nearest(qtreetbl_t *tbl, const void *name,
713  size_t namesize, bool newmem) {
714  qtreetbl_obj_t retobj;
715  memset((void*) &retobj, 0, sizeof(retobj));
716 
717  if (name == NULL || namesize == 0) {
718  errno = EINVAL;
719  return retobj;
720  }
721 
722  qtreetbl_lock(tbl);
723  qtreetbl_obj_t *obj, *lastobj;
724  for (obj = lastobj = tbl->root; obj != NULL;) {
725  int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
726  if (cmp == 0) {
727  break;
728  }
729  lastobj = obj;
730  if (cmp < 0) {
731  if (obj->left)
732  obj->left->next = obj;
733  obj = obj->left;
734  } else {
735  if (obj->right)
736  obj->right->next = obj;
737  obj = obj->right;
738  }
739  }
740 
741  if (obj == NULL) {
742  for (obj = lastobj;
743  obj != NULL
744  && (tbl->compare(name, namesize, obj->name,
745  obj->namesize) < 0); obj = obj->next)
746  ;
747  if (obj == NULL) {
748  obj = lastobj;
749  }
750  }
751 
752  if (obj) {
753  retobj = *obj;
754  if (newmem) {
755  retobj.name = qmemdup(obj->name, obj->namesize);
756  retobj.data = qmemdup(obj->data, obj->datasize);
757  }
758  // set travel info to be used for iteration in getnext()
759  retobj.tid = tbl->tid;
760  retobj.next = obj;
761 
762  } else {
763  errno = ENOENT;
764  }
765 
766  qtreetbl_unlock(tbl);
767  return retobj;
768 }
769 
770 /**
771  * qtreetbl->size(): Returns the number of keys in the table.
772  *
773  * @param tbl qtreetbl_t container pointer.
774  *
775  * @return number of elements stored
776  */
777 size_t qtreetbl_size(qtreetbl_t *tbl) {
778  return tbl->num;
779 }
780 
781 /**
782  * qtreetbl->clear(): Clears the table so that it contains no keys.
783  *
784  * @param tbl qtreetbl_t container pointer.
785  */
786 void qtreetbl_clear(qtreetbl_t *tbl) {
787  qtreetbl_lock(tbl);
788  free_objs(tbl->root);
789  tbl->root = NULL;
790  tbl->num = 0;
791  qtreetbl_unlock(tbl);
792 }
793 
794 /**
795  * qtreetbl->lock(): Enter critical section.
796  *
797  * @param tbl qtreetbl_t container pointer.
798  *
799  * @note
800  * From user side, normally locking operation is only needed when traverse
801  * all elements using qtreetbl->getnext().
802  *
803  * @note
804  * This operation will do nothing if QTREETBL_THREADSAFE option was not
805  * given at the initialization time.
806  */
807 void qtreetbl_lock(qtreetbl_t *tbl) {
808  Q_MUTEX_ENTER(tbl->qmutex);
809 }
810 
811 /**
812  * qtreetbl->unlock(): Leave critical section.
813  *
814  * @param tbl qtreetbl_t container pointer.
815  *
816  * @note
817  * This operation will do nothing if QTREETBL_THREADSAFE option was not
818  * given at the initialization time.
819  */
820 void qtreetbl_unlock(qtreetbl_t *tbl) {
821  Q_MUTEX_LEAVE(tbl->qmutex);
822 }
823 
824 /**
825  * qtreetbl->free(): De-allocate the table
826  *
827  * @param tbl qtreetbl_t container pointer.
828  */
829 void qtreetbl_free(qtreetbl_t *tbl) {
830  qtreetbl_clear(tbl);
831  Q_MUTEX_DESTROY(tbl->qmutex);
832  free(tbl);
833 }
834 
835 int qtreetbl_byte_cmp(const void *name1, size_t namesize1, const void *name2,
836  size_t namesize2) {
837  size_t minsize = (namesize1 < namesize2) ? namesize1 : namesize2;
838  int cmp = memcmp(name1, name2, minsize);
839  if (cmp != 0 || namesize1 == namesize2)
840  return cmp;
841  return (namesize1 < namesize2) ? -1 : +1;
842 }
843 
844 /**
845  * qtreetbl->debug(): Print hash table for debugging purpose
846  *
847  * @param tbl qtreetbl_t container pointer.
848  * @param out output stream
849  *
850  * @return true if successful, otherwise returns false.
851  * @retval errno will be set in error condition.
852  * - EIO : Invalid output stream.
853  */
854 bool qtreetbl_debug(qtreetbl_t *tbl, FILE *out) {
855  if (out == NULL) {
856  errno = EIO;
857  return false;
858  }
859 
860  qtreetbl_lock(tbl);
861 
862  qtreetbl_unlock(tbl);
863 
864  return true;
865 }
866 
867 /**
868  * Display the tree structure from the node pointed by `obj` and down.
869  *
870  * @param tbl A pointer to a node of the tree.
871  * @param depth The depth of the node in the tree.
872  * The depth of the tree root is 0.
873  *
874  */
875 void print_node(qtreetbl_obj_t *obj, int depth) {
876  // Skip tree-leaves
877  if (!obj) return;
878 
879  // 4-spaces indentation for each node level
880  for (int i = 0; i < depth; i++) {
881  printf(" ");
882  }
883 
884  // Print the red-ness of the node
885  printf("R=%c W= ", (obj->red) ? 'Y' : 'N');
886 
887  // Print the content of the node
888  for (int i = 0; i < obj->namesize; i++) {
889  printf("%c", ((char *) obj->name)[i]);
890  }
891 
892  // No more data to print for this node, go to next line.
893  printf("\n");
894 
895  // Recursive call to display the children
896  print_node(obj->left, depth + 1);
897  print_node(obj->right, depth + 1);
898 }
899 
900 /**
901  * Display the tree structure.
902  *
903  * @param tbl qtreetbl_t container pointer.
904  *
905  */
906 void qtreetbl_print(qtreetbl_t *tbl) {
907 
908  qtreetbl_lock(tbl);
909 
910  print_node(tbl->root, 0);
911 
912  qtreetbl_unlock(tbl);
913 }
914 
915 /**
916  * Verifies that RULE 4 of the red-black tree is verified for the node pointed
917  * by `obj` and all its children.
918  *
919  * Rule 4 states that no red node shall have a red child.
920  *
921  * @param tbl A pointer to the tree object.
922  * @param obj A pointer to a node of the tree object.
923  */
924 int node_check_rule4(qtreetbl_t *tbl, qtreetbl_obj_t *obj) {
925  // RULE 4: No red node has a red child
926 
927  if (obj == NULL) return 0;
928 
929  if (is_red(obj)) {
930  if (is_red(obj->right) || is_red(obj->left)) {
931  printf("ERROR: Rule 4 violated.\n");
932  printf("Red node with key '");
933  // Print the name
934  for (int i = 0; i < obj->namesize; i++) {
935  printf("%c", ((char *) obj->name)[i]);
936  }
937  printf("' has at least one red child.\n");
938  print_node(tbl->root, 0);
939 
940  return 1;
941  }
942  }
943 
944  if (node_check_rule4(tbl, obj->right))
945  return 1;
946  if (node_check_rule4(tbl, obj->left))
947  return 1;
948 
949  return 0;
950 }
951 
952 /**
953  * Verifies that RULE 5 of the red-black tree is verified for the node pointed
954  * by `obj` and all its children.
955  *
956  * Rule 5 states that every path from the root of the tree to any leaf of the
957  * tree has the same number of black nodes.
958  *
959  * @param tbl A pointer to the tree object.
960  * @param obj A pointer to a node of the tree object.
961  */
962 int node_check_rule5(qtreetbl_t *tbl, qtreetbl_obj_t *obj, int *path_len) {
963 
964  if (obj == NULL) {
965  *path_len = 0;
966  } else {
967  int right_path_len;
968  if (node_check_rule5(tbl, obj->right, &right_path_len))
969  return 1;
970 
971  int left_path_len;
972  if (node_check_rule5(tbl, obj->left, &left_path_len))
973  return 1;
974 
975  if (right_path_len != left_path_len) {
976  printf("ERROR: Rule 5 violated.");
977  print_node(tbl->root, 0);
978  return 1;
979  } else {
980  *path_len = right_path_len;
981 
982  if (!is_red(obj)) (*path_len)++;
983  }
984  }
985 
986  return 0;
987 }
988 
989 /**
990  * Verifies that the (some) invariants of the red-black tree are satisfied.
991  *
992  * @param tbl A pointer to the tree object to check.
993  */
994 int qtreetbl_check(qtreetbl_t *tbl) {
995 
996  printf("Checking tree... \n");
997  if (tbl == NULL) return 0;
998 
999  if (node_check_rule4(tbl, tbl->root))
1000  return 1;
1001  int path_len = 0;
1002  if (node_check_rule5(tbl, tbl->root, &path_len))
1003  return 1;
1004 
1005 
1006 
1007  print_node(tbl->root, 0);
1008 
1009  return 0;
1010 }
1011 
1012 #ifndef _DOXYGEN_SKIP
1013 
1014 static bool is_red(qtreetbl_obj_t *obj) {
1015  return (obj != NULL) ? obj->red : false;
1016 }
1017 
1018 static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj) {
1019  obj->red = !(obj->red);
1020  obj->left->red = !(obj->left->red);
1021  obj->right->red = !(obj->right->red);
1022  return obj;
1023 }
1024 
1025 static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj) {
1026  qtreetbl_obj_t *x = obj->right;
1027  obj->right = x->left;
1028  x->left = obj;
1029  x->red = x->left->red;
1030  x->left->red = true;
1031  return x;
1032 }
1033 
1034 static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj) {
1035  qtreetbl_obj_t *x = obj->left;
1036  obj->left = x->right;
1037  x->right = obj;
1038  x->red = x->right->red;
1039  x->right->red = true;
1040  return x;
1041 }
1042 
1043 static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj) {
1044  flip_color(obj);
1045  if (obj->right && is_red(obj->right->left)) {
1046  obj->right = rotate_right(obj->right);
1047  obj = rotate_left(obj);
1048  flip_color(obj);
1049  }
1050  return obj;
1051 }
1052 
1053 static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj) {
1054  flip_color(obj);
1055  if (obj->left && is_red(obj->left->left)) {
1056  obj = rotate_right(obj);
1057  flip_color(obj);
1058  }
1059  return obj;
1060 }
1061 
1062 static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj) {
1063  // rotate right red to left
1064  if (is_red(obj->right)) {
1065  obj = rotate_left(obj);
1066  }
1067  // rotate left red-red to right
1068  if (obj->left && is_red(obj->left) && is_red(obj->left->left)) {
1069  obj = rotate_right(obj);
1070  }
1071  // split 4-nodes
1072  if (is_red(obj->left) && is_red(obj->right)) {
1073  flip_color(obj);
1074  }
1075  return obj;
1076 }
1077 
1078 static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj) {
1079  if (obj == NULL) {
1080  errno = ENOENT;
1081  return NULL;
1082  }
1083 
1084  qtreetbl_obj_t *o;
1085  for (o = obj; o->left != NULL; o = o->left)
1086  ;
1087  return o;
1088 }
1089 
1090 static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj) {
1091  if (obj == NULL) {
1092  errno = ENOENT;
1093  return NULL;
1094  }
1095 
1096  qtreetbl_obj_t *o;
1097  for (o = obj; o->right != NULL; o = o->right)
1098  ;
1099  return o;
1100 }
1101 
1102 static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl, const void *name,
1103  size_t namesize) {
1104  if (name == NULL || namesize == 0) {
1105  errno = EINVAL;
1106  return NULL;
1107  }
1108 
1109  qtreetbl_lock(tbl);
1110  qtreetbl_obj_t *obj;
1111  for (obj = tbl->root; obj != NULL;) {
1112  int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
1113  if (cmp == 0) {
1114  qtreetbl_unlock(tbl);
1115  return obj;
1116  }
1117  obj = (cmp < 0) ? obj->left : obj->right;
1118  }
1119  qtreetbl_unlock(tbl);
1120 
1121  errno = ENOENT;
1122  return NULL;
1123 }
1124 
1125 static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj) {
1126  if (obj->left == NULL) {
1127  // 3-nodes are left-leaning, so this is a leaf.
1128  free(obj->name);
1129  free(obj->data);
1130  return NULL;
1131  }
1132  if (!is_red(obj->left) && !is_red(obj->left->left)) {
1133  obj = move_red_left(obj);
1134  }
1135  obj->left = remove_min(obj->left);
1136  return fix(obj);
1137 }
1138 
1139 static qtreetbl_obj_t *new_obj(bool red, const void *name, size_t namesize,
1140  const void *data, size_t datasize) {
1141  qtreetbl_obj_t *obj = (qtreetbl_obj_t *) calloc(1, sizeof(qtreetbl_obj_t));
1142  void *copyname = qmemdup(name, namesize);
1143  void *copydata = qmemdup(data, datasize);
1144 
1145  if (obj == NULL || copyname == NULL || copydata == NULL) {
1146  errno = ENOMEM;
1147  free(obj);
1148  free(copyname);
1149  free(copydata);
1150  return NULL;
1151  }
1152 
1153  obj->red = red;
1154  obj->name = copyname;
1155  obj->namesize = namesize;
1156  obj->data = copydata;
1157  obj->datasize = datasize;
1158 
1159  return obj;
1160 }
1161 
1162 static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
1163  const void *name, size_t namesize,
1164  const void *data, size_t datasize) {
1165  if (obj == NULL) {
1166  tbl->num++;
1167  return new_obj(true, name, namesize, data, datasize);
1168  }
1169 
1170  // split 4-nodes on the way down.
1171  if (is_red(obj->left) && is_red(obj->right)) {
1172  flip_color(obj);
1173  }
1174 
1175  int cmp = tbl->compare(obj->name, obj->namesize, name, namesize);
1176  if (cmp == 0) { // existing key found.
1177  void *copydata = qmemdup(data, datasize);
1178  if (copydata != NULL) {
1179  free(obj->data);
1180  obj->data = copydata;
1181  obj->datasize = datasize;
1182  }
1183  } else if (cmp < 0) {
1184  obj->right = put_obj(tbl, obj->right, name, namesize, data, datasize);
1185  } else {
1186  obj->left = put_obj(tbl, obj->left, name, namesize, data, datasize);
1187  }
1188 
1189  // fix right-leaning reds on the way up
1190  if (is_red(obj->right)) {
1191  obj = rotate_left(obj);
1192  }
1193 
1194  // fix two reds in a row on the way up
1195  if (is_red(obj->left) && is_red(obj->left->left)) {
1196  obj = rotate_right(obj);
1197  }
1198 
1199  return obj;
1200 }
1201 
1202 static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
1203  const void *name, size_t namesize) {
1204  if (obj == NULL) {
1205  errno = ENOENT;
1206  return NULL;
1207  }
1208 
1209  if (tbl->compare(name, namesize, obj->name, obj->namesize) < 0) { // left
1210  // move red left
1211  if (obj->left && (!is_red(obj->left) && !is_red(obj->left->left))) {
1212  obj = move_red_left(obj);
1213  }
1214  // keep going down to the left
1215  obj->left = remove_obj(tbl, obj->left, name, namesize);
1216  } else { // right or equal
1217  if (is_red(obj->left) && !is_red(obj->right)) {
1218  obj = rotate_right(obj);
1219  }
1220  // remove if equal at the bottom
1221  if (tbl->compare(name, namesize, obj->name, obj->namesize)
1222  == 0&& obj->right == NULL) {
1223  free(obj->name);
1224  free(obj->data);
1225  free(obj);
1226  tbl->num--;
1227  assert(tbl->num >= 0);
1228  return NULL;
1229  }
1230  // move red right
1231  if (obj->right != NULL
1232  && (!is_red(obj->right) && !is_red(obj->right->left))) {
1233  obj = move_red_right(obj);
1234  }
1235  // found in the middle
1236  if (tbl->compare(name, namesize, obj->name, obj->namesize) == 0) {
1237  // copy min to this then remove min. What a genius inventor!
1238  qtreetbl_obj_t *minobj = find_min(obj->right);
1239  assert(minobj != NULL);
1240  free(obj->name);
1241  free(obj->data);
1242  obj->name = qmemdup(minobj->name, minobj->namesize);
1243  obj->namesize = minobj->namesize;
1244  obj->data = qmemdup(minobj->data, minobj->datasize);
1245  obj->datasize = minobj->datasize;
1246  obj->right = remove_min(obj->right);
1247  tbl->num--;
1248  } else {
1249  // keep going down to the right
1250  obj->right = remove_obj(tbl, obj->right, name, namesize);
1251  }
1252  }
1253  // Fix right-leaning red nodes on the way up.
1254  return fix(obj);
1255 }
1256 
1257 static void free_objs(qtreetbl_obj_t *obj) {
1258  if (obj == NULL) {
1259  return;
1260  }
1261  if (obj->left) {
1262  free_objs(obj->left);
1263  }
1264  if (obj->right) {
1265  free_objs(obj->right);
1266  }
1267  free_obj(obj);
1268 }
1269 
1270 static void free_obj(qtreetbl_obj_t *obj) {
1271  if (obj == NULL) {
1272  return;
1273  }
1274  free(obj->name);
1275  free(obj->data);
1276  free(obj);
1277 }
1278 
1279 static uint8_t reset_iterator(qtreetbl_t *tbl) {
1280  return (++tbl->tid);
1281 }
1282 
1283 #endif
qtreetbl_t * qtreetbl(int options)
Initialize a tree table.
Definition: qtreetbl.c:171
void print_node(qtreetbl_obj_t *obj, int depth)
Display the tree structure from the node pointed by obj and down.
Definition: qtreetbl.c:875
void qtreetbl_print(qtreetbl_t *tbl)
Display the tree structure.
Definition: qtreetbl.c:906
char * qtreetbl_getstr(qtreetbl_t *tbl, const char *name, const bool newmem)
qtreetbl->getstr(): Finds an object and returns as string type.
Definition: qtreetbl.c:410
bool qtreetbl_remove_by_obj(qtreetbl_t *tbl, const void *name, size_t namesize)
qtreetbl->remove(): Remove an object from this table with an object name.
Definition: qtreetbl.c:485
bool qtreetbl_debug(qtreetbl_t *tbl, FILE *out)
qtreetbl->debug(): Print hash table for debugging purpose
Definition: qtreetbl.c:854
int qtreetbl_check(qtreetbl_t *tbl)
Verifies that the (some) invariants of the red-black tree are satisfied.
Definition: qtreetbl.c:994
size_t qtreetbl_size(qtreetbl_t *tbl)
qtreetbl->size(): Returns the number of keys in the table.
Definition: qtreetbl.c:777
bool qtreetbl_putstr(qtreetbl_t *tbl, const char *name, const char *str)
qtreetbl->putstr(): Put a string into this table.
Definition: qtreetbl.c:279
void qtreetbl_free(qtreetbl_t *tbl)
qtreetbl->free(): De-allocate the table
Definition: qtreetbl.c:829
void * qtreetbl_find_min(qtreetbl_t *tbl, size_t *namesize)
qtreetbl->find_min(): Find the name of very left object.
Definition: qtreetbl.c:632
void * qtreetbl_get_by_obj(qtreetbl_t *tbl, const char *name, size_t namesize, size_t *datasize, bool newmem)
qtreetbl->get_by_obj(): Get an object from this table with an object name.
Definition: qtreetbl.c:437
bool qtreetbl_getnext(qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem)
qhashtbl->getnext(): Get next element.
Definition: qtreetbl.c:578
void * qtreetbl_find_max(qtreetbl_t *tbl, size_t *namesize)
qtreetbl->find_max(): Find the name of very right object.
Definition: qtreetbl.c:660
void qtreetbl_unlock(qtreetbl_t *tbl)
qtreetbl->unlock(): Leave critical section.
Definition: qtreetbl.c:820
bool qtreetbl_put_by_obj(qtreetbl_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
qtreetbl->put_by_obj(): Put an object data into this table with an object name.
Definition: qtreetbl.c:328
void qtreetbl_clear(qtreetbl_t *tbl)
qtreetbl->clear(): Clears the table so that it contains no keys.
Definition: qtreetbl.c:786
qtreetbl_obj_t qtreetbl_find_nearest(qtreetbl_t *tbl, const void *name, size_t namesize, bool newmem)
qtreetbl->find_nearest(): Find equal or nearest object.
Definition: qtreetbl.c:712
bool qtreetbl_remove(qtreetbl_t *tbl, const char *name)
qtreetbl->remove(): Remove an object from this table.
Definition: qtreetbl.c:468
void * qmemdup(const void *data, size_t size)
Duplicate a copy of memory data.
Definition: qstring.c:406
int node_check_rule5(qtreetbl_t *tbl, qtreetbl_obj_t *obj, int *path_len)
Verifies that RULE 5 of the red-black tree is verified for the node pointed by obj and all its childr...
Definition: qtreetbl.c:962
bool qtreetbl_put(qtreetbl_t *tbl, const char *name, const void *data, size_t datasize)
qtreetbl->put(): Put an object into this table with string type key.
Definition: qtreetbl.c:260
void qtreetbl_set_compare(qtreetbl_t *tbl, int(*cmp)(const void *name1, size_t namesize1, const void *name2, size_t namesize2))
qtreetbl->set_compare(): Set user comparator.
Definition: qtreetbl.c:240
void * qtreetbl_get(qtreetbl_t *tbl, const char *name, size_t *datasize, bool newmem)
qtreetbl->get(): Get an object from this table.
Definition: qtreetbl.c:384
void qtreetbl_lock(qtreetbl_t *tbl)
qtreetbl->lock(): Enter critical section.
Definition: qtreetbl.c:807
bool qtreetbl_putstrf(qtreetbl_t *tbl, const char *name, const char *format,...)
qtreetbl->putstrf(): Put a formatted string into this table.
Definition: qtreetbl.c:297
int node_check_rule4(qtreetbl_t *tbl, qtreetbl_obj_t *obj)
Verifies that RULE 4 of the red-black tree is verified for the node pointed by obj and all its childr...
Definition: qtreetbl.c:924