1 /*
2  * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3  *                         University Research and Technology
4  *                         Corporation.  All rights reserved.
5  * Copyright (c) 2004-2005 The University of Tennessee and The University
6  *                         of Tennessee Research Foundation.  All rights
7  *                         reserved.
8  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9  *                         University of Stuttgart.  All rights reserved.
10  * Copyright (c) 2004-2005 The Regents of the University of California.
11  *                         All rights reserved.
12  * Copyright (c) 2014-2015 Hewlett-Packard Development Company, LP.
13  *                         All rights reserved.
14  * Copyright (c) 2014-2015 Mellanox Technologies, Inc.
15  *                         All rights reserved.
16  * Copyright (c) 2014      Research Organization for Information Science
17  *                         and Technology (RIST). All rights reserved.
18  * $COPYRIGHT$
19  *
20  * Additional copyrights may follow
21  *
22  * $HEADER$
23  */
24 
25 #include "opal_config.h"
26 
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #include "opal/util/output.h"
31 #include "opal/class/opal_hash_table.h"
32 #include "opal/constants.h"
33 
34 /*
35  * opal_hash_table_t
36  *
37  * Sketch: [Contributed by David Linden of Hewlett-Packard]
38  *
39  * This has been found to be good for search and insert and
40  * (seldom-)remove, all with probablistic O(1) time.  Having a good
41  * distribution of the hash indices is important, so even if you know
42  * the keys distribute well under a mask, that micro-optimization
43  * isn't worth doing.
44  *
45  * One aspect is that the concept of buckets and elements is
46  * unified. The buckets aka elements are in a single array, each
47  * element having a valid flag.  The key hashes to a keyhash, the
48  * keyhash determines the first index to probe.  Missing probes search
49  * forward (wrapping) until the key is found or an invalid entry is
50  * found.
51  *
52  * One parameter of the hash table is a maximum density, which must be
53  * less than 1, expressed a numerator and denominator.  1/2 seems to
54  * work well.  A density less than 1 ensures the search will stop
55  * because searching will eventually find an invalid element.  At
56  * maximum density, assuming random usage of the elements, the
57  * expected search length is 1/(1-density); for a density of 1/2, this
58  * is 2.
59  *
60  * I believe this blinded bucket/element scheme is actually more
61  * storage-efficient than a bucket having a linear list of elements.
62  * It is certainly better on the cache.
63  *
64  * Another parameter is the growth factor, another ratio, greater than
65  * 1, expressed as a numerator and denominator.  2/1 seems to work
66  * well.  When the hash table reaches maximum density, it is grown by
67  * the growth factor (thus reducing the density).  Growing requires
68  * rehashing and reinserting existing elements.  It turns out this
69  * keeps insertion at O(1): multiplies the coefficient by
70  * growth/(growth-1); for a growth of 2/1 this is 2.
71  *
72  * The key is hashed to a keyhash.  The keyhash determines the first
73  * index to probe by using the remainder of the keyhash by the table's
74  * 'capacity.'  The capacity is not a power of 2.  (Keys that vary
75  * only in the high 32 bits of a 64 bit key would always colide with a
76  * power-of-2 capacity.)  Rather, the capacity is arranged not to be a
77  * multiple of 2, 3 or 5.  A potential capacity is rounded up to be (1
78  * mod 30).
79  *
80  * Removing a key is the most involved operation.  It is necessary to
81  * rehash any valid elements immediately after the removed element,
82  * because some (perhaps all) of those elements would normally hash
83  * lower if the removed key were never there.  This remains O(1); the
84  * implementation just needs to be a little careful.
85  *
86  */
87 
88 #define HASH_MULTIPLIER 31
89 
90 /*
91  * Define the structs that are opaque in the .h
92  */
93 
94 struct opal_hash_element_t {
95     int         valid;          /* whether this element is valid */
96     union {                     /* the key, in its various forms */
97         uint32_t        u32;
98         uint64_t        u64;
99         struct {
100             const void * key;
101             size_t      key_size;
102         }       ptr;
103     }           key;
104     void *      value;          /* the value */
105 };
106 typedef struct opal_hash_element_t opal_hash_element_t;
107 
108 struct opal_hash_type_methods_t {
109     /* Frees any storage associated with the element
110      * The value is not owned by the hash table
111      * The key,key_size of pointer keys is
112      */
113     void        (*elt_destructor)(opal_hash_element_t * elt);
114     /* Hash the key of the element -- for growing and adjusting-after-removal */
115     uint64_t    (*hash_elt)(opal_hash_element_t * elt);
116 };
117 
118 /* interact with the class-like mechanism */
119 
120 static void opal_hash_table_construct(opal_hash_table_t* ht);
121 static void opal_hash_table_destruct(opal_hash_table_t* ht);
122 
123 OBJ_CLASS_INSTANCE(
124     opal_hash_table_t,
125     opal_object_t,
126     opal_hash_table_construct,
127     opal_hash_table_destruct
128 );
129 
130 static void
opal_hash_table_construct(opal_hash_table_t * ht)131 opal_hash_table_construct(opal_hash_table_t* ht)
132 {
133   ht->ht_table = NULL;
134   ht->ht_capacity = ht->ht_size = ht->ht_growth_trigger = 0;
135   ht->ht_density_numer = ht->ht_density_denom = 0;
136   ht->ht_growth_numer = ht->ht_growth_denom = 0;
137   ht->ht_type_methods = NULL;
138 }
139 
140 static void
opal_hash_table_destruct(opal_hash_table_t * ht)141 opal_hash_table_destruct(opal_hash_table_t* ht)
142 {
143     opal_hash_table_remove_all(ht);
144     free(ht->ht_table);
145 }
146 
147 /*
148  * Init, etc
149  */
150 
151 static size_t
opal_hash_round_capacity_up(size_t capacity)152 opal_hash_round_capacity_up(size_t capacity)
153 {
154     /* round up to (1 mod 30) */
155     return ((capacity+29)/30*30 + 1);
156 }
157 
158 /* this could be the new init if people wanted a more general API */
159 /* (that's why it isn't static) */
160 int                             /* OPAL_ return code */
opal_hash_table_init2(opal_hash_table_t * ht,size_t estimated_max_size,int density_numer,int density_denom,int growth_numer,int growth_denom)161 opal_hash_table_init2(opal_hash_table_t* ht, size_t estimated_max_size,
162                       int density_numer, int density_denom,
163                       int growth_numer, int growth_denom)
164 {
165     size_t est_capacity = estimated_max_size * density_denom / density_numer;
166     size_t capacity = opal_hash_round_capacity_up(est_capacity);
167     ht->ht_table = (opal_hash_element_t*) calloc(capacity, sizeof(opal_hash_element_t));
168     if (NULL == ht->ht_table) {
169         return OPAL_ERR_OUT_OF_RESOURCE;
170     }
171     ht->ht_capacity       = capacity;
172     ht->ht_density_numer  = density_numer;
173     ht->ht_density_denom  = density_denom;
174     ht->ht_growth_numer   = growth_numer;
175     ht->ht_growth_denom   = growth_denom;
176     ht->ht_growth_trigger = capacity * density_numer / density_denom;
177     ht->ht_type_methods   = NULL;
178     return OPAL_SUCCESS;
179 }
180 
181 int                             /* OPAL_ return code */
opal_hash_table_init(opal_hash_table_t * ht,size_t table_size)182 opal_hash_table_init(opal_hash_table_t* ht, size_t table_size)
183 {
184     /* default to density of 1/2 and growth of 2/1 */
185     return opal_hash_table_init2(ht, table_size, 1, 2, 2, 1);
186 }
187 
188 int                             /* OPAL_ return code */
opal_hash_table_remove_all(opal_hash_table_t * ht)189 opal_hash_table_remove_all(opal_hash_table_t* ht)
190 {
191     size_t ii;
192     for (ii = 0; ii < ht->ht_capacity; ii += 1) {
193         opal_hash_element_t * elt = &ht->ht_table[ii];
194         if (elt->valid && ht->ht_type_methods && ht->ht_type_methods->elt_destructor) {
195             ht->ht_type_methods->elt_destructor(elt);
196         }
197         elt->valid = 0;
198         elt->value = NULL;
199     }
200     ht->ht_size = 0;
201     /* the tests reuse the hash table for different types after removing all */
202     /* so we should allow that by forgetting what type it used to be */
203     ht->ht_type_methods = NULL;
204     return OPAL_SUCCESS;
205 }
206 
207 static int                      /* OPAL_ return code */
opal_hash_grow(opal_hash_table_t * ht)208 opal_hash_grow(opal_hash_table_t * ht)
209 {
210     size_t jj, ii;
211     opal_hash_element_t* old_table;
212     opal_hash_element_t* new_table;
213     size_t old_capacity;
214     size_t new_capacity;
215 
216     old_table    = ht->ht_table;
217     old_capacity = ht->ht_capacity;
218 
219     new_capacity = old_capacity * ht->ht_growth_numer / ht->ht_growth_denom;
220     new_capacity = opal_hash_round_capacity_up(new_capacity);
221 
222     new_table    = (opal_hash_element_t*) calloc(new_capacity, sizeof(new_table[0]));
223     if (NULL == new_table) {
224         return OPAL_ERR_OUT_OF_RESOURCE;
225     }
226 
227     /* for each element of the old table (indexed by jj), insert it
228        into the new table (indexed by ii), using the hash_elt method
229        to generically hash an element, then modulo the new capacity,
230        and using struct-assignment to copy an old element into its
231        place int he new table.  The hash table never owns the value,
232        and in the case of ptr keys the old dlements will be blindly
233        deleted, so we still own the ptr key storage, just in the new
234        table now */
235     for (jj = 0; jj < old_capacity; jj += 1) {
236         opal_hash_element_t * old_elt;
237         opal_hash_element_t * new_elt;
238         old_elt =  &old_table[jj];
239         if (old_elt->valid) {
240             for (ii = (ht->ht_type_methods->hash_elt(old_elt)%new_capacity); ; ii += 1) {
241                 if (ii == new_capacity) { ii = 0; }
242                 new_elt = &new_table[ii];
243                 if (! new_elt->valid) {
244                     *new_elt = *old_elt;
245                     break;
246                 }
247             }
248         }
249     }
250     /* update with the new, free the old, return */
251     ht->ht_table = new_table;
252     ht->ht_capacity = new_capacity;
253     ht->ht_growth_trigger = new_capacity * ht->ht_density_numer / ht->ht_density_denom;
254     free(old_table);
255     return OPAL_SUCCESS;
256 }
257 
258 /* one of the removal functions has determined which element should be
259    removed.  With the help of the type methods this can be generic.
260    The important thing is to rehash any valid elements immediately
261    following the element-being-removed */
262 static int                      /* OPAL_ return code */
opal_hash_table_remove_elt_at(opal_hash_table_t * ht,size_t ii)263 opal_hash_table_remove_elt_at(opal_hash_table_t * ht, size_t ii)
264 {
265     size_t jj, capacity = ht->ht_capacity;
266     opal_hash_element_t* elts = ht->ht_table;
267     opal_hash_element_t * elt;
268 
269     elt = &elts[ii];
270 
271     if (! elt->valid) {
272         /* huh?  removing a not-valid element? */
273         return OPAL_ERROR;
274     }
275 
276     elt->valid = 0;
277     if (ht->ht_type_methods->elt_destructor) {
278         ht->ht_type_methods->elt_destructor(elt);
279     }
280 
281     /* need to possibly re-insert followers because of the now-gap */
282     /* E.g., XYyAaCbz.  (where upper is ideal, lower is not)
283      * remove A
284      * leaving XYy.aCbz. and we need to reconsider aCbz
285      * first a gets reinserted where it wants to be: XYya.Cbz.
286      * next  C doesn't move:                         XYya.Cbz.
287      * then  b gets put where it wants to be:        XYyabC.z.
288      * then  z moves down a little:                  XYyabCz..
289      * then  . means we're done
290      */
291     for (ii = ii+1; ; ii += 1) { /* scan immediately following elements */
292         if (ii == capacity) { ii = 0; }
293         elt = &elts[ii];
294         if (! elt->valid) {
295             break;              /* done */
296         }
297         /* rehash it and move it if necessary */
298         for (jj = ht->ht_type_methods->hash_elt(elt)%capacity; ; jj += 1) {
299             if (jj == capacity) { jj = 0; }
300             if (jj == ii) {
301                 /* already in place, either ideal or best-for-now */
302                 break;
303             } else if (! elts[jj].valid) {
304                 /* move it down, and invaildate where it came from */
305                 elts[jj] = elts[ii];
306                 elts[ii].valid = 0;
307                 break;
308             } else {
309                 /* still need to find its place */
310             }
311         }
312     }
313     ht->ht_size -= 1;
314     return OPAL_SUCCESS;
315 }
316 
317 
318 /***************************************************************************/
319 
320 static uint64_t
opal_hash_hash_elt_uint32(opal_hash_element_t * elt)321 opal_hash_hash_elt_uint32(opal_hash_element_t * elt)
322 {
323   return elt->key.u32;
324 }
325 
326 static const struct opal_hash_type_methods_t
327 opal_hash_type_methods_uint32 = {
328     NULL,
329     opal_hash_hash_elt_uint32
330 };
331 
332 int                             /* OPAL_ return code */
opal_hash_table_get_value_uint32(opal_hash_table_t * ht,uint32_t key,void ** value)333 opal_hash_table_get_value_uint32(opal_hash_table_t* ht, uint32_t key, void * *value)
334 {
335     size_t ii, capacity = ht->ht_capacity;
336     opal_hash_element_t * elt;
337 
338 #if OPAL_ENABLE_DEBUG
339     if(capacity == 0) {
340         opal_output(0, "opal_hash_table_get_value_uint32:"
341                     "opal_hash_table_init() has not been called");
342         return OPAL_ERROR;
343     }
344     if (NULL != ht->ht_type_methods &&
345         &opal_hash_type_methods_uint32 != ht->ht_type_methods) {
346         opal_output(0, "opal_hash_table_get_value_uint32:"
347                     "hash table is for a different key type");
348             return OPAL_ERROR;
349     }
350 #endif
351 
352     ht->ht_type_methods = &opal_hash_type_methods_uint32;
353     for (ii = key%capacity; ; ii += 1) {
354         if (ii == capacity) { ii = 0; }
355         elt = &ht->ht_table[ii];
356         if (! elt->valid) {
357             return OPAL_ERR_NOT_FOUND;
358         } else if (elt->key.u32 == key) {
359             *value = elt->value;
360             return OPAL_SUCCESS;
361         } else {
362             /* keey looking */
363         }
364     }
365 
366 }
367 
368 int                             /* OPAL_ return code */
opal_hash_table_set_value_uint32(opal_hash_table_t * ht,uint32_t key,void * value)369 opal_hash_table_set_value_uint32(opal_hash_table_t * ht, uint32_t key, void * value)
370 {
371     int rc;
372     size_t ii, capacity = ht->ht_capacity;
373     opal_hash_element_t * elt;
374 
375 #if OPAL_ENABLE_DEBUG
376     if(capacity == 0) {
377         opal_output(0, "opal_hash_table_set_value_uint32:"
378                    "opal_hash_table_init() has not been called");
379         return OPAL_ERR_BAD_PARAM;
380     }
381     if (NULL != ht->ht_type_methods &&
382         &opal_hash_type_methods_uint32 != ht->ht_type_methods) {
383         opal_output(0, "opal_hash_table_set_value_uint32:"
384                     "hash table is for a different key type");
385             return OPAL_ERROR;
386     }
387 #endif
388 
389     ht->ht_type_methods = &opal_hash_type_methods_uint32;
390     for (ii = key%capacity; ; ii += 1) {
391         if (ii == capacity) { ii = 0; }
392         elt = &ht->ht_table[ii];
393         if (! elt->valid) {
394             /* new entry */
395             elt->key.u32 = key;
396             elt->value = value;
397             elt->valid = 1;
398             ht->ht_size += 1;
399             if (ht->ht_size >= ht->ht_growth_trigger) {
400                 if (OPAL_SUCCESS != (rc = opal_hash_grow(ht))) {
401                     return rc;
402                 }
403             }
404             return OPAL_SUCCESS;
405         } else if (elt->key.u32 == key) {
406             /* replace existing element */
407             elt->value = value;
408             return OPAL_SUCCESS;
409         } else {
410             /* keep looking */
411         }
412     }
413 }
414 
415 int
opal_hash_table_remove_value_uint32(opal_hash_table_t * ht,uint32_t key)416 opal_hash_table_remove_value_uint32(opal_hash_table_t * ht, uint32_t key)
417 {
418     size_t ii, capacity = ht->ht_capacity;
419 
420 #if OPAL_ENABLE_DEBUG
421     if(capacity == 0) {
422         opal_output(0, "opal_hash_table_get_value_uint32:"
423                     "opal_hash_table_init() has not been called");
424         return OPAL_ERROR;
425     }
426     if (NULL != ht->ht_type_methods &&
427         &opal_hash_type_methods_uint32 != ht->ht_type_methods) {
428         opal_output(0, "opal_hash_table_remove_value_uint32:"
429                     "hash table is for a different key type");
430             return OPAL_ERROR;
431     }
432 #endif
433 
434     ht->ht_type_methods = &opal_hash_type_methods_uint32;
435     for (ii = key%capacity; ; ii += 1) {
436         opal_hash_element_t * elt;
437         if (ii == capacity) ii = 0;
438         elt = &ht->ht_table[ii];
439         if (! elt->valid) {
440             return OPAL_ERR_NOT_FOUND;
441         } else if (elt->key.u32 == key) {
442             return opal_hash_table_remove_elt_at(ht, ii);
443         } else {
444             /* keep looking */
445         }
446     }
447 }
448 
449 
450 /***************************************************************************/
451 
452 
453 static uint64_t
opal_hash_hash_elt_uint64(opal_hash_element_t * elt)454 opal_hash_hash_elt_uint64(opal_hash_element_t * elt)
455 {
456   return elt->key.u64;
457 }
458 
459 static const struct opal_hash_type_methods_t
460 opal_hash_type_methods_uint64 = {
461     NULL,
462     opal_hash_hash_elt_uint64
463 };
464 
465 int                             /* OPAL_ return code */
opal_hash_table_get_value_uint64(opal_hash_table_t * ht,uint64_t key,void ** value)466 opal_hash_table_get_value_uint64(opal_hash_table_t * ht, uint64_t key, void * *value)
467 {
468     size_t ii;
469     size_t capacity = ht->ht_capacity;
470     opal_hash_element_t * elt;
471 
472 #if OPAL_ENABLE_DEBUG
473     if(capacity == 0) {
474         opal_output(0, "opal_hash_table_get_value_uint64:"
475                    "opal_hash_table_init() has not been called");
476         return OPAL_ERROR;
477     }
478     if (NULL != ht->ht_type_methods &&
479         &opal_hash_type_methods_uint64 != ht->ht_type_methods) {
480         opal_output(0, "opal_hash_table_get_value_uint64:"
481                     "hash table is for a different key type");
482             return OPAL_ERROR;
483     }
484 #endif
485 
486     ht->ht_type_methods = &opal_hash_type_methods_uint64;
487     for (ii = key%capacity; ; ii += 1) {
488         if (ii == capacity) { ii = 0; }
489         elt = &ht->ht_table[ii];
490         if (! elt->valid) {
491             return OPAL_ERR_NOT_FOUND;
492         } else if (elt->key.u64 == key) {
493             *value = elt->value;
494             return OPAL_SUCCESS;
495         } else {
496             /* keep looking */
497         }
498     }
499 
500 }
501 
502 int                             /* OPAL_ return code */
opal_hash_table_set_value_uint64(opal_hash_table_t * ht,uint64_t key,void * value)503 opal_hash_table_set_value_uint64(opal_hash_table_t * ht, uint64_t key, void * value)
504 {
505     int rc;
506     size_t ii, capacity = ht->ht_capacity;
507     opal_hash_element_t * elt;
508 
509 #if OPAL_ENABLE_DEBUG
510     if(capacity == 0) {
511         opal_output(0, "opal_hash_table_set_value_uint64:"
512                    "opal_hash_table_init() has not been called");
513         return OPAL_ERR_BAD_PARAM;
514     }
515     if (NULL != ht->ht_type_methods &&
516         &opal_hash_type_methods_uint64 != ht->ht_type_methods) {
517         opal_output(0, "opal_hash_table_set_value_uint64:"
518                     "hash table is for a different key type");
519             return OPAL_ERROR;
520     }
521 #endif
522 
523     ht->ht_type_methods = &opal_hash_type_methods_uint64;
524     for (ii = key%capacity; ; ii += 1) {
525         if (ii == capacity) { ii = 0; }
526         elt = &ht->ht_table[ii];
527         if (! elt->valid) {
528             /* new entry */
529             elt->key.u64 = key;
530             elt->value = value;
531             elt->valid = 1;
532             ht->ht_size += 1;
533             if (ht->ht_size >= ht->ht_growth_trigger) {
534                 if (OPAL_SUCCESS != (rc = opal_hash_grow(ht))) {
535                     return rc;
536                 }
537             }
538             return OPAL_SUCCESS;
539         } else if (elt->key.u64 == key) {
540             elt->value = value;
541             return OPAL_SUCCESS;
542         } else {
543             /* keep looking */
544         }
545     }
546 }
547 
548 
549 int                             /* OPAL_ return code */
opal_hash_table_remove_value_uint64(opal_hash_table_t * ht,uint64_t key)550 opal_hash_table_remove_value_uint64(opal_hash_table_t * ht, uint64_t key)
551 {
552     size_t ii, capacity = ht->ht_capacity;
553 
554 #if OPAL_ENABLE_DEBUG
555     if(capacity == 0) {
556         opal_output(0, "opal_hash_table_get_value_uint64:"
557                     "opal_hash_table_init() has not been called");
558         return OPAL_ERROR;
559     }
560     if (NULL != ht->ht_type_methods &&
561         &opal_hash_type_methods_uint64 != ht->ht_type_methods) {
562         opal_output(0, "opal_hash_table_remove_value_uint64:"
563                     "hash table is for a different key type");
564             return OPAL_ERROR;
565     }
566 #endif
567 
568     ht->ht_type_methods = &opal_hash_type_methods_uint64;
569     for (ii = key%capacity; ; ii += 1) {
570         opal_hash_element_t * elt;
571         if (ii == capacity) { ii = 0; }
572         elt = &ht->ht_table[ii];
573         if (! elt->valid) {
574             return OPAL_ERR_NOT_FOUND;
575         } else if (elt->key.u64 == key) {
576             return opal_hash_table_remove_elt_at(ht, ii);
577         } else {
578             /* keep looking */
579         }
580     }
581 }
582 
583 
584 /***************************************************************************/
585 
586 /* helper function used in several places */
587 static uint64_t
opal_hash_hash_key_ptr(const void * key,size_t key_size)588 opal_hash_hash_key_ptr(const void * key, size_t key_size)
589 {
590     uint64_t hash;
591     const unsigned char *scanner;
592     size_t ii;
593 
594     hash = 0;
595     scanner = (const unsigned char *)key;
596     for (ii = 0; ii < key_size; ii += 1) {
597         hash = HASH_MULTIPLIER*hash + *scanner++;
598     }
599     return hash;
600 }
601 
602 /* ptr methods */
603 
604 static void
opal_hash_destruct_elt_ptr(opal_hash_element_t * elt)605 opal_hash_destruct_elt_ptr(opal_hash_element_t * elt)
606 {
607     elt->key.ptr.key_size = 0;
608     void * key = (void *) elt->key.ptr.key; /* cast away const so we can free it */
609     if (NULL != key) {
610         elt->key.ptr.key = NULL;
611         free(key);
612     }
613 }
614 
615 static uint64_t
opal_hash_hash_elt_ptr(opal_hash_element_t * elt)616 opal_hash_hash_elt_ptr(opal_hash_element_t * elt)
617 {
618     return opal_hash_hash_key_ptr(elt->key.ptr.key, elt->key.ptr.key_size);
619 }
620 
621 static const struct opal_hash_type_methods_t
622 opal_hash_type_methods_ptr = {
623     opal_hash_destruct_elt_ptr,
624     opal_hash_hash_elt_ptr
625 };
626 
627 int                             /* OPAL_ return code */
opal_hash_table_get_value_ptr(opal_hash_table_t * ht,const void * key,size_t key_size,void ** value)628 opal_hash_table_get_value_ptr(opal_hash_table_t * ht,
629                               const void * key, size_t key_size,
630                               void * *value)
631 {
632     size_t ii, capacity = ht->ht_capacity;
633     opal_hash_element_t * elt;
634 
635 #if OPAL_ENABLE_DEBUG
636     if(capacity == 0) {
637         opal_output(0, "opal_hash_table_get_value_ptr:"
638                    "opal_hash_table_init() has not been called");
639         return OPAL_ERROR;
640     }
641     if (NULL != ht->ht_type_methods &&
642         &opal_hash_type_methods_ptr != ht->ht_type_methods) {
643         opal_output(0, "opal_hash_table_get_value_ptr:"
644                     "hash table is for a different key type");
645             return OPAL_ERROR;
646     }
647 #endif
648 
649     ht->ht_type_methods = &opal_hash_type_methods_ptr;
650     for (ii = opal_hash_hash_key_ptr(key, key_size)%capacity; ; ii += 1) {
651         if (ii == capacity) { ii = 0; }
652         elt = &ht->ht_table[ii];
653         if (! elt->valid) {
654             return OPAL_ERR_NOT_FOUND;
655         } else if (elt->key.ptr.key_size == key_size &&
656                    0 == memcmp(elt->key.ptr.key, key, key_size)) {
657             *value = elt->value;
658             return OPAL_SUCCESS;
659         } else {
660             /* keep going */
661         }
662     }
663 }
664 
665 int                             /* OPAL_ return code */
opal_hash_table_set_value_ptr(opal_hash_table_t * ht,const void * key,size_t key_size,void * value)666 opal_hash_table_set_value_ptr(opal_hash_table_t * ht,
667                               const void * key, size_t key_size,
668                               void * value)
669 {
670     int rc;
671     size_t ii, capacity = ht->ht_capacity;
672     opal_hash_element_t * elt;
673 
674 #if OPAL_ENABLE_DEBUG
675     if(capacity == 0) {
676         opal_output(0, "opal_hash_table_set_value_ptr:"
677                    "opal_hash_table_init() has not been called");
678         return OPAL_ERR_BAD_PARAM;
679     }
680     if (NULL != ht->ht_type_methods &&
681         &opal_hash_type_methods_ptr != ht->ht_type_methods) {
682         opal_output(0, "opal_hash_table_set_value_ptr:"
683                     "hash table is for a different key type");
684             return OPAL_ERROR;
685     }
686 #endif
687 
688     ht->ht_type_methods = &opal_hash_type_methods_ptr;
689     for (ii = opal_hash_hash_key_ptr(key, key_size)%capacity; ; ii += 1) {
690         if (ii == capacity) { ii = 0; }
691         elt = &ht->ht_table[ii];
692         if (! elt->valid) {
693             /* new entry */
694             void * key_local = malloc(key_size);
695             memcpy(key_local, key, key_size);
696             elt->key.ptr.key      = key_local;
697             elt->key.ptr.key_size = key_size;
698             elt->value = value;
699             elt->valid = 1;
700             ht->ht_size += 1;
701             if (ht->ht_size >= ht->ht_growth_trigger) {
702                 if (OPAL_SUCCESS != (rc = opal_hash_grow(ht))) {
703                     return rc;
704                 }
705             }
706             return OPAL_SUCCESS;
707         } else if (elt->key.ptr.key_size == key_size &&
708                    0 == memcmp(elt->key.ptr.key, key, key_size)) {
709             /* replace existing value */
710             elt->value = value;
711             return OPAL_SUCCESS;
712         } else {
713             /* keep looking */
714         }
715     }
716 }
717 
718 int                             /* OPAL_ return code */
opal_hash_table_remove_value_ptr(opal_hash_table_t * ht,const void * key,size_t key_size)719 opal_hash_table_remove_value_ptr(opal_hash_table_t * ht,
720                                  const void * key, size_t key_size)
721 {
722     size_t ii, capacity = ht->ht_capacity;
723 
724 #if OPAL_ENABLE_DEBUG
725     if(capacity == 0) {
726         opal_output(0, "opal_hash_table_get_value_ptr:"
727                     "opal_hash_table_init() has not been called");
728         return OPAL_ERROR;
729     }
730     if (NULL != ht->ht_type_methods &&
731         &opal_hash_type_methods_ptr != ht->ht_type_methods) {
732         opal_output(0, "opal_hash_table_remove_value_ptr:"
733                     "hash table is for a different key type");
734             return OPAL_ERROR;
735     }
736 #endif
737 
738     ht->ht_type_methods = &opal_hash_type_methods_ptr;
739     for (ii = opal_hash_hash_key_ptr(key, key_size)%capacity; ; ii += 1) {
740         opal_hash_element_t * elt;
741         if (ii == capacity) { ii = 0; }
742         elt = &ht->ht_table[ii];
743         if (! elt->valid) {
744             return OPAL_ERR_NOT_FOUND;
745         } else if (elt->key.ptr.key_size == key_size &&
746                    0 == memcmp(elt->key.ptr.key, key, key_size)) {
747             return opal_hash_table_remove_elt_at(ht, ii);
748         } else {
749             /* keep looking */
750         }
751     }
752 }
753 
754 /***************************************************************************/
755 /* Traversals */
756 
757 static int                      /* OPAL_ return code */
opal_hash_table_get_next_elt(opal_hash_table_t * ht,opal_hash_element_t * prev_elt,opal_hash_element_t ** next_elt)758 opal_hash_table_get_next_elt(opal_hash_table_t *ht,
759                              opal_hash_element_t * prev_elt, /* NULL means find first */
760                              opal_hash_element_t * *next_elt)
761 {
762   opal_hash_element_t* elts = ht->ht_table;
763   size_t ii, capacity = ht->ht_capacity;
764 
765   for (ii = (NULL == prev_elt ? 0 : (prev_elt-elts)+1); ii < capacity; ii += 1) {
766     opal_hash_element_t * elt = &elts[ii];
767     if (elt->valid) {
768       *next_elt = elt;
769       return OPAL_SUCCESS;
770     }
771   }
772   return OPAL_ERROR;
773 }
774 
775 int                             /* OPAL_ return code */
opal_hash_table_get_first_key_uint32(opal_hash_table_t * ht,uint32_t * key,void ** value,void ** node)776 opal_hash_table_get_first_key_uint32(opal_hash_table_t * ht,
777                                      uint32_t *key, void * *value,
778                                      void * *node)
779 {
780   return opal_hash_table_get_next_key_uint32(ht, key, value, NULL, node);
781 }
782 
783 int                             /* OPAL_ return code */
opal_hash_table_get_next_key_uint32(opal_hash_table_t * ht,uint32_t * key,void ** value,void * in_node,void ** out_node)784 opal_hash_table_get_next_key_uint32(opal_hash_table_t * ht,
785                                     uint32_t *key, void * *value,
786                                     void * in_node, void * *out_node)
787 {
788   opal_hash_element_t * elt;
789   if (OPAL_SUCCESS == opal_hash_table_get_next_elt(ht, (opal_hash_element_t *) in_node, &elt)) {
790     *key       = elt->key.u32;
791     *value     = elt->value;
792     *out_node  = elt;
793     return OPAL_SUCCESS;
794   }
795   return OPAL_ERROR;
796 }
797 
798 int                             /* OPAL_ return code */
opal_hash_table_get_first_key_ptr(opal_hash_table_t * ht,void ** key,size_t * key_size,void ** value,void ** node)799 opal_hash_table_get_first_key_ptr(opal_hash_table_t * ht,
800                                   void * *key, size_t *key_size, void * *value,
801                                   void * *node)
802 {
803   return opal_hash_table_get_next_key_ptr(ht, key, key_size, value, NULL, node);
804 }
805 
806 int                             /* OPAL_ return code */
opal_hash_table_get_next_key_ptr(opal_hash_table_t * ht,void ** key,size_t * key_size,void ** value,void * in_node,void ** out_node)807 opal_hash_table_get_next_key_ptr(opal_hash_table_t * ht,
808                                  void * *key, size_t *key_size, void * *value,
809                                  void * in_node, void * *out_node)
810 {
811   opal_hash_element_t * elt;
812   if (OPAL_SUCCESS == opal_hash_table_get_next_elt(ht, (opal_hash_element_t *) in_node, &elt)) {
813     *key       = (void *)elt->key.ptr.key;
814     *key_size  = elt->key.ptr.key_size;
815     *value     = elt->value;
816     *out_node  = elt;
817     return OPAL_SUCCESS;
818   }
819   return OPAL_ERROR;
820 }
821 
822 int                             /* OPAL_ return code */
opal_hash_table_get_first_key_uint64(opal_hash_table_t * ht,uint64_t * key,void ** value,void ** node)823 opal_hash_table_get_first_key_uint64(opal_hash_table_t * ht,
824                                      uint64_t *key, void * *value,
825                                      void * *node)
826 {
827   return opal_hash_table_get_next_key_uint64(ht, key, value, NULL, node);
828 }
829 
830 int                             /* OPAL_ return code */
opal_hash_table_get_next_key_uint64(opal_hash_table_t * ht,uint64_t * key,void ** value,void * in_node,void ** out_node)831 opal_hash_table_get_next_key_uint64(opal_hash_table_t * ht,
832                                     uint64_t *key, void * *value,
833                                     void * in_node, void * *out_node)
834 {
835   opal_hash_element_t * elt;
836   if (OPAL_SUCCESS == opal_hash_table_get_next_elt(ht, (opal_hash_element_t *) in_node, &elt)) {
837     *key       = elt->key.u64;
838     *value     = elt->value;
839     *out_node  = elt;
840     return OPAL_SUCCESS;
841   }
842   return OPAL_ERROR;
843 }
844 
845 /* there was/is no traversal for the ptr case; it would go here */
846 /* interact with the class-like mechanism */
847 
848 static void opal_proc_table_construct(opal_proc_table_t* pt);
849 static void opal_proc_table_destruct(opal_proc_table_t* pt);
850 
851 OBJ_CLASS_INSTANCE(
852     opal_proc_table_t,
853     opal_hash_table_t,
854     opal_proc_table_construct,
855     opal_proc_table_destruct
856 );
857 
858 static void
opal_proc_table_construct(opal_proc_table_t * pt)859 opal_proc_table_construct(opal_proc_table_t* pt)
860 {
861   pt->pt_size = 0;
862 }
863 
864 static void
opal_proc_table_destruct(opal_proc_table_t * pt)865 opal_proc_table_destruct(opal_proc_table_t* pt)
866 {
867 }
868 
869 /*
870  * Init, etc
871  */
872 
opal_proc_table_init(opal_proc_table_t * pt,size_t jobids,size_t vpids)873 int opal_proc_table_init(opal_proc_table_t* pt, size_t jobids, size_t vpids) {
874     int rc;
875     if (OPAL_SUCCESS != (rc=opal_hash_table_init(&pt->super, jobids))) {
876         return rc;
877     }
878     pt->vpids_size = vpids;
879     return OPAL_SUCCESS;
880 }
881 
opal_proc_table_remove_all(opal_proc_table_t * pt)882 int opal_proc_table_remove_all(opal_proc_table_t *pt) {
883     int rc;
884     opal_hash_table_t * vpids;
885     uint32_t jobid;
886     void * node;
887 
888     rc = opal_hash_table_get_first_key_uint32(&pt->super, &jobid, (void **)&vpids, &node);
889 
890     if (OPAL_SUCCESS == rc) {
891         do {
892             if (NULL != vpids) {
893                 opal_hash_table_remove_all(vpids);
894                 OBJ_RELEASE(vpids);
895             }
896             rc = opal_hash_table_get_next_key_uint32 (&pt->super, &jobid,
897                                                (void **) &vpids, node, &node);
898         } while (OPAL_SUCCESS == rc);
899     }
900 
901     return rc;
902 }
903 
opal_proc_table_get_value(opal_proc_table_t * pt,opal_process_name_t key,void ** ptr)904 int opal_proc_table_get_value(opal_proc_table_t* pt, opal_process_name_t key,
905                               void** ptr) {
906     int rc;
907     opal_hash_table_t * vpids;
908     rc = opal_hash_table_get_value_uint32(&pt->super, key.jobid, (void **)&vpids);
909     if (rc != OPAL_SUCCESS) {
910         return rc;
911     }
912     rc = opal_hash_table_get_value_uint32(vpids, key.vpid, ptr);
913     return rc;
914 }
915 
opal_proc_table_set_value(opal_proc_table_t * pt,opal_process_name_t key,void * value)916 int opal_proc_table_set_value(opal_proc_table_t* pt, opal_process_name_t key, void* value) {
917     int rc;
918     opal_hash_table_t * vpids;
919     rc = opal_hash_table_get_value_uint32(&pt->super, key.jobid, (void **)&vpids);
920     if (rc != OPAL_SUCCESS) {
921         vpids = OBJ_NEW(opal_hash_table_t);
922         if (NULL == vpids) {
923             return OPAL_ERR_OUT_OF_RESOURCE;
924         }
925         if (OPAL_SUCCESS != (rc=opal_hash_table_init(vpids, pt->vpids_size))) {
926             OBJ_RELEASE(vpids);
927             return rc;
928         }
929         if (OPAL_SUCCESS != (rc=opal_hash_table_set_value_uint32(&pt->super, key.jobid, vpids))) {
930             OBJ_RELEASE(vpids);
931             return rc;
932         }
933     }
934     rc = opal_hash_table_set_value_uint32(vpids, key.vpid, value);
935     return rc;
936 }
937 
opal_proc_table_remove_value(opal_proc_table_t * pt,opal_process_name_t key)938 int opal_proc_table_remove_value(opal_proc_table_t* pt, opal_process_name_t key) {
939     int rc;
940     opal_hash_table_t * vpids;
941     if (OPAL_SUCCESS != (rc=opal_hash_table_get_value_uint32(&pt->super, key.jobid, (void **)&vpids))) {
942         return rc;
943     }
944     if (OPAL_SUCCESS == (rc=opal_hash_table_remove_value_uint32(vpids, key.vpid))) {
945         if (0 == vpids->ht_size) {
946             opal_hash_table_remove_value_uint32(&pt->super, key.jobid);
947             OBJ_RELEASE(vpids);
948         }
949     }
950     return rc;
951 }
952 
opal_proc_table_get_first_key(opal_proc_table_t * pt,opal_process_name_t * key,void ** value,void ** node1,void ** node2)953 int opal_proc_table_get_first_key(opal_proc_table_t *pt, opal_process_name_t *key,
954                                   void **value, void **node1, void **node2) {
955     int rc;
956     uint32_t jobid, vpid;
957     opal_hash_table_t * vpids;
958     if (OPAL_SUCCESS != (rc=opal_hash_table_get_first_key_uint32(&pt->super, &jobid, (void **)&vpids, node1))) {
959         return rc;
960     }
961     rc = opal_hash_table_get_first_key_uint32(vpids, &vpid, value, node2);
962     if (OPAL_SUCCESS == rc) {
963         key->jobid = jobid;
964         key->vpid = vpid;
965     }
966     return rc;
967 }
968 
opal_proc_table_get_next_key(opal_proc_table_t * pt,opal_process_name_t * key,void ** value,void * in_node1,void ** out_node1,void * in_node2,void ** out_node2)969 int opal_proc_table_get_next_key(opal_proc_table_t *pt, opal_process_name_t *key,
970                                  void **value, void *in_node1, void **out_node1,
971                                  void *in_node2, void **out_node2) {
972     int rc;
973     uint32_t jobid = ((opal_hash_element_t *)in_node1)->key.u32;
974     uint32_t vpid;
975     opal_hash_table_t * vpids = ((opal_hash_element_t *)in_node1)->value;
976 
977     rc = opal_hash_table_get_next_key_uint32(vpids, &vpid, value, in_node2, out_node2);
978     if (OPAL_SUCCESS == rc) {
979         key->jobid = jobid;
980         key->vpid = vpid;
981         *out_node1 = in_node1;
982         return rc;
983     }
984     if (OPAL_SUCCESS != (rc=opal_hash_table_get_next_key_uint32(&pt->super, &jobid, (void **)&vpids, in_node1, out_node1))) {
985         return rc;
986     }
987     rc = opal_hash_table_get_first_key_uint32(vpids, &vpid, value, out_node2);
988     if (OPAL_SUCCESS == rc) {
989         key->jobid = jobid;
990         key->vpid = vpid;
991     }
992     return rc;
993 }
994