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

multimap.c

00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <string.h>
00004 #include <errno.h>
00005 
00006 #include "collection.h"
00007 #include "vector.h"
00008 #include "multimap.h"
00009 
00010 cp_index_map_node *
00011     cp_index_map_node_create(void *entry, cp_mempool *pool)
00012 {
00013     cp_index_map_node *node;
00014     
00015     if (pool) 
00016         node = (cp_index_map_node *) cp_mempool_calloc(pool);
00017     else
00018         node = (cp_index_map_node *) calloc(1, sizeof(cp_index_map_node));
00019 
00020     if (node == NULL) return NULL;
00021 
00022     node->entry = entry;
00023 
00024     return node;
00025 }
00026     
00027 /* implement COLLECTION_MODE_COPY if set */
00028 static cp_index_map_node *
00029     create_index_map_node(cp_index_map *tree, void *entry)
00030 {
00031     if (tree->mode & COLLECTION_MODE_COPY)
00032     {
00033         void *e;
00034         e = tree->owner->copy ? (*tree->owner->copy)(entry) : entry;
00035         if (e)
00036         {
00037             if ((tree->mode & COLLECTION_MODE_MULTIPLE_VALUES))
00038             {
00039                 cp_vector *m = cp_vector_create(1);
00040                 if (m == NULL) return NULL;
00041                 cp_vector_add_element(m, e);
00042                 e = m;
00043             }
00044                 
00045             return cp_index_map_node_create(e, tree->owner->mempool);
00046         }
00047     }
00048     else if ((tree->mode & COLLECTION_MODE_MULTIPLE_VALUES))
00049     {
00050         cp_vector *m = cp_vector_create(1);
00051         if (m == NULL) return NULL;
00052         cp_vector_add_element(m, entry);
00053         return cp_index_map_node_create(m, tree->owner->mempool);
00054     }
00055     else 
00056         return cp_index_map_node_create(entry, tree->owner->mempool);
00057 
00058     return NULL;
00059 }
00060 
00061 void cp_index_map_destroy_node(cp_index_map *tree, cp_index_map_node *node)
00062 {
00063     if (node)
00064     {
00065         if ((tree->owner->mode & COLLECTION_MODE_DEEP))
00066         {
00067             if ((tree->mode & COLLECTION_MODE_MULTIPLE_VALUES) && node->entry)
00068                 cp_vector_destroy_custom(node->entry, tree->owner->dtr);
00069             else if (tree->owner->dtr) 
00070                 (*tree->owner->dtr)(node->entry);
00071         }
00072         else if ((tree->mode & COLLECTION_MODE_MULTIPLE_VALUES) && node->entry)
00073             cp_vector_destroy(node->entry);
00074 
00075         if (tree->owner->mempool)
00076             cp_mempool_free(tree->owner->mempool, node);
00077         else
00078             free(node);
00079     }
00080 }
00081 
00082 void cp_index_map_drop_node(cp_index_map *tree, cp_index_map_node *node)
00083 {
00084     if (node)
00085     {
00086         if (tree->mode & COLLECTION_MODE_MULTIPLE_VALUES && node->entry)
00087             cp_vector_destroy(node->entry);
00088 
00089         if (tree->owner->mempool)
00090             cp_mempool_free(tree->owner->mempool, node);
00091         else
00092             free(node);
00093     }
00094 }
00095 
00096 void cp_index_map_drop_node_deep(cp_index_map *tree, cp_index_map_node *node)
00097 {
00098     if (node)
00099     {
00100         if (node->left) 
00101             cp_index_map_drop_node_deep(tree, node->left);
00102         if (node->right)
00103             cp_index_map_drop_node_deep(tree, node->right);
00104         cp_index_map_drop_node(tree, node);
00105     }
00106 }
00107 
00108 void cp_index_map_destroy_ref(cp_index_map *tree)
00109 {
00110     cp_index_map_drop_node_deep(tree, tree->root);
00111     free(tree);
00112 }
00113 
00114 void cp_index_map_destroy_node_deep(cp_index_map *owner, 
00115                                     cp_index_map_node *node)
00116 {
00117     while (node)
00118     {
00119         if (node->right)
00120         {
00121             node = node->right;
00122             node->up->right = NULL;
00123         }
00124         else if (node->left)
00125         {
00126             node = node->left;
00127             node->up->left = NULL;
00128         }
00129         else
00130         {
00131             cp_index_map_node *tmp = node;
00132             node = node->up;
00133             cp_index_map_destroy_node(owner, tmp);
00134         }
00135     }
00136 }
00137 
00138 void cp_index_map_destroy(cp_index_map *tree)
00139 {
00140     if (tree)
00141     {
00142         cp_index_map_destroy_node_deep(tree, tree->root);
00143         free(tree);
00144     }
00145 }
00146 
00147 void *cp_index_map_insert(cp_index_map *tree, void *entry);
00148 
00149 cp_multimap *
00150     cp_multimap_create_by_option(int mode, cp_key_fn key, 
00151                                  cp_compare_fn cmp, cp_copy_fn copy, 
00152                                  cp_destructor_fn dtr)
00153 {
00154     cp_multimap *map = (cp_multimap *) calloc(1, sizeof(cp_multimap));
00155     if (map == NULL) goto CREATE_ERROR;
00156 
00157     if (!(mode & COLLECTION_MODE_NOSYNC))
00158     {
00159         map->lock = (cp_lock *) malloc(sizeof(cp_lock));
00160         if (map->lock == NULL) goto CREATE_ERROR;
00161 
00162         if (cp_lock_init(map->lock, NULL) != 0) goto CREATE_ERROR;
00163     }
00164 
00165     map->mode = mode;
00166 
00167     map->copy = copy;
00168     map->dtr = dtr;
00169 
00170     map->default_index.type = 
00171         (mode & COLLECTION_MODE_MULTIPLE_VALUES) ? CP_MULTIPLE : CP_UNIQUE;
00172     map->default_index.key = key;
00173     map->default_index.cmp = cmp;
00174 
00175     map->default_map = 
00176         cp_index_map_create(map, mode , &map->default_index);
00177     if (map->default_map == NULL) goto CREATE_ERROR;
00178 
00179     map->index = 
00180         cp_hashlist_create_by_option(COLLECTION_MODE_NOSYNC | 
00181                                      COLLECTION_MODE_DEEP, 
00182                                      3, 
00183                                      cp_hash_addr, cp_hash_compare_addr, 
00184                                      (cp_copy_fn) cp_index_copy, free, 
00185                                      NULL, 
00186                                      (cp_destructor_fn) 
00187                                         cp_index_map_destroy_ref);
00188     if (map->index == NULL) goto CREATE_ERROR;
00189 
00190     if (mode & COLLECTION_MODE_MULTIPLE_VALUES)
00191     {
00192         cp_index *primary_index = (cp_index *) calloc(1, sizeof(cp_index));
00193         if (primary_index == NULL) goto CREATE_ERROR;
00194         primary_index->type = CP_UNIQUE;
00195         primary_index->key = NULL;
00196         primary_index->cmp = cp_hash_compare_addr;
00197         map->primary = 
00198             cp_index_map_create(map, mode & ~(COLLECTION_MODE_MULTIPLE_VALUES), primary_index);
00199         if (map->primary == NULL) 
00200         {
00201             free(primary_index);
00202             goto CREATE_ERROR;
00203         }
00204 
00205         cp_hashlist_append(map->index, &map->default_index, map->default_map);
00206     }
00207     else
00208         map->primary = map->default_map;
00209 
00210     return map;
00211 
00212 CREATE_ERROR:
00213     if (map)
00214     {
00215         if ((map->mode & COLLECTION_MODE_MULTIPLE_VALUES) &&
00216             map->primary != NULL)
00217             cp_index_map_destroy(map->primary);
00218         if (map->default_map) cp_index_map_destroy(map->default_map);
00219         if (map->index) cp_hashlist_destroy(map->index);
00220         if (map->lock) cp_lock_destroy(map->lock);
00221         free(map);
00222     }
00223     return NULL;
00224 }
00225 
00226 cp_multimap *cp_multimap_create(cp_key_fn key_fn, cp_compare_fn cmp)
00227 {
00228     return cp_multimap_create_by_option(0, key_fn, cmp, NULL, NULL);
00229 }
00230 
00231 void cp_multimap_destroy(cp_multimap *map)
00232 {
00233     if (map)
00234     {
00235         if ((map->mode & COLLECTION_MODE_MULTIPLE_VALUES))
00236         {
00237             free(map->primary->index);
00238             cp_hashlist_remove_by_option(map->index, &map->default_index, COLLECTION_MODE_NOSYNC);
00239             cp_hashlist_destroy(map->index);
00240             if (map->primary) cp_index_map_destroy(map->primary);
00241             if (map->default_map) cp_index_map_destroy_ref(map->default_map);
00242         }
00243         else
00244         {
00245             if (map->index) cp_hashlist_destroy(map->index);
00246             if (map->default_map) cp_index_map_destroy(map->default_map);
00247         }
00248         if (map->lock) cp_lock_destroy(map->lock);
00249         free(map);
00250     }
00251 }
00252 
00253 void cp_multimap_destroy_custom(cp_multimap *map, cp_destructor_fn dtr)
00254 {
00255     if (map)
00256     {
00257         map->dtr = dtr;
00258         cp_multimap_destroy(map);
00259     }
00260 }
00261 
00262 cp_index_map *
00263     cp_index_map_create(cp_multimap *owner, int mode, cp_index *index)
00264 {
00265     cp_index_map *tree;
00266     
00267     if (index == NULL) return NULL;
00268 
00269     tree = calloc(1, sizeof(cp_index_map));
00270     if (tree == NULL) return NULL;
00271 
00272     tree->index = index;
00273     tree->mode = mode;
00274 
00275     tree->owner = owner;
00276 
00277     return tree;
00278 }
00279 
00280 void cp_index_map_destroy_custom(cp_index_map *tree, cp_destructor_fn dtr)
00281 {
00282     tree->mode |= COLLECTION_MODE_DEEP;
00283     tree->owner->dtr = dtr;
00284     cp_index_map_destroy(tree);
00285 }
00286 
00287 static int cp_multimap_lock_internal(cp_multimap *map, int type)
00288 {
00289     int rc;
00290 
00291     switch (type)
00292     {
00293         case COLLECTION_LOCK_READ:
00294             rc = cp_lock_rdlock(map->lock);
00295             break;
00296 
00297         case COLLECTION_LOCK_WRITE:
00298             rc = cp_lock_wrlock(map->lock);
00299             break;
00300 
00301         case COLLECTION_LOCK_NONE:
00302             rc = 0;
00303             break;
00304 
00305         default:
00306             rc = EINVAL;
00307             break;
00308     }
00309 
00310     /* api functions may rely on errno to report locking failure */
00311     if (rc) errno = rc;
00312 
00313     return rc;
00314 }
00315 
00316 static int cp_multimap_unlock_internal(cp_multimap *map)
00317 {
00318     return cp_lock_unlock(map->lock);
00319 }
00320 
00321 int cp_multimap_txlock(cp_multimap *map, int type)
00322 {
00323     /* clear errno to allow client code to distinguish between a NULL return
00324      * value indicating the map doesn't contain the requested value and NULL
00325      * on locking failure in map operations
00326      */
00327     if (map->mode & COLLECTION_MODE_NOSYNC) return 0;
00328     if ((map->mode & COLLECTION_MODE_IN_TRANSACTION) && 
00329         map->txtype == COLLECTION_LOCK_WRITE)
00330     {
00331         cp_thread self = cp_thread_self();
00332         if (cp_thread_equal(self, map->txowner)) return 0;
00333     }
00334     errno = 0;
00335     return cp_multimap_lock_internal(map, type);
00336 }
00337 
00338 int cp_multimap_txunlock(cp_multimap *map)
00339 {
00340     if (map->mode & COLLECTION_MODE_NOSYNC) return 0;
00341     if ((map->mode & COLLECTION_MODE_IN_TRANSACTION) && 
00342         map->txtype == COLLECTION_LOCK_WRITE)
00343     {
00344         cp_thread self = cp_thread_self();
00345         if (cp_thread_equal(self, map->txowner)) return 0;
00346     }
00347     return cp_multimap_unlock_internal(map);
00348 }
00349 
00350 /* lock and set the transaction indicators */
00351 int cp_multimap_lock(cp_multimap *map, int type)
00352 {
00353     int rc;
00354     if ((map->mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00355     if ((rc = cp_multimap_lock_internal(map, type))) return rc;
00356     map->txtype = type;
00357     map->txowner = cp_thread_self();
00358     map->mode |= COLLECTION_MODE_IN_TRANSACTION;
00359     return 0;
00360 }
00361 
00362 /* unset the transaction indicators and unlock */
00363 int cp_multimap_unlock(cp_multimap *map)
00364 {
00365     cp_thread self = cp_thread_self();
00366     if (map->txowner == self)
00367     {
00368         map->txowner = 0;
00369         map->txtype = 0;
00370         map->mode ^= COLLECTION_MODE_IN_TRANSACTION;
00371     }
00372     else if (map->txtype == COLLECTION_LOCK_WRITE)
00373         return -1;
00374     return cp_multimap_unlock_internal(map);
00375 }
00376 
00377 /* set mode bits on the map mode indicator */
00378 int cp_multimap_set_mode(cp_multimap *map, int mode)
00379 {
00380     int rc;
00381     int nosync; 
00382 
00383     /* can't set NOSYNC in the middle of a transaction */
00384     if ((map->mode & COLLECTION_MODE_IN_TRANSACTION) && 
00385         (mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00386     /* COLLECTION_MODE_MULTIPLE_VALUES must be set at creation time */    
00387     if ((mode & COLLECTION_MODE_MULTIPLE_VALUES)) return EINVAL;
00388 
00389     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_WRITE))) return rc;
00390     
00391     nosync = map->mode & COLLECTION_MODE_NOSYNC;
00392 
00393     map->mode |= mode;
00394 
00395     if (!nosync)
00396         cp_multimap_txunlock(map);
00397 
00398     return 0;
00399 }
00400 
00401 /* unset mode bits on the map mode indicator. if unsetting 
00402  * COLLECTION_MODE_NOSYNC and the map was not previously synchronized, the 
00403  * internal synchronization structure is initialized.
00404  */
00405 int cp_multimap_unset_mode(cp_multimap *map, int mode)
00406 {
00407     int rc;
00408     int nosync;
00409 
00410     /* COLLECTION_MODE_MULTIPLE_VALUES can't be unset */
00411     if ((mode & COLLECTION_MODE_MULTIPLE_VALUES)) return EINVAL;
00412 
00413     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_WRITE))) return rc;
00414     
00415     nosync = map->mode & COLLECTION_MODE_NOSYNC;
00416 
00417     /* handle the special case of unsetting COLLECTION_MODE_NOSYNC */
00418     if ((mode & COLLECTION_MODE_NOSYNC) && map->lock == NULL)
00419     {
00420         /* map can't be locked in this case, no need to unlock on failure */
00421         if ((map->lock = malloc(sizeof(cp_lock))) == NULL)
00422             return -1;
00423         if (cp_lock_init(map->lock, NULL))
00424             return -1;
00425     }
00426     
00427     /* unset specified bits */
00428     map->mode &= map->mode ^ mode;
00429     if (!nosync)
00430         cp_multimap_txunlock(map);
00431 
00432     return 0;
00433 }
00434 
00435 int cp_multimap_get_mode(cp_multimap *map)
00436 {
00437     return map->mode;
00438 }
00439 
00440 
00441 static cp_index_map_node *sibling(cp_index_map_node *node)
00442 {
00443     return (node->up != NULL ) && (node == node->up->left) ? 
00444         node->up->right : node->up->left;
00445 }
00446 
00447 static int is_right_child(cp_index_map_node *node)
00448 {
00449     return (node->up->right == node);
00450 }
00451 
00452 static int is_left_child(cp_index_map_node *node)
00453 {
00454     return (node->up->left == node);
00455 }
00456 
00457 /*         left rotate
00458  *
00459  *    (P)                (Q)
00460  *   /   \              /   \
00461  *  1    (Q)    ==>   (P)    3
00462  *      /   \        /   \
00463  *     2     3      1     2
00464  *
00465  */
00466 static void left_rotate(cp_index_map *tree, cp_index_map_node *p)
00467 {
00468     cp_index_map_node *q = p->right;
00469     cp_index_map_node **sup;
00470     
00471     if (p->up)
00472         sup = is_left_child(p) ? &(p->up->left) : &(p->up->right);
00473     else
00474         sup = &tree->root;
00475 
00476     p->right = q->left;
00477     if (p->right) p->right->up = p;
00478     q->left = p;
00479     q->up = p->up;
00480     p->up = q;
00481     *sup = q;
00482 }
00483 
00484 /*           right rotate
00485  *  
00486  *       (P)                (Q)
00487  *      /   \              /   \
00488  *    (Q)    3    ==>     1    (P)  
00489  *   /   \                    /   \
00490  *  1     2                  2     3
00491  *
00492  */
00493 static void right_rotate(cp_index_map *tree, cp_index_map_node *p)
00494 {
00495     cp_index_map_node *q = p->left;
00496     cp_index_map_node **sup;
00497     
00498     if (p->up)
00499         sup = is_left_child(p) ? &(p->up->left) : &(p->up->right);
00500     else
00501         sup = &tree->root;
00502 
00503     p->left = q->right;
00504     if (p->left) p->left->up = p;
00505     q->right = p;
00506     q->up = p->up;
00507     p->up = q;
00508     *sup = q;
00509 }
00510 
00511 
00512 /*
00513  * newly entered node is RED; check balance recursively as required 
00514  */
00515 static void rebalance(cp_index_map *tree, cp_index_map_node *node)
00516 {
00517     cp_index_map_node *up = node->up;
00518     if (up == NULL || up->color == MM_BLACK) return;
00519     if (sibling(up) && sibling(up)->color == MM_RED)
00520     {
00521         up->color = MM_BLACK;
00522         sibling(up)->color = MM_BLACK;
00523         if (up->up->up)
00524         {
00525             up->up->color = MM_RED;
00526             rebalance(tree, up->up);
00527         }
00528     }
00529     else
00530     {
00531         if (is_left_child(node) && is_right_child(up))
00532         {
00533             right_rotate(tree, up);
00534             node = node->right;
00535         }
00536         else if (is_right_child(node) && is_left_child(up))
00537         {
00538             left_rotate(tree, up);
00539             node = node->left;
00540         }
00541 
00542         node->up->color = MM_BLACK;
00543         node->up->up->color = MM_RED;
00544 
00545         if (is_left_child(node)) // && is_left_child(node->up)
00546             right_rotate(tree, node->up->up);
00547         else 
00548             left_rotate(tree, node->up->up);
00549     }
00550 }
00551 
00552 void cp_index_map_drop(cp_index_map *tree, void *entry);
00553 
00554 static void 
00555     multimap_swap_secondaries(cp_multimap *map, void *before, void *after)
00556 {
00557     if (map->index)
00558     {
00559         cp_hashlist_iterator itr;
00560         if (cp_hashlist_iterator_init(&itr, map->index, 
00561                                       COLLECTION_LOCK_NONE) == 0)
00562         {
00563             cp_index_map *curr;
00564             while ((curr = (cp_index_map *) 
00565                             cp_hashlist_iterator_next_value(&itr)))
00566                 cp_index_map_drop(curr, before);
00567             cp_hashlist_iterator_release(&itr);
00568         }
00569     }
00570 }
00571 
00572 /* update_index_map_node - implement COLLECTION_MODE_COPY, 
00573  * COLLECTION_MODE_DEEP and COLLECTION_MODE_MULTIPLE_VALUES when inserting a 
00574  * value for an existing key
00575  */
00576 static void *
00577     update_index_map_node(cp_index_map *tree, 
00578                           cp_index_map_node *node, 
00579                           void *entry)
00580 {
00581     void *before = node->entry;
00582     void *new_entry = entry;
00583 
00584     if (tree->mode & COLLECTION_MODE_COPY)
00585     {
00586         if (tree->owner->copy) 
00587         {
00588             new_entry = (*tree->owner->copy)(entry);
00589             if (new_entry == NULL) return NULL;
00590         }
00591     }
00592         
00593     if (!(tree->mode & COLLECTION_MODE_MULTIPLE_VALUES))
00594         node->entry = new_entry;
00595     else
00596     {
00597         cp_vector_add_element(node->entry, new_entry);
00598         new_entry = node->entry;
00599     }
00600 
00601     /* if this is the primary index, we'll have to iterate through all other
00602      * indices to swap pointers to the new value
00603      */
00604     if (tree->owner->primary == tree && new_entry != before)
00605         multimap_swap_secondaries(tree->owner, before, new_entry);
00606 
00607     if (tree->mode & COLLECTION_MODE_DEEP)
00608     {
00609         if (tree->owner->dtr && !(tree->mode & COLLECTION_MODE_MULTIPLE_VALUES))
00610             (*tree->owner->dtr)(before);
00611     }
00612 
00613     return new_entry;
00614 }
00615 
00616 int cp_index_compare_internal(cp_index *index, void *a, void *b)
00617 {
00618     void *e;
00619     if (index->type == CP_UNIQUE)
00620         e = a;
00621     else
00622     {
00623         cp_vector *v = a;
00624         if (v && cp_vector_size(v))
00625             e = cp_vector_element_at(v, 0);
00626 #ifdef DEBUG
00627         else
00628             cp_error(CP_INVALID_VALUE, "empty multiple value node %p", a);
00629 #endif
00630     }
00631 
00632     return index->key ? 
00633         (*index->cmp)((*index->key)(e), (*index->key)(b)) : 
00634         (*index->cmp)(e, b);
00635 }
00636 
00637 
00638 /*
00639  * cp_index_map_insert iterates through the tree, finds where the new node fits
00640  * in, puts it there, then calls rebalance. 
00641  *
00642  * If a mapping for the given key already exists it is replaced unless 
00643  * COLLECTION_MODE_MULTIPLE_VALUES is set, is which case a new mapping is 
00644  * added. By default COLLECTION_MODE_MULTIPLE_VALUES is not set.
00645  */
00646 void *cp_index_map_insert(cp_index_map *tree, void *entry)
00647 {
00648     void *res = NULL;
00649 
00650     if (tree->root == NULL)
00651     {
00652         tree->root = create_index_map_node(tree, entry);
00653         if (tree->root == NULL) 
00654         {
00655             errno = CP_MEMORY_ALLOCATION_FAILURE;
00656             goto DONE;
00657         }
00658         res = tree->root->entry;
00659         tree->root->color = MM_BLACK;
00660     }
00661     else
00662     {
00663         int cmp;
00664         cp_index_map_node **curr = &tree->root;
00665         cp_index_map_node *prev = NULL;
00666 
00667         while (*curr)
00668         {
00669             prev = *curr;
00670             cmp = cp_index_compare_internal(tree->index, (*curr)->entry, entry);
00671             if (cmp < 0)
00672                 curr = &(*curr)->right;
00673             else if (cmp > 0) 
00674                 curr = &(*curr)->left;
00675             else /* replace */
00676             {
00677                 res = update_index_map_node(tree, *curr, entry);
00678                 break;
00679             }
00680         }
00681 
00682         if (*curr == NULL) /* not replacing, create new node */
00683         {
00684             *curr = create_index_map_node(tree, entry);
00685             if (*curr == NULL) goto DONE;
00686             res = (*curr)->entry;
00687             (*curr)->up = prev;
00688             rebalance(tree, *curr);
00689         }
00690     }
00691 
00692 DONE:
00693     return res;
00694 }
00695 
00696 void *cp_index_map_get(cp_index_map *tree, void *entry);
00697 
00698 int cp_multimap_match_unique(cp_multimap *map, 
00699                              void *entry, 
00700                              cp_index **index, 
00701                              void **existing)
00702 {                            
00703     void *res = NULL;
00704     int count = 0;
00705     cp_index_map *idx;
00706     cp_hashlist_iterator itr;
00707 
00708     /* try default index first */
00709     if (map->default_map->index->type == CP_UNIQUE)
00710         if ((res = cp_multimap_get(map, entry))) 
00711     {
00712         if (index) *index = map->default_map->index;
00713         if (existing) *existing = res;
00714         count = 1;
00715     }
00716 
00717     cp_hashlist_iterator_init(&itr, map->index, COLLECTION_LOCK_NONE);
00718     while ((idx = (cp_index_map *) cp_hashlist_iterator_next_value(&itr)))
00719     {
00720         if (idx->index->type == CP_UNIQUE)
00721             if ((res = cp_index_map_get(idx, entry)))
00722             {
00723                 if (count == 0)
00724                 {
00725                     if (index) *index = idx->index;
00726                     if (existing) *existing = res;
00727                 }
00728                 count++;
00729             }
00730     }
00731     cp_hashlist_iterator_release(&itr);
00732 
00733     return count;
00734 }
00735 
00736 void *cp_index_map_delete(cp_index_map *tree, void *entry);
00737 
00738 void *cp_multimap_insert(cp_multimap *map, void *entry, int *err)
00739 {
00740     void *res = NULL;
00741     void *dupl = NULL;
00742     cp_hashlist_iterator *itr = NULL;
00743     cp_hashlist_entry *idx;
00744 
00745     if (cp_multimap_txlock(map, COLLECTION_LOCK_WRITE)) 
00746     {
00747         if (err != NULL) *err = CP_LOCK_FAILED;
00748         return NULL;
00749     }
00750 
00751     if (cp_multimap_match_unique(map, entry, NULL, &res))
00752     {
00753         if (err != NULL) *err = CP_UNIQUE_INDEX_VIOLATION;
00754         goto DONE;
00755     }
00756 
00757     /* add entry on primary index */
00758     if ((res = cp_index_map_insert(map->primary, entry)) == NULL)
00759     {
00760         if (err != NULL) *err = CP_MEMORY_ALLOCATION_FAILURE;
00761         goto DONE;
00762     }
00763 
00764     /* add entry on secondary indices */
00765     itr = cp_hashlist_create_iterator(map->index, COLLECTION_MODE_NOSYNC);
00766     if (itr == NULL) 
00767     {
00768         if (err != NULL) *err = errno;
00769         map->items++;
00770         goto DONE;
00771     }
00772 
00773     while (idx = cp_hashlist_iterator_next(itr))
00774         if (cp_index_map_insert(idx->value, res) == NULL)
00775         {
00776             if (err != NULL) *err = CP_MEMORY_ALLOCATION_FAILURE;
00777             goto INSERT_ERROR;
00778         }
00779 
00780     map->items++;
00781     goto DONE;
00782 
00783 
00784 INSERT_ERROR:
00785     if (itr)
00786     {
00787         cp_hashlist_entry *idx;
00788         while (idx = cp_hashlist_iterator_prev(itr))
00789             cp_index_map_drop(idx->value, entry);
00790     }
00791     cp_index_map_delete(map->primary, entry);
00792     if ((dupl != NULL) && (map->mode & COLLECTION_MODE_DEEP) && map->dtr)
00793         (*map->dtr)(dupl);
00794 
00795 DONE:
00796     if (itr) cp_hashlist_iterator_destroy(itr);
00797     cp_multimap_txunlock(map);
00798     return res;
00799 }
00800 
00801 /* cp_index_map_reindex() repositions an entry being edited in a way that 
00802  * modifies its value in respect to the index maintained by this index map. 
00803  * This function is called by cp_multimap_reindex. 
00804  *
00805  * (1) insert ``before'' at the position determined for ``after''
00806  * (2) remove ``before''
00807  *
00808  * This creates a temporary situation where the tree is not correctly ordered. 
00809  * This is corrected when cp_multimap_reindex() is done reordering all index 
00810  * maps and copies the content of ``after'' onto ``before''. If you have a 
00811  * better way of doing this, please send a patch.
00812  */
00813 int cp_index_map_reindex(cp_index_map *tree, void *before, void *after)
00814 {
00815     int rc = 0;
00816     int cmp;
00817     cp_index_map_node **curr;
00818     cp_index_map_node *prev;
00819 
00820     if (tree->root == NULL) 
00821     {
00822 #ifdef DEBUG
00823         cp_error(CP_ITEM_DOES_NOT_EXIST, "can\'t reindex entry at %p", before);
00824 #endif
00825         return -1;
00826     }
00827 
00828     /* remove old entry */
00829     cp_index_map_drop(tree, before);
00830     if (tree->root == NULL)
00831     {
00832         tree->root = create_index_map_node(tree, after);
00833         if (tree->root == NULL) 
00834         {
00835 #ifdef DEBUG
00836             cp_error(CP_MEMORY_ALLOCATION_FAILURE, 
00837                      "can\'t reindex entry at %p", before);
00838 #endif
00839             return -1;
00840         }
00841 //        res = tree->root->entry;
00842         tree->root->color = MM_BLACK;
00843         return 0;
00844     }
00845 
00846     /* perform insertion */
00847     curr = &tree->root;
00848     prev = NULL;
00849     while (*curr)
00850     {
00851         prev = *curr;
00852         cmp = cp_index_compare_internal(tree->index, (*curr)->entry, after);
00853         if (cmp < 0)
00854             curr = &(*curr)->right;
00855         else if (cmp > 0) 
00856             curr = &(*curr)->left;
00857         else /* replace */
00858         {
00859             if ((tree->mode & COLLECTION_MODE_MULTIPLE_VALUES) == 0)
00860                 (*curr)->entry = after;
00861             else
00862                 cp_vector_add_element((*curr)->entry, after);
00863             break;
00864         }
00865     }
00866 
00867     if (*curr == NULL) /* not replacing, create new node */
00868     {
00869         *curr = create_index_map_node(tree, after);
00870         if (*curr == NULL) 
00871         {
00872 #ifdef DEBUG
00873             int err = errno;
00874             cp_perror(CP_MEMORY_ALLOCATION_FAILURE, err, 
00875                       "can\'t create new node reindexing %p", before);
00876 #endif
00877             return -1;
00878         }
00879         (*curr)->up = prev;
00880         rebalance(tree, *curr);
00881     }
00882 
00883     return 0;
00884 }
00885 
00886 
00887 /* determine which indices need to be refreshed, then swap ``after'' and 
00888  * ``before''
00889  */
00890 int cp_multimap_reindex(cp_multimap *map, void *before, void *after)
00891 {
00892     int rc;
00893     void *existing;
00894     cp_index *index;
00895     int violation_count;
00896 
00897     if (cp_multimap_txlock(map, COLLECTION_LOCK_WRITE)) return CP_LOCK_FAILED;
00898 
00899     violation_count = cp_multimap_match_unique(map, after, &index, &existing);
00900     if (violation_count > 2 || 
00901         (violation_count == 2 && existing != before))
00902     {
00903         return CP_UNIQUE_INDEX_VIOLATION;
00904     }
00905 
00906     if (map->index)
00907     {
00908         cp_hashlist_iterator itr;
00909         if (cp_hashlist_iterator_init(&itr, map->index, 
00910                                       COLLECTION_LOCK_NONE) == 0)
00911         {
00912             cp_index_map *curr;
00913             while ((curr = (cp_index_map *) 
00914                             cp_hashlist_iterator_next_value(&itr)))
00915             {
00916                 if (cp_index_compare(curr->index, before, after))
00917                     cp_index_map_reindex(curr, before, after);
00918             }
00919             cp_hashlist_iterator_release(&itr);
00920         }
00921     }
00922 
00923     if (cp_index_compare(&map->default_index, before, after))
00924         cp_index_map_reindex(map->default_map, before, after);
00925 
00926     cp_multimap_txunlock(map);
00927 
00928     return 0;
00929 }
00930 
00931 /* cp_index_map_get - return the value mapped to the given key or NULL if none
00932  * is found. If COLLECTION_MODE_MULTIPLE_VALUES is set the returned value is a
00933  * cp_vector object or NULL if no mapping is found. 
00934  */
00935 void *cp_index_map_get(cp_index_map *tree, void *entry)
00936 {
00937     cp_index_map_node *curr;
00938     void *res = NULL;
00939 
00940     curr = tree->root;
00941     while (curr)
00942     {
00943         int c = cp_index_compare_internal(tree->index, curr->entry, entry);
00944         if (c == 0) return curr->entry;
00945         curr = (c > 0) ? curr->left : curr->right;
00946     }
00947 
00948     if (curr) res = curr->entry;
00949 
00950     return res;
00951 }
00952     
00953 void *cp_multimap_get(cp_multimap *map, void *entry)
00954 {
00955     void *res = NULL;
00956 
00957     if (cp_multimap_txlock(map, COLLECTION_LOCK_READ)) return NULL;
00958     res = cp_index_map_get(map->default_map, entry); 
00959     cp_multimap_txunlock(map);
00960 
00961     return res;
00962 }
00963 
00964 void *cp_index_map_find(cp_index_map *tree, void *entry, cp_op op)
00965 {
00966     int cmp;
00967     cp_index_map_node **curr;
00968     cp_index_map_node *prev;
00969     cp_index_map_node *res = NULL;
00970 
00971     if (tree->root == NULL) return NULL;
00972 
00973     curr = &tree->root;
00974     while (*curr)
00975     {
00976         prev = *curr;
00977         cmp = cp_index_compare_internal(tree->index, (*curr)->entry, entry);
00978 
00979         if (cmp == 0) break;
00980         if (op == CP_OP_NE)
00981         {
00982             res = *curr;
00983             goto FIND_DONE;
00984         }
00985 
00986         if (cmp < 0)
00987             curr = &(*curr)->right;
00988         else 
00989             curr = &(*curr)->left;
00990     }
00991 
00992     if (*curr)
00993     {
00994         if (op == CP_OP_EQ || op == CP_OP_LE || op == CP_OP_GE)
00995         {
00996             res = *curr;
00997             goto FIND_DONE;
00998         }
00999 
01000         if (op == CP_OP_GT)
01001         {
01002             if ((*curr)->right)
01003             {
01004                 curr = &(*curr)->right;
01005                 while ((*curr)->left) curr = &(*curr)->left;
01006                 res = *curr;
01007             }
01008             else
01009             {
01010                 while ((*curr)->up)
01011                 {
01012                     prev = *curr;
01013                     curr = &(*curr)->up;
01014                     if ((*curr)->left == prev)
01015                     {
01016                         res = *curr;
01017                         break;
01018                     }
01019                 }
01020             }
01021         }
01022         else
01023         {
01024             if ((*curr)->left)
01025             {
01026                 curr = &(*curr)->left;
01027                 while ((*curr)->right) curr = &(*curr)->right;
01028                 res = *curr;
01029             }
01030             else
01031             {
01032                 while ((*curr)->up)
01033                 {
01034                     prev = *curr;
01035                     curr = &(*curr)->up;
01036                     if ((*curr)->right == prev)
01037                     {
01038                         res = *curr;
01039                         break;
01040                     }
01041                 }
01042             }
01043         }
01044 
01045         goto FIND_DONE;
01046     }
01047 
01048     /* *curr is NULL */
01049     if (op == CP_OP_EQ) goto FIND_DONE;
01050 
01051     if (op == CP_OP_LT || op == CP_OP_LE)
01052     {
01053         if (curr == &prev->right) 
01054         {
01055             res = prev;
01056             goto FIND_DONE;
01057         }
01058 
01059         while (prev)
01060         {
01061             if (prev->up && prev->up->right == prev)
01062             {
01063                 res = prev->up;
01064                 break;
01065             }
01066             prev = prev->up;
01067         }
01068     } 
01069     else /* op == CP_OP_GE || op == CP_OP_GT */
01070     {
01071         if (curr == &prev->left)
01072         {
01073             res = prev;
01074             goto FIND_DONE;
01075         }
01076 
01077         while (prev)
01078         {
01079             if (prev->up && prev->up->left == prev)
01080             {
01081                 res = prev->up;
01082                 break;
01083             }
01084             prev = prev->up;
01085         }
01086     }
01087 
01088 FIND_DONE:
01089     return res ? res->entry : NULL;
01090 }
01091 
01092 void *cp_multimap_find(cp_multimap *map, void *entry, cp_op op)
01093 {
01094     void *res = NULL;
01095 
01096     if (cp_multimap_txlock(map, COLLECTION_LOCK_READ)) return NULL;
01097     res = cp_index_map_find(map->default_map, entry, op); 
01098     cp_multimap_txunlock(map);
01099 
01100     return res;
01101 }
01102 
01103 void *cp_multimap_find_by_index(cp_multimap *map, 
01104                                 cp_index *index, 
01105                                 void *entry, 
01106                                 cp_op op)
01107 {
01108     void *res = NULL;
01109 
01110     cp_index_map *tree = cp_hashlist_get(map->index, index);
01111     if (tree == NULL)
01112     {
01113 #ifdef DEBUG
01114         cp_error(CP_INVALID_VALUE, 
01115             "request for search by unknown index %p", index);
01116 #endif
01117         return NULL;
01118     }
01119 
01120     if (cp_multimap_txlock(map, COLLECTION_LOCK_READ)) return NULL;
01121     res = cp_index_map_find(tree, entry, op); 
01122     cp_multimap_txunlock(map);
01123 
01124     return res;
01125 }
01126 
01127 
01128 void *cp_multimap_get_by_index(cp_multimap *map, cp_index *index, void *entry)
01129 {
01130     void *res = NULL;
01131     cp_index_map *tree;
01132     if (index == NULL) return cp_multimap_get(map, entry);
01133     
01134     if (cp_multimap_txlock(map, COLLECTION_LOCK_READ)) return NULL;
01135 
01136 #ifdef DEBUG
01137     if (map->index == NULL)
01138     {
01139         cp_error(CP_INVALID_VALUE, "no secondary indices set on multimap");
01140         return NULL;
01141     }
01142 #endif /* DEBUG */
01143 
01144     tree = cp_hashlist_get(map->index, index);
01145     if (tree)
01146         res = cp_index_map_get(tree, entry);
01147 #ifdef DEBUG
01148     else
01149         cp_error(CP_INVALID_VALUE, 
01150             "request for lookup by unknown index %p", index);
01151 #endif
01152     cp_multimap_txunlock(map);
01153 
01154     return res;
01155 }
01156 
01157 int cp_index_map_contains(cp_index_map *tree, void *entry)
01158 {
01159     return (cp_index_map_get(tree, entry) != NULL);
01160 }
01161 
01162 /* return non-zero if entry could be found on any index */
01163 CPROPS_DLL
01164 int cp_multimap_contains(cp_multimap *map, void *entry)
01165 {
01166     int res = 0;
01167     cp_index_map *idx;
01168     cp_hashlist_iterator itr;
01169 
01170     /* try default index first */
01171     if (cp_multimap_get(map, entry) != NULL) return 1;
01172 
01173     cp_hashlist_iterator_init(&itr, map->index, COLLECTION_LOCK_NONE);
01174     while ((idx = (cp_index_map *) cp_hashlist_iterator_next_value(&itr)))
01175     {
01176         if (cp_index_map_get(idx, entry) != NULL)
01177         {
01178             res = 1;
01179             break;
01180         }
01181     }
01182     cp_hashlist_iterator_release(&itr);
01183 
01184     return res;
01185 }
01186 
01187 
01188 /* helper function for deletion */
01189 static void swap_node_content(cp_index_map_node *a, cp_index_map_node *b)
01190 {
01191     void *tmp;
01192 
01193     tmp = a->entry;
01194     a->entry = b->entry;
01195     b->entry = tmp;
01196 }
01197 
01198 /*
01199  * helper function for cp_index_map_delete to remove nodes with either a left 
01200  * NULL branch or a right NULL branch
01201  */
01202 static void index_map_unlink(cp_index_map *tree, cp_index_map_node *node)
01203 {
01204     if (node->left)
01205     {
01206         node->left->up = node->up;
01207         if (node->up)
01208         {
01209             if (is_left_child(node))
01210                 node->up->left = node->left;
01211             else
01212                 node->up->right = node->left;
01213         }
01214         else
01215             tree->root = node->left;
01216     }
01217     else
01218     {
01219         if (node->right) node->right->up = node->up;
01220         if (node->up)
01221         {
01222             if (is_left_child(node))
01223                 node->up->left = node->right;
01224             else
01225                 node->up->right = node->right;
01226         }
01227         else
01228             tree->root = node->right;
01229     }
01230 }
01231 
01232 /* delete_rebalance - perform rebalancing after a deletion */
01233 static void delete_rebalance(cp_index_map *tree, cp_index_map_node *n)
01234 {
01235     if (n->up)
01236     {
01237         cp_index_map_node *sibl = sibling(n);
01238 
01239         if (sibl->color == MM_RED)
01240         {
01241             n->up->color = MM_RED;
01242             sibl->color = MM_BLACK;
01243             if (is_left_child(n))
01244                 left_rotate(tree, n->up);
01245             else
01246                 right_rotate(tree, n->up);
01247             sibl = sibling(n);
01248         }
01249 
01250         if (n->up->color == MM_BLACK &&
01251             sibl->color == MM_BLACK &&
01252             (sibl->left == NULL || sibl->left->color == MM_BLACK) &&
01253             (sibl->right == NULL || sibl->right->color == MM_BLACK))
01254         {
01255             sibl->color = MM_RED;
01256             delete_rebalance(tree, n->up);
01257         }
01258         else
01259         {
01260             if (n->up->color == MM_RED &&
01261                 sibl->color == MM_BLACK &&
01262                 (sibl->left == NULL || sibl->left->color == MM_BLACK) &&
01263                 (sibl->right == NULL || sibl->right->color == MM_BLACK))
01264             {
01265                 sibl->color = MM_RED;
01266                 n->up->color = MM_BLACK;
01267             }
01268             else
01269             {
01270                 if (is_left_child(n) && 
01271                     sibl->color == MM_BLACK &&
01272                     sibl->left && sibl->left->color == MM_RED && 
01273                     (sibl->right == NULL || sibl->right->color == MM_BLACK))
01274                 {
01275                     sibl->color = MM_RED;
01276                     sibl->left->color = MM_BLACK;
01277                     right_rotate(tree, sibl);
01278                     
01279                     sibl = sibling(n);
01280                 }
01281                 else if (is_right_child(n) &&
01282                     sibl->color == MM_BLACK &&
01283                     sibl->right && sibl->right->color == MM_RED &&
01284                     (sibl->left == NULL || sibl->left->color == MM_BLACK))
01285                 {
01286                     sibl->color = MM_RED;
01287                     sibl->right->color = MM_BLACK;
01288                     left_rotate(tree, sibl);
01289 
01290                     sibl = sibling(n);
01291                 }
01292 
01293                 sibl->color = n->up->color;
01294                 n->up->color = MM_BLACK;
01295                 if (is_left_child(n))
01296                 {
01297                     sibl->right->color = MM_BLACK;
01298                     left_rotate(tree, n->up);
01299                 }
01300                 else
01301                 {
01302                     sibl->left->color = MM_BLACK;
01303                     right_rotate(tree, n->up);
01304                 }
01305             }
01306         }
01307     }
01308 }
01309 
01310 /* cp_index_map_delete - deletes the value mapped to the given key from the 
01311  * tree and returns the value removed. 
01312  */
01313 void *cp_index_map_delete(cp_index_map *tree, void *entry)
01314 {
01315     void *res = NULL;
01316     cp_index_map_node *node; 
01317     int cmp;
01318 
01319     node = tree->root;
01320     while (node)
01321     {
01322         cmp = cp_index_compare_internal(tree->index, node->entry, entry);
01323         if (cmp < 0)
01324             node = node->right;
01325         else if (cmp > 0)
01326             node = node->left;
01327         else /* found */
01328             break;
01329     }
01330 
01331     if (node) /* may be null if not found */
01332     {
01333         cp_index_map_node *child; 
01334         res = node->entry;
01335 
01336         if (node->right && node->left)
01337         {
01338             cp_index_map_node *surrogate = node;
01339             node = node->right;
01340             while (node->left) node = node->left;
01341             swap_node_content(node, surrogate);
01342         }
01343         child = node->right ? node->right : node->left;
01344 
01345         /* if the node was red - no rebalancing required */
01346         if (node->color == MM_BLACK)
01347         {
01348             if (child)
01349             {
01350                 /* single red child - paint it black */
01351                 if (child->color == MM_RED)
01352                     child->color = MM_BLACK; /* and the balance is restored */
01353                 else
01354                     delete_rebalance(tree, child);
01355             }
01356             else 
01357                 delete_rebalance(tree, node);
01358         }
01359 
01360         index_map_unlink(tree, node);
01361         cp_index_map_destroy_node(tree, node);
01362     }
01363 
01364     return res;
01365 }
01366 
01367 /* drop a single entry. Locate the entry, if it's a multiple mapping iterate
01368  * over multiple values and remove the specified entry only. If operating on
01369  * the default index, implement mode settings, ie destroy node if required.
01370  */
01371 void cp_index_map_drop(cp_index_map *tree, void *entry)
01372 {
01373     cp_index_map_node *node; 
01374     int cmp;
01375 
01376     node = tree->root;
01377     while (node)
01378     {
01379         cmp = cp_index_compare_internal(tree->index, node->entry, entry);
01380         if (cmp < 0)
01381             node = node->right;
01382         else if (cmp > 0)
01383             node = node->left;
01384         else /* found */
01385             break;
01386     }
01387 
01388     if (node) /* may be null if not found */
01389     {
01390         cp_index_map_node *child; 
01391 
01392         /* find the single entry to be removed */
01393         if (tree->index->type == CP_MULTIPLE)
01394         {
01395             int i;
01396             cp_vector *v = node->entry;
01397             int size = cp_vector_size(v);
01398 
01399             for (i = 0; i < size; i++)
01400                 if (cp_vector_element_at(v, i) == entry)
01401                 {
01402                     cp_vector_remove_element_at(v, i);
01403                     break;
01404                 }
01405 
01406             /* still more multiple values in this vector, safe to return */
01407             if (cp_vector_size(v)) return;
01408         }
01409 
01410         if (node->right && node->left)
01411         {
01412             cp_index_map_node *surrogate = node;
01413             node = node->right;
01414             while (node->left) node = node->left;
01415             swap_node_content(node, surrogate);
01416         }
01417         child = node->right ? node->right : node->left;
01418 
01419         /* if the node was red - no rebalancing required */
01420         if (node->color == MM_BLACK)
01421         {
01422             if (child)
01423             {
01424                 /* single red child - paint it black */
01425                 if (child->color == MM_RED)
01426                     child->color = MM_BLACK; /* and the balance is restored */
01427                 else
01428                     delete_rebalance(tree, child);
01429             }
01430             else 
01431                 delete_rebalance(tree, node);
01432         }
01433 
01434         index_map_unlink(tree, node);
01435         cp_index_map_drop_node(tree, node);        
01436     }
01437 }
01438 
01439 /* drop the given entry from all indices */
01440 void cp_multimap_drop(cp_multimap *map, 
01441                       cp_index *index, 
01442                       void *entry)
01443 {
01444     cp_hashlist_iterator *itr = NULL;
01445 
01446     if (map->index) 
01447         itr = cp_hashlist_create_iterator(map->index, COLLECTION_LOCK_NONE);
01448 
01449     if (itr)
01450     {
01451         cp_hashlist_entry *idx;
01452 
01453         while (idx = cp_hashlist_iterator_next(itr))
01454             if (idx->key != index && idx->key != &map->default_index) cp_index_map_drop(idx->value, entry);
01455 
01456         cp_hashlist_iterator_destroy(itr);
01457     }
01458 
01459     if (index)
01460         cp_index_map_drop(map->default_map, entry);
01461 }
01462 
01463 /* delete the entry (or entries) matching the given value by the tree primary
01464  * key. 
01465  */
01466 void *cp_multimap_remove(cp_multimap *map, void *entry)
01467 {
01468     void *res = NULL;
01469     int count = 0;
01470 
01471     if (cp_multimap_txlock(map, COLLECTION_LOCK_WRITE)) return NULL;
01472 
01473     if (0) // map->mode & COLLECTION_MODE_MULTIPLE_VALUES)
01474     {
01475         int i;
01476         cp_vector *v = cp_index_map_get(map->primary, entry);
01477         if (v)
01478         {
01479             res = v;
01480             count = cp_vector_size(v);
01481             for (i = 0; i < count; i++)
01482             {
01483                 void *element = cp_vector_element_at(v, i);
01484                 cp_multimap_drop(map, NULL, element);
01485                 cp_index_map_delete(map->primary, element);
01486             }
01487         }
01488     }
01489     else
01490     {
01491         void *e = cp_index_map_get(map->primary, entry);
01492         if (e)
01493         {
01494             res = e;
01495             cp_multimap_drop(map, NULL, e);
01496 //          cp_index_map_delete(map->primary, entry);
01497             count = 1;
01498         }
01499     }
01500 
01501     if (count)
01502         map->items -= count;
01503 
01504     cp_multimap_txunlock(map);
01505 
01506     return res;
01507 }
01508 
01509 /* the point in deleting entries by index is when you have multiple values for
01510  * a given entry respective to the given index, and you want to delete them all
01511  * in one go.
01512  */
01513 void *cp_multimap_remove_by_index(cp_multimap *map, 
01514                                   cp_index *index, 
01515                                   void *entry)
01516 {
01517     void *res = NULL;
01518     cp_index_map *lookup;
01519     int count = 0;
01520 
01521     if (index == NULL) 
01522         return cp_multimap_remove(map, entry);
01523 
01524     if (cp_multimap_txlock(map, COLLECTION_LOCK_WRITE)) return NULL;
01525     
01526     lookup = cp_hashlist_get(map->index, index);
01527 #ifdef DEBUG
01528     if (lookup == NULL)
01529     {
01530         cp_error(CP_INVALID_VALUE, 
01531                  "request for removal by unknown index %p", index);
01532         goto DELETE_DONE;
01533     }
01534 #endif
01535 
01536     if (lookup->mode & COLLECTION_MODE_MULTIPLE_VALUES)
01537     {
01538         int i;
01539         cp_vector *v = cp_index_map_get(lookup, entry);
01540         if (v)
01541         {
01542             res = v;
01543             count = cp_vector_size(v);
01544             for (i = 0; i < count; i++)
01545                 cp_multimap_drop(map, index, cp_vector_element_at(v, i));
01546         }
01547     }
01548     else
01549     {
01550         void *e = cp_index_map_get(lookup, entry);
01551         if (e)
01552         {
01553             res = e;
01554             cp_multimap_drop(map, index, e);
01555             count = 1;
01556         }
01557     }
01558 
01559     if (count)
01560     {
01561         cp_index_map_delete(lookup, entry);
01562         map->items -= count;
01563     }
01564     
01565 #ifdef DEBUG
01566 DELETE_DONE:
01567 #endif
01568     cp_multimap_txunlock(map);
01569 
01570     return res;
01571 }
01572 
01573 static int 
01574     index_map_scan_pre_order(cp_index_map_node *node, 
01575                              cp_callback_fn callback, 
01576                              void *prm)
01577 {
01578     int rc;
01579     
01580     if (node) 
01581     {
01582         if ((rc = (*callback)(node, prm))) return rc;
01583         if ((rc = index_map_scan_pre_order(node->left, callback, prm))) return rc;
01584         if ((rc = index_map_scan_pre_order(node->right, callback, prm))) return rc;
01585     }
01586 
01587     return 0;
01588 }
01589 
01590 int cp_multimap_callback_preorder(cp_multimap *map, 
01591                                   cp_callback_fn callback, 
01592                                   void *prm)
01593 {
01594     int rc;
01595 
01596     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_READ))) return rc;
01597     rc = index_map_scan_pre_order(map->default_map->root, callback, prm);
01598     cp_multimap_txunlock(map);
01599 
01600     return rc;
01601 }
01602 
01603 static int 
01604     index_map_scan_in_order(cp_index_map_node *node, 
01605                             cp_callback_fn callback, 
01606                             void *prm)
01607 {
01608     int rc;
01609     
01610     if (node) 
01611     {
01612         if ((rc = index_map_scan_in_order(node->left, callback, prm))) return rc;
01613         if ((rc = (*callback)(node, prm))) return rc;
01614         if ((rc = index_map_scan_in_order(node->right, callback, prm))) return rc;
01615     }
01616 
01617     return 0;
01618 }
01619 
01620 int cp_multimap_callback(cp_multimap *map, cp_callback_fn callback, void *prm)
01621 {
01622     int rc;
01623 
01624     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_READ))) return rc;
01625     rc = index_map_scan_in_order(map->default_map->root, callback, prm);
01626     cp_multimap_txunlock(map);
01627 
01628     return rc;
01629 }
01630 
01631 int cp_multimap_callback_by_index(cp_multimap *map, 
01632                                   cp_index *index, 
01633                                   cp_callback_fn callback, 
01634                                   void *prm)
01635 {
01636     int rc;
01637     cp_index_map *imap;
01638 
01639     if (index == NULL) return cp_multimap_callback(map, callback, prm);
01640 
01641     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_READ))) return rc;
01642 
01643     imap = cp_hashlist_get(map->index, index);
01644     if (imap == NULL) 
01645         rc = -1;
01646     else
01647         rc = index_map_scan_in_order(imap->root, callback, prm);
01648     cp_multimap_txunlock(map);
01649 
01650     return rc;
01651 }
01652 
01653 
01654 
01655 static int 
01656     index_map_scan_post_order(cp_index_map_node *node, cp_callback_fn callback, void *prm)
01657 {
01658     int rc;
01659     
01660     if (node) 
01661     {
01662         if ((rc = index_map_scan_post_order(node->left, callback, prm))) return rc;
01663         if ((rc = index_map_scan_post_order(node->right, callback, prm))) return rc;
01664         if ((rc = (*callback)(node, prm))) return rc;
01665     }
01666 
01667     return 0;
01668 }
01669 
01670 int cp_multimap_callback_postorder(cp_multimap *map, 
01671                                  cp_callback_fn callback, 
01672                                  void *prm)
01673 {
01674     int rc;
01675 
01676     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_READ))) return rc;
01677     rc = index_map_scan_post_order(map->default_map->root, callback, prm);
01678     cp_multimap_txunlock(map);
01679 
01680     return rc;
01681 }
01682 
01683 int cp_multimap_count(cp_multimap *tree)
01684 {
01685     return tree->items;
01686 }
01687 
01688 
01689 void cp_index_map_node_print(cp_index_map_node *node, int level)
01690 {
01691     int i;
01692     if (node->right) cp_index_map_node_print(node->right, level + 1);
01693     for (i = 0; i < level; i++) printf("  . ");
01694     printf("(%d) [%s]\n", node->color, (char *) node->entry);
01695     if (node->left) cp_index_map_node_print(node->left, level + 1);
01696 }
01697 
01698 void cp_index_map_node_multi_print(cp_index_map_node *node, int level)
01699 {
01700     int i;
01701     cp_vector *v = node->entry;
01702     if (node->right) cp_index_map_node_multi_print(node->right, level + 1);
01703     
01704     for (i = 0; i < level; i++) printf("  . ");
01705     printf("(%d) [ ", node->color);
01706 
01707     for (i = 0; i < cp_vector_size(v); i++)
01708         printf("%s; ", (char *) cp_vector_element_at(v, i));
01709 
01710     printf("]\n");
01711 
01712     if (node->left) cp_index_map_node_multi_print(node->left, level + 1);
01713 }
01714 
01715 void cp_multimap_dump(cp_multimap *map)
01716 {
01717     if (map->default_map->root) 
01718     {
01719         if ((map->mode & COLLECTION_MODE_MULTIPLE_VALUES))
01720             cp_index_map_node_multi_print(map->default_map->root, 0);
01721         else
01722             cp_index_map_node_print(map->default_map->root, 0);
01723     }
01724 }
01725 
01726 /* set map to use given mempool or allocate a new one if pool is NULL */
01727 int cp_multimap_use_mempool(cp_multimap *map, cp_mempool *pool)
01728 {
01729     int rc = 0;
01730     
01731     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_WRITE))) return rc;
01732     
01733     if (pool)
01734     {
01735         if (pool->item_size < sizeof(cp_index_map_node))
01736         {
01737             rc = EINVAL;
01738             goto DONE;
01739         }
01740         if (map->mempool) 
01741         {
01742             if (map->items) 
01743             {
01744                 rc = ENOTEMPTY;
01745                 goto DONE;
01746             }
01747             cp_mempool_destroy(map->mempool);
01748         }
01749         cp_mempool_inc_refcount(pool);
01750         map->mempool = pool;
01751     }
01752     else
01753     {
01754         map->mempool = 
01755             cp_mempool_create_by_option(COLLECTION_MODE_NOSYNC, 
01756                                         sizeof(cp_index_map_node), 0);
01757         if (map->mempool == NULL) 
01758         {
01759             rc = ENOMEM;
01760             goto DONE;
01761         }
01762     }
01763 
01764 DONE:
01765     cp_multimap_txunlock(map);
01766     return rc;
01767 }
01768 
01769 
01770 /* set map to use a shared memory pool */
01771 int cp_multimap_share_mempool(cp_multimap *map, cp_shared_mempool *pool)
01772 {
01773     int rc;
01774 
01775     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_WRITE))) return rc;
01776 
01777     if (map->mempool)
01778     {
01779         if (map->items)
01780         {
01781             rc = ENOTEMPTY;
01782             goto DONE;
01783         }
01784 
01785         cp_mempool_destroy(map->mempool);
01786     }
01787 
01788     map->mempool = cp_shared_mempool_register(pool, sizeof(cp_index_map_node));
01789     if (map->mempool == NULL) 
01790     {
01791         rc = ENOMEM;
01792         goto DONE;
01793     }
01794     
01795 DONE:
01796     cp_multimap_txunlock(map);
01797     return rc;
01798 }
01799 
01800 /* add an index */
01801 cp_index *cp_multimap_create_index(cp_multimap *map, 
01802                                    cp_index_type type,
01803                                    cp_key_fn key, 
01804                                    cp_compare_fn cmp,
01805                                    int *err)
01806 {
01807     int mode;
01808     cp_index *res = NULL;
01809     cp_index *desc;
01810     cp_index_map *index;
01811     int rc;
01812 
01813     if ((rc = cp_multimap_txlock(map, COLLECTION_LOCK_WRITE))) 
01814     {
01815         if (err != NULL) *err = rc;
01816         return NULL;
01817     }
01818 
01819     if (map->index == NULL)
01820     {
01821         map->index = 
01822             cp_hashlist_create_by_option(COLLECTION_MODE_NOSYNC |
01823                                          COLLECTION_MODE_DEEP, 3,
01824                                          cp_hash_addr, cp_hash_compare_addr, 
01825                                          NULL, NULL, 
01826                                          NULL, (cp_destructor_fn) free);
01827         if (map->index == NULL)
01828         {
01829             if (err != NULL) *err = CP_MEMORY_ALLOCATION_FAILURE;
01830             return NULL;
01831         }
01832     }
01833     else /* verify that no such index has been registered */
01834     {
01835         int exists = 0;
01836         cp_hashlist_entry *entry;
01837         cp_hashlist_iterator *itr = 
01838             cp_hashlist_create_iterator(map->index, COLLECTION_LOCK_NONE);
01839 
01840         while ((entry = cp_hashlist_iterator_next(itr)))
01841         {
01842             cp_index *curr = (cp_index *) entry->key;
01843             if (curr->key == key && curr->cmp == cmp)
01844             {
01845                 res = curr;
01846                 exists = 1;
01847                 break;
01848             }
01849         }
01850 
01851         cp_hashlist_iterator_destroy(itr);
01852 
01853         if (exists)
01854         {
01855             if (err != NULL) *err = CP_ITEM_EXISTS;
01856             goto DONE;
01857         }
01858     }
01859 
01860     desc = calloc(1, sizeof(cp_index));
01861     if (desc == NULL) 
01862     {
01863         if (err != NULL) *err = CP_MEMORY_ALLOCATION_FAILURE;
01864         goto DONE;
01865     }
01866     desc->type = type;
01867     desc->key = key;
01868     desc->cmp = cmp;
01869 
01870     mode = map->mode & ~(COLLECTION_MODE_COPY | COLLECTION_MODE_DEEP);
01871     if (type == CP_UNIQUE)
01872         mode &= ~COLLECTION_MODE_MULTIPLE_VALUES;
01873     else
01874         mode |= COLLECTION_MODE_MULTIPLE_VALUES;
01875 
01876     index = cp_index_map_create(map, mode, desc);
01877     if (index == NULL) goto DONE;
01878 
01879     if (cp_hashlist_append(map->index, desc, index)) res = desc;
01880 
01881 DONE:
01882     cp_multimap_txunlock(map);
01883     return res;
01884 }
01885 

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