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