diff --git a/License b/License index 1d4f843..fcbce0a 100644 --- a/License +++ b/License @@ -1,25 +1,34 @@ -Copyright (c) 2020 Andy Günther +This software is provided under the BSD license. The text of this license +is provided below: + +-------------------------------------------------------------------------- + +Copyright (C) 2010-2021 Andy Guenther All rights reserved. -BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. +3. Neither the name of the author nor the names of any contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/include/oha.h b/include/oha.h index 307b20b..85ecaf3 100644 --- a/include/oha.h +++ b/include/oha.h @@ -73,6 +73,7 @@ struct oha_bh_config { bool resizable; }; #define OHA_BH_NOT_FOUND INT64_MIN +#define OHA_BH_MIN_VALUE (OHA_BH_NOT_FOUND + 1) struct oha_bh; struct oha_bh * oha_bh_create(const struct oha_bh_config * config); void oha_bh_destroy(struct oha_bh * heap); @@ -81,6 +82,7 @@ int64_t oha_bh_find_min(const struct oha_bh * heap); void * oha_bh_delete_min(struct oha_bh * heap); void * oha_bh_insert(struct oha_bh * heap, int64_t key); int64_t oha_bh_change_key(struct oha_bh * heap, void * value, int64_t new_val); +void * oha_bh_remove(struct oha_bh * const heap, void * const value, int64_t * out_key); /********************************************************************************************************************** * tpht (temporal prioritized hash table) @@ -95,13 +97,14 @@ struct oha_tpht_config { struct oha_tpht * oha_tpht_create(const struct oha_tpht_config * config); void oha_tpht_destroy(struct oha_tpht * tpht); -void * oha_tpht_insert(struct oha_tpht * tpht, const void * key, int64_t timestamp, uint8_t timeout_slot_id); +bool oha_tpht_increase_global_time(struct oha_tpht * tpht, int64_t timestamp); +void * oha_tpht_insert(struct oha_tpht * tpht, const void * key, uint8_t timeout_slot_id); __attribute__((pure)) void * oha_tpht_look_up(const struct oha_tpht * tpht, const void * key); void * oha_tpht_remove(struct oha_tpht * tpht, const void * key); int8_t oha_tpht_add_timeout_slot(struct oha_tpht * tpht, int64_t timeout, uint32_t num_elements, bool resizable); void * oha_tpht_set_timeout_slot(struct oha_tpht * tpht, const void * key, uint8_t timeout_slot_id); -bool oha_tpht_next_timeout_entry(struct oha_tpht * tpht, struct oha_key_value_pair * next_pair); +size_t oha_tpht_next_timeout_entries(struct oha_tpht * tpht, struct oha_key_value_pair * next_pair, size_t num_pairs); void * oha_tpht_update_time_for_entry(struct oha_tpht * tpht, const void * key, int64_t new_timestamp); #ifdef __cplusplus diff --git a/src/binary_heap.c b/src/binary_heap.c index af2f08f..4666cde 100644 --- a/src/binary_heap.c +++ b/src/binary_heap.c @@ -25,6 +25,14 @@ struct oha_bh { struct key_bucket * keys; }; +static inline struct value_bucket * get_value_bucket(void * value) +{ + struct value_bucket * value_bucket = + (struct value_bucket *)((uint8_t *)value - offsetof(struct value_bucket, value_buffer)); + assert(value_bucket->key->value == value_bucket); + return value_bucket; +} + static inline uint_fast32_t parent(uint_fast32_t i) { return (i - 1) / 2; @@ -192,6 +200,10 @@ int64_t oha_bh_find_min(const struct oha_bh * const heap) if (heap == NULL || heap->elems == 0) { return OHA_BH_NOT_FOUND; } + if (heap->elems == 0) { + return OHA_BH_NOT_FOUND; + } + return heap->keys[0].key; } @@ -216,15 +228,39 @@ void * oha_bh_delete_min(struct oha_bh * const heap) return heap->keys[heap->elems].value->value_buffer; } +void * oha_bh_remove(struct oha_bh * const heap, void * const value, int64_t * out_key) +{ + if (heap == NULL || value == NULL) { + return NULL; + } + + struct value_bucket * value_bucket = get_value_bucket(value); + struct key_bucket * key = value_bucket->key; + int64_t key_value = key->key; + + // move to top + if (OHA_BH_MIN_VALUE != oha_bh_change_key(heap, value, OHA_BH_MIN_VALUE)) { + return NULL; + } + // delete the swapped element + if (oha_bh_delete_min(heap) != value_bucket->value_buffer) { + return NULL; + } + + if (out_key != NULL) { + *out_key = key_value; + } + + return value_bucket->value_buffer; +} + int64_t oha_bh_change_key(struct oha_bh * const heap, void * const value, int64_t new_val) { if (heap == NULL || value == NULL) { - return 0; + return OHA_BH_NOT_FOUND; } - struct value_bucket * value_bucket = - (struct value_bucket *)((uint8_t *)value - offsetof(struct value_bucket, value_buffer)); - assert(value_bucket->key->value == value_bucket); + struct value_bucket * value_bucket = get_value_bucket(value); enum mode { UNCHANGED_KEY, diff --git a/src/linear_probing_hash_table.c b/src/linear_probing_hash_table.c index a78234e..09c5308 100644 --- a/src/linear_probing_hash_table.c +++ b/src/linear_probing_hash_table.c @@ -11,8 +11,6 @@ #include "utils.h" -#define XXHASH_SEED 0xc800c831bc63dff8 - #ifdef OHA_FIX_KEY_SIZE_IN_BYTES #if OHA_FIX_KEY_SIZE_IN_BYTES == 0 #error "unsupported compile time key size" @@ -88,9 +86,9 @@ static uint64_t hash_key(const struct oha_lpht * const table, const void * const { #ifdef OHA_FIX_KEY_SIZE_IN_BYTES (void)table; - return XXH3_64bits_withSeed(key, OHA_FIX_KEY_SIZE_IN_BYTES, XXHASH_SEED); + return XXH3_64bits(key, OHA_FIX_KEY_SIZE_IN_BYTES); #else - return XXH3_64bits_withSeed(key, table->config.key_size, XXHASH_SEED); + return XXH3_64bits(key, table->config.key_size); #endif } diff --git a/src/temporal_prioritized_hash_table.c b/src/temporal_prioritized_hash_table.c index e989f3e..80717ce 100644 --- a/src/temporal_prioritized_hash_table.c +++ b/src/temporal_prioritized_hash_table.c @@ -24,6 +24,15 @@ struct oha_tpht { struct oha_lpht_config lpht_config; }; +struct value_bucket { + void * heap_value; + union { + uint8_t slot_id; + void * memory_alignment; + }; + uint8_t data[]; +}; + struct oha_tpht * oha_tpht_create(const struct oha_tpht_config * const config) { if (config == NULL) { @@ -34,7 +43,14 @@ struct oha_tpht * oha_tpht_create(const struct oha_tpht_config * const config) if (table == NULL) { return NULL; } - table->lpht = oha_lpht_create(&config->lpht_config); + + table->lpht_config = config->lpht_config; + table->lpht_config.value_size += sizeof(struct value_bucket); // space for the connection to the heap + table->lpht = oha_lpht_create(&table->lpht_config); + if (table->lpht == NULL) { + oha_tpht_destroy(table); + return NULL; + } return table; } @@ -73,6 +89,7 @@ int8_t oha_tpht_add_timeout_slot(struct oha_tpht * const tpht, int64_t timeout, sizeof(*tpht->slots), &tpht->num_timeout_slots, next_free_index); + return -4; } return tpht->num_timeout_slots; @@ -96,42 +113,201 @@ void oha_tpht_destroy(struct oha_tpht * const tpht) oha_free(&tpht->lpht_config.memory, tpht); } -void * oha_tpht_insert(struct oha_tpht * const pht, const void * const key, int64_t timestamp, uint8_t timeout_slot_id) +bool oha_tpht_increase_global_time(struct oha_tpht * const tpht, int64_t timestamp) { - (void)pht; - (void)key; - (void)timestamp; - (void)timeout_slot_id; - return NULL; + if (tpht == NULL) { + return false; + } + if (timestamp < tpht->last_timestamp) { + return false; + } + tpht->last_timestamp = timestamp; + return true; } -void * oha_tpht_look_up(const struct oha_tpht * const pht, const void * const key) +static inline void connect_heap_with_hash_table(struct value_bucket * hash_table_value, + void * heap_value, + const void * const origin_key, + size_t key_size, + uint8_t slot_id) { - (void)pht; - (void)key; + // TODO encode slot id in pointer + memcpy(heap_value, origin_key, key_size); + hash_table_value->heap_value = heap_value; + hash_table_value->slot_id = slot_id; +} - return NULL; +void * oha_tpht_insert(struct oha_tpht * const tpht, const void * const key, uint8_t timeout_slot_id) +{ + if (tpht == NULL) { + return NULL; + } + + if (timeout_slot_id > tpht->num_timeout_slots) { + return NULL; + } + + struct value_bucket * hash_table_value = oha_lpht_insert(tpht->lpht, key); + if (hash_table_value == NULL) { + return NULL; + } + + if (timeout_slot_id > 0) { + void * heap_value = oha_bh_insert(tpht->slots[tpht->num_timeout_slots - 1].bh, tpht->last_timestamp); + if (heap_value == NULL) { + oha_lpht_remove(tpht->lpht, key); + return NULL; + } + + connect_heap_with_hash_table(hash_table_value, heap_value, key, tpht->lpht_config.key_size, timeout_slot_id); + } else { + hash_table_value->slot_id = 0; + } + + return hash_table_value->data; } -void * oha_tpht_remove(struct oha_tpht * const pht, const void * const key) +void * oha_tpht_look_up(const struct oha_tpht * const tpht, const void * const key) { - (void)pht; - (void)key; - return NULL; + if (tpht == NULL) { + return NULL; + } + struct value_bucket * value = oha_lpht_look_up(tpht->lpht, key); + if (value == NULL) { + return NULL; + } + return value->data; } -bool oha_tpht_next_timeout_entry(struct oha_tpht * const pht, struct oha_key_value_pair * const next_pair) +void * oha_tpht_remove(struct oha_tpht * const tpht, const void * const key) { - (void)pht; - (void)next_pair; + if (tpht == NULL) { + return NULL; + } + + struct value_bucket * value = oha_lpht_remove(tpht->lpht, key); + if (value == NULL) { + return NULL; + } + + if (value->slot_id > 0) { + const uint8_t slot = value->slot_id - 1; + // remove also from heap + assert(value->heap_value); + assert(tpht->slots[slot].bh); + if (OHA_BH_NOT_FOUND != oha_bh_change_key(tpht->slots[slot].bh, value->heap_value, OHA_BH_NOT_FOUND)) { + assert(0 && "unexpected missing heap element. bug in 'oha_tpht_insert' function."); + return value->data; + } + oha_bh_delete_min(tpht->slots[slot].bh); + } - return false; + return value->data; } -void * oha_tpht_update_time_for_entry(struct oha_tpht * const pht, const void * const key, int64_t new_timestamp) +size_t oha_tpht_next_timeout_entries(struct oha_tpht * const tpht, + struct oha_key_value_pair * const next_pair, + size_t num_pairs) { - (void)pht; - (void)key; - (void)new_timestamp; + if (tpht == NULL || next_pair == NULL) { + return 0; + } + + size_t num_found_entries = 0; + for (size_t slot = 0; slot < tpht->num_timeout_slots; slot++) { + do { + if (num_pairs == num_found_entries) { + return num_found_entries; + } + struct timeout_slot * current_slot = &tpht->slots[slot]; + const int64_t min_ts = oha_bh_find_min(current_slot->bh); + if (OHA_BH_NOT_FOUND == min_ts) { + break; + } + + if (tpht->last_timestamp < min_ts + current_slot->timeout) { + break; + } else { + next_pair[num_found_entries].key = oha_bh_delete_min(current_slot->bh); + assert(next_pair[num_found_entries].key != NULL); + struct value_bucket * value = oha_lpht_remove(tpht->lpht, next_pair[num_found_entries].key); + assert(value != NULL); + next_pair[num_found_entries].value = value->data; + num_found_entries++; + assert(value->heap_value != NULL); + } + + } while (1); + } + + return num_found_entries; +} + +void * oha_tpht_update_time_for_entry(struct oha_tpht * const tpht, const void * const key, int64_t new_timestamp) +{ + + if (tpht == NULL) { + return NULL; + } + + if (new_timestamp < tpht->last_timestamp) { + return NULL; + } + + struct value_bucket * value = oha_lpht_look_up(tpht->lpht, key); + if (value == NULL) { + return NULL; + } + + if (value->slot_id > 0 && value->slot_id <= tpht->num_timeout_slots) { + const uint8_t slot = value->slot_id - 1; + int64_t updated_timestamp = oha_bh_change_key(tpht->slots[slot].bh, value->heap_value, new_timestamp); + if (new_timestamp != updated_timestamp) { + return NULL; + } + return value->data; + } + return NULL; } + +void * oha_tpht_set_timeout_slot(struct oha_tpht * tpht, const void * key, uint8_t new_slot) +{ + if (tpht == NULL) { + return NULL; + } + + if (new_slot == 0 || new_slot > tpht->num_timeout_slots) { + return NULL; + } + + struct value_bucket * hash_table_value = oha_lpht_look_up(tpht->lpht, key); + if (hash_table_value == NULL) { + return NULL; + } + + if (hash_table_value->slot_id == new_slot) { + return hash_table_value->data; + } + + int64_t timestamp; + if (hash_table_value->slot_id > 0) { + // add check if new slot has at least space for one new element + uint8_t old_slot = hash_table_value->slot_id - 1; + void * old_heap_value = oha_bh_remove(tpht->slots[old_slot].bh, hash_table_value->heap_value, ×tamp); + assert(old_heap_value != NULL); + assert(0 == memcmp(old_heap_value, key, tpht->lpht_config.key_size)); + } else { + timestamp = tpht->last_timestamp; + } + + uint8_t slot = new_slot - 1; + void * new_heap_value = oha_bh_insert(tpht->slots[slot].bh, timestamp); + if (new_heap_value == NULL) { + return NULL; + } + + connect_heap_with_hash_table(hash_table_value, new_heap_value, key, tpht->lpht_config.key_size, new_slot); + + return hash_table_value->data; +} diff --git a/test/temporal_prioritized_hash_table_test.c b/test/temporal_prioritized_hash_table_test.c index b6a6750..8faa2b4 100644 --- a/test/temporal_prioritized_hash_table_test.c +++ b/test/temporal_prioritized_hash_table_test.c @@ -53,12 +53,305 @@ void test_create_destroy_add_slot() oha_tpht_destroy(table); } +void test_set_timeout_slot() +{ + const size_t num_timeouts = 100; + struct oha_key_value_pair next_pairs[num_timeouts]; + + struct oha_lpht_config config_lpht; + memset(&config_lpht, 0, sizeof(config_lpht)); + config_lpht.load_factor = LOAF_FACTOR; + config_lpht.key_size = sizeof(uint64_t); + config_lpht.value_size = sizeof(uint64_t); + config_lpht.max_elems = num_timeouts; + struct oha_tpht_config config; + config.lpht_config = config_lpht; + + struct oha_tpht * table = oha_tpht_create(&config); + TEST_ASSERT_NOT_NULL(table); + + const uint8_t first_slot = 1; + const uint8_t second_slot = 2; + + TEST_ASSERT_EQUAL(first_slot, oha_tpht_add_timeout_slot(table, 50, 100, false)); + TEST_ASSERT_EQUAL(second_slot, oha_tpht_add_timeout_slot(table, 200, 100, false)); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 1000)); + for (uint64_t i = 0; i < config_lpht.max_elems; i++) { + uint64_t * value_insert = oha_tpht_insert(table, &i, 0); + *value_insert = i; + } + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000)); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + uint64_t key = 5; + uint64_t * value = oha_tpht_set_timeout_slot(table, &key, first_slot); + TEST_ASSERT_EQUAL(5, *value); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + // move 10 - 29 element into the first timeout queue + for (uint64_t i = 10; i < 30; i++) { + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + i)); + value = oha_tpht_set_timeout_slot(table, &i, first_slot); + TEST_ASSERT_EQUAL(i, *value); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + } + + // timeout element 5 + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + 50)); + TEST_ASSERT_EQUAL(1, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + TEST_ASSERT_EQUAL(5, *(uint64_t *)next_pairs[0].value); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + 50 + 9)); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + // timeout 5 elements from first queue + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + 50 + 10 + 4)); + TEST_ASSERT_EQUAL(5, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + for (uint64_t i = 0; i < 5; i++) { + TEST_ASSERT_EQUAL(i + 10, *(uint64_t *)next_pairs[i].value); + } + + // move next 10 elements into second queue + for (uint64_t i = 15; i < 25; i++) { + value = oha_tpht_set_timeout_slot(table, &i, second_slot); + TEST_ASSERT_NOT_NULL(value); + TEST_ASSERT_EQUAL(i, *value); + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + 50 + i)); + } + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + // timeout all elements of timeout queue 1 + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + 50 + 30)); + TEST_ASSERT_EQUAL(5, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + for (uint64_t i = 0; i < 5; i++) { + TEST_ASSERT_EQUAL(i + 25, *(uint64_t *)next_pairs[i].value); + } + + // check elements of second queue, the orgin timestamp keep untouched + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + 200 + 14)); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + 200 + 14 + 10)); + TEST_ASSERT_EQUAL(10, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + for (uint64_t i = 0; i < 10; i++) { + TEST_ASSERT_EQUAL(i + 15, *(uint64_t *)next_pairs[i].value); + } + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000000)); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); +} + +void test_simple_insert_look_up_remove() +{ + for (size_t elems = 1; elems < 100; elems++) { + + struct oha_tpht_config config; + memset(&config, 0, sizeof(config)); + config.lpht_config.load_factor = LOAF_FACTOR; + config.lpht_config.key_size = sizeof(uint64_t); + config.lpht_config.value_size = sizeof(uint64_t); + config.lpht_config.max_elems = elems; + + struct oha_tpht * table = oha_tpht_create(&config); + + for (uint64_t i = 0; i < elems; i++) { + TEST_ASSERT_NULL(oha_tpht_look_up(table, &i)); + uint64_t * value_insert = oha_tpht_insert(table, &i, 0); + TEST_ASSERT_NOT_NULL(value_insert); + *value_insert = i; + TEST_ASSERT_NOT_NULL(oha_tpht_look_up(table, &i)); + } + + TEST_ASSERT_NULL(oha_tpht_insert(table, &elems, 0)); + + for (uint64_t i = 0; i < elems; i++) { + for (uint64_t j = 0; j < i; j++) { + uint64_t * value_lool_up = oha_tpht_look_up(table, &j); + TEST_ASSERT_NULL(value_lool_up); + } + for (uint64_t j = i; j < elems; j++) { + uint64_t * value_lool_up = oha_tpht_look_up(table, &j); + TEST_ASSERT_NOT_NULL(value_lool_up); + } + uint64_t * removed_value = oha_tpht_remove(table, &i); + TEST_ASSERT_NOT_NULL(removed_value); + TEST_ASSERT_EQUAL_UINT64(*removed_value, i); + *removed_value = (uint64_t)-1; + TEST_ASSERT_NULL(oha_tpht_look_up(table, &i)); + TEST_ASSERT_NULL(oha_tpht_remove(table, &i)); + } + + oha_tpht_destroy(table); + } +} + +void test_insert_look_up_remove_with_timeout_slot() +{ + for (size_t elems = 1; elems < 200; elems++) { + + struct oha_tpht_config config; + memset(&config, 0, sizeof(config)); + config.lpht_config.load_factor = LOAF_FACTOR; + config.lpht_config.key_size = sizeof(uint64_t); + config.lpht_config.value_size = sizeof(uint64_t); + config.lpht_config.max_elems = elems; + + struct oha_tpht * table = oha_tpht_create(&config); + + TEST_ASSERT_EQUAL(1, oha_tpht_add_timeout_slot(table, 1000, elems, false)); + + for (uint64_t i = 0; i < elems; i++) { + uint64_t * value_insert = oha_tpht_insert(table, &i, 1); + TEST_ASSERT_NOT_NULL(value_insert); + *value_insert = i; + } + + TEST_ASSERT_NULL(oha_tpht_insert(table, &elems, 1)); + + for (uint64_t i = 0; i < elems; i++) { + for (uint64_t j = 0; j < i; j++) { + uint64_t * value_lool_up = oha_tpht_look_up(table, &j); + TEST_ASSERT_NULL(value_lool_up); + } + for (uint64_t j = i; j < elems; j++) { + uint64_t * value_lool_up = oha_tpht_look_up(table, &j); + TEST_ASSERT_NOT_NULL(value_lool_up); + } + uint64_t * removed_value = oha_tpht_remove(table, &i); + TEST_ASSERT_NOT_NULL(removed_value); + TEST_ASSERT_EQUAL_UINT64(*removed_value, i); + *removed_value = (uint64_t)-1; + TEST_ASSERT_NULL(oha_tpht_look_up(table, &i)); + TEST_ASSERT_NULL(oha_tpht_remove(table, &i)); + } + + oha_tpht_destroy(table); + } +} + +void test_insert_look_up_timeout() +{ + const size_t num_timeouts = 1000; + struct oha_key_value_pair next_pairs[num_timeouts]; + for (size_t elems = 1; elems < 200; elems++) { + + struct oha_tpht_config config; + memset(&config, 0, sizeof(config)); + config.lpht_config.load_factor = LOAF_FACTOR; + config.lpht_config.key_size = sizeof(uint64_t); + config.lpht_config.value_size = sizeof(uint64_t); + config.lpht_config.max_elems = elems; + + struct oha_tpht * table = oha_tpht_create(&config); + + TEST_ASSERT_EQUAL(1, oha_tpht_add_timeout_slot(table, 1000, elems, false)); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 1000)); + for (uint64_t i = 0; i < elems; i++) { + oha_tpht_increase_global_time(table, 1000 + i); + uint64_t * value_insert = oha_tpht_insert(table, &i, 1); + TEST_ASSERT_NOT_NULL(value_insert); + *value_insert = i; + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + } + + // table is full + TEST_ASSERT_NULL(oha_tpht_insert(table, &elems, 1)); + + for (uint64_t i = 0; i < elems; i++) { + uint64_t * value_lool_up = oha_tpht_look_up(table, &i); + TEST_ASSERT_NOT_NULL(value_lool_up); + TEST_ASSERT_EQUAL_UINT64(*value_lool_up, i); + } + TEST_ASSERT_NULL(oha_tpht_look_up(table, &elems)); + + for (uint64_t i = 0; i < elems; i++) { + memset(next_pairs, 0, sizeof(struct oha_key_value_pair) * num_timeouts); + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000 + i)); + TEST_ASSERT_NOT_NULL(oha_tpht_look_up(table, &i)); + TEST_ASSERT_EQUAL(1, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + uint64_t * value_timeout = next_pairs[0].value; + TEST_ASSERT_NOT_NULL(value_timeout); + TEST_ASSERT_EQUAL(i, *value_timeout); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + TEST_ASSERT_NULL(oha_tpht_look_up(table, &i)); + } + + oha_tpht_destroy(table); + } +} + +void test_update_time_for_entry() +{ + const size_t num_timeouts = 5; + struct oha_key_value_pair next_pairs[num_timeouts]; + struct oha_tpht_config config; + memset(&config, 0, sizeof(config)); + config.lpht_config.load_factor = LOAF_FACTOR; + config.lpht_config.key_size = sizeof(uint64_t); + config.lpht_config.value_size = sizeof(uint64_t); + config.lpht_config.max_elems = num_timeouts; + + struct oha_tpht * table = oha_tpht_create(&config); + + TEST_ASSERT_EQUAL(1, oha_tpht_add_timeout_slot(table, 1000, num_timeouts, false)); + + uint64_t key = 7; + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 1000)); + uint64_t * value_insert = oha_tpht_insert(table, &key, 1); + *value_insert = key; + + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + key = 9; + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 1001)); + value_insert = oha_tpht_insert(table, &key, 1); + *value_insert = key; + + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 1999)); + + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + key = 7; + uint64_t * value_update = oha_tpht_update_time_for_entry(table, &key, 2500); + TEST_ASSERT_NOT_NULL(value_update); + TEST_ASSERT_EQUAL(7, *value_update); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2000)); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 2001)); + TEST_ASSERT_EQUAL(1, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + TEST_ASSERT_EQUAL(9, *(uint64_t *)next_pairs->value); + + oha_tpht_increase_global_time(table, 3499); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 3500)); + TEST_ASSERT_EQUAL(1, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + TEST_ASSERT_EQUAL(7, *(uint64_t *)next_pairs->value); + + TEST_ASSERT_TRUE(oha_tpht_increase_global_time(table, 4000)); + TEST_ASSERT_EQUAL(0, oha_tpht_next_timeout_entries(table, next_pairs, num_timeouts)); + + oha_tpht_destroy(table); +} + int main(void) { UNITY_BEGIN(); RUN_TEST(test_create_destroy); RUN_TEST(test_create_destroy_add_slot); + RUN_TEST(test_set_timeout_slot); + RUN_TEST(test_simple_insert_look_up_remove); + RUN_TEST(test_insert_look_up_remove_with_timeout_slot); + RUN_TEST(test_insert_look_up_timeout); + RUN_TEST(test_update_time_for_entry); return UNITY_END(); }