/* RETURN a pointer to an element with the given key (if a hash is not unique and there're many elements with this key - the "first" matching element) NULL if nothing is found MY_ERRPTR if OOM NOTE see my_lsearch() for pin usage notes */ void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen) { LF_SLIST * volatile *el, *found; uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen); bucket= hashnr % hash->size; lf_rwlock_by_pins(pins); el= _lf_dynarray_lvalue(&hash->array, bucket); if (unlikely(!el)) return MY_ERRPTR; if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) return MY_ERRPTR; found= my_lsearch(el, hash->charset, my_reverse_bits(hashnr) | 1, (uchar *)key, keylen, pins); lf_rwunlock_by_pins(pins); return found ? found+1 : 0; }
// old public method static inline int qt_hash_remove(qt_hash h, const qt_key_t key) { size_t bucket; uint64_t lkey = (uint64_t)(uintptr_t)(h->op_hash(key)); HASH_KEY(lkey); bucket = lkey % h->size; if (h->B[bucket] == UNINITIALIZED) { initialize_bucket(h, bucket); } if (!qt_lf_list_delete(&(h->B[bucket]), so_regularkey(lkey), key, h->op_equals, h->op_cleanup)) { return 0; } qthread_incr(&h->count, -1); return 1; }
/* RETURN a pointer to an element with the given key (if a hash is not unique and there're many elements with this key - the "first" matching element) NULL if nothing is found NOTE see l_search() for pin usage notes */ void *lf_hash_search_using_hash_value(LF_HASH *hash, LF_PINS *pins, my_hash_value_type hashnr, const void *key, uint keylen) { LF_SLIST * volatile *el, *found; uint bucket; /* hide OOM errors - if we cannot initalize a bucket, try the previous one */ for (bucket= hashnr % hash->size; ;bucket= my_clear_highest_bit(bucket)) { el= lf_dynarray_lvalue(&hash->array, bucket); if (el && (*el || initialize_bucket(hash, el, bucket, pins) == 0)) break; if (unlikely(bucket == 0)) return 0; /* if there's no bucket==0, the hash is empty */ } found= l_search(el, hash->charset, my_reverse_bits(hashnr) | 1, (uchar *)key, keylen, pins); return found ? found+1 : 0; }
// old public method; added last param to distinguish between put and put if absent static inline hash_entry *qt_hash_put(qt_hash h, qt_key_t key, void *value, int put_choice) { hash_entry *node = qpool_alloc(hash_entry_pool); hash_entry *ret = node; size_t bucket; uint64_t lkey = (uint64_t)(uintptr_t)(h->op_hash(key)); HASH_KEY(lkey); bucket = lkey % h->size; assert(node); assert((lkey & MSB) == 0); node->hashed_key = so_regularkey(lkey); node->key = key; // Also store original key! node->value = value; node->next = (hash_entry*)UNINITIALIZED; if (h->B[bucket] == UNINITIALIZED) { initialize_bucket(h, bucket); } if(put_choice == PUT_IF_ABSENT) { if (!qt_lf_list_insert(&(h->B[bucket]), node, NULL, &ret, h->op_equals)) { qpool_free(hash_entry_pool, node); return ret->value; } } else { qt_lf_force_list_insert(&(h->B[bucket]), node, h->op_equals); } size_t csize = h->size; if (qthread_incr(&h->count, 1) / csize > MAX_LOAD) { if (2 * csize <= hard_max_buckets) { // this caps the size of the hash qthread_cas(&h->size, csize, 2 * csize); } } return ret->value; }
/** Iterate over all elements in hash and call function with the element @note If one of 'action' invocations returns 1 the iteration aborts. 'action' might see some elements twice! @retval 0 ok @retval 1 error (action returned 1) */ int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins, my_hash_walk_action action, void *argument) { CURSOR cursor; uint bucket= 0; int res; LF_SLIST * volatile *el; el= lf_dynarray_lvalue(&hash->array, bucket); if (unlikely(!el)) return 0; /* if there's no bucket==0, the hash is empty */ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) return 0; /* if there's no bucket==0, the hash is empty */ res= l_find(el, 0, 0, (uchar*)argument, 0, &cursor, pins, action); lf_unpin(pins, 2); lf_unpin(pins, 1); lf_unpin(pins, 0); return res; }
static void initialize_bucket(qt_hash h, size_t bucket) { size_t parent = GET_PARENT(bucket); marked_ptr_t cur; if (h->B[parent] == UNINITIALIZED) { initialize_bucket(h, parent); } hash_entry *dummy = qpool_alloc(hash_entry_pool); assert(dummy); dummy->hashed_key = so_dummykey(bucket); dummy->key = NULL; dummy->value = NULL; dummy->next = (hash_entry*)UNINITIALIZED; if (!qt_lf_list_insert(&(h->B[parent]), dummy, &cur, NULL, h->op_equals)) { qpool_free(hash_entry_pool, dummy); dummy = PTR_OF(cur); while (h->B[bucket] != CONSTRUCT(0, dummy)) ; } else { h->B[bucket] = CONSTRUCT(0, dummy); } }
/* DESCRIPTION deletes an element with the given key from the hash (if a hash is not unique and there're many elements with this key - the "first" matching element is deleted) RETURN 0 - deleted 1 - didn't (not found) NOTE see l_delete() for pin usage notes */ int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen) { LF_SLIST * volatile *el; uint bucket, hashnr; hashnr= hash->hash_function(hash->charset, (uchar *)key, keylen) & INT_MAX32; /* hide OOM errors - if we cannot initalize a bucket, try the previous one */ for (bucket= hashnr % hash->size; ;bucket= my_clear_highest_bit(bucket)) { el= lf_dynarray_lvalue(&hash->array, bucket); if (el && (*el || initialize_bucket(hash, el, bucket, pins) == 0)) break; if (unlikely(bucket == 0)) return 1; /* if there's no bucket==0, the hash is empty */ } if (l_delete(el, hash->charset, my_reverse_bits(hashnr) | 1, (uchar *)key, keylen, pins)) { return 1; } my_atomic_add32(&hash->count, -1); return 0; }