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; }
/* DESCRIPTION searches for a node as identified by hashnr/keey/keylen in the list that starts from 'head' RETURN 0 - not found node - found NOTE it uses pins[0..2], on return the pin[2] keeps the node found all other pins are removed. */ static LF_SLIST *lsearch(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, const uchar *key, uint keylen, LF_PINS *pins) { CURSOR cursor; int res= lfind(head, cs, hashnr, key, keylen, &cursor, pins); if (res) lf_pin(pins, 2, cursor.curr); lf_unpin(pins, 0); lf_unpin(pins, 1); return res ? cursor.curr : 0; }
/* DESCRIPTION deletes a node as identified by hashnr/keey/keylen from the list that starts from 'head' RETURN 0 - ok 1 - not found NOTE it uses pins[0..2], on return all pins are removed. */ static int ldelete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, const uchar *key, uint keylen, LF_PINS *pins) { CURSOR cursor; int res; for (;;) { if (!lfind(head, cs, hashnr, key, keylen, &cursor, pins)) { res= 1; /* not found */ break; } else { /* mark the node deleted */ if (my_atomic_casptr((void **)&(cursor.curr->link), (void **)&cursor.next, (void *)(((intptr)cursor.next) | 1))) { /* and remove it from the list */ if (my_atomic_casptr((void **)cursor.prev, (void **)&cursor.curr, cursor.next)) lf_pinbox_free(pins, cursor.curr); else { /* somebody already "helped" us and removed the node ? Let's check if we need to help that someone too! (to ensure the number of "set DELETED flag" actions is equal to the number of "remove from the list" actions) */ lfind(head, cs, hashnr, key, keylen, &cursor, pins); } res= 0; break; } } } lf_unpin(pins, 0); lf_unpin(pins, 1); lf_unpin(pins, 2); return res; }
/** 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; }
/* DESCRIPTION insert a 'node' in the list that starts from 'head' in the correct position (as found by l_find) RETURN 0 - inserted not 0 - a pointer to a duplicate (not pinned and thus unusable) NOTE it uses pins[0..2], on return all pins are removed. if there're nodes with the same key value, a new node is added before them. */ static LF_SLIST *l_insert(LF_SLIST * volatile *head, CHARSET_INFO *cs, LF_SLIST *node, LF_PINS *pins, uint flags) { CURSOR cursor; int res; for (;;) { if (l_find(head, cs, node->hashnr, node->key, node->keylen, &cursor, pins, 0) && (flags & LF_HASH_UNIQUE)) { res= 0; /* duplicate found */ break; } else { node->link= (intptr)cursor.curr; DBUG_ASSERT(node->link != (intptr)node); /* no circular references */ DBUG_ASSERT(cursor.prev != &node->link); /* no circular references */ if (my_atomic_casptr((void **) cursor.prev, (void **)(char*) &cursor.curr, node)) { res= 1; /* inserted ok */ break; } } } lf_unpin(pins, 0); lf_unpin(pins, 1); lf_unpin(pins, 2); /* Note that cursor.curr is not pinned here and the pointer is unreliable, the object may dissapear anytime. But if it points to a dummy node, the pointer is safe, because dummy nodes are never freed - initialize_bucket() uses this fact. */ return res ? 0 : cursor.curr; }