|
|
@ -42,11 +42,13 @@ class CuckooBuilderTest : public testing::Test { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CheckFileContents(const std::vector<std::string>& keys, |
|
|
|
void CheckFileContents(const std::vector<std::string>& keys, |
|
|
|
const std::vector<std::string>& values, |
|
|
|
const std::vector<std::string>& values, |
|
|
|
const std::vector<uint64_t>& expected_locations, |
|
|
|
const std::vector<uint64_t>& expected_locations, |
|
|
|
std::string expected_unused_bucket, uint64_t expected_table_size, |
|
|
|
std::string expected_unused_bucket, |
|
|
|
uint32_t expected_num_hash_func, bool expected_is_last_level, |
|
|
|
uint64_t expected_table_size, |
|
|
|
uint32_t expected_cuckoo_block_size = 1) { |
|
|
|
uint32_t expected_num_hash_func, |
|
|
|
|
|
|
|
bool expected_is_last_level, |
|
|
|
|
|
|
|
uint32_t expected_cuckoo_block_size = 1) { |
|
|
|
uint64_t num_deletions = 0; |
|
|
|
uint64_t num_deletions = 0; |
|
|
|
for (const auto& key : keys) { |
|
|
|
for (const auto& key : keys) { |
|
|
|
ParsedInternalKey parsed; |
|
|
|
ParsedInternalKey parsed; |
|
|
@ -72,39 +74,44 @@ class CuckooBuilderTest : public testing::Test { |
|
|
|
ASSERT_OK(ReadTableProperties(file_reader.get(), read_file_size, |
|
|
|
ASSERT_OK(ReadTableProperties(file_reader.get(), read_file_size, |
|
|
|
kCuckooTableMagicNumber, ioptions, &props)); |
|
|
|
kCuckooTableMagicNumber, ioptions, &props)); |
|
|
|
// Check unused bucket.
|
|
|
|
// Check unused bucket.
|
|
|
|
std::string unused_key = props->user_collected_properties[ |
|
|
|
std::string unused_key = |
|
|
|
CuckooTablePropertyNames::kEmptyKey]; |
|
|
|
props->user_collected_properties[CuckooTablePropertyNames::kEmptyKey]; |
|
|
|
ASSERT_EQ(expected_unused_bucket.substr(0, |
|
|
|
ASSERT_EQ(expected_unused_bucket.substr(0, props->fixed_key_len), |
|
|
|
props->fixed_key_len), unused_key); |
|
|
|
unused_key); |
|
|
|
|
|
|
|
|
|
|
|
uint64_t value_len_found = |
|
|
|
uint64_t value_len_found = *reinterpret_cast<const uint64_t*>( |
|
|
|
*reinterpret_cast<const uint64_t*>(props->user_collected_properties[ |
|
|
|
props->user_collected_properties[CuckooTablePropertyNames::kValueLength] |
|
|
|
CuckooTablePropertyNames::kValueLength].data()); |
|
|
|
.data()); |
|
|
|
ASSERT_EQ(values.empty() ? 0 : values[0].size(), value_len_found); |
|
|
|
ASSERT_EQ(values.empty() ? 0 : values[0].size(), value_len_found); |
|
|
|
ASSERT_EQ(props->raw_value_size, values.size()*value_len_found); |
|
|
|
ASSERT_EQ(props->raw_value_size, values.size() * value_len_found); |
|
|
|
const uint64_t table_size = |
|
|
|
const uint64_t table_size = *reinterpret_cast<const uint64_t*>( |
|
|
|
*reinterpret_cast<const uint64_t*>(props->user_collected_properties[ |
|
|
|
props |
|
|
|
CuckooTablePropertyNames::kHashTableSize].data()); |
|
|
|
->user_collected_properties |
|
|
|
|
|
|
|
[CuckooTablePropertyNames::kHashTableSize] |
|
|
|
|
|
|
|
.data()); |
|
|
|
ASSERT_EQ(expected_table_size, table_size); |
|
|
|
ASSERT_EQ(expected_table_size, table_size); |
|
|
|
const uint32_t num_hash_func_found = |
|
|
|
const uint32_t num_hash_func_found = *reinterpret_cast<const uint32_t*>( |
|
|
|
*reinterpret_cast<const uint32_t*>(props->user_collected_properties[ |
|
|
|
props->user_collected_properties[CuckooTablePropertyNames::kNumHashFunc] |
|
|
|
CuckooTablePropertyNames::kNumHashFunc].data()); |
|
|
|
.data()); |
|
|
|
ASSERT_EQ(expected_num_hash_func, num_hash_func_found); |
|
|
|
ASSERT_EQ(expected_num_hash_func, num_hash_func_found); |
|
|
|
const uint32_t cuckoo_block_size = |
|
|
|
const uint32_t cuckoo_block_size = *reinterpret_cast<const uint32_t*>( |
|
|
|
*reinterpret_cast<const uint32_t*>(props->user_collected_properties[ |
|
|
|
props |
|
|
|
CuckooTablePropertyNames::kCuckooBlockSize].data()); |
|
|
|
->user_collected_properties |
|
|
|
|
|
|
|
[CuckooTablePropertyNames::kCuckooBlockSize] |
|
|
|
|
|
|
|
.data()); |
|
|
|
ASSERT_EQ(expected_cuckoo_block_size, cuckoo_block_size); |
|
|
|
ASSERT_EQ(expected_cuckoo_block_size, cuckoo_block_size); |
|
|
|
const bool is_last_level_found = |
|
|
|
const bool is_last_level_found = *reinterpret_cast<const bool*>( |
|
|
|
*reinterpret_cast<const bool*>(props->user_collected_properties[ |
|
|
|
props->user_collected_properties[CuckooTablePropertyNames::kIsLastLevel] |
|
|
|
CuckooTablePropertyNames::kIsLastLevel].data()); |
|
|
|
.data()); |
|
|
|
ASSERT_EQ(expected_is_last_level, is_last_level_found); |
|
|
|
ASSERT_EQ(expected_is_last_level, is_last_level_found); |
|
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(props->num_entries, keys.size()); |
|
|
|
ASSERT_EQ(props->num_entries, keys.size()); |
|
|
|
ASSERT_EQ(props->num_deletions, num_deletions); |
|
|
|
ASSERT_EQ(props->num_deletions, num_deletions); |
|
|
|
ASSERT_EQ(props->fixed_key_len, keys.empty() ? 0 : keys[0].size()); |
|
|
|
ASSERT_EQ(props->fixed_key_len, keys.empty() ? 0 : keys[0].size()); |
|
|
|
ASSERT_EQ(props->data_size, expected_unused_bucket.size() * |
|
|
|
ASSERT_EQ(props->data_size, |
|
|
|
(expected_table_size + expected_cuckoo_block_size - 1)); |
|
|
|
expected_unused_bucket.size() * |
|
|
|
ASSERT_EQ(props->raw_key_size, keys.size()*props->fixed_key_len); |
|
|
|
(expected_table_size + expected_cuckoo_block_size - 1)); |
|
|
|
|
|
|
|
ASSERT_EQ(props->raw_key_size, keys.size() * props->fixed_key_len); |
|
|
|
ASSERT_EQ(props->column_family_id, 0); |
|
|
|
ASSERT_EQ(props->column_family_id, 0); |
|
|
|
ASSERT_EQ(props->column_family_name, kDefaultColumnFamilyName); |
|
|
|
ASSERT_EQ(props->column_family_name, kDefaultColumnFamilyName); |
|
|
|
|
|
|
|
|
|
|
@ -156,7 +163,6 @@ class CuckooBuilderTest : public testing::Test { |
|
|
|
return NextPowOf2(static_cast<uint64_t>(num / kHashTableRatio)); |
|
|
|
return NextPowOf2(static_cast<uint64_t>(num / kHashTableRatio)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Env* env_; |
|
|
|
Env* env_; |
|
|
|
FileOptions file_options_; |
|
|
|
FileOptions file_options_; |
|
|
|
std::string fname; |
|
|
|
std::string fname; |
|
|
@ -276,8 +282,8 @@ TEST_F(CuckooBuilderTest, WriteSuccessWithCollisionFullKey) { |
|
|
|
|
|
|
|
|
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
CheckFileContents(keys, values, expected_locations, |
|
|
|
CheckFileContents(keys, values, expected_locations, expected_unused_bucket, |
|
|
|
expected_unused_bucket, expected_table_size, 4, false); |
|
|
|
expected_table_size, 4, false); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(CuckooBuilderTest, WriteSuccessWithCollisionAndCuckooBlock) { |
|
|
|
TEST_F(CuckooBuilderTest, WriteSuccessWithCollisionAndCuckooBlock) { |
|
|
@ -324,8 +330,8 @@ TEST_F(CuckooBuilderTest, WriteSuccessWithCollisionAndCuckooBlock) { |
|
|
|
|
|
|
|
|
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
CheckFileContents(keys, values, expected_locations, |
|
|
|
CheckFileContents(keys, values, expected_locations, expected_unused_bucket, |
|
|
|
expected_unused_bucket, expected_table_size, 3, false, cuckoo_block_size); |
|
|
|
expected_table_size, 3, false, cuckoo_block_size); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(CuckooBuilderTest, WithCollisionPathFullKey) { |
|
|
|
TEST_F(CuckooBuilderTest, WithCollisionPathFullKey) { |
|
|
@ -333,17 +339,14 @@ TEST_F(CuckooBuilderTest, WithCollisionPathFullKey) { |
|
|
|
// Finally insert an element with hash value somewhere in the middle
|
|
|
|
// Finally insert an element with hash value somewhere in the middle
|
|
|
|
// so that it displaces all the elements after that.
|
|
|
|
// so that it displaces all the elements after that.
|
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", "key04", |
|
|
|
"key04", "key05"}; |
|
|
|
"key05"}; |
|
|
|
std::vector<std::string> values = {"v01", "v02", "v03", "v04", "v05"}; |
|
|
|
std::vector<std::string> values = {"v01", "v02", "v03", "v04", "v05"}; |
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
{user_keys[0], {0, 1}}, |
|
|
|
{user_keys[0], {0, 1}}, {user_keys[1], {1, 2}}, {user_keys[2], {2, 3}}, |
|
|
|
{user_keys[1], {1, 2}}, |
|
|
|
{user_keys[3], {3, 4}}, {user_keys[4], {0, 2}}, |
|
|
|
{user_keys[2], {2, 3}}, |
|
|
|
|
|
|
|
{user_keys[3], {3, 4}}, |
|
|
|
|
|
|
|
{user_keys[4], {0, 2}}, |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
hash_map = std::move(hm); |
|
|
|
hash_map = std::move(hm); |
|
|
|
|
|
|
|
|
|
|
@ -376,23 +379,20 @@ TEST_F(CuckooBuilderTest, WithCollisionPathFullKey) { |
|
|
|
|
|
|
|
|
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
CheckFileContents(keys, values, expected_locations, |
|
|
|
CheckFileContents(keys, values, expected_locations, expected_unused_bucket, |
|
|
|
expected_unused_bucket, expected_table_size, 2, false); |
|
|
|
expected_table_size, 2, false); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(CuckooBuilderTest, WithCollisionPathFullKeyAndCuckooBlock) { |
|
|
|
TEST_F(CuckooBuilderTest, WithCollisionPathFullKeyAndCuckooBlock) { |
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", "key04", |
|
|
|
"key04", "key05"}; |
|
|
|
"key05"}; |
|
|
|
std::vector<std::string> values = {"v01", "v02", "v03", "v04", "v05"}; |
|
|
|
std::vector<std::string> values = {"v01", "v02", "v03", "v04", "v05"}; |
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
{user_keys[0], {0, 1}}, |
|
|
|
{user_keys[0], {0, 1}}, {user_keys[1], {1, 2}}, {user_keys[2], {3, 4}}, |
|
|
|
{user_keys[1], {1, 2}}, |
|
|
|
{user_keys[3], {4, 5}}, {user_keys[4], {0, 3}}, |
|
|
|
{user_keys[2], {3, 4}}, |
|
|
|
|
|
|
|
{user_keys[3], {4, 5}}, |
|
|
|
|
|
|
|
{user_keys[4], {0, 3}}, |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
hash_map = std::move(hm); |
|
|
|
hash_map = std::move(hm); |
|
|
|
|
|
|
|
|
|
|
@ -425,8 +425,8 @@ TEST_F(CuckooBuilderTest, WithCollisionPathFullKeyAndCuckooBlock) { |
|
|
|
|
|
|
|
|
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
std::string expected_unused_bucket = GetInternalKey("key00", true); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
CheckFileContents(keys, values, expected_locations, |
|
|
|
CheckFileContents(keys, values, expected_locations, expected_unused_bucket, |
|
|
|
expected_unused_bucket, expected_table_size, 2, false, 2); |
|
|
|
expected_table_size, 2, false, 2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) { |
|
|
|
TEST_F(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) { |
|
|
@ -469,7 +469,7 @@ TEST_F(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) { |
|
|
|
std::string expected_unused_bucket = "key00"; |
|
|
|
std::string expected_unused_bucket = "key00"; |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
CheckFileContents(user_keys, values, expected_locations, |
|
|
|
CheckFileContents(user_keys, values, expected_locations, |
|
|
|
expected_unused_bucket, expected_table_size, 2, true); |
|
|
|
expected_unused_bucket, expected_table_size, 2, true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) { |
|
|
|
TEST_F(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) { |
|
|
@ -513,22 +513,19 @@ TEST_F(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) { |
|
|
|
std::string expected_unused_bucket = "key00"; |
|
|
|
std::string expected_unused_bucket = "key00"; |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
CheckFileContents(user_keys, values, expected_locations, |
|
|
|
CheckFileContents(user_keys, values, expected_locations, |
|
|
|
expected_unused_bucket, expected_table_size, 4, true); |
|
|
|
expected_unused_bucket, expected_table_size, 4, true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(CuckooBuilderTest, WithCollisionPathUserKey) { |
|
|
|
TEST_F(CuckooBuilderTest, WithCollisionPathUserKey) { |
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", "key04", |
|
|
|
"key04", "key05"}; |
|
|
|
"key05"}; |
|
|
|
std::vector<std::string> values = {"v01", "v02", "v03", "v04", "v05"}; |
|
|
|
std::vector<std::string> values = {"v01", "v02", "v03", "v04", "v05"}; |
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
{user_keys[0], {0, 1}}, |
|
|
|
{user_keys[0], {0, 1}}, {user_keys[1], {1, 2}}, {user_keys[2], {2, 3}}, |
|
|
|
{user_keys[1], {1, 2}}, |
|
|
|
{user_keys[3], {3, 4}}, {user_keys[4], {0, 2}}, |
|
|
|
{user_keys[2], {2, 3}}, |
|
|
|
|
|
|
|
{user_keys[3], {3, 4}}, |
|
|
|
|
|
|
|
{user_keys[4], {0, 2}}, |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
hash_map = std::move(hm); |
|
|
|
hash_map = std::move(hm); |
|
|
|
|
|
|
|
|
|
|
@ -559,7 +556,7 @@ TEST_F(CuckooBuilderTest, WithCollisionPathUserKey) { |
|
|
|
std::string expected_unused_bucket = "key00"; |
|
|
|
std::string expected_unused_bucket = "key00"; |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
expected_unused_bucket += std::string(values[0].size(), 'a'); |
|
|
|
CheckFileContents(user_keys, values, expected_locations, |
|
|
|
CheckFileContents(user_keys, values, expected_locations, |
|
|
|
expected_unused_bucket, expected_table_size, 2, true); |
|
|
|
expected_unused_bucket, expected_table_size, 2, true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(CuckooBuilderTest, FailWhenCollisionPathTooLong) { |
|
|
|
TEST_F(CuckooBuilderTest, FailWhenCollisionPathTooLong) { |
|
|
@ -567,16 +564,13 @@ TEST_F(CuckooBuilderTest, FailWhenCollisionPathTooLong) { |
|
|
|
// Finally try inserting an element with hash value somewhere in the middle
|
|
|
|
// Finally try inserting an element with hash value somewhere in the middle
|
|
|
|
// and it should fail because the no. of elements to displace is too high.
|
|
|
|
// and it should fail because the no. of elements to displace is too high.
|
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
uint32_t num_hash_fun = 2; |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", |
|
|
|
std::vector<std::string> user_keys = {"key01", "key02", "key03", "key04", |
|
|
|
"key04", "key05"}; |
|
|
|
"key05"}; |
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// Need to have a temporary variable here as VS compiler does not currently
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
// support operator= with initializer_list as a parameter
|
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
std::unordered_map<std::string, std::vector<uint64_t>> hm = { |
|
|
|
{user_keys[0], {0, 1}}, |
|
|
|
{user_keys[0], {0, 1}}, {user_keys[1], {1, 2}}, {user_keys[2], {2, 3}}, |
|
|
|
{user_keys[1], {1, 2}}, |
|
|
|
{user_keys[3], {3, 4}}, {user_keys[4], {0, 1}}, |
|
|
|
{user_keys[2], {2, 3}}, |
|
|
|
|
|
|
|
{user_keys[3], {3, 4}}, |
|
|
|
|
|
|
|
{user_keys[4], {0, 1}}, |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
hash_map = std::move(hm); |
|
|
|
hash_map = std::move(hm); |
|
|
|
|
|
|
|
|
|
|
|