Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals

sorted_hash.c

00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <errno.h>
00004 
00005 #include "collection.h"
00006 #include "vector.h"
00007 #include "sorted_hash.h"
00008 
00009 cp_sh_entry *cp_sh_entry_create(void *key, void *value, cp_mempool *pool)
00010 {
00011     cp_sh_entry *node;
00012     
00013     if (pool) 
00014         node = (cp_sh_entry *) cp_mempool_calloc(pool);
00015     else
00016         node = (cp_sh_entry *) calloc(1, sizeof(cp_sh_entry));
00017 
00018     if (node == NULL) return NULL;
00019 
00020     node->key = key;
00021     node->value = value;
00022 
00023     return node;
00024 }
00025     
00026 /* implement COLLECTION_MODE_COPY if set */
00027 static cp_sh_entry *create_sh_entry(cp_sorted_hash *tree, void *key, void *value)
00028 {
00029     if (tree->mode & COLLECTION_MODE_COPY)
00030     {
00031         void *k, *v;
00032         k = tree->key_copy ? (*tree->key_copy)(key) : key;
00033         if (k)
00034         {
00035             v = tree->value_copy ? (*tree->value_copy)(value) : value;
00036             if (v)
00037             {
00038                 if (tree->mode & COLLECTION_MODE_MULTIPLE_VALUES)
00039                 {
00040                     cp_vector *m = cp_vector_create(1);
00041                     if (m == NULL) return NULL;
00042                     cp_vector_add_element(m, v);
00043                     v = m;
00044                 }
00045                 return cp_sh_entry_create(k, v, tree->mempool);
00046             }
00047         }
00048     }
00049     else if (tree->mode & COLLECTION_MODE_MULTIPLE_VALUES)
00050     {
00051         cp_vector *m = cp_vector_create(1);
00052         if (m == NULL) return NULL;
00053         cp_vector_add_element(m, value);
00054         return cp_sh_entry_create(key, m, tree->mempool);
00055     }
00056     else 
00057         return cp_sh_entry_create(key, value, tree->mempool);
00058 
00059     return NULL;
00060 }
00061 
00062 void cp_sorted_hash_destroy_entry_deep(cp_sorted_hash *owner, cp_sh_entry *node)
00063 {
00064     while (node)
00065     {
00066         if (node->right)
00067         {
00068             node = node->right;
00069             node->up->right = NULL;
00070         }
00071         else if (node->left)
00072         {
00073             node = node->left;
00074             node->up->left = NULL;
00075         }
00076         else
00077         {
00078             cp_sh_entry *tmp = node;
00079             if (node->multiple)
00080             {
00081                 cp_sh_entry *m = node->multiple;
00082                 cp_sh_entry *n;
00083                 do
00084                 {
00085                     n = m->multiple;
00086                     cp_sorted_hash_destroy_entry(owner, m);
00087                     m = n;
00088                 } while (m);
00089             }
00090             node = node->up;
00091             cp_sorted_hash_destroy_entry(owner, tmp);
00092         }
00093     }
00094 }
00095 
00096 void cp_sorted_hash_destroy_entry(cp_sorted_hash *tree, cp_sh_entry *node)
00097 {
00098     if (node)
00099     {
00100         if ((tree->mode & COLLECTION_MODE_DEEP))
00101         {
00102             if (tree->key_dtr) (*tree->key_dtr)(node->key);
00103             if (tree->mode & COLLECTION_MODE_MULTIPLE_VALUES)
00104             {
00105                 if (node->value)
00106                     cp_vector_destroy_custom(node->value, tree->value_dtr);
00107             }
00108             else if (tree->value_dtr) 
00109                 (*tree->value_dtr)(node->value);
00110         }
00111         else if ((tree->mode & COLLECTION_MODE_MULTIPLE_VALUES) && node->value)
00112             cp_vector_destroy(node->value);
00113         if (tree->mempool)
00114             cp_mempool_free(tree->mempool, node);
00115         else
00116             free(node);
00117     }
00118 }
00119 
00120 
00121 cp_sorted_hash *cp_sorted_hash_create(cp_hashfunction hash, 
00122                                       cp_compare_fn cmp_key, 
00123                                       cp_mapping_cmp_fn cmp_mapping)
00124 {
00125     cp_sorted_hash *tree = calloc(1, sizeof(cp_sorted_hash));
00126     if (tree == NULL) return NULL;
00127 
00128     tree->size = 11;
00129     tree->table = (cp_sh_entry **) calloc(tree->size, sizeof(cp_sh_entry));
00130     if (tree->table == NULL)
00131     {
00132         free(tree);
00133         return NULL;
00134     }
00135 
00136     tree->mode = COLLECTION_MODE_NOSYNC;
00137     tree->hash = hash;
00138     tree->cmp_key = cmp_key;
00139     tree->cmp_mapping = cmp_mapping;
00140 
00141     return tree;
00142 }
00143 
00144 /*
00145  * complete parameter create function
00146  */
00147 cp_sorted_hash *
00148     cp_sorted_hash_create_by_option(int mode, 
00149                                     unsigned long size_hint,
00150                                     cp_hashfunction hash, 
00151                                     cp_compare_fn cmp_key, 
00152                                     cp_mapping_cmp_fn cmp_mapping, 
00153                                     cp_copy_fn key_copy, 
00154                                     cp_destructor_fn key_dtr,
00155                                     cp_copy_fn val_copy, 
00156                                     cp_destructor_fn val_dtr)
00157 {
00158     cp_sorted_hash *tree = calloc(1, sizeof(cp_sorted_hash));
00159     if (tree == NULL) return NULL;
00160 
00161     tree->size = cp_hashtable_choose_size(size_hint);
00162     tree->table = (cp_sh_entry **) calloc(tree->size, sizeof(cp_sh_entry));
00163     if (tree->table == NULL)
00164     {
00165         free(tree);
00166         return NULL;
00167     }
00168 
00169     tree->mode = COLLECTION_MODE_NOSYNC;
00170     tree->hash = hash;
00171     tree->cmp_key = cmp_key;
00172     tree->cmp_mapping = cmp_mapping;
00173     
00174     tree->mode = mode;
00175     tree->key_copy = key_copy;
00176     tree->key_dtr = key_dtr;
00177     tree->value_copy = val_copy;
00178     tree->value_dtr = val_dtr;
00179 
00180     if (!(mode & COLLECTION_MODE_NOSYNC))
00181     {
00182         tree->lock = malloc(sizeof(cp_lock));
00183         if (tree->lock == NULL) 
00184         {
00185             cp_sorted_hash_destroy(tree);
00186             return NULL;
00187         }
00188         if (cp_lock_init(tree->lock, NULL) != 0)
00189         {
00190             cp_sorted_hash_destroy(tree);
00191             return NULL;
00192         }
00193     }
00194 
00195     return tree;
00196 }
00197 
00198 
00199 void cp_sorted_hash_destroy(cp_sorted_hash *tree)
00200 {
00201     if (tree)
00202     {
00203         cp_sorted_hash_destroy_entry_deep(tree, tree->root);
00204         if (tree->lock)
00205         {
00206             cp_lock_destroy(tree->lock);
00207             free(tree->lock);
00208         }
00209         free(tree->table);
00210         free(tree);
00211     }
00212 }
00213 
00214 void cp_sorted_hash_destroy_custom(cp_sorted_hash *tree, 
00215                               cp_destructor_fn key_dtr,
00216                               cp_destructor_fn val_dtr)
00217 {
00218     tree->mode |= COLLECTION_MODE_DEEP;
00219     tree->key_dtr = key_dtr;
00220     tree->value_dtr = val_dtr;
00221     cp_sorted_hash_destroy(tree);
00222 }
00223 
00224 static int cp_sorted_hash_lock_internal(cp_sorted_hash *tree, int type)
00225 {
00226     int rc;
00227 
00228     switch (type)
00229     {
00230         case COLLECTION_LOCK_READ:
00231             rc = cp_lock_rdlock(tree->lock);
00232             break;
00233 
00234         case COLLECTION_LOCK_WRITE:
00235             rc = cp_lock_wrlock(tree->lock);
00236             break;
00237 
00238         case COLLECTION_LOCK_NONE:
00239             rc = 0;
00240             break;
00241 
00242         default:
00243             rc = EINVAL;
00244             break;
00245     }
00246 
00247     /* api functions may rely on errno to report locking failure */
00248     if (rc) errno = rc;
00249 
00250     return rc;
00251 }
00252 
00253 static int cp_sorted_hash_unlock_internal(cp_sorted_hash *tree)
00254 {
00255     return cp_lock_unlock(tree->lock);
00256 }
00257 
00258 int cp_sorted_hash_txlock(cp_sorted_hash *tree, int type)
00259 {
00260     /* clear errno to allow client code to distinguish between a NULL return
00261      * value indicating the tree doesn't contain the requested value and NULL
00262      * on locking failure in tree operations
00263      */
00264     if (tree->mode & COLLECTION_MODE_NOSYNC) return 0;
00265     if (tree->mode & COLLECTION_MODE_IN_TRANSACTION && 
00266         tree->txtype == COLLECTION_LOCK_WRITE)
00267     {
00268         cp_thread self = cp_thread_self();
00269         if (cp_thread_equal(self, tree->txowner)) return 0;
00270     }
00271     errno = 0;
00272     return cp_sorted_hash_lock_internal(tree, type);
00273 }
00274 
00275 int cp_sorted_hash_txunlock(cp_sorted_hash *tree)
00276 {
00277     if (tree->mode & COLLECTION_MODE_NOSYNC) return 0;
00278     if (tree->mode & COLLECTION_MODE_IN_TRANSACTION && 
00279         tree->txtype == COLLECTION_LOCK_WRITE)
00280     {
00281         cp_thread self = cp_thread_self();
00282         if (cp_thread_equal(self, tree->txowner)) return 0;
00283     }
00284     return cp_sorted_hash_unlock_internal(tree);
00285 }
00286 
00287 /* lock and set the transaction indicators */
00288 int cp_sorted_hash_lock(cp_sorted_hash *tree, int type)
00289 {
00290     int rc;
00291     if ((tree->mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00292     if ((rc = cp_sorted_hash_lock_internal(tree, type))) return rc;
00293     tree->txtype = type;
00294     tree->txowner = cp_thread_self();
00295     tree->mode |= COLLECTION_MODE_IN_TRANSACTION;
00296     return 0;
00297 }
00298 
00299 /* unset the transaction indicators and unlock */
00300 int cp_sorted_hash_unlock(cp_sorted_hash *tree)
00301 {
00302     cp_thread self = cp_thread_self();
00303     if (tree->txowner == self)
00304     {
00305         tree->txowner = 0;
00306         tree->txtype = 0;
00307         tree->mode ^= COLLECTION_MODE_IN_TRANSACTION;
00308     }
00309     else if (tree->txtype == COLLECTION_LOCK_WRITE)
00310         return -1;
00311     return cp_sorted_hash_unlock_internal(tree);
00312 }
00313 
00314 /* set mode bits on the tree mode indicator */
00315 int cp_sorted_hash_set_mode(cp_sorted_hash *tree, int mode)
00316 {
00317     int rc;
00318     int nosync; 
00319 
00320     /* can't set NOSYNC in the middle of a transaction */
00321     if ((tree->mode & COLLECTION_MODE_IN_TRANSACTION) && 
00322         (mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00323     /* COLLECTION_MODE_MULTIPLE_VALUES must be set at creation time */  
00324     if (mode & COLLECTION_MODE_MULTIPLE_VALUES) return EINVAL;
00325 
00326     if ((rc = cp_sorted_hash_txlock(tree, COLLECTION_LOCK_WRITE))) return rc;
00327     
00328     nosync = tree->mode & COLLECTION_MODE_NOSYNC;
00329 
00330     tree->mode |= mode;
00331 
00332     if (!nosync)
00333         cp_sorted_hash_txunlock(tree);
00334 
00335     return 0;
00336 }
00337 
00338 /* unset mode bits on the tree mode indicator. if unsetting 
00339  * COLLECTION_MODE_NOSYNC and the tree was not previously synchronized, the 
00340  * internal synchronization structure is initialized.
00341  */
00342 int cp_sorted_hash_unset_mode(cp_sorted_hash *tree, int mode)
00343 {
00344     int rc;
00345     int nosync;
00346 
00347     /* COLLECTION_MODE_MULTIPLE_VALUES can't be unset */
00348     if (mode & COLLECTION_MODE_MULTIPLE_VALUES) return EINVAL;
00349 
00350     if ((rc = cp_sorted_hash_txlock(tree, COLLECTION_LOCK_WRITE))) return rc;
00351     
00352     nosync = tree->mode & COLLECTION_MODE_NOSYNC;
00353 
00354     /* handle the special case of unsetting COLLECTION_MODE_NOSYNC */
00355     if ((mode & COLLECTION_MODE_NOSYNC) && tree->lock == NULL)
00356     {
00357         /* tree can't be locked in this case, no need to unlock on failure */
00358         if ((tree->lock = malloc(sizeof(cp_lock))) == NULL)
00359             return -1;
00360         if (cp_lock_init(tree->lock, NULL))
00361             return -1;
00362     }
00363     
00364     /* unset specified bits */
00365     tree->mode &= tree->mode ^ mode;
00366     if (!nosync)
00367         cp_sorted_hash_txunlock(tree);
00368 
00369     return 0;
00370 }
00371 
00372 int cp_sorted_hash_get_mode(cp_sorted_hash *tree)
00373 {
00374     return tree->mode;
00375 }
00376 
00377 
00378 static cp_sh_entry *sibling(cp_sh_entry *node)
00379 {
00380     return node == node->up->left ? node->up->right : node->up->left;
00381 }
00382 
00383 static int is_right_child(cp_sh_entry *node)
00384 {
00385     return (node->up->right == node);
00386 }
00387 
00388 static int is_left_child(cp_sh_entry *node)
00389 {
00390     return (node->up->left == node);
00391 }
00392 
00393 /*         left rotate
00394  *
00395  *    (P)                (Q)
00396  *   /   \              /   \
00397  *  1    (Q)    ==>   (P)    3
00398  *      /   \        /   \
00399  *     2     3      1     2
00400  *
00401  */
00402 static void left_rotate(cp_sorted_hash *tree, cp_sh_entry *p)
00403 {
00404     cp_sh_entry *q = p->right;
00405     cp_sh_entry **sup;
00406     
00407     if (p->up)
00408         sup = is_left_child(p) ? &(p->up->left) : &(p->up->right);
00409     else
00410         sup = &tree->root;
00411 
00412     p->right = q->left;
00413     if (p->right) p->right->up = p;
00414     q->left = p;
00415     q->up = p->up;
00416     p->up = q;
00417     *sup = q;
00418 }
00419 
00420 /*           right rotate
00421  *  
00422  *       (P)                (Q)
00423  *      /   \              /   \
00424  *    (Q)    3    ==>     1    (P)  
00425  *   /   \                    /   \
00426  *  1     2                  2     3
00427  *
00428  */
00429 static void right_rotate(cp_sorted_hash *tree, cp_sh_entry *p)
00430 {
00431     cp_sh_entry *q = p->left;
00432     cp_sh_entry **sup;
00433     
00434     if (p->up)
00435         sup = is_left_child(p) ? &(p->up->left) : &(p->up->right);
00436     else
00437         sup = &tree->root;
00438 
00439     p->left = q->right;
00440     if (p->left) p->left->up = p;
00441     q->right = p;
00442     q->up = p->up;
00443     p->up = q;
00444     *sup = q;
00445 }
00446 
00447 
00448 /*
00449  * newly entered node is RED; check balance recursively as required 
00450  */
00451 static void rebalance(cp_sorted_hash *tree, cp_sh_entry *node)
00452 {
00453     cp_sh_entry *up = node->up;
00454     if (up == NULL || up->color == RB_BLACK) return;
00455     if (sibling(up) && sibling(up)->color == RB_RED)
00456     {
00457         up->color = RB_BLACK;
00458         sibling(up)->color = RB_BLACK;
00459         if (up->up->up)
00460         {
00461             up->up->color = RB_RED;
00462             rebalance(tree, up->up);
00463         }
00464     }
00465     else
00466     {
00467         if (is_left_child(node) && is_right_child(up))
00468         {
00469             right_rotate(tree, up);
00470             node = node->right;
00471         }
00472         else if (is_right_child(node) && is_left_child(up))
00473         {
00474             left_rotate(tree, up);
00475             node = node->left;
00476         }
00477 
00478         node->up->color = RB_BLACK;
00479         node->up->up->color = RB_RED;
00480 
00481         if (is_left_child(node)) // && is_left_child(node->up)
00482             right_rotate(tree, node->up->up);
00483         else 
00484             left_rotate(tree, node->up->up);
00485     }
00486 }
00487 
00488 /* update_sh_entry - implement COLLECTION_MODE_COPY, COLLECTION_MODE_DEEP and
00489  * COLLECTION_MODE_MULTIPLE_VALUES when inserting a value for an existing key
00490  */
00491 static void *
00492     update_sh_entry(cp_sorted_hash *tree, cp_sh_entry *node, void *key, void *value)
00493 {
00494     void *new_key = key;
00495     void *new_value = value;
00496 
00497     if (tree->mode & COLLECTION_MODE_COPY)
00498     {
00499         if (tree->key_copy) 
00500         {
00501             new_key = (*tree->key_copy)(key);
00502             if (new_key == NULL) return NULL;
00503         }
00504         if (tree->value_copy)
00505         {
00506             new_value = (*tree->value_copy)(value);
00507             if (new_value == NULL) return NULL;
00508         }
00509     }
00510 
00511     if (tree->mode & COLLECTION_MODE_DEEP)
00512     {
00513         if (tree->key_dtr)
00514             (*tree->key_dtr)(node->key);
00515         if (tree->value_dtr && !(tree->mode & COLLECTION_MODE_MULTIPLE_VALUES))
00516             (*tree->value_dtr)(node->value);
00517     }
00518         
00519     node->key = new_key;
00520     if (tree->mode & COLLECTION_MODE_MULTIPLE_VALUES)
00521     {
00522         cp_vector_add_element(node->value, new_value);
00523         return node->value;
00524     }
00525     else
00526         node->value = new_value;
00527 
00528     return new_value;
00529 }
00530 
00531 /*
00532  * cp_sorted_hash_insert iterates through the tree, finds where the new node 
00533  * fits in, puts it there, then calls rebalance. 
00534  *
00535  * If a mapping for the given key already exists it is replaced unless 
00536  * COLLECTION_MODE_MULTIPLE_VALUES is set, is which case a new mapping is 
00537  * added. By default COLLECTION_MODE_MULTIPLE_VALUES is not set.
00538  */
00539 void *cp_sorted_hash_insert(cp_sorted_hash *tree, void *key, void *value)
00540 {
00541     void *res = NULL;
00542     cp_sh_entry *entry, **curr;
00543     void *ikey, *ivalue;
00544     unsigned long index;
00545     
00546     if (cp_sorted_hash_txlock(tree, COLLECTION_LOCK_WRITE)) return NULL;
00547 
00548     if (tree->mode & COLLECTION_MODE_COPY)
00549     {
00550         ikey = tree->key_copy ? (*tree->key_copy)(key) : key;
00551         ivalue = tree->value_copy ? (*tree->value_copy)(value) : value;
00552     }
00553     else
00554     {
00555         ikey = key;
00556         ivalue = value;
00557     }
00558 
00559     entry = cp_sh_entry_create(ikey, ivalue, tree->mempool);
00560     if (entry == NULL) goto INSERT_DONE;
00561 
00562     entry->code = (*tree->hash)(key);
00563     index = entry->code % tree->size;
00564 
00565     curr = &tree->table[index];
00566     while (*curr) 
00567     {
00568         if (!(tree->mode & COLLECTION_MODE_MULTIPLE_VALUES) &&
00569             (*tree->cmp_key)(key, (*curr)->key) == 0)
00570         {
00571             if (tree->mode & COLLECTION_MODE_DEEP)
00572             {
00573                 if (tree->key_dtr) (*tree->key_dtr)((*curr)->key);
00574                 if (tree->value_dtr) (*tree->value_dtr)((*curr)->value);
00575             }
00576             (*curr)->key = ikey;
00577             (*curr)->value = ivalue;
00578             cp_sorted_hash_destroy_entry(tree, entry);
00579             break;
00580         }
00581         curr = &(*curr)->bucket;
00582     }
00583 
00584     if (*curr == NULL) *curr = entry;
00585 
00586     if (tree->root == NULL)
00587     {
00588         tree->root = entry;
00589         res = value;
00590         tree->root->color = RB_BLACK;
00591         tree->items++;
00592     }
00593     else
00594     {
00595         int cmp;
00596         cp_sh_entry *prev = NULL;
00597         curr = &tree->root;
00598 
00599         while (*curr)
00600         {
00601             prev = *curr;
00602             if (tree->cmp_mapping)
00603             {
00604                 cmp = (*tree->cmp_mapping)((cp_mapping *) *curr, 
00605                                            (cp_mapping *) entry);
00606             }
00607             else
00608             {
00609                 cmp = (*tree->cmp_key)((*curr)->key, entry->key);
00610             }
00611 
00612             if (cmp < 0)
00613                 curr = &(*curr)->right;
00614             else if (cmp > 0) 
00615                 curr = &(*curr)->left;
00616             else /* replace */
00617             {
00618                 prev = *curr;
00619                 curr = &(*curr)->multiple;
00620                 while (*curr)
00621                 {
00622                     (*curr)->multiple_prev = prev;
00623                     prev = *curr;
00624                     curr = &(*curr)->multiple;
00625                 }
00626                 *curr = entry;
00627                 entry->multiple_prev = NULL;
00628                 res = entry->value;
00629                 tree->items++;
00630                 break;
00631             }
00632         }
00633 
00634         if (*curr == NULL) /* not replacing, create new node */
00635         {
00636             *curr = entry;
00637             if (*curr == NULL) goto INSERT_DONE;
00638             res = (*curr)->value;
00639             tree->items++;
00640             (*curr)->up = prev;
00641             rebalance(tree, *curr);
00642         }
00643     }
00644 
00645 INSERT_DONE:
00646     cp_sorted_hash_txunlock(tree);
00647     return res;
00648 }
00649 
00650 /* cp_sorted_hash_get - return the value mapped to the given key or NULL if none is
00651  * found. If COLLECTION_MODE_MULTIPLE_VALUES is set the returned value is a
00652  * cp_vector object or NULL if no mapping is found. 
00653  */
00654 void *cp_sorted_hash_get(cp_sorted_hash *tree, void *key)
00655 {
00656     int c;
00657     unsigned long index;
00658     cp_sh_entry *curr;
00659     void *value = NULL;
00660     
00661     if (cp_sorted_hash_txlock(tree, COLLECTION_LOCK_READ)) return NULL;
00662 
00663     index = (*tree->hash)(key) % tree->size;
00664     curr = tree->table[index];
00665     //~~ handle mutliple values
00666     while (curr)
00667     {
00668         c = tree->cmp_key(curr->key, key);
00669         if (c == 0) 
00670         {
00671             value = curr->value;
00672             break;
00673         }
00674         curr = curr->bucket;
00675     }
00676 
00677     cp_sorted_hash_txunlock(tree);
00678     return value;;
00679 }
00680     
00681 
00682 /* find the value of the closest key by operator. All of this becuase the tree
00683  * isn't threaded.
00684  */
00685 CPROPS_DLL
00686 void *cp_sorted_hash_find(cp_sorted_hash *tree, cp_mapping *mapping, cp_op op)
00687 {
00688     int cmp;
00689     unsigned long index;
00690     cp_sh_entry **curr;
00691     cp_sh_entry *prev;
00692     cp_sh_entry *res = NULL;
00693 
00694     if (tree->root == NULL) return NULL;
00695 
00696     if (cp_sorted_hash_txlock(tree, COLLECTION_LOCK_READ)) return NULL;
00697 
00698     curr = &tree->root;
00699     while (*curr)
00700     {
00701         prev = *curr;
00702         if (tree->cmp_mapping)
00703         {
00704             cmp = (*tree->cmp_mapping)((cp_mapping *) *curr, mapping);
00705         }
00706         else
00707         {
00708             cmp = (*tree->cmp_key)((*curr)->key, mapping->key);
00709         }
00710 
00711         if (cmp == 0) break;
00712         if (op == CP_OP_NE)
00713         {
00714             res = *curr;
00715             goto FIND_DONE;
00716         }
00717 
00718         if (cmp < 0)
00719             curr = &(*curr)->right;
00720         else 
00721             curr = &(*curr)->left;
00722     }
00723 
00724     if (*curr)
00725     {
00726         if (op == CP_OP_EQ || op == CP_OP_LE || op == CP_OP_GE)
00727         {
00728             res = *curr;
00729             goto FIND_DONE;
00730         }
00731 
00732         if (op == CP_OP_GT)
00733         {
00734             if ((*curr)->right)
00735             {
00736                 curr = &(*curr)->right;
00737                 while ((*curr)->left) curr = &(*curr)->left;
00738                 res = *curr;
00739             }
00740             else
00741             {
00742                 while ((*curr)->up)
00743                 {
00744                     prev = *curr;
00745                     curr = &(*curr)->up;
00746                     if ((*curr)->left == prev)
00747                     {
00748                         res = *curr;
00749                         break;
00750                     }
00751                 }
00752             }
00753         }
00754         else
00755         {
00756             if ((*curr)->left)
00757             {
00758                 curr = &(*curr)->left;
00759                 while ((*curr)->right) curr = &(*curr)->right;
00760                 res = *curr;
00761             }
00762             else
00763             {
00764                 while ((*curr)->up)
00765                 {
00766                     prev = *curr;
00767                     curr = &(*curr)->up;
00768                     if ((*curr)->right == prev)
00769                     {
00770                         res = *curr;
00771                         break;
00772                     }
00773                 }
00774             }
00775         }
00776 
00777         goto FIND_DONE;
00778     }
00779 
00780     /* *curr is NULL */
00781     if (op == CP_OP_EQ) goto FIND_DONE;
00782 
00783     if (op == CP_OP_LT || op == CP_OP_LE)
00784     {
00785         if (curr == &prev->right) 
00786         {
00787             res = prev;
00788             goto FIND_DONE;
00789         }
00790 
00791         while (prev)
00792         {
00793             if (prev->up && prev->up->right == prev)
00794             {
00795                 res = prev->up;
00796                 break;
00797             }
00798             prev = prev->up;
00799         }
00800     } 
00801     else /* op == CP_OP_GE || op == CP_OP_GT */
00802     {
00803         if (curr == &prev->left)
00804         {
00805             res = prev;
00806             goto FIND_DONE;
00807         }
00808 
00809         while (prev)
00810         {
00811             if (prev->up && prev->up->left == prev)
00812             {
00813                 res = prev->up;
00814                 break;
00815             }
00816             prev = prev->up;
00817         }
00818     }
00819 
00820 FIND_DONE:
00821     cp_sorted_hash_txunlock(tree);
00822     return res->value;
00823 }
00824 
00825 int cp_sorted_hash_contains(cp_sorted_hash *tree, void *key)
00826 {
00827     return (cp_sorted_hash_get(tree, key) != NULL);
00828 }
00829 
00830 /* helper function for deletion */
00831 static void swap_node_content(cp_sh_entry *a, cp_sh_entry *b)
00832 {
00833     void *tmpkey, *tmpval;
00834 
00835     tmpkey = a->key;
00836     a->key = b->key;
00837     b->key = tmpkey;
00838     
00839     tmpval = a->value;
00840     a->value = b->value;
00841     b->value = tmpval;
00842 }
00843 
00844 /*
00845  * helper function for cp_sorted_hash_delete to remove nodes with either a left 
00846  * NULL branch or a right NULL branch
00847  */
00848 static void rb_unlink(cp_sorted_hash *tree, cp_sh_entry *node)
00849 {
00850     if (node->multiple) node->multiple->multiple_prev = node->multiple_prev;
00851     if (node->multiple_prev) node->multiple_prev->multiple = node->multiple;
00852 
00853     if (node->left)
00854     {
00855         node->left->up = node->up;
00856         if (node->up)
00857         {
00858             if (is_left_child(node))
00859                 node->up->left = node->left;
00860             else
00861                 node->up->right = node->left;
00862         }
00863         else
00864             tree->root = node->left;
00865     }
00866     else
00867     {
00868         if (node->right) node->right->up = node->up;
00869         if (node->up)
00870         {
00871             if (is_left_child(node))
00872                 node->up->left = node->right;
00873             else
00874                 node->up->right = node->right;
00875         }
00876         else
00877             tree->root = node->right;
00878     }
00879 }
00880 
00881 /* delete_rebalance - perform rebalancing after a deletion */
00882 static void delete_rebalance(cp_sorted_hash *tree, cp_sh_entry *n)
00883 {
00884     if (n->up)
00885     {
00886         cp_sh_entry *sibl = sibling(n);
00887 
00888         if (sibl->color == RB_RED)
00889         {
00890             n->up->color = RB_RED;
00891             sibl->color = RB_BLACK;
00892             if (is_left_child(n))
00893                 left_rotate(tree, n->up);
00894             else
00895                 right_rotate(tree, n->up);
00896             sibl = sibling(n);
00897         }
00898 
00899         if (n->up->color == RB_BLACK &&
00900             sibl->color == RB_BLACK &&
00901             (sibl->left == NULL || sibl->left->color == RB_BLACK) &&
00902             (sibl->right == NULL || sibl->right->color == RB_BLACK))
00903         {
00904             sibl->color = RB_RED;
00905             delete_rebalance(tree, n->up);
00906         }
00907         else
00908         {
00909             if (n->up->color == RB_RED &&
00910                 sibl->color == RB_BLACK &&
00911                 (sibl->left == NULL || sibl->left->color == RB_BLACK) &&
00912                 (sibl->right == NULL || sibl->right->color == RB_BLACK))
00913             {
00914                 sibl->color = RB_RED;
00915                 n->up->color = RB_BLACK;
00916             }
00917             else
00918             {
00919                 if (is_left_child(n) && 
00920                     sibl->color == RB_BLACK &&
00921                     sibl->left && sibl->left->color == RB_RED && 
00922                     (sibl->right == NULL || sibl->right->color == RB_BLACK))
00923                 {
00924                     sibl->color = RB_RED;
00925                     sibl->left->color = RB_BLACK;
00926                     right_rotate(tree, sibl);
00927                     
00928                     sibl = sibling(n);
00929                 }
00930                 else if (is_right_child(n) &&
00931                     sibl->color == RB_BLACK &&
00932                     sibl->right && sibl->right->color == RB_RED &&
00933                     (sibl->left == NULL || sibl->left->color == RB_BLACK))
00934                 {
00935                     sibl->color = RB_RED;
00936                     sibl->right->color = RB_BLACK;
00937                     left_rotate(tree, sibl);
00938 
00939                     sibl = sibling(n);
00940                 }
00941 
00942                 sibl->color = n->up->color;
00943                 n->up->color = RB_BLACK;
00944                 if (is_left_child(n))
00945                 {
00946                     sibl->right->color = RB_BLACK;
00947                     left_rotate(tree, n->up);
00948                 }
00949                 else
00950                 {
00951                     sibl->left->color = RB_BLACK;
00952                     right_rotate(tree, n->up);
00953                 }
00954             }
00955         }
00956     }
00957 }
00958 
00959 /* cp_sorted_hash_delete_impl - delete one node from a red black tree */
00960 void *cp_sorted_hash_delete_impl(cp_sorted_hash *tree, void *key)
00961 {
00962     void *res = NULL;
00963     cp_sh_entry **table_node;
00964     unsigned long index;
00965     cp_sh_entry *node; 
00966     int cmp;
00967 
00968     //~~ handle multiples
00969     index = (*tree->hash)(key) % tree->size;
00970     table_node = &tree->table[index];
00971     while (*table_node && (*tree->cmp_key)((*table_node)->key, key))
00972         table_node = &(*table_node)->bucket;
00973 
00974     node = *table_node;
00975 
00976     if (node) /* may be null if not found */
00977     {
00978         cp_sh_entry *child; 
00979         res = node->value;
00980         tree->items--;
00981 
00982         if (node->right && node->left)
00983         {
00984             cp_sh_entry *surrogate = node;
00985             node = node->right;
00986             while (node->left) node = node->left;
00987             swap_node_content(node, surrogate);
00988         }
00989         child = node->right ? node->right : node->left;
00990 
00991         /* if the node was red - no rebalancing required */
00992         if (node->color == RB_BLACK)
00993         {
00994             if (child)
00995             {
00996                 /* single red child - paint it black */
00997                 if (child->color == RB_RED)
00998                     child->color = RB_BLACK; /* and the balance is restored */
00999                 else
01000                     delete_rebalance(tree, child);
01001             }
01002             else 
01003                 delete_rebalance(tree, node);
01004         }
01005 
01006         *table_node = (*table_node)->bucket;
01007         rb_unlink(tree, node);
01008         cp_sorted_hash_destroy_entry(tree, node);
01009         
01010     }
01011 
01012     return res;
01013 }
01014 
01015 /* cp_sorted_hash_delete - deletes the value mapped to the given key from the 
01016  * tree and returns the value removed. 
01017  */
01018 void *cp_sorted_hash_delete(cp_sorted_hash *tree, void *key)
01019 {
01020     void *res = NULL;
01021 
01022     if (cp_sorted_hash_txlock(tree, COLLECTION_LOCK_WRITE)) return NULL;
01023 
01024     res = cp_sorted_hash_delete_impl(tree, key);
01025 
01026     cp_sorted_hash_txunlock(tree);
01027     return res;
01028 }
01029 
01030 static int 
01031     rb_scan_pre_order(cp_sh_entry *node, cp_callback_fn callback, void *prm)
01032 {
01033     int rc;
01034     
01035     if (node) 
01036     {
01037         if ((rc = (*callback)(node, prm))) return rc;
01038         if (node->multiple)
01039         {
01040             cp_sh_entry *multiple = node->multiple;
01041             do
01042             {
01043                 if ((rc = (*callback)(multiple, prm))) return rc;
01044                 multiple = multiple->multiple;
01045             } while (multiple);
01046         }
01047         if ((rc = rb_scan_pre_order(node->left, callback, prm))) return rc;
01048         if ((rc = rb_scan_pre_order(node->right, callback, prm))) return rc;
01049     }
01050 
01051     return 0;
01052 }
01053 
01054 int cp_sorted_hash_callback_preorder(cp_sorted_hash *tree, 
01055                                 cp_callback_fn callback, 
01056                                 void *prm)
01057 {
01058     int rc;
01059 
01060     if ((rc = cp_sorted_hash_txlock(tree, COLLECTION_LOCK_READ))) return rc;
01061     rc = rb_scan_pre_order(tree->root, callback, prm);
01062     cp_sorted_hash_txunlock(tree);
01063 
01064     return rc;
01065 }
01066 
01067 static int 
01068     rb_scan_in_order(cp_sh_entry *node, cp_callback_fn callback, void *prm)
01069 {
01070     int rc;
01071     
01072     if (node) 
01073     {
01074         if ((rc = rb_scan_in_order(node->left, callback, prm))) return rc;
01075         if ((rc = (*callback)(node, prm))) return rc;
01076         if (node->multiple)
01077         {
01078             cp_sh_entry *multiple = node->multiple;
01079             do
01080             {
01081                 if ((rc = (*callback)(multiple, prm))) return rc;
01082                 multiple = multiple->multiple;
01083             } while (multiple);
01084         }
01085         if ((rc = rb_scan_in_order(node->right, callback, prm))) return rc;
01086     }
01087 
01088     return 0;
01089 }
01090 
01091 int cp_sorted_hash_callback(cp_sorted_hash *tree, 
01092                             cp_callback_fn callback, 
01093                             void *prm)
01094 {
01095     int rc;
01096 
01097     if ((rc = cp_sorted_hash_txlock(tree, COLLECTION_LOCK_READ))) return rc;
01098     rc = rb_scan_in_order(tree->root, callback, prm);
01099     cp_sorted_hash_txunlock(tree);
01100 
01101     return rc;
01102 }
01103 
01104 static int 
01105     rb_scan_post_order(cp_sh_entry *node, cp_callback_fn callback, void *prm)
01106 {
01107     int rc;
01108     
01109     if (node) 
01110     {
01111         if ((rc = rb_scan_post_order(node->left, callback, prm))) return rc;
01112         if ((rc = rb_scan_post_order(node->right, callback, prm))) return rc;
01113         if ((rc = (*callback)(node, prm))) return rc;
01114         if (node->multiple)
01115         {
01116             cp_sh_entry *multiple = node->multiple;
01117             do
01118             {
01119                 if ((rc = (*callback)(multiple, prm))) return rc;
01120                 multiple = multiple->multiple;
01121             } while (multiple);
01122         }
01123     }
01124 
01125     return 0;
01126 }
01127 
01128 int cp_sorted_hash_callback_postorder(cp_sorted_hash *tree, 
01129                                  cp_callback_fn callback, 
01130                                  void *prm)
01131 {
01132     int rc;
01133 
01134     if ((rc = cp_sorted_hash_txlock(tree, COLLECTION_LOCK_READ))) return rc;
01135     rc = rb_scan_post_order(tree->root, callback, prm);
01136     cp_sorted_hash_txunlock(tree);
01137 
01138     return rc;
01139 }
01140 
01141 int cp_sorted_hash_count(cp_sorted_hash *tree)
01142 {
01143     return tree->items;
01144 }
01145 
01146 
01147 void cp_sh_entry_print(cp_sh_entry *node, int level)
01148 {
01149     int i;
01150     if (node->right) cp_sh_entry_print(node->right, level + 1);
01151     for (i = 0; i < level; i++) printf("  . ");
01152     printf("(%d) [%s => %s]\n", node->color, (char *) node->key, (char *) node->value);
01153     if (node->left) cp_sh_entry_print(node->left, level + 1);
01154 }
01155 
01156 void cp_sh_entry_multi_print(cp_sh_entry *node, int level)
01157 {
01158     int i;
01159     cp_vector *v = node->value;
01160     if (node->right) cp_sh_entry_multi_print(node->right, level + 1);
01161     
01162     for (i = 0; i < level; i++) printf("  . ");
01163     printf("(%d) [%s => ", node->color, (char *) node->key);
01164 
01165     for (i = 0; i < cp_vector_size(v); i++)
01166         printf("%s; ", (char *) cp_vector_element_at(v, i));
01167 
01168     printf("]\n");
01169 
01170     if (node->left) cp_sh_entry_multi_print(node->left, level + 1);
01171 }
01172 
01173 void cp_sorted_hash_dump(cp_sorted_hash *tree)
01174 {
01175     if (tree->root) 
01176     {
01177         if (tree->mode & COLLECTION_MODE_MULTIPLE_VALUES)
01178             cp_sh_entry_multi_print(tree->root, 0);
01179         else
01180             cp_sh_entry_print(tree->root, 0);
01181     }
01182 }
01183 
01184 /* set tree to use given mempool or allocate a new one if pool is NULL */
01185 int cp_sorted_hash_use_mempool(cp_sorted_hash *tree, cp_mempool *pool)
01186 {
01187     int rc = 0;
01188     
01189     if ((rc = cp_sorted_hash_txlock(tree, COLLECTION_LOCK_WRITE))) return rc;
01190     
01191     if (pool)
01192     {
01193         if (pool->item_size < sizeof(cp_sh_entry))
01194         {
01195             rc = EINVAL;
01196             goto DONE;
01197         }
01198         if (tree->mempool) 
01199         {
01200             if (tree->items) 
01201             {
01202                 rc = ENOTEMPTY;
01203                 goto DONE;
01204             }
01205             cp_mempool_destroy(tree->mempool);
01206         }
01207         cp_mempool_inc_refcount(pool);
01208         tree->mempool = pool;
01209     }
01210     else
01211     {
01212         tree->mempool = 
01213             cp_mempool_create_by_option(COLLECTION_MODE_NOSYNC, 
01214                                         sizeof(cp_sh_entry), 0);
01215         if (tree->mempool == NULL) 
01216         {
01217             rc = ENOMEM;
01218             goto DONE;
01219         }
01220     }
01221 
01222 DONE:
01223     cp_sorted_hash_txunlock(tree);
01224     return rc;
01225 }
01226 
01227 
01228 /* set tree to use a shared memory pool */
01229 int cp_sorted_hash_share_mempool(cp_sorted_hash *tree, cp_shared_mempool *pool)
01230 {
01231     int rc;
01232 
01233     if ((rc = cp_sorted_hash_txlock(tree, COLLECTION_LOCK_WRITE))) return rc;
01234 
01235     if (tree->mempool)
01236     {
01237         if (tree->items)
01238         {
01239             rc = ENOTEMPTY;
01240             goto DONE;
01241         }
01242 
01243         cp_mempool_destroy(tree->mempool);
01244     }
01245 
01246     tree->mempool = cp_shared_mempool_register(pool, sizeof(cp_sh_entry));
01247     if (tree->mempool == NULL) 
01248     {
01249         rc = ENOMEM;
01250         goto DONE;
01251     }
01252     
01253 DONE:
01254     cp_sorted_hash_txunlock(tree);
01255     return rc;
01256 }
01257 

Generated on Sat Dec 1 10:25:29 2007 for cprops by  doxygen 1.3.9.1