126#include "qinternal.h"
127#include "utilities/qstring.h"
128#include "containers/qtreetbl.h"
134static bool is_red(qtreetbl_obj_t *obj);
135static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj);
136static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj);
137static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj);
138static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj);
139static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj);
140static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj);
141static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj);
142static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj);
143static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj);
144static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl,
const void *name,
146static qtreetbl_obj_t *new_obj(
bool red,
const void *name,
size_t namesize,
147 const void *data,
size_t datasize);
148static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
149 const void *name,
size_t namesize,
150 const void *data,
size_t datasize);
151static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
152 const void *name,
size_t namesize);
153static void free_objs(qtreetbl_obj_t *obj);
154static uint8_t reset_iterator(qtreetbl_t *tbl);
157 struct branch_obj_s *p;
160static void print_branch(
struct branch_obj_s *branch, FILE *out);
161static void print_node(qtreetbl_obj_t *obj, FILE *out,
struct branch_obj_s *prev,
183 qtreetbl_t *tbl = (qtreetbl_t *) calloc(1,
sizeof(qtreetbl_t));
188 if (options & QTREETBL_THREADSAFE) {
189 Q_MUTEX_NEW(tbl->qmutex,
true);
190 if (tbl->qmutex == NULL)
233 assert(tbl->qmutex == NULL);
251 int (*cmp)(
const void *name1,
size_t namesize1,
252 const void *name2,
size_t namesize2)) {
272 name, (name != NULL) ? (strlen(name) + 1) : 0, data, datasize);
289 name, (name != NULL) ? (strlen(name) + 1) : 0,
290 str, (str != NULL) ? (strlen(str) + 1) : 0);
308 DYNAMIC_VSPRINTF(str, format);
337 const void *data,
size_t datasize) {
338 if (name == NULL || namesize == 0) {
345 qtreetbl_obj_t *root = put_obj(tbl, tbl->root, name, namesize, data,
347 if (root == NULL || errno == ENOMEM) {
395 name, (name != NULL) ? (strlen(name) + 1) : 0, datasize, newmem);
419 name, (name != NULL) ? (strlen(name) + 1) : 0, NULL, newmem);
444 size_t *datasize,
bool newmem) {
445 if (name == NULL || namesize == 0) {
451 qtreetbl_obj_t *obj = find_obj(tbl, name, namesize);
454 data = (newmem) ?
qmemdup(obj->data, obj->datasize) : obj->data;
455 if (datasize != NULL) {
456 *datasize = (data != NULL) ? obj->datasize : 0;
476 name, (name != NULL) ? strlen(name) + 1 : 0);
499 tbl->root = remove_obj(tbl, tbl->root, name, namesize);
500 if (tbl->root != NULL) {
501 tbl->root->red =
false;
503 bool removed = (errno != ENOENT) ?
true :
false;
586 uint8_t tid = obj->tid;
587 if (obj->next == NULL) {
588 if (tbl->root == NULL) {
592 tid = reset_iterator(tbl);;
595 qtreetbl_obj_t *cursor = ((obj->next != NULL) ? obj->next : tbl->root);
596 while (cursor != NULL) {
597 if (cursor->left != NULL && cursor->left->tid != tid) {
598 cursor->left->next = cursor;
599 cursor = cursor->left;
601 }
else if (cursor->tid != tid) {
605 obj->name =
qmemdup(cursor->name, cursor->namesize);
606 obj->data =
qmemdup(cursor->data, cursor->datasize);
610 }
else if (cursor->right != NULL && cursor->right->tid != tid) {
611 cursor->right->next = cursor;
612 cursor = cursor->right;
615 cursor = cursor->next;
636 qtreetbl_obj_t *obj = find_min(tbl->root);
643 if (namesize != NULL) {
644 *namesize = obj->namesize;
646 void *name =
qmemdup(obj->name, obj->namesize);
664 qtreetbl_obj_t *obj = find_max(tbl->root);
671 if (namesize != NULL) {
672 *namesize = obj->namesize;
674 void *name =
qmemdup(obj->name, obj->namesize);
712 size_t namesize,
bool newmem) {
713 qtreetbl_obj_t retobj;
714 memset((
void*) &retobj, 0,
sizeof(retobj));
716 if (name == NULL || namesize == 0) {
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);
730 if (obj->left != NULL) {
731 obj->left->next = obj;
735 if (obj->right != NULL) {
736 obj->right->next = obj;
744 obj != NULL && (tbl->compare(name, namesize, obj->name, obj->namesize) < 0);
754 retobj.name =
qmemdup(obj->name, obj->namesize);
755 retobj.data =
qmemdup(obj->data, obj->datasize);
758 retobj.tid = tbl->tid;
786 free_objs(tbl->root);
806 Q_MUTEX_ENTER(tbl->qmutex);
819 Q_MUTEX_LEAVE(tbl->qmutex);
829 Q_MUTEX_DESTROY(tbl->qmutex);
833int qtreetbl_byte_cmp(
const void *name1,
size_t namesize1,
const void *name2,
835 size_t minsize = (namesize1 < namesize2) ? namesize1 : namesize2;
836 int cmp = memcmp(name1, name2, minsize);
837 if (cmp != 0 || namesize1 == namesize2) {
840 return (namesize1 < namesize2) ? -1 : +1;
878 print_node(tbl->root, out, NULL,
false);
895 if (is_red(tbl->root)) {
915 if (is_red(obj->right) || is_red(obj->left)) {
955 if (right_path_len != left_path_len) {
958 *path_len = (!is_red(obj)) ? (right_path_len + 1) : right_path_len;
976 if (is_red(obj->right) && !is_red(obj->left)) {
1023#ifndef _DOXYGEN_SKIP
1025static bool is_red(qtreetbl_obj_t *obj) {
1026 return (obj != NULL) ? obj->red :
false;
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++;
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;
1043 x->red = x->left->red;
1044 x->left->red =
true;
1045 _q_treetbl_rotate_left_cnt++;
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;
1054 x->red = x->right->red;
1055 x->right->red =
true;
1056 _q_treetbl_rotate_right_cnt++;
1060static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj) {
1062 if (is_red(obj->right->left)) {
1063 obj->right = rotate_right(obj->right);
1064 obj = rotate_left(obj);
1068 if (is_red(obj->right->right)) {
1069 obj->right = rotate_left(obj->right);
1076static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj) {
1078 if (is_red(obj->left->left)) {
1079 obj = rotate_right(obj);
1085static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj) {
1091 for (; obj->left != NULL; obj = obj->left);
1095static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj) {
1101 for (; obj->right != NULL; obj = obj->right);
1105static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj) {
1106 if (obj->left == NULL) {
1112 if (!is_red(obj->left) && !is_red(obj->left->left)) {
1113 obj = move_red_left(obj);
1115 obj->left = remove_min(obj->left);
1119static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj) {
1121 if (is_red(obj->right)) {
1124 if (is_red(obj->right->left)) {
1125 obj->right = rotate_right(obj->right);
1128 obj = rotate_left(obj);
1131 if (is_red(obj->left) && is_red(obj->left->left)) {
1132 obj = rotate_right(obj);
1136 if (is_red(obj->left) && is_red(obj->right)) {
1143static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl,
const void *name,
1145 if (name == NULL || namesize == 0) {
1150 qtreetbl_obj_t *obj;
1151 for (obj = tbl->root; obj != NULL;) {
1152 int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
1156 obj = (cmp < 0) ? obj->left : obj->right;
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);
1169 if (obj == NULL || copyname == NULL) {
1178 obj->name = copyname;
1179 obj->namesize = namesize;
1180 obj->data = copydata;
1181 obj->datasize = datasize;
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) {
1191 return new_obj(
true, name, namesize, data, datasize);
1196 if (is_red(obj->left) && is_red(obj->right)) {
1201 int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
1203 void *copydata =
qmemdup(data, datasize);
1204 if (copydata != NULL) {
1206 obj->data = copydata;
1207 obj->datasize = datasize;
1209 }
else if (cmp < 0) {
1210 obj->left = put_obj(tbl, obj->left, name, namesize, data, datasize);
1212 obj->right = put_obj(tbl, obj->right, name, namesize, data, datasize);
1216 if (is_red(obj->right) && !is_red(obj->left)) {
1217 obj = rotate_left(obj);
1221 if (is_red(obj->left) && is_red(obj->left->left)) {
1222 obj = rotate_right(obj);
1227 if (is_red(obj->left) && is_red(obj->right)) {
1236static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
1237 const void *name,
size_t namesize) {
1243 int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
1246 if (obj->left != NULL
1247 && (!is_red(obj->left) && !is_red(obj->left->left))) {
1248 obj = move_red_left(obj);
1251 obj->left = remove_obj(tbl, obj->left, name, namesize);
1254 if (is_red(obj->left)) {
1255 obj = rotate_right(obj);
1259 if (obj->right == NULL) {
1261 cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
1273 if (obj->right != NULL
1274 && (!is_red(obj->right) && !is_red(obj->right->left))) {
1275 obj = move_red_right(obj);
1280 cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
1284 qtreetbl_obj_t *minobj = find_min(obj->right);
1285 assert(minobj != NULL);
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);
1296 obj->right = remove_obj(tbl, obj->right, name, namesize);
1303static void free_objs(qtreetbl_obj_t *obj) {
1308 free_objs(obj->left);
1309 free_objs(obj->right);
1316static uint8_t reset_iterator(qtreetbl_t *tbl) {
1317 if (tbl->root != NULL) {
1318 tbl->root->next = NULL;
1320 return (++tbl->tid);
1323static void print_branch(
struct branch_obj_s *branch, FILE *out) {
1324 if (branch == NULL) {
1327 print_branch(branch->p, out);
1328 fprintf(out,
"%s", branch->s);
1331static void print_node(qtreetbl_obj_t *obj, FILE *out,
struct branch_obj_s *prev,
1337 struct branch_obj_s branch;
1340 branch.s = (prev != NULL) ? (right) ?
" " :
"│ " :
"";
1341 print_node(obj->right, out, &branch,
true);
1343 print_branch(prev, out);
1345 fprintf(out,
"%s%s", (right) ?
"┌──" :
"└──", (obj->red) ?
"[" :
"─");
1347 if (obj->data == NULL && obj->namesize ==
sizeof(uint32_t)) {
1348 fprintf(out,
"%u", *(uint32_t *)obj->name);
1350 _q_textout(out, obj->name, obj->namesize, 15);
1352 fprintf(out,
"%s", (obj->red) ?
"]\n" :
"\n");
1354 branch.s = (prev != NULL) ? (right) ?
"│ " :
" " :
"";
1355 print_node(obj->left, out, &branch,
false);
void * qmemdup(const void *data, size_t size)
Duplicate a copy of memory data.
qtreetbl_t * qtreetbl(int options)
Initialize a tree table.
void * qtreetbl_get(qtreetbl_t *tbl, const char *name, size_t *datasize, bool newmem)
qtreetbl->get(): Get an object from this table.
int node_check_root(qtreetbl_t *tbl)
Verifies that root property of the red-black tree is satisfied.
void qtreetbl_free(qtreetbl_t *tbl)
qtreetbl->free(): De-allocate the table.
bool qtreetbl_putstr(qtreetbl_t *tbl, const char *name, const char *str)
qtreetbl->putstr(): Put a string into this table.
bool qtreetbl_remove(qtreetbl_t *tbl, const char *name)
qtreetbl->remove(): Remove an object from this table.
size_t qtreetbl_size(qtreetbl_t *tbl)
qtreetbl->size(): Returns the number of keys in the table.
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.
void * qtreetbl_find_max(qtreetbl_t *tbl, size_t *namesize)
qtreetbl->find_max(): Find the name of the rightmost object.
void qtreetbl_lock(qtreetbl_t *tbl)
qtreetbl->lock(): Enter critical section.
bool qtreetbl_debug(qtreetbl_t *tbl, FILE *out)
qtreetbl->debug(): Print the internal tree structure in text.
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.
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.
void qtreetbl_clear(qtreetbl_t *tbl)
qtreetbl->clear(): Clears the table so that it contains no keys.
char * qtreetbl_getstr(qtreetbl_t *tbl, const char *name, const bool newmem)
qtreetbl->getstr(): Finds an object and returns it as string type.
void qtreetbl_unlock(qtreetbl_t *tbl)
qtreetbl->unlock(): Leave critical section.
int node_check_llrb(qtreetbl_t *tbl, qtreetbl_obj_t *obj)
Verifies that LLRB property of the left-leaning red-black tree is satisfied.
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.
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.
int qtreetbl_check(qtreetbl_t *tbl)
Verifies that the invariants of the red-black tree are satisfied.
void * qtreetbl_find_min(qtreetbl_t *tbl, size_t *namesize)
qtreetbl->find_min(): Find the name of the leftmost object.
bool qtreetbl_putstrf(qtreetbl_t *tbl, const char *name, const char *format,...)
qtreetbl->putstrf(): Put a formatted string into this table.
int node_check_red(qtreetbl_t *tbl, qtreetbl_obj_t *obj)
Verifies that red property of the red-black tree is satisfied.
bool qtreetbl_removeobj(qtreetbl_t *tbl, const void *name, size_t namesize)
qtreetbl->remove(): Remove an object from this table with an object name.
bool qtreetbl_getnext(qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem)
qtreetbl->getnext(): Get next element.
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.