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 #ifndef _DOXYGEN_SKIP
868 
869 static bool is_red(qtreetbl_obj_t *obj) {
870  return (obj != NULL) ? obj->red : false;
871 }
872 
873 static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj) {
874  obj->red = !(obj->red);
875  obj->left->red = !(obj->left->red);
876  obj->right->red = !(obj->right->red);
877  return obj;
878 }
879 
880 static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj) {
881  qtreetbl_obj_t *x = obj->right;
882  obj->right = x->left;
883  x->left = obj;
884  x->red = x->left->red;
885  x->left->red = true;
886  return x;
887 }
888 
889 static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj) {
890  qtreetbl_obj_t *x = obj->left;
891  obj->left = x->right;
892  x->right = obj;
893  x->red = x->right->red;
894  x->right->red = true;
895  return x;
896 }
897 
898 static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj) {
899  flip_color(obj);
900  if (obj->right && is_red(obj->right->left)) {
901  obj->right = rotate_right(obj->right);
902  obj = rotate_left(obj);
903  flip_color(obj);
904  }
905  return obj;
906 }
907 
908 static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj) {
909  flip_color(obj);
910  if (obj->left && is_red(obj->left->left)) {
911  obj = rotate_right(obj);
912  flip_color(obj);
913  }
914  return obj;
915 }
916 
917 static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj) {
918  // rotate right red to left
919  if (is_red(obj->right)) {
920  obj = rotate_left(obj);
921  }
922  // rotate left red-red to right
923  if (obj->left && is_red(obj->left) && is_red(obj->left->left)) {
924  obj = rotate_right(obj);
925  }
926  // split 4-nodes
927  if (is_red(obj->left) && is_red(obj->right)) {
928  flip_color(obj);
929  }
930  return obj;
931 }
932 
933 static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj) {
934  if (obj == NULL) {
935  errno = ENOENT;
936  return NULL;
937  }
938 
939  qtreetbl_obj_t *o;
940  for (o = obj; o->left != NULL; o = o->left)
941  ;
942  return o;
943 }
944 
945 static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj) {
946  if (obj == NULL) {
947  errno = ENOENT;
948  return NULL;
949  }
950 
951  qtreetbl_obj_t *o;
952  for (o = obj; o->right != NULL; o = o->right)
953  ;
954  return o;
955 }
956 
957 static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl, const void *name,
958  size_t namesize) {
959  if (name == NULL || namesize == 0) {
960  errno = EINVAL;
961  return NULL;
962  }
963 
964  qtreetbl_lock(tbl);
965  qtreetbl_obj_t *obj;
966  for (obj = tbl->root; obj != NULL;) {
967  int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
968  if (cmp == 0) {
969  qtreetbl_unlock(tbl);
970  return obj;
971  }
972  obj = (cmp < 0) ? obj->left : obj->right;
973  }
974  qtreetbl_unlock(tbl);
975 
976  errno = ENOENT;
977  return NULL;
978 }
979 
980 static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj) {
981  if (obj->left == NULL) {
982  // 3-nodes are left-leaning, so this is a leaf.
983  free(obj->name);
984  free(obj->data);
985  return NULL;
986  }
987  if (!is_red(obj->left) && !is_red(obj->left->left)) {
988  obj = move_red_left(obj);
989  }
990  obj->left = remove_min(obj->left);
991  return fix(obj);
992 }
993 
994 static qtreetbl_obj_t *new_obj(bool red, const void *name, size_t namesize,
995  const void *data, size_t datasize) {
996  qtreetbl_obj_t *obj = (qtreetbl_obj_t *) calloc(1, sizeof(qtreetbl_obj_t));
997  void *copyname = qmemdup(name, namesize);
998  void *copydata = qmemdup(data, datasize);
999 
1000  if (obj == NULL || copyname == NULL || copydata == NULL) {
1001  errno = ENOMEM;
1002  free(obj);
1003  free(copyname);
1004  free(copydata);
1005  return NULL;
1006  }
1007 
1008  obj->red = red;
1009  obj->name = copyname;
1010  obj->namesize = namesize;
1011  obj->data = copydata;
1012  obj->datasize = datasize;
1013 
1014  return obj;
1015 }
1016 
1017 static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
1018  const void *name, size_t namesize,
1019  const void *data, size_t datasize) {
1020  if (obj == NULL) {
1021  tbl->num++;
1022  return new_obj(true, name, namesize, data, datasize);
1023  }
1024 
1025  // split 4-nodes on the way down.
1026  if (is_red(obj->left) && is_red(obj->right)) {
1027  flip_color(obj);
1028  }
1029 
1030  int cmp = tbl->compare(obj->name, obj->namesize, name, namesize);
1031  if (cmp == 0) { // existing key found.
1032  void *copydata = qmemdup(data, datasize);
1033  if (copydata != NULL) {
1034  free(obj->data);
1035  obj->data = copydata;
1036  obj->datasize = datasize;
1037  }
1038  } else if (cmp < 0) {
1039  obj->right = put_obj(tbl, obj->right, name, namesize, data, datasize);
1040  } else {
1041  obj->left = put_obj(tbl, obj->left, name, namesize, data, datasize);
1042  }
1043 
1044  // fix right-leaning reds on the way up
1045  if (is_red(obj->right)) {
1046  obj = rotate_left(obj);
1047  }
1048 
1049  // fix two reds in a row on the way up
1050  if (is_red(obj->left) && is_red(obj->left->left)) {
1051  obj = rotate_right(obj);
1052  }
1053 
1054  return obj;
1055 }
1056 
1057 static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
1058  const void *name, size_t namesize) {
1059  if (obj == NULL) {
1060  errno = ENOENT;
1061  return NULL;
1062  }
1063 
1064  if (tbl->compare(name, namesize, obj->name, obj->namesize) < 0) { // left
1065  // move red left
1066  if (obj->left && (!is_red(obj->left) && !is_red(obj->left->left))) {
1067  obj = move_red_left(obj);
1068  }
1069  // keep going down to the left
1070  obj->left = remove_obj(tbl, obj->left, name, namesize);
1071  } else { // right or equal
1072  if (is_red(obj->left)) {
1073  obj = rotate_right(obj);
1074  }
1075  // remove if equal at the bottom
1076  if (tbl->compare(name, namesize, obj->name, obj->namesize)
1077  == 0&& obj->right == NULL) {
1078  free(obj->name);
1079  free(obj->data);
1080  free(obj);
1081  tbl->num--;
1082  assert(tbl->num >= 0);
1083  return NULL;
1084  }
1085  // move red right
1086  if (obj->right != NULL
1087  && (!is_red(obj->right) && !is_red(obj->right->left))) {
1088  obj = move_red_right(obj);
1089  }
1090  // found in the middle
1091  if (tbl->compare(name, namesize, obj->name, obj->namesize) == 0) {
1092  // copy min to this then remove min. What a genius inventor!
1093  qtreetbl_obj_t *minobj = find_min(obj->right);
1094  assert(minobj != NULL);
1095  free(obj->name);
1096  free(obj->data);
1097  obj->name = qmemdup(minobj->name, minobj->namesize);
1098  obj->namesize = minobj->namesize;
1099  obj->data = qmemdup(minobj->data, minobj->datasize);
1100  obj->datasize = minobj->datasize;
1101  obj->right = remove_min(obj->right);
1102  tbl->num--;
1103  } else {
1104  // keep going down to the right
1105  obj->right = remove_obj(tbl, obj->right, name, namesize);
1106  }
1107  }
1108  // Fix right-leaning red nodes on the way up.
1109  return fix(obj);
1110 }
1111 
1112 static void free_objs(qtreetbl_obj_t *obj) {
1113  if (obj == NULL) {
1114  return;
1115  }
1116  if (obj->left) {
1117  free_objs(obj->left);
1118  }
1119  if (obj->right) {
1120  free_objs(obj->right);
1121  }
1122  free_obj(obj);
1123 }
1124 
1125 static void free_obj(qtreetbl_obj_t *obj) {
1126  if (obj == NULL) {
1127  return;
1128  }
1129  free(obj->name);
1130  free(obj->data);
1131  free(obj);
1132 }
1133 
1134 static uint8_t reset_iterator(qtreetbl_t *tbl) {
1135  return (++tbl->tid);
1136 }
1137 
1138 #endif
qtreetbl_t * qtreetbl(int options)
Initialize a tree table.
Definition: qtreetbl.c:171
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
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
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