From f955ca0c5ab798ed2c2d926cf99075095530d03d Mon Sep 17 00:00:00 2001 From: Ilya Verbin <iverbin@tarantool.org> Date: Thu, 28 Mar 2024 17:50:48 +0300 Subject: [PATCH] salad: light: limit table size by (2^32)-8 records This is the maximum possible capacity of a hash table with 32-bit record identifiers and 8-element `LIGHT_GROW_INCREMENT`. Needed for #9864 NO_DOC=see next commit NO_CHANGELOG=see next commit --- src/lib/core/random.c | 9 ++++++ src/lib/core/random.h | 8 ++++++ src/lib/salad/light.h | 9 ++++++ test/unit/CMakeLists.txt | 4 +-- test/unit/light.cc | 59 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/lib/core/random.c b/src/lib/core/random.c index 994230c540..621f462181 100644 --- a/src/lib/core/random.c +++ b/src/lib/core/random.c @@ -148,6 +148,15 @@ xoshiro_random(void) return result; } +void +xoshiro_srand(uint64_t *seed) +{ + state[0] = seed[0]; + state[1] = seed[1]; + state[2] = seed[2]; + state[3] = seed[3]; +} + const char * xoshiro_state_str(void) { diff --git a/src/lib/core/random.h b/src/lib/core/random.h index 9108eab58c..66ef70f2f2 100644 --- a/src/lib/core/random.h +++ b/src/lib/core/random.h @@ -60,6 +60,14 @@ real_random(void); uint64_t xoshiro_random(void); +/** + * Sets the `seed` for a new sequence of pseudo-random integers to be returned + * by xoshiro_random(). These sequences are repeatable by calling xoshiro_srand + * with the same seed value. + */ +void +xoshiro_srand(uint64_t *seed); + const char * xoshiro_state_str(void); diff --git a/src/lib/salad/light.h b/src/lib/salad/light.h index 61c800aaf2..94f53d2e21 100644 --- a/src/lib/salad/light.h +++ b/src/lib/salad/light.h @@ -845,6 +845,15 @@ LIGHT(grow)(struct LIGHT(common) *ht) { assert(!matras_is_read_view_created(ht->view)); assert(ht->empty_slot == LIGHT(end)); + /* + * The number UINT32_MAX has a special meaning (see LIGHT(end)), hence + * it can not be used as a record identifier. Given that the table is + * enlarged by 8 records (see LIGHT_GROW_INCREMENT), the maximum table + * size is limited by (2^32)-8 records. + */ + if ((size_t)ht->table_size + LIGHT_GROW_INCREMENT >= UINT32_MAX) + return -1; + uint32_t new_slot; struct LIGHT(record) *new_record = (struct LIGHT(record) *) matras_alloc_range(ht->mtable, &new_slot, LIGHT_GROW_INCREMENT); diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 03e3a6d9f3..27b52768e2 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -161,8 +161,8 @@ create_unit_test(PREFIX rtree_multidim LIBRARIES salad small ) create_unit_test(PREFIX light - SOURCES light.cc - LIBRARIES small + SOURCES light.cc core_test_utils.c + LIBRARIES core small ) create_unit_test(PREFIX light_view SOURCES light_view.c diff --git a/test/unit/light.cc b/test/unit/light.cc index a0d829b275..521c6cf1e2 100644 --- a/test/unit/light.cc +++ b/test/unit/light.cc @@ -4,6 +4,7 @@ #include <inttypes.h> #include <vector> #include <time.h> +#include <core/random.h> #include "unit.h" @@ -350,16 +351,72 @@ slot_in_big_table_test() footer(); } +/** + * Insert nearly 2^32 records into the hash table. + */ +static void +max_capacity_test() +{ + /* + * XXX: The test is disabled, because it requires 64 GB of RAM. + */ + return; + + header(); + + /* The maximum number of records that can be stored in the table. */ + const size_t data_count = (size_t)UINT32_MAX + 1 - LIGHT_GROW_INCREMENT; + + struct light_core ht; + light_create(&ht, 0, light_extent_size, my_light_alloc, my_light_free, + &extents_count, NULL); + + uint64_t seed[4]; + random_bytes((char *)seed, sizeof(seed)); + + /* Test light_insert(). */ + xoshiro_srand(seed); + for (size_t i = 0; i < data_count; i++) { + hash_value_t val = xoshiro_random(); + hash_t id = light_insert(&ht, hash(val), val); + fail_if(id == light_end); + if ((i & 0xfffff) == 0) + printf("%f%%\n", i * 100.0 / data_count); + } + /* Try to exceed the maximum capacity. */ + hash_value_t val = xoshiro_random(); + hash_t id = light_insert(&ht, hash(val), val); + fail_if(id != light_end); + + /* Test light_find(). */ + xoshiro_srand(seed); + for (size_t i = 0; i < data_count; i++) { + hash_value_t val = xoshiro_random(); + hash_t id = light_find(&ht, hash(val), val); + fail_if(id == light_end); + if ((i & 0xfffff) == 0) + printf("%f%%\n", i * 100.0 / data_count); + } + + light_destroy(&ht); + + footer(); +} + int main(int, const char**) { - srand(time(0)); + random_init(); + simple_test(); collision_test(); iterator_test(); iterator_freeze_check(); slot_in_big_table_test(); + max_capacity_test(); if (extents_count != 0) fail("memory leak!", "true"); + + random_free(); } -- GitLab