00001 #include <stdlib.h>
00002 #include "collection.h"
00003 #include "trie.h"
00004 #include "mtab.h"
00005
00006
00007
00008
00009 cp_trie_node *cp_trie_node_new(void *leaf, cp_mempool *pool)
00010 {
00011 cp_trie_node *node;
00012 if (pool)
00013 node = (cp_trie_node *) cp_mempool_calloc(pool);
00014 else
00015 node = (cp_trie_node *) calloc(1, sizeof(cp_trie_node));
00016 if (node)
00017 {
00018 node->others = mtab_new(0);
00019 node->leaf = leaf;
00020 }
00021
00022 return node;
00023 }
00024
00025
00026
00027
00028 void *cp_trie_node_delete(cp_trie *grp, cp_trie_node *node)
00029 {
00030 void *leaf = NULL;
00031
00032 if (node)
00033 {
00034 mtab_delete_custom(node->others, grp, (mtab_dtr)cp_trie_delete_mapping);
00035 leaf = node->leaf;
00036 if (grp->mempool)
00037 cp_mempool_free(grp->mempool, node);
00038 else
00039 free(node);
00040 }
00041
00042 if (leaf && grp->delete_leaf && grp->mode & COLLECTION_MODE_DEEP)
00043 (*grp->delete_leaf)(leaf);
00044
00045 return leaf;
00046 }
00047
00048
00049
00050
00051
00052 void cp_trie_delete_mapping(cp_trie *grp, mtab_node *map_node)
00053 {
00054 if (map_node)
00055 {
00056 if (map_node->attr) free(map_node->attr);
00057 if (map_node->value) cp_trie_node_delete(grp, map_node->value);
00058 }
00059 }
00060
00061 void cp_trie_node_unmap(cp_trie *grp, cp_trie_node **node)
00062 {
00063 cp_trie_node_delete(grp, *node);
00064 *node = NULL;
00065 }
00066
00067 int cp_trie_lock_internal(cp_trie *grp, int type)
00068 {
00069 int rc = 0;
00070 switch(type)
00071 {
00072 case COLLECTION_LOCK_READ:
00073 rc = cp_lock_rdlock(grp->lock);
00074 break;
00075
00076 case COLLECTION_LOCK_WRITE:
00077 rc = cp_lock_wrlock(grp->lock);
00078 break;
00079
00080 case COLLECTION_LOCK_NONE:
00081 default:
00082 break;
00083 }
00084
00085 return rc;
00086 }
00087
00088 int cp_trie_unlock_internal(cp_trie *grp)
00089 {
00090 return cp_lock_unlock(grp->lock);
00091 }
00092
00093 int cp_trie_txlock(cp_trie *grp, int type)
00094 {
00095 if (grp->mode & COLLECTION_MODE_NOSYNC) return 0;
00096 if (grp->mode & COLLECTION_MODE_IN_TRANSACTION &&
00097 grp->txtype == COLLECTION_LOCK_WRITE)
00098 {
00099 cp_thread self = cp_thread_self();
00100 if (cp_thread_equal(self, grp->txowner)) return 0;
00101 }
00102 return cp_trie_lock_internal(grp, type);
00103 }
00104
00105 int cp_trie_txunlock(cp_trie *grp)
00106 {
00107 if (grp->mode & COLLECTION_MODE_NOSYNC) return 0;
00108 if (grp->mode & COLLECTION_MODE_IN_TRANSACTION &&
00109 grp->txtype == COLLECTION_LOCK_WRITE)
00110 {
00111 cp_thread self = cp_thread_self();
00112 if (cp_thread_equal(self, grp->txowner)) return 0;
00113 }
00114 return cp_trie_unlock_internal(grp);
00115 }
00116
00117
00118 int cp_trie_lock(cp_trie *grp, int type)
00119 {
00120 int rc;
00121 if ((grp->mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00122 if ((rc = cp_trie_lock_internal(grp, type))) return rc;
00123 grp->txtype = type;
00124 grp->txowner = cp_thread_self();
00125 grp->mode |= COLLECTION_MODE_IN_TRANSACTION;
00126 return 0;
00127 }
00128
00129
00130 int cp_trie_unlock(cp_trie *grp)
00131 {
00132 cp_thread self = cp_thread_self();
00133 if (grp->txowner == self)
00134 {
00135 grp->txtype = 0;
00136 grp->txowner = 0;
00137 grp->mode ^= COLLECTION_MODE_IN_TRANSACTION;
00138 }
00139 else if (grp->txtype == COLLECTION_LOCK_WRITE)
00140 return -1;
00141 return cp_trie_unlock_internal(grp);
00142 }
00143
00144
00145 int cp_trie_get_mode(cp_trie *grp)
00146 {
00147 return grp->mode;
00148 }
00149
00150
00151 int cp_trie_set_mode(cp_trie *grp, int mode)
00152 {
00153 int nosync;
00154
00155
00156 if ((grp->mode & COLLECTION_MODE_IN_TRANSACTION) &&
00157 (mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00158
00159 nosync = grp->mode & COLLECTION_MODE_NOSYNC;
00160 if (!nosync)
00161 if (cp_trie_txlock(grp, COLLECTION_LOCK_WRITE))
00162 return -1;
00163
00164 grp->mode |= mode;
00165
00166 if (!nosync)
00167 cp_trie_txunlock(grp);
00168
00169 return 0;
00170 }
00171
00172
00173
00174
00175
00176 int cp_trie_unset_mode(cp_trie *grp, int mode)
00177 {
00178 int nosync = grp->mode & COLLECTION_MODE_NOSYNC;
00179
00180 if (!nosync)
00181 if (cp_trie_txlock(grp, COLLECTION_LOCK_WRITE))
00182 return -1;
00183
00184
00185 if ((mode & COLLECTION_MODE_NOSYNC) && grp->lock == NULL)
00186 {
00187
00188 if ((grp->lock = malloc(sizeof(cp_lock))) == NULL)
00189 return -1;
00190 if (cp_lock_init(grp->lock, NULL))
00191 return -1;
00192 }
00193
00194
00195 grp->mode &= grp->mode ^ mode;
00196 if (!nosync)
00197 cp_trie_txunlock(grp);
00198
00199 return 0;
00200 }
00201
00202
00203 cp_trie *cp_trie_create_trie(int mode,
00204 cp_copy_fn copy_leaf,
00205 cp_destructor_fn delete_leaf)
00206 {
00207 cp_trie *grp = calloc(1, sizeof(cp_trie));
00208
00209 if (grp == NULL) return NULL;
00210 grp->root = cp_trie_node_new(NULL, NULL);
00211 if (grp->root == NULL)
00212 {
00213 free(grp);
00214 return NULL;
00215 }
00216
00217 if (!(mode & COLLECTION_MODE_NOSYNC))
00218 {
00219 if ((grp->lock = malloc(sizeof(cp_lock))) == NULL)
00220 {
00221 cp_trie_node_delete(grp, grp->root);
00222 free(grp);
00223 return NULL;
00224 }
00225
00226 if (cp_lock_init(grp->lock, NULL))
00227 {
00228 free(grp->lock);
00229 cp_trie_node_delete(grp, grp->root);
00230 free(grp);
00231 return NULL;
00232 }
00233 }
00234
00235 grp->mode = mode;
00236 grp->copy_leaf = copy_leaf;
00237 grp->delete_leaf = delete_leaf;
00238
00239 return grp;
00240 }
00241
00242 cp_trie *cp_trie_create(int mode)
00243 {
00244 return cp_trie_create_trie(mode, NULL, NULL);
00245 }
00246
00247
00248
00249
00250 int cp_trie_destroy(cp_trie *grp)
00251 {
00252 int rc = 0;
00253
00254
00255
00256
00257
00258 if (grp)
00259 {
00260 cp_trie_node_delete(grp, grp->root);
00261 if (grp->lock)
00262 {
00263 rc = cp_lock_destroy(grp->lock);
00264 free(grp->lock);
00265 }
00266 free(grp);
00267 }
00268 else rc = -1;
00269
00270 return rc;
00271 }
00272
00273 void *NODE_STORE(cp_trie_node *node, char *key, cp_trie_node *sub)
00274 {
00275 char *k = strdup(key);
00276 if (k == NULL) return NULL;
00277 return mtab_put(node->others, *k, sub, k);
00278 }
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 int cp_trie_node_store(cp_trie *grp,
00297 cp_trie_node *node,
00298 char *key,
00299 void *leaf)
00300 {
00301
00302 char *next;
00303 mtab_node *map_node;
00304 cp_trie_node *sub;
00305
00306 map_node = NODE_MATCH(node, key);
00307
00308 if (map_node == NULL)
00309 {
00310 sub = cp_trie_node_new(leaf, grp->mempool);
00311 if (NODE_STORE(node, key, sub) == NULL) return -1;
00312 }
00313 else
00314 {
00315 next = map_node->attr;
00316 while (*key && *key == *next)
00317 {
00318 key++;
00319 next++;
00320 }
00321
00322 if (*next)
00323 {
00324 cp_trie_node *old = map_node->value;
00325 if ((sub = cp_trie_node_new(NULL, grp->mempool)) == NULL) return -1;
00326 if (NODE_STORE(sub, next, old) == NULL) return -1;
00327 *next = '\0';
00328 map_node->value = sub;
00329 if (*key)
00330 {
00331 if ((NODE_STORE(sub, key,
00332 cp_trie_node_new(leaf, grp->mempool)) == NULL))
00333 return -1;
00334 }
00335 else
00336 sub->leaf = leaf;
00337 }
00338 else if (*key)
00339 return cp_trie_node_store(grp, map_node->value, key, leaf);
00340
00341 else
00342 {
00343 cp_trie_node *node = ((cp_trie_node *) map_node->value);
00344 if (node->leaf && grp->delete_leaf &&
00345 grp->mode & COLLECTION_MODE_DEEP)
00346 (*grp->delete_leaf)(node->leaf);
00347 node->leaf = leaf;
00348 }
00349 }
00350 return 0;
00351 }
00352
00353
00354
00355
00356 int cp_trie_add(cp_trie *grp, char *key, void *leaf)
00357 {
00358 int rc = 0;
00359 if ((rc = cp_trie_txlock(grp, COLLECTION_LOCK_WRITE))) return rc;
00360
00361 if ((grp->mode & COLLECTION_MODE_COPY) && grp->copy_leaf && (leaf != NULL))
00362 {
00363 leaf = (*grp->copy_leaf)(leaf);
00364 if (leaf == NULL) goto DONE;
00365 }
00366
00367 if (key == NULL)
00368 {
00369 if (grp->root->leaf &&
00370 grp->delete_leaf && grp->mode & COLLECTION_MODE_DEEP)
00371 (*grp->delete_leaf)(grp->root->leaf);
00372 grp->root->leaf = leaf;
00373 }
00374 else
00375 {
00376 if ((rc = cp_trie_node_store(grp, grp->root, key, leaf)))
00377 goto DONE;
00378 }
00379
00380 grp->path_count++;
00381
00382 DONE:
00383 cp_trie_txunlock(grp);
00384 return rc;
00385 }
00386
00387
00388
00389 static char *mergestr(char *s1, char *s2)
00390 {
00391 char *s;
00392 int len = strlen(s1) + strlen(s2);
00393
00394 s = malloc((len + 1) * sizeof(char));
00395 if (s == NULL) return NULL;
00396
00397 #ifdef CP_HAS_STRLCPY
00398 strlcpy(s, s1, len + 1);
00399 #else
00400 strcpy(s, s1);
00401 #endif
00402 #ifdef CP_HAS_STRLCAT
00403 strlcat(s, s2, len + 1);
00404 #else
00405 strcat(s, s2);
00406 #endif
00407
00408 return s;
00409 }
00410
00411 static mtab_node *get_single_entry(mtab *t)
00412 {
00413 int i;
00414
00415 for (i = 0; i < t->size; i++)
00416 if (t->table[i]) return t->table[i];
00417
00418 return NULL;
00419 }
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433 int cp_trie_remove(cp_trie *grp, char *key, void **leaf)
00434 {
00435 int rc = 0;
00436 cp_trie_node *link = grp->root;
00437 cp_trie_node *prev = NULL;
00438 char ccurr, cprev = 0;
00439 mtab_node *map_node;
00440 char *branch;
00441
00442 if (link == NULL) return 0;
00443
00444 if ((rc = cp_trie_txlock(grp, COLLECTION_LOCK_READ))) return rc;
00445
00446 if (key == NULL)
00447 {
00448 if (link->leaf)
00449 {
00450 grp->path_count--;
00451 link->leaf = NULL;
00452 }
00453 goto DONE;
00454 }
00455
00456
00457 ccurr = *key;
00458 while ((map_node = NODE_MATCH(link, key)) != NULL)
00459 {
00460 branch = map_node->attr;
00461
00462 while (*key && *key == *branch)
00463 {
00464 key++;
00465 branch++;
00466 }
00467 if (*branch) break;
00468 if (*key == '\0')
00469 {
00470 char *attr;
00471 cp_trie_node *node = map_node->value;
00472 if (leaf) *leaf = node->leaf;
00473 if (node->leaf)
00474 {
00475 grp->path_count--;
00476 rc = 1;
00477
00478 if (grp->delete_leaf && grp->mode & COLLECTION_MODE_DEEP)
00479 (*grp->delete_leaf)(node->leaf);
00480 node->leaf = NULL;
00481
00482
00483 if (mtab_count(node->others) == 0)
00484 {
00485 mtab_remove(link->others, ccurr);
00486 cp_trie_node_unmap(grp, &node);
00487
00488
00489 if (prev &&
00490 mtab_count(link->others) == 1 && link->leaf == NULL)
00491 {
00492 mtab_node *sub2 = mtab_get(prev->others, cprev);
00493 mtab_node *sub = get_single_entry(link->others);
00494 attr = mergestr(sub2->attr, sub->attr);
00495 if (attr)
00496 {
00497 if (NODE_STORE(prev, attr, sub->value))
00498 {
00499 mtab_remove(link->others, sub->key);
00500 cp_trie_node_delete(grp, link);
00501 }
00502 free(attr);
00503 }
00504 }
00505 }
00506 else if (mtab_count(node->others) == 1)
00507 {
00508 mtab_node *sub = get_single_entry(node->others);
00509 attr = mergestr(map_node->attr, sub->attr);
00510 if (attr)
00511 {
00512 if (NODE_STORE(link, attr, sub->value))
00513 {
00514 mtab_remove(node->others, sub->key);
00515 cp_trie_node_delete(grp, node);
00516 }
00517 free(attr);
00518 }
00519 }
00520 }
00521 break;
00522 }
00523
00524 prev = link;
00525 cprev = ccurr;
00526 ccurr = *key;
00527 link = map_node->value;
00528 }
00529
00530 DONE:
00531 cp_trie_txunlock(grp);
00532 return rc;
00533 }
00534
00535 void *cp_trie_exact_match(cp_trie *grp, char *key)
00536 {
00537 void *last = NULL;
00538 cp_trie_node *link = grp->root;
00539 mtab_node *map_node;
00540 char *branch = NULL;
00541
00542 if (cp_trie_txlock(grp, COLLECTION_LOCK_READ)) return NULL;
00543
00544 while ((map_node = NODE_MATCH(link, key)) != NULL)
00545 {
00546 branch = map_node->attr;
00547
00548 while (*key && *key == *branch)
00549 {
00550 key++;
00551 branch++;
00552 }
00553 if (*branch) break;
00554
00555 link = map_node->value;
00556 }
00557
00558 if (link) last = link->leaf;
00559
00560 cp_trie_txunlock(grp);
00561
00562 if (*key == '\0' && branch && *branch == '\0')
00563 return last;
00564 return NULL;
00565 }
00566
00567
00568 cp_vector *cp_trie_fetch_matches(cp_trie *grp, char *key)
00569 {
00570 int rc;
00571 cp_vector *res = NULL;
00572 cp_trie_node *link = grp->root;
00573 mtab_node *map_node;
00574 char *branch;
00575
00576 if ((rc = cp_trie_txlock(grp, COLLECTION_LOCK_READ))) return NULL;
00577
00578 while ((map_node = NODE_MATCH(link, key)) != NULL)
00579 {
00580 branch = map_node->attr;
00581
00582 while (*key && *key == *branch)
00583 {
00584 key++;
00585 branch++;
00586 }
00587 if (*branch) break;
00588
00589 link = map_node->value;
00590 if (link->leaf)
00591 {
00592 if (res == NULL)
00593 {
00594 res = cp_vector_create(1);
00595 if (res == NULL) break;
00596 }
00597 cp_vector_add_element(res, link->leaf);
00598 }
00599 }
00600
00601 cp_trie_txunlock(grp);
00602 return res;
00603 }
00604
00605 static void extract_subtrie(cp_trie_node *link, cp_vector *v);
00606
00607 static int extract_node(void *n, void *v)
00608 {
00609 mtab_node *node = n;
00610 cp_vector *res = v;
00611
00612 extract_subtrie(node->value, v);
00613
00614 return 0;
00615 }
00616
00617 static void extract_subtrie(cp_trie_node *link, cp_vector *v)
00618 {
00619 if (link->leaf)
00620 cp_vector_add_element(v, link->leaf);
00621
00622 if (link->others)
00623 mtab_callback(link->others, extract_node, v);
00624 }
00625
00626
00627 cp_vector *cp_trie_submatch(cp_trie *grp, char *key)
00628 {
00629 int rc;
00630 cp_vector *res = NULL;
00631 cp_trie_node *link = grp->root;
00632 mtab_node *map_node;
00633 char *branch;
00634
00635 if ((rc = cp_trie_txlock(grp, COLLECTION_LOCK_READ))) return NULL;
00636
00637 while ((map_node = NODE_MATCH(link, key)) != NULL)
00638 {
00639 branch = map_node->attr;
00640
00641 while (*key && *key == *branch)
00642 {
00643 key++;
00644 branch++;
00645 }
00646
00647 if (*branch && *key) break;
00648
00649 link = map_node->value;
00650
00651 if (*key == '\0')
00652 {
00653 res = cp_vector_create(1);
00654 extract_subtrie(link, res);
00655 break;
00656 }
00657 }
00658
00659 cp_trie_txunlock(grp);
00660 return res;
00661 }
00662
00663
00664 int cp_trie_prefix_match(cp_trie *grp, char *key, void **leaf)
00665 {
00666 int rc, match_count = 0;
00667 void *last = grp->root->leaf;
00668 cp_trie_node *link = grp->root;
00669 mtab_node *map_node;
00670 char *branch;
00671
00672 if ((rc = cp_trie_txlock(grp, COLLECTION_LOCK_READ))) return rc;
00673
00674 while ((map_node = NODE_MATCH(link, key)) != NULL)
00675 {
00676 branch = map_node->attr;
00677
00678 while (*key && *key == *branch)
00679 {
00680 key++;
00681 branch++;
00682 }
00683 if (*branch) break;
00684
00685 link = map_node->value;
00686 if (link->leaf)
00687 {
00688 last = link->leaf;
00689 match_count++;
00690 }
00691 }
00692
00693 *leaf = last;
00694
00695 cp_trie_txunlock(grp);
00696
00697 return match_count;
00698 }
00699
00700 int cp_trie_count(cp_trie *grp)
00701 {
00702 return grp->path_count;
00703 }
00704
00705 void cp_trie_set_root(cp_trie *grp, void *leaf)
00706 {
00707 grp->root->leaf = leaf;
00708 }
00709
00710
00711
00712 static int node_count;
00713 static int depth_total;
00714 static int max_level;
00715
00716 void cp_trie_dump_node(cp_trie_node *node, int level, char *prefix)
00717 {
00718 int i;
00719 mtab_node *map_node;
00720
00721 node_count++;
00722 depth_total += level;
00723 if (level > max_level) max_level = level;
00724
00725 for (i = 0; i < node->others->size; i++)
00726 {
00727 map_node = node->others->table[i];
00728 while (map_node)
00729 {
00730 cp_trie_dump_node(map_node->value, level + 1, map_node->attr);
00731 map_node = map_node->next;
00732 }
00733 }
00734
00735 for (i = 0; i < level; i++) printf("\t");
00736
00737 printf(" - %s => [%s]\n", prefix, node->leaf ? (char *) node->leaf : "");
00738 }
00739
00740 void cp_trie_dump(cp_trie *grp)
00741 {
00742 node_count = 0;
00743 depth_total = 0;
00744 max_level = 0;
00745
00746 cp_trie_dump_node(grp->root, 0, "");
00747
00748 #ifdef DEBUG
00749 printf("\n %d nodes, %d deepest, avg. depth is %.2f\n\n",
00750 node_count, max_level, (float) depth_total / node_count);
00751 #endif
00752 }
00753
00754
00755 int cp_trie_use_mempool(cp_trie *trie, cp_mempool *pool)
00756 {
00757 int rc = 0;
00758
00759 if ((rc = cp_trie_txlock(trie, COLLECTION_LOCK_WRITE))) return rc;
00760
00761 if (pool)
00762 {
00763 if (pool->item_size < sizeof(cp_trie_node))
00764 {
00765 rc = EINVAL;
00766 goto DONE;
00767 }
00768 if (trie->mempool)
00769 {
00770 if (trie->path_count)
00771 {
00772 rc = ENOTEMPTY;
00773 goto DONE;
00774 }
00775 cp_mempool_destroy(trie->mempool);
00776 }
00777 cp_mempool_inc_refcount(pool);
00778 trie->mempool = pool;
00779 }
00780 else
00781 {
00782 trie->mempool =
00783 cp_mempool_create_by_option(COLLECTION_MODE_NOSYNC,
00784 sizeof(cp_trie_node), 0);
00785 if (trie->mempool == NULL)
00786 {
00787 rc = ENOMEM;
00788 goto DONE;
00789 }
00790 }
00791
00792 DONE:
00793 cp_trie_txunlock(trie);
00794 return rc;
00795 }
00796
00797
00798
00799 int cp_trie_share_mempool(cp_trie *trie, cp_shared_mempool *pool)
00800 {
00801 int rc;
00802
00803 if ((rc = cp_trie_txlock(trie, COLLECTION_LOCK_WRITE))) return rc;
00804
00805 if (trie->mempool)
00806 {
00807 if (trie->path_count)
00808 {
00809 rc = ENOTEMPTY;
00810 goto DONE;
00811 }
00812
00813 cp_mempool_destroy(trie->mempool);
00814 }
00815
00816 trie->mempool = cp_shared_mempool_register(pool, sizeof(cp_trie_node));
00817 if (trie->mempool == NULL)
00818 {
00819 rc = ENOMEM;
00820 goto DONE;
00821 }
00822
00823 DONE:
00824 cp_trie_txunlock(trie);
00825 return rc;
00826 }
00827
00828