diff --git a/src/box/memtx_hash.cc b/src/box/memtx_hash.cc index 78b732c6bfc00836484850344ccbd12f01ff3dfa..4443e3f309b3c8abbfb4921d8d3709ba6ee6b627 100644 --- a/src/box/memtx_hash.cc +++ b/src/box/memtx_hash.cc @@ -156,7 +156,7 @@ typedef uint32_t hash_t; struct hash_iterator { struct iterator base; /* Must be the first member. */ struct light_index_core *hash_table; - uint32_t h_pos; + struct light_index_iterator hitr; }; void @@ -171,13 +171,8 @@ hash_iterator_ge(struct iterator *ptr) { assert(ptr->free == hash_iterator_free); struct hash_iterator *it = (struct hash_iterator *) ptr; - - while (it->h_pos < it->hash_table->table_size) { - if (light_index_pos_valid(it->hash_table, it->h_pos)) - return light_index_get(it->hash_table, it->h_pos++); - it->h_pos++; - } - return NULL; + struct tuple **res = light_index_itr_get_and_next(it->hash_table, &it->hitr); + return res ? *res : 0; } static struct tuple * @@ -307,9 +302,8 @@ MemtxHash::replace(struct tuple *old_tuple, struct tuple *new_tuple, if (old_tuple) { uint32_t h = tuple_hash(old_tuple, key_def); - hash_t slot = light_index_find(hash_table, h, old_tuple); - assert(slot != light_index_end); - light_index_delete(hash_table, slot); + int res = light_index_delete_value(hash_table, h, old_tuple); + assert(res == 0); } return old_tuple; } @@ -327,6 +321,8 @@ MemtxHash::allocIterator() const it->base.next = hash_iterator_ge; it->base.free = hash_iterator_free; + it->hash_table = hash_table; + light_index_itr_begin(it->hash_table, &it->hitr); return (struct iterator *) it; } @@ -339,19 +335,15 @@ MemtxHash::initIterator(struct iterator *ptr, enum iterator_type type, assert(ptr->free == hash_iterator_free); struct hash_iterator *it = (struct hash_iterator *) ptr; - it->hash_table = hash_table; switch (type) { case ITER_ALL: - it->h_pos = 0; - while (it->h_pos < it->hash_table->table_size - && !light_index_pos_valid(it->hash_table, it->h_pos)) - it->h_pos++; + light_index_itr_begin(it->hash_table, &it->hitr); it->base.next = hash_iterator_ge; break; case ITER_EQ: assert(part_count > 0); - it->h_pos = light_index_find_key(hash_table, key_hash(key, key_def), key); + light_index_itr_key(it->hash_table, &it->hitr, key_hash(key, key_def), key); it->base.next = hash_iterator_eq; break; default: diff --git a/src/lib/salad/light.h b/src/lib/salad/light.h index ca3c22a01e338cafa0773b2a9a8f61466e19c2a7..d49785dbc709703f57a234f4972b7c39985cba77 100644 --- a/src/lib/salad/light.h +++ b/src/lib/salad/light.h @@ -139,7 +139,6 @@ struct LIGHT(core) { * Start of chain of empty slots */ uint32_t empty_slot; - struct LIGHT(record) *empty_record; /* additional parameter for data comparison */ LIGHT_CMP_ARG_TYPE arg; @@ -148,6 +147,17 @@ struct LIGHT(core) { struct matras mtable; }; +/** + * Iterator, for iterating all values in hash_table. + * It also may be used for restoring one value by key. + */ +struct LIGHT(iterator) { + /* Current position on table (ID of a current record) */ + uint32_t slotpos; + /* Version of matras memory. 0 for main version, nonzero for MVCC */ + uint32_t matras_version; +}; + /** * Type of functions for memory allocation and deallocation */ @@ -156,9 +166,12 @@ typedef void (*LIGHT(extent_free_t))(void *); /** * Special result of light_find that means that nothing was found + * Must be equal or greater than possible hash table size */ static const uint32_t LIGHT(end) = 0xFFFFFFFF; +/* Functions declaration */ + /** * @brief Hash table construction. Fills struct light members. * @param ht - pointer to a hash table struct @@ -226,10 +239,22 @@ LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, * @brief Delete a record from a hash table by given record ID * @param ht - pointer to a hash table struct * @param slotpos - ID of an record. See LIGHT(find) for details. + * @return 0 if ok, -1 on memory error (only with freezed iterators) */ -void +int LIGHT(delete)(struct LIGHT(core) *ht, uint32_t slotpos); +/** + * @brief Delete a record from a hash table by that value and its hash. + * @param ht - pointer to a hash table struct + * @param slotpos - ID of an record. See LIGHT(find) for details. + * @return 0 if ok, 1 if not found or -1 on memory error + * (only with freezed iterators) + */ +int +LIGHT(delete_value)(struct LIGHT(core) *ht, + uint32_t hash, LIGHT_DATA_TYPE value); + /** * @brief Get a value from a desired position * @param ht - pointer to a hash table struct @@ -248,8 +273,65 @@ LIGHT(get)(struct LIGHT(core) *ht, uint32_t slotpos); bool LIGHT(pos_valid)(struct LIGHT(core) *ht, uint32_t slotpos); +/** + * @brief Set iterator to the beginning of hash table + * @param ht - pointer to a hash table struct + * @param itr - iterator to set + */ +void +LIGHT(itr_begin)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr); + +/** + * @brief Set iterator to position determined by key + * @param ht - pointer to a hash table struct + * @param itr - iterator to set + * @param hash - hash to find + * @param data - key to find + */ +void +LIGHT(itr_key)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr, + uint32_t hash, LIGHT_KEY_TYPE data); +/** + * @brief Get the value that iterator currently points to + * @param ht - pointer to a hash table struct + * @param itr - iterator to set + * @return poiner to the value or NULL if iteration is complete + */ +LIGHT_DATA_TYPE * +LIGHT(itr_get_and_next)(const struct LIGHT(core) *ht, + struct LIGHT(iterator) *itr); +/** + * @brief Freezes state for given iterator. All following hash table modification + * will not apply to that iterator iteration. That iterator should be destroyed + * with a light_itr_destroy call after usage. + * @param ht - pointer to a hash table struct + * @param itr - iterator to freeze + * @return - 0 on success, -1 if no more version is available in matras + */ +int +LIGHT(itr_freeze)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr); + +/** + * @brief Destroy an iterator that was frozen before. Useless for not frozen + * iterators. + * @param ht - pointer to a hash table struct + * @param itr - iterator to destroy + */ +void +LIGHT(itr_destroy)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr); + +/* Functions definition */ + +/** + * @brief Hash table construction. Fills struct light members. + * @param ht - pointer to a hash table struct + * @param extent_size - size of allocating memory blocks + * @param extent_alloc_func - memory blocks allocation function + * @param extent_free_func - memory blocks allocation function + * @param arg - optional parameter to save for comparing function + */ inline void LIGHT(create)(struct LIGHT(core) *ht, size_t extent_size, LIGHT(extent_alloc_t) extent_alloc_func, @@ -261,19 +343,26 @@ LIGHT(create)(struct LIGHT(core) *ht, size_t extent_size, ht->count = 0; ht->table_size = 0; ht->empty_slot = LIGHT(end); - ht->empty_record = 0; ht->arg = arg; matras_create(&ht->mtable, extent_size, sizeof(struct LIGHT(record)), extent_alloc_func, extent_free_func); } +/** + * @brief Hash table destruction. Frees all allocated memory + * @param ht - pointer to a hash table struct + */ inline void LIGHT(destroy)(struct LIGHT(core) *ht) { matras_destroy(&ht->mtable); } +/** + * Find a slot (index in the hash table), where an item with + * given hash should be placed. + */ inline uint32_t LIGHT(slot)(const struct LIGHT(core) *ht, uint32_t hash) { @@ -286,6 +375,13 @@ LIGHT(slot)(const struct LIGHT(core) *ht, uint32_t hash) } +/** + * @brief Find a record with given hash and value + * @param ht - pointer to a hash table struct + * @param hash - hash to find + * @param data - value to find + * @return integer ID of found record or light_end if nothing found + */ inline uint32_t LIGHT(find)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value) { @@ -297,7 +393,8 @@ LIGHT(find)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value) if (record->next == slot) return LIGHT(end); while (1) { - if (record->hash == hash && LIGHT_EQUAL((record->value), (value), (ht->arg))) + if (record->hash == hash + && LIGHT_EQUAL((record->value), (value), (ht->arg))) return slot; slot = record->next; if (slot == LIGHT(end)) @@ -309,8 +406,15 @@ LIGHT(find)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value) return LIGHT(end); } +/** + * @brief Find a record with given hash and key + * @param ht - pointer to a hash table struct + * @param hash - hash to find + * @param data - key to find + * @return integer ID of found record or light_end if nothing found + */ inline uint32_t -LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value, LIGHT_DATA_TYPE *replaced) +LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key) { if (ht->count == 0) return LIGHT(end); @@ -320,11 +424,9 @@ LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value, LIG if (record->next == slot) return LIGHT(end); while (1) { - if (record->hash == hash && LIGHT_EQUAL((record->value), (value), (ht->arg))) { - *replaced = record->value; - record->value = value; + if (record->hash == hash && + LIGHT_EQUAL_KEY((record->value), (key), (ht->arg))) return slot; - } slot = record->next; if (slot == LIGHT(end)) return LIGHT(end); @@ -335,10 +437,17 @@ LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value, LIG return LIGHT(end); } - - +/** + * @brief Replace a record with given hash and value + * @param ht - pointer to a hash table struct + * @param hash - hash to find + * @param data - value to find and replace + * @param replaced - pointer to a value that was stored in table before replace + * @return integer ID of found record or light_end if nothing found + */ inline uint32_t -LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key) +LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, + LIGHT_DATA_TYPE value, LIGHT_DATA_TYPE *replaced) { if (ht->count == 0) return LIGHT(end); @@ -348,8 +457,16 @@ LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key) if (record->next == slot) return LIGHT(end); while (1) { - if (record->hash == hash && LIGHT_EQUAL_KEY((record->value), (key), (ht->arg))) + if (record->hash == hash + && LIGHT_EQUAL((record->value), (value), (ht->arg))) { + record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, slot); + if (!record) + return LIGHT(end); + *replaced = record->value; + record->value = value; return slot; + } slot = record->next; if (slot == LIGHT(end)) return LIGHT(end); @@ -360,81 +477,138 @@ LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key) return LIGHT(end); } +/* + * Empty records (that do not store value) are linked into doubly linked list. + * Get an slot of the previous record in that list. + */ inline uint32_t LIGHT(get_empty_prev)(struct LIGHT(record) *record) { return record->hash; } +/* + * Empty records (that do not store value) are linked into doubly linked list. + * Set an slot of the previous record in that list. + */ inline void LIGHT(set_empty_prev)(struct LIGHT(record) *record, uint32_t pos) { record->hash = pos; } +/* + * Empty records (that do not store value) are linked into doubly linked list. + * Get an slot of the next record in that list. + */ inline uint32_t LIGHT(get_empty_next)(struct LIGHT(record) *record) { return (uint32_t)(uint64_t)record->value; } +/* + * Empty records (that do not store value) are linked into doubly linked list. + * Get an slot of the next record in that list. + */ inline void LIGHT(set_empty_next)(struct LIGHT(record) *record, uint32_t pos) { record->value = (LIGHT_DATA_TYPE)(int64_t)pos; } -inline void -LIGHT(enqueue_empty)(struct LIGHT(core) *ht, uint32_t slot, struct LIGHT(record) *record) +/* + * Empty records (that do not store value) are linked into doubly linked list. + * Add given record with given slot to that list. + * Touches matras of the record + */ +inline int +LIGHT(enqueue_empty)(struct LIGHT(core) *ht, uint32_t slot, + struct LIGHT(record) *record) { record->next = slot; - if (ht->empty_record) - LIGHT(set_empty_prev)(ht->empty_record, slot); + if (ht->empty_slot != LIGHT(end)) { + struct LIGHT(record) *empty_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, ht->empty_slot); + if (!empty_record) + return -1; + LIGHT(set_empty_prev)(empty_record, slot); + } LIGHT(set_empty_prev)(record, LIGHT(end)); LIGHT(set_empty_next)(record, ht->empty_slot); - ht->empty_record = record; ht->empty_slot = slot; + return 0; } -inline void +/* + * Empty records (that do not store value) are linked into doubly linked list. + * Remove from list first record of that list and return that record + * Touches matras of result and all chaning records + */ +inline struct LIGHT(record) * LIGHT(detach_first_empty)(struct LIGHT(core) *ht) { - assert(ht->empty_record); - ht->empty_slot = LIGHT(get_empty_next)(ht->empty_record); - if (ht->empty_slot == LIGHT(end)) { - ht->empty_record = 0; - } else { - ht->empty_record = (struct LIGHT(record) *) - matras_get(&ht->mtable, ht->empty_slot); - LIGHT(set_empty_prev)(ht->empty_record, LIGHT(end)); + assert(ht->empty_slot != LIGHT(end)); + struct LIGHT(record) *empty_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, ht->empty_slot); + if (!empty_record) + return 0; + assert(empty_record->next == ht->empty_slot); + uint32_t new_empty_slot = LIGHT(get_empty_next)(empty_record); + if (new_empty_slot != LIGHT(end)) { + struct LIGHT(record) *new_empty_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, new_empty_slot); + if (!new_empty_record) + return 0; + LIGHT(set_empty_prev)(new_empty_record, LIGHT(end)); } + ht->empty_slot = new_empty_slot; + return empty_record; } -inline void -LIGHT(detach_empty)(struct LIGHT(core) *ht, struct LIGHT(record) *record) +/* + * Empty records (that do not store value) are linked into doubly linked list. + * Remove from list the record by given slot and return that record + * Touches matras of result and all chaning records + */ +inline struct LIGHT(record) * +LIGHT(detach_empty)(struct LIGHT(core) *ht, uint32_t slot) { + struct LIGHT(record) *record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, slot); + if (!record) + return 0; uint32_t prev_slot = LIGHT(get_empty_prev)(record); uint32_t next_slot = LIGHT(get_empty_next)(record); - if (prev_slot == LIGHT(end)) { - ht->empty_slot = next_slot; - if (next_slot == LIGHT(end)) - ht->empty_record = 0; - else - ht->empty_record = (struct LIGHT(record) *) - matras_get(&ht->mtable, next_slot); - } else { - struct LIGHT(record) *prev_record = (struct LIGHT(record) *) - matras_get(&ht->mtable, prev_slot); + struct LIGHT(record) *prev_record = 0; + if (prev_slot != LIGHT(end)) { + prev_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, prev_slot); + if (!prev_record) + return 0; + } + struct LIGHT(record) *next_record = 0; + if (next_slot != LIGHT(end)) { + next_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, next_slot); + if (!next_record) + return 0; + } + if (prev_slot != LIGHT(end)) { LIGHT(set_empty_next)(prev_record, next_slot); + } else { + ht->empty_slot = next_slot; } if (next_slot != LIGHT(end)) { - struct LIGHT(record) *next_record = (struct LIGHT(record) *) - matras_get(&ht->mtable, next_slot); LIGHT(set_empty_prev)(next_record, prev_slot); } + return record; } -inline bool +/* + * Allocate memory and initialize empty list to get ready for first insertion + */ +inline int LIGHT(prepare_first_insert)(struct LIGHT(core) *ht) { assert(ht->count == 0); @@ -445,12 +619,11 @@ LIGHT(prepare_first_insert)(struct LIGHT(core) *ht) struct LIGHT(record) *record = (struct LIGHT(record) *) matras_alloc_range(&ht->mtable, &slot, ht->GROW_INCREMENT); if (!record) - return false; + return -1; assert(slot == 0); ht->table_size = ht->GROW_INCREMENT; ht->cover_mask = ht->GROW_INCREMENT - 1; ht->empty_slot = 0; - ht->empty_record = record; for (int i = 0; i < ht->GROW_INCREMENT; i++) { record[i].next = i; LIGHT(set_empty_prev)(record + i, i - 1); @@ -458,19 +631,30 @@ LIGHT(prepare_first_insert)(struct LIGHT(core) *ht) } LIGHT(set_empty_prev)(record, LIGHT(end)); LIGHT(set_empty_next)(record + ht->GROW_INCREMENT - 1, LIGHT(end)); - return true; + return 0; } -inline bool +/* + * Enlarge hash table to store more values + */ +inline int LIGHT(grow)(struct LIGHT(core) *ht) { + assert(ht->empty_slot == LIGHT(end)); uint32_t new_slot; struct LIGHT(record) *new_record = (struct LIGHT(record) *) matras_alloc_range(&ht->mtable, &new_slot, ht->GROW_INCREMENT); if (!new_record) /* memory failure */ - return false; - ht->table_size += ht->GROW_INCREMENT; + return -1; + new_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, new_slot); + if (!new_record) { /* memory failure */ + matras_dealloc_range(&ht->mtable, ht->GROW_INCREMENT); + return -1; + } + uint32_t save_cover_mask = ht->cover_mask; + ht->table_size += ht->GROW_INCREMENT; if (ht->cover_mask < ht->table_size - 1) ht->cover_mask = (ht->cover_mask << 1) | (uint32_t)1; @@ -479,9 +663,16 @@ LIGHT(grow)(struct LIGHT(core) *ht) uint32_t susp_slot = new_slot & split_comm_mask; struct LIGHT(record) *susp_record = (struct LIGHT(record) *) - matras_get(&ht->mtable, susp_slot); + matras_touch(&ht->mtable, susp_slot); + if (!susp_record) { + matras_dealloc_range(&ht->mtable, ht->GROW_INCREMENT); + ht->cover_mask = save_cover_mask; + ht->table_size -= ht->GROW_INCREMENT; + return -1; + } - for (int i = 0; i < ht->GROW_INCREMENT; i++, susp_slot++, susp_record++, new_slot++, new_record++) { + for (int i = 0; i < ht->GROW_INCREMENT; + i++, susp_slot++, susp_record++, new_slot++, new_record++) { if (susp_record->next == susp_slot) { /* Suspicious slot is empty, nothing to split */ LIGHT(enqueue_empty)(ht, new_slot, new_record); @@ -500,30 +691,34 @@ LIGHT(grow)(struct LIGHT(core) *ht) assert(split_diff_mask == (((uint32_t)1) << shift)); uint32_t last_empty_slot = new_slot; - struct LIGHT(record) *last_empty_record = new_record; uint32_t prev_flag = 0; struct LIGHT(record) *test_record = susp_record; uint32_t test_slot = susp_slot; struct LIGHT(record) *prev_record = 0; + uint32_t prev_slot = LIGHT(end); while (1) { - uint32_t test_flag = (test_record->hash >> shift) & ((uint32_t)1); - if (test_flag != prev_flag) { + uint32_t test_flag = (test_record->hash >> shift) + & ((uint32_t)1); + if (test_flag != prev_flag) { + if (prev_slot != LIGHT(end)) + prev_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, + prev_slot); + /* TODO: check the result */ chain_tail[prev_flag] = prev_record; if (chain_tail[test_flag]) { chain_tail[test_flag]->next = test_slot; } else { *chain_head[test_flag] = *test_record; last_empty_slot = test_slot; - last_empty_record = test_record; - test_record = chain_head[test_flag]; test_slot = chain_head_slot[test_flag]; } prev_flag = test_flag; } + prev_slot = test_slot; test_slot = test_record->next; if (test_slot == LIGHT(end)) break; - prev_record = test_record; test_record = (struct LIGHT(record) *) matras_get(&ht->mtable, test_slot); } @@ -531,40 +726,71 @@ LIGHT(grow)(struct LIGHT(core) *ht) if (chain_tail[prev_flag]) chain_tail[prev_flag]->next = LIGHT(end); + struct LIGHT(record) *last_empty_record = + (struct LIGHT(record) *) + matras_touch(&ht->mtable, last_empty_slot); LIGHT(enqueue_empty)(ht, last_empty_slot, last_empty_record); } - return true; + return 0; } +/** + * @brief Insert a record with given hash and value + * @param ht - pointer to a hash table struct + * @param hash - hash to insert + * @param data - value to insert + * @return integer ID of inserted record or light_end if failed + */ inline uint32_t LIGHT(insert)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value) { if (ht->table_size == 0) - if (!LIGHT(prepare_first_insert)(ht)) + if (LIGHT(prepare_first_insert)(ht)) return LIGHT(end); - if (!ht->empty_record) - if (!LIGHT(grow)(ht)) + if (ht->empty_slot == LIGHT(end)) + if (LIGHT(grow)(ht)) return LIGHT(end); + assert(ht->table_size == ht->mtable.block_counts[0]); ht->count++; uint32_t slot = LIGHT(slot)(ht, hash); struct LIGHT(record) *record = (struct LIGHT(record) *) - matras_get(&ht->mtable, slot); + matras_touch(&ht->mtable, slot); + if (!record) + return LIGHT(end); if (record->next == slot) { /* Inserting to an empty slot */ - LIGHT(detach_empty)(ht, record); + record = LIGHT(detach_empty)(ht, slot); + if (!record) + return LIGHT(end); record->value = value; record->hash = hash; record->next = LIGHT(end); return slot; } + uint32_t chain_slot = LIGHT(slot)(ht, record->hash); + struct LIGHT(record) *chain = 0; + if (chain_slot != slot) { + chain = (struct LIGHT(record) *) + matras_get(&ht->mtable, chain_slot); + while (chain->next != slot) { + chain_slot = chain->next; + chain = (struct LIGHT(record) *) + matras_get(&ht->mtable, chain_slot); + } + chain = (struct LIGHT(record) *) + matras_touch(&ht->mtable, chain_slot); + if (!chain) + return LIGHT(end); + } + uint32_t empty_slot = ht->empty_slot; - struct LIGHT(record) *empty_record = ht->empty_record; - LIGHT(detach_first_empty)(ht); + struct LIGHT(record) *empty_record = LIGHT(detach_first_empty)(ht); + if (!empty_record) + return LIGHT(end); - uint32_t chain_slot = LIGHT(slot)(ht, record->hash); if (chain_slot == slot) { /* add to existing chain */ empty_record->value = value; @@ -574,13 +800,6 @@ LIGHT(insert)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value) return empty_slot; } else { /* create new chain */ - struct LIGHT(record) *chain = (struct LIGHT(record) *) - matras_get(&ht->mtable, chain_slot); - while (chain->next != slot) { - chain_slot = chain->next; - chain = (struct LIGHT(record) *) - matras_get(&ht->mtable, chain_slot); - } *empty_record = *record; chain->next = empty_slot; record->value = value; @@ -590,19 +809,33 @@ LIGHT(insert)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value) } } -inline void +/** + * @brief Delete a record from a hash table by given record ID + * @param ht - pointer to a hash table struct + * @param slotpos - ID of an record. See LIGHT(find) for details. + * @return 0 if ok, -1 on memory error (only with freezed iterators) + */ +inline int LIGHT(delete)(struct LIGHT(core) *ht, uint32_t slot) { assert(slot < ht->table_size); uint32_t empty_slot; struct LIGHT(record) *empty_record; struct LIGHT(record) *record = (struct LIGHT(record) *) - matras_get(&ht->mtable, slot); + matras_touch(&ht->mtable, slot); + if (!record) + return -1; assert(record->next != slot); + if (ht->empty_slot != LIGHT(end)) { + if (!matras_touch(&ht->mtable, ht->empty_slot)) + return -1; + } if (record->next != LIGHT(end)) { empty_slot = record->next; empty_record = (struct LIGHT(record) *) - matras_get(&ht->mtable, empty_slot); + matras_touch(&ht->mtable, empty_slot); + if (!empty_record) + return -1; *record = *empty_record; } else { empty_slot = slot; @@ -617,55 +850,94 @@ LIGHT(delete)(struct LIGHT(core) *ht, uint32_t slot) while (chain_next_slot != slot) { chain_slot = chain_next_slot; chain = (struct LIGHT(record) *) - matras_get(&ht->mtable, chain_next_slot); + matras_get(&ht->mtable, chain_slot); chain_next_slot = chain->next; assert(chain_next_slot != LIGHT(end)); } + chain = (struct LIGHT(record) *) + matras_touch(&ht->mtable, chain_slot); + if (!chain) + return -1; chain->next = LIGHT(end); } } LIGHT(enqueue_empty)(ht, empty_slot, empty_record); ht->count--; + return 0; } -inline void +/** + * @brief Delete a record from a hash table by that value and its hash. + * @param ht - pointer to a hash table struct + * @param slotpos - ID of an record. See LIGHT(find) for details. + * @return 0 if ok, 1 if not found or -1 on memory error + * (only with freezed iterators) + */ +inline int LIGHT(delete_value)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value) { if (ht->count == 0) - return; + return 1; /* not found */ uint32_t slot = LIGHT(slot)(ht, hash); struct LIGHT(record) *record = (struct LIGHT(record) *) matras_get(&ht->mtable, slot); if (record->next == slot) - return; + return 1; /* not found */ + uint32_t prev_slot = LIGHT(end); struct LIGHT(record) *prev_record = 0; while (1) { - if (record->hash == hash && LIGHT_EQUAL((record->value), (value), (ht->arg))) + if (record->hash == hash + && LIGHT_EQUAL((record->value), (value), (ht->arg))) break; + prev_slot = slot; slot = record->next; if (slot == LIGHT(end)) - return; + return 1; /* not found */ prev_record = record; record = (struct LIGHT(record) *) matras_get(&ht->mtable, slot); } - ht->count--; + record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, slot); + if (!record) { + return -1; /* mem fail */ + } + if (ht->empty_slot != LIGHT(end)) { + if (!matras_touch(&ht->mtable, ht->empty_slot)) + return -1; /* mem fail */ + } if (prev_record) { + prev_record = (struct LIGHT(record) *) + matras_touch(&ht->mtable, prev_slot); + if (!prev_record) + return -1; /* mem fail */ prev_record->next = record->next; LIGHT(enqueue_empty)(ht, slot, record); - return; + ht->count--; + return 0; } if (record->next == LIGHT(end)) { LIGHT(enqueue_empty)(ht, slot, record); - return; + ht->count--; + return 0; } uint32_t next_slot = record->next; struct LIGHT(record) *next_record = (struct LIGHT(record) *) - matras_get(&ht->mtable, next_slot); + matras_touch(&ht->mtable, next_slot); + if (!next_record) + return -1; /* mem fail */ *record = *next_record; LIGHT(enqueue_empty)(ht, next_slot, next_record); + ht->count--; + return 0; } +/** + * @brief Get a value from a desired position + * @param ht - pointer to a hash table struct + * @param slotpos - ID of an record + * ID must be vaild, check it by light_pos_valid (asserted). + */ inline LIGHT_DATA_TYPE LIGHT(get)(struct LIGHT(core) *ht, uint32_t slotpos) { @@ -680,6 +952,7 @@ LIGHT(get)(struct LIGHT(core) *ht, uint32_t slotpos) * @brief Determine if posision holds a value * @param ht - pointer to a hash table struct * @param slotpos - ID of an record + * ID must be in valid range [0, ht->table_size) (asserted). */ inline bool LIGHT(pos_valid)(LIGHT(core) *ht, uint32_t slotpos) @@ -690,7 +963,91 @@ LIGHT(pos_valid)(LIGHT(core) *ht, uint32_t slotpos) return record->next != slotpos; } +/** + * @brief Set iterator to the beginning of hash table + * @param ht - pointer to a hash table struct + * @param itr - iterator to set + */ +inline void +LIGHT(itr_begin)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr) +{ + (void)ht; + itr->slotpos = 0; + itr->matras_version = 0; +} + +/** + * @brief Set iterator to position determined by key + * @param ht - pointer to a hash table struct + * @param itr - iterator to set + * @param hash - hash to find + * @param data - key to find + */ +inline void +LIGHT(itr_key)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr, + uint32_t hash, LIGHT_KEY_TYPE data) +{ + itr->slotpos = LIGHT(find_key)(ht, hash, data); + itr->matras_version = 0; +} +/** + * @brief Get the value that iterator currently points to + * @param ht - pointer to a hash table struct + * @param itr - iterator to set + * @return poiner to the value or NULL if iteration is complete + */ +inline LIGHT_DATA_TYPE * +LIGHT(itr_get_and_next)(const struct LIGHT(core) *ht, + struct LIGHT(iterator) *itr) +{ + while (itr->slotpos < ht->mtable.block_counts[itr->matras_version]) { + uint32_t slotpos = itr->slotpos; + struct LIGHT(record) *record = (struct LIGHT(record) *) + matras_getv(&ht->mtable, slotpos, + itr->matras_version); + itr->slotpos++; + if (record->next != slotpos) + return &record->value; + } + return 0; +} + +/** + * @brief Freezes state for given iterator. All following hash table modification + * will not apply to that iterator iteration. That iterator should be destroyed + * with a light_itr_destroy call after usage. + * @param ht - pointer to a hash table struct + * @param itr - iterator to freeze + * @return - 0 on success, -1 if no more version is available in matras + */ +inline int +LIGHT(itr_freeze)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr) +{ + itr->matras_version = matras_create_read_view(&ht->mtable); + return itr->matras_version > 0 ? 0 : -1; +} + +/** + * @brief Destroy an iterator that was frozen before. Useless for not frozen + * iterators. + * @param ht - pointer to a hash table struct + * @param itr - iterator to destroy + */ +inline void +LIGHT(itr_destroy)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr) +{ + if (itr->matras_version) { + matras_destroy_read_view(&ht->mtable, itr->matras_version); + itr->matras_version = 0; + } +} + +/* + * Selfcheck of the internal state of hash table. Used only for debugging. + * That means that you should not use this function. + * If return not zero, something went terribly wrong. + */ inline int LIGHT(selfcheck)(const struct LIGHT(core) *ht) { @@ -698,15 +1055,6 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht) if (ht->table_size != ht->mtable.block_counts[0]) res |= 64; uint32_t empty_slot = ht->empty_slot; - if (empty_slot == LIGHT(end)) { - if (ht->empty_record) - res |= 512; - } else { - struct LIGHT(record) *should_be = (struct LIGHT(record) *) - matras_get(&ht->mtable, empty_slot); - if (ht->empty_record != should_be) - res |= 1024; - } uint32_t prev_empty_slot = LIGHT(end); while (empty_slot != LIGHT(end)) { struct LIGHT(record) *empty_record = (struct LIGHT(record) *) @@ -724,7 +1072,8 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht) if (record->next == i) { uint32_t empty_slot = ht->empty_slot; while (empty_slot != LIGHT(end) && empty_slot != i) { - struct LIGHT(record) *empty_record = (struct LIGHT(record) *) + struct LIGHT(record) *empty_record = + (struct LIGHT(record) *) matras_get(&ht->mtable, empty_slot); empty_slot = LIGHT(get_empty_next)(empty_record); } @@ -738,7 +1087,8 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht) uint32_t chain_slot = slot; uint32_t chain_start_slot = slot; do { - struct LIGHT(record) *chain_record = (struct LIGHT(record) *) + struct LIGHT(record) *chain_record = + (struct LIGHT(record) *) matras_get(&ht->mtable, chain_slot); chain_slot = chain_record->next; if (chain_slot >= ht->table_size) { @@ -758,12 +1108,14 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht) res |= 1; /* slot is out of chain */ } else { do { - struct LIGHT(record) *record = (struct LIGHT(record) *) + struct LIGHT(record) *record = + (struct LIGHT(record) *) matras_get(&ht->mtable, slot); if (LIGHT(slot)(ht, record->hash) != i) res |= 2; /* wrong value in chain */ slot = record->next; - if (slot != LIGHT(end) && slot >= ht->table_size) { + if (slot != LIGHT(end) + && slot >= ht->table_size) { res |= 32; /* out of bounds (2) */ break; } diff --git a/src/lib/small/matras.c b/src/lib/small/matras.c index 3bf2e21c2cf3d4cbe7b3438c0fae2237a1026914..9ae7dc0168af6d1230eefcbff4d27f8f577c0c73 100644 --- a/src/lib/small/matras.c +++ b/src/lib/small/matras.c @@ -314,7 +314,7 @@ matras_extent_count(const struct matras *m) /* * Create new version of matras memory. - * Return -1 if all version IDs are occupied. + * Return 0 if all version IDs are occupied. */ matras_id_t matras_create_read_view(struct matras *m) @@ -331,7 +331,7 @@ matras_create_read_view(struct matras *m) #endif assert(ver_id > 0); if (ver_id >= MATRAS_VERSION_COUNT) - return -1; + return 0; m->ver_occ_mask |= ((matras_version_tag_t)1) << ver_id; m->roots[ver_id] = m->roots[0]; m->block_counts[ver_id] = m->block_counts[0]; diff --git a/src/lib/small/matras.h b/src/lib/small/matras.h index ae1aaf1c4c099307efe44e291f31f68824ee9b57..66912c4a4c3594f0e866f3946c8e5003193b2d99 100644 --- a/src/lib/small/matras.h +++ b/src/lib/small/matras.h @@ -284,7 +284,7 @@ matras_extent_count(const struct matras *m); /* * Create new version of matras memory. - * Return -1 if all version IDs are occupied. + * Return 0 if all version IDs are occupied. */ matras_id_t matras_create_read_view(struct matras *m); diff --git a/test/unit/bps_tree_itr.cc b/test/unit/bps_tree_itr.cc index 3a464235bdd60baf517e0b37b1deea20cc7216f3..0c7deadf4080807e9666a0809432c30c4bd0ea84 100644 --- a/test/unit/bps_tree_itr.cc +++ b/test/unit/bps_tree_itr.cc @@ -2,6 +2,7 @@ #include <stdint.h> #include <stdio.h> #include <stdbool.h> +#include <time.h> #include "unit.h" @@ -473,6 +474,7 @@ itr_freeze_check() int main(void) { + srand(time(0)); itr_check(); itr_invalidate_check(); itr_freeze_check(); diff --git a/test/unit/light.cc b/test/unit/light.cc index 2dee5dcba3040cb2260857dfaea5e4ac08d5ce19..4de77d92cbb0a912e1bc3471d61cb930c24a380b 100644 --- a/test/unit/light.cc +++ b/test/unit/light.cc @@ -4,6 +4,7 @@ #include <stdbool.h> #include <inttypes.h> #include <vector> +#include <time.h> #include "unit.h" @@ -183,11 +184,145 @@ collision_test() footer(); } +static void +itr_test() +{ + header(); + + struct light_core ht; + light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0); + const size_t rounds = 1000; + const size_t start_limits = 20; + + const size_t iterator_count = 16; + struct light_iterator itrs[iterator_count]; + for (size_t i = 0; i < iterator_count; i++) + light_itr_begin(&ht, itrs + i); + size_t cur_iterator = 0; + hash_value_t strage_thing = 0; + + for(size_t limits = start_limits; limits <= 2 * rounds; limits *= 10) { + for (size_t i = 0; i < rounds; i++) { + hash_value_t val = rand() % limits; + hash_t h = hash(val); + hash_t fnd = light_find(&ht, h, val); + + if (fnd == light_end) { + light_insert(&ht, h, val); + } else { + light_delete(&ht, fnd); + } + + hash_value_t *pval = light_itr_get_and_next(&ht, itrs + cur_iterator); + if (pval) + strage_thing ^= *pval; + if (!pval || (rand() % iterator_count) == 0) { + if (rand() % iterator_count) { + hash_value_t val = rand() % limits; + hash_t h = hash(val); + light_itr_key(&ht, itrs + cur_iterator, h, val); + } else { + light_itr_begin(&ht, itrs + cur_iterator); + } + } + + cur_iterator++; + if (cur_iterator >= iterator_count) + cur_iterator = 0; + } + } + light_destroy(&ht); + + if (strage_thing >> 20) { + printf("impossible!\n"); // prevent strage_thing to be optimized out + } + + footer(); +} + +static void +itr_freeze_check() +{ + header(); + + const int test_rounds_size = 10; + const int test_data_size = 1000; + hash_value_t comp_buf1[test_data_size]; + hash_value_t comp_buf2[test_data_size]; + const int test_data_mod = 2000; + srand(0); + struct light_core ht; + + for (int i = 0; i < 10; i++) { + light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0); + int comp_buf_size1 = 0; + int comp_buf_size2 = 0; + for (int j = 0; j < test_data_size; j++) { + hash_value_t val = rand() % test_data_mod; + hash_t h = hash(val); + light_insert(&ht, h, val); + } + struct light_iterator itr; + light_itr_begin(&ht, &itr); + hash_value_t *e; + while ((e = light_itr_get_and_next(&ht, &itr))) { + comp_buf1[comp_buf_size1++] = *e; + } + struct light_iterator itr1; + light_itr_begin(&ht, &itr1); + light_itr_freeze(&ht, &itr1); + struct light_iterator itr2; + light_itr_begin(&ht, &itr2); + light_itr_freeze(&ht, &itr2); + for (int j = 0; j < test_data_size; j++) { + hash_value_t val = rand() % test_data_mod; + hash_t h = hash(val); + light_insert(&ht, h, val); + } + int tested_count = 0; + while ((e = light_itr_get_and_next(&ht, &itr1))) { + if (*e != comp_buf1[tested_count]) { + fail("version restore failed (1)", "true"); + } + tested_count++; + if (tested_count > comp_buf_size1) { + fail("version restore failed (2)", "true"); + } + } + light_itr_destroy(&ht, &itr1); + for (int j = 0; j < test_data_size; j++) { + hash_value_t val = rand() % test_data_mod; + hash_t h = hash(val); + hash_t pos = light_find(&ht, h, val); + if (pos != light_end) + light_delete(&ht, pos); + } + + tested_count = 0; + while ((e = light_itr_get_and_next(&ht, &itr2))) { + if (*e != comp_buf1[tested_count]) { + fail("version restore failed (1)", "true"); + } + tested_count++; + if (tested_count > comp_buf_size1) { + fail("version restore failed (2)", "true"); + } + } + + light_destroy(&ht); + } + + footer(); +} + int main(int, const char**) { + srand(time(0)); simple_test(); collision_test(); + itr_test(); + itr_freeze_check(); if (extents_count != 0) fail("memory leak!", "true"); } diff --git a/test/unit/light.result b/test/unit/light.result index b37b0ed568394b2cebd65e2e951c4a5c5c7528f9..43011b2fcef38f96ff326122f11a6c712f74d9ad 100644 --- a/test/unit/light.result +++ b/test/unit/light.result @@ -2,4 +2,8 @@ *** simple_test: done *** *** collision_test *** *** collision_test: done *** + *** itr_test *** + *** itr_test: done *** + *** itr_freeze_check *** + *** itr_freeze_check: done *** \ No newline at end of file diff --git a/test/unit/matras.cc b/test/unit/matras.cc index 6907ae7cbbc1652af5ae86605a0912e8d659d2d0..ee22b3cdcd9156e66db3db42896755f8c493fcaf 100644 --- a/test/unit/matras.cc +++ b/test/unit/matras.cc @@ -189,8 +189,8 @@ matras_vers_test() add_ver = rand() % 2 == 0; if (add_ver) { cur_num_or_ver++; - int new_ver = matras_create_read_view(&local); - assert(new_ver > 0); + matras_id_t new_ver = matras_create_read_view(&local); + check(new_ver > 0, "create read view failed"); use_mask |= (1 << new_ver); comps[new_ver] = comps[0]; } else {