/* 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) -1 - out of memory NOTE see ldelete() 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= 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 -1; /* note that we still need to initialize_bucket here, we cannot return "node not found", because an old bucket of that node may've been split and the node was assigned to a new bucket that was never accessed before and thus is not initialized. */ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) return -1; if (ldelete(el, hash->charset, my_reverse_bits(hashnr) | 1, (uchar *)key, keylen, pins)) { lf_rwunlock_by_pins(pins); return 1; } my_atomic_add32(&hash->count, -1); lf_rwunlock_by_pins(pins); return 0; }
/* RETURN 0 - ok -1 - out of memory */ static int initialize_bucket(LF_HASH *hash, LF_SLIST * volatile *node, uint bucket, LF_PINS *pins) { uint parent= my_clear_highest_bit(bucket); LF_SLIST *dummy= (LF_SLIST *)my_malloc(sizeof(LF_SLIST), MYF(MY_WME)); LF_SLIST **tmp= 0, *cur; LF_SLIST * volatile *el= _lf_dynarray_lvalue(&hash->array, parent); if (unlikely(!el || !dummy)) return -1; if (*el == NULL && bucket && unlikely(initialize_bucket(hash, el, parent, pins))) return -1; dummy->hashnr= my_reverse_bits(bucket) | 0; /* dummy node */ dummy->key= dummy_key; dummy->keylen= 0; if ((cur= linsert(el, hash->charset, dummy, pins, LF_HASH_UNIQUE))) { my_free(dummy); dummy= cur; } my_atomic_casptr((void **)node, (void **)(char*) &tmp, dummy); /* note that if the CAS above failed (after linsert() succeeded), it would mean that some other thread has executed linsert() for the same dummy node, its linsert() failed, it picked up our dummy node (in "dummy= cur") and executed the same CAS as above. Which means that even if CAS above failed we don't need to retry, and we should not free(dummy) - there's no memory leak here */ return 0; }
/* DESCRIPTION inserts a new element to a hash. it will have a _copy_ of data, not a pointer to it. RETURN 0 - inserted 1 - didn't (unique key conflict) -1 - out of memory NOTE see linsert() for pin usage notes */ int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data) { int csize, bucket, hashnr; LF_SLIST *node, * volatile *el; lf_rwlock_by_pins(pins); node= (LF_SLIST *)_lf_alloc_new(pins); if (unlikely(!node)) return -1; memcpy(node+1, data, hash->element_size); node->key= hash_key(hash, (uchar *)(node+1), &node->keylen); hashnr= calc_hash(hash, node->key, node->keylen); bucket= hashnr % hash->size; el= _lf_dynarray_lvalue(&hash->array, bucket); if (unlikely(!el)) return -1; if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) return -1; node->hashnr= my_reverse_bits(hashnr) | 1; /* normal node */ if (linsert(el, hash->charset, node, pins, hash->flags)) { _lf_alloc_free(pins, node); lf_rwunlock_by_pins(pins); return 1; } csize= hash->size; if ((my_atomic_add32(&hash->count, 1)+1.0) / csize > MAX_LOAD) my_atomic_cas32(&hash->size, &csize, csize*2); lf_rwunlock_by_pins(pins); return 0; }
void *lf_hash_random_match(LF_HASH *hash, LF_PINS *pins, lf_hash_match_func *match, uint rand_val) { /* Convert random value to valid hash value. */ uint hashnr= (rand_val & INT_MAX32); uint bucket; uint32 rev_hashnr; LF_SLIST * volatile *el; CURSOR cursor; int res; bucket= hashnr % hash->size; rev_hashnr= my_reverse_bits(hashnr); el= lf_dynarray_lvalue(&hash->array, bucket); if (unlikely(!el)) return MY_ERRPTR; /* Bucket might be totally empty if it has not been accessed since last time LF_HASH::size has been increased. In this case we initialize it by inserting dummy node for this bucket to the correct position in split-ordered list. This should help future lf_hash_* calls trying to access the same bucket. */ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) return MY_ERRPTR; /* To avoid bias towards the first matching element in the bucket, we start looking for elements with inversed hash value greater or equal than inversed value of our random hash. */ res= lfind_match(el, rev_hashnr | 1, UINT_MAX32, match, &cursor, pins); if (! res && hashnr != 0) { /* We have not found matching element - probably we were too close to the tail of our split-ordered list. To avoid bias against elements at the head of the list we restart our search from its head. Unless we were already searching from it. To avoid going through elements at which we have already looked twice we stop once we reach element from which we have begun our first search. */ el= lf_dynarray_lvalue(&hash->array, 0); if (unlikely(!el)) return MY_ERRPTR; res= lfind_match(el, 1, rev_hashnr, match, &cursor, pins); } if (res) lf_pin(pins, 2, cursor.curr); lf_unpin(pins, 0); lf_unpin(pins, 1); return res ? cursor.curr + 1 : 0; }
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; 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= lsearch(el, hash->charset, my_reverse_bits(hashnr) | 1, (uchar *)key, keylen, pins); return found ? found+1 : 0; }
/* 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; }
/* 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; }