@ -37,8 +37,9 @@ class CuckooBuilderTest {
void CheckFileContents ( const std : : vector < std : : string > & keys ,
const std : : vector < std : : string > & values ,
const std : : vector < uint64_t > & expected_locations ,
std : : string expected_unused_bucket , uint64_t expected_max_buckets ,
uint32_t expected_num_hash_fun , bool expected_is_last_level ) {
std : : string expected_unused_bucket , uint64_t expected_table_size ,
uint32_t expected_num_hash_func , bool expected_is_last_level ,
uint32_t expected_cuckoo_block_size = 1 ) {
// Read file
unique_ptr < RandomAccessFile > read_file ;
ASSERT_OK ( env_ - > NewRandomAccessFile ( fname , & read_file , env_options_ ) ) ;
@ -51,7 +52,8 @@ class CuckooBuilderTest {
kCuckooTableMagicNumber , env_ , nullptr , & props ) ) ;
ASSERT_EQ ( props - > num_entries , keys . size ( ) ) ;
ASSERT_EQ ( props - > fixed_key_len , keys . empty ( ) ? 0 : keys [ 0 ] . size ( ) ) ;
ASSERT_EQ ( props - > data_size , keys . size ( ) * expected_unused_bucket . size ( ) ) ;
ASSERT_EQ ( props - > data_size , expected_unused_bucket . size ( ) *
( expected_table_size + expected_cuckoo_block_size - 1 ) ) ;
ASSERT_EQ ( props - > raw_key_size , keys . size ( ) * props - > fixed_key_len ) ;
// Check unused bucket.
@ -65,14 +67,18 @@ class CuckooBuilderTest {
CuckooTablePropertyNames : : kValueLength ] . data ( ) ) ;
ASSERT_EQ ( values . empty ( ) ? 0 : values [ 0 ] . size ( ) , value_len_found ) ;
ASSERT_EQ ( props - > raw_value_size , values . size ( ) * value_len_found ) ;
const uint64_t max_buckets =
const uint64_t table_size =
* reinterpret_cast < const uint64_t * > ( props - > user_collected_properties [
CuckooTablePropertyNames : : kMaxNumBuckets ] . data ( ) ) ;
ASSERT_EQ ( expected_max_buckets , max_buckets ) ;
const uint32_t num_hash_fun_found =
CuckooTablePropertyNames : : kHashTableSize ] . data ( ) ) ;
ASSERT_EQ ( expected_table_size , table_size ) ;
const uint32_t num_hash_func _found =
* reinterpret_cast < const uint32_t * > ( props - > user_collected_properties [
CuckooTablePropertyNames : : kNumHashTable ] . data ( ) ) ;
ASSERT_EQ ( expected_num_hash_fun , num_hash_fun_found ) ;
CuckooTablePropertyNames : : kNumHashFunc ] . data ( ) ) ;
ASSERT_EQ ( expected_num_hash_func , num_hash_func_found ) ;
const uint32_t cuckoo_block_size =
* reinterpret_cast < const uint32_t * > ( props - > user_collected_properties [
CuckooTablePropertyNames : : kCuckooBlockSize ] . data ( ) ) ;
ASSERT_EQ ( expected_cuckoo_block_size , cuckoo_block_size ) ;
const bool is_last_level_found =
* reinterpret_cast < const bool * > ( props - > user_collected_properties [
CuckooTablePropertyNames : : kIsLastLevel ] . data ( ) ) ;
@ -82,7 +88,7 @@ class CuckooBuilderTest {
// Check contents of the bucket.
std : : vector < bool > keys_found ( keys . size ( ) , false ) ;
uint32_t bucket_size = expected_unused_bucket . size ( ) ;
for ( uint32_t i = 0 ; i < max_buckets ; + + i ) {
for ( uint32_t i = 0 ; i < table_size + cuckoo_block_size - 1 ; + + i ) {
Slice read_slice ;
ASSERT_OK ( read_file - > Read ( i * bucket_size , bucket_size ,
& read_slice , nullptr ) ) ;
@ -119,7 +125,7 @@ TEST(CuckooBuilderTest, SuccessWithEmptyFile) {
fname = test : : TmpDir ( ) + " /NoCollisionFullKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
4 , 100 , BytewiseComparator ( ) , GetSliceHash ) ;
4 , 100 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
@ -146,7 +152,7 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionFullKey) {
fname = test : : TmpDir ( ) + " /NoCollisionFullKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 100 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( keys [ i ] ) , Slice ( values [ i ] ) ) ;
@ -156,11 +162,11 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionFullKey) {
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_max_buckets = keys . size ( ) / kHashTableRatio ;
uint32_t expected_table_size = keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = GetInternalKey ( " key00 " , true ) ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( keys , values , expected_locations ,
expected_unused_bucket , expected_max_buckets , 2 , false ) ;
expected_unused_bucket , expected_table_size , 2 , false ) ;
}
TEST ( CuckooBuilderTest , WriteSuccessWithCollisionFullKey ) {
@ -183,7 +189,7 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionFullKey) {
fname = test : : TmpDir ( ) + " /WithCollisionFullKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 100 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( keys [ i ] ) , Slice ( values [ i ] ) ) ;
@ -193,11 +199,49 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionFullKey) {
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_max_buckets = keys . size ( ) / kHashTableRatio ;
uint32_t expected_table_size = keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = GetInternalKey ( " key00 " , true ) ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( keys , values , expected_locations ,
expected_unused_bucket , expected_max_buckets , 4 , false ) ;
expected_unused_bucket , expected_table_size , 4 , false ) ;
}
TEST ( CuckooBuilderTest , WriteSuccessWithCollisionAndCuckooBlock ) {
uint32_t num_hash_fun = 4 ;
std : : vector < std : : string > user_keys = { " key01 " , " key02 " , " key03 " , " key04 " } ;
std : : vector < std : : string > values = { " v01 " , " v02 " , " v03 " , " v04 " } ;
hash_map = {
{ user_keys [ 0 ] , { 0 , 1 , 2 , 3 } } ,
{ user_keys [ 1 ] , { 0 , 1 , 2 , 3 } } ,
{ user_keys [ 2 ] , { 0 , 1 , 2 , 3 } } ,
{ user_keys [ 3 ] , { 0 , 1 , 2 , 3 } } ,
} ;
std : : vector < uint64_t > expected_locations = { 0 , 1 , 2 , 3 } ;
std : : vector < std : : string > keys ;
for ( auto & user_key : user_keys ) {
keys . push_back ( GetInternalKey ( user_key , false ) ) ;
}
unique_ptr < WritableFile > writable_file ;
uint32_t cuckoo_block_size = 2 ;
fname = test : : TmpDir ( ) + " /WithCollisionFullKey2 " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , cuckoo_block_size , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( keys [ i ] ) , Slice ( values [ i ] ) ) ;
ASSERT_EQ ( builder . NumEntries ( ) , i + 1 ) ;
ASSERT_OK ( builder . status ( ) ) ;
}
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_table_size = keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = GetInternalKey ( " key00 " , true ) ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( keys , values , expected_locations ,
expected_unused_bucket , expected_table_size , 3 , false , cuckoo_block_size ) ;
}
TEST ( CuckooBuilderTest , WithCollisionPathFullKey ) {
@ -225,7 +269,46 @@ TEST(CuckooBuilderTest, WithCollisionPathFullKey) {
fname = test : : TmpDir ( ) + " /WithCollisionPathFullKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 100 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( keys [ i ] ) , Slice ( values [ i ] ) ) ;
ASSERT_EQ ( builder . NumEntries ( ) , i + 1 ) ;
ASSERT_OK ( builder . status ( ) ) ;
}
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_table_size = keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = GetInternalKey ( " key00 " , true ) ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( keys , values , expected_locations ,
expected_unused_bucket , expected_table_size , 2 , false ) ;
}
TEST ( CuckooBuilderTest , WithCollisionPathFullKeyAndCuckooBlock ) {
uint32_t num_hash_fun = 2 ;
std : : vector < std : : string > user_keys = { " key01 " , " key02 " , " key03 " ,
" key04 " , " key05 " } ;
std : : vector < std : : string > values = { " v01 " , " v02 " , " v03 " , " v04 " , " v05 " } ;
hash_map = {
{ user_keys [ 0 ] , { 0 , 1 } } ,
{ user_keys [ 1 ] , { 1 , 2 } } ,
{ user_keys [ 2 ] , { 3 , 4 } } ,
{ user_keys [ 3 ] , { 4 , 5 } } ,
{ user_keys [ 4 ] , { 0 , 3 } } ,
} ;
std : : vector < uint64_t > expected_locations = { 2 , 1 , 3 , 4 , 0 } ;
std : : vector < std : : string > keys ;
for ( auto & user_key : user_keys ) {
keys . push_back ( GetInternalKey ( user_key , false ) ) ;
}
unique_ptr < WritableFile > writable_file ;
fname = test : : TmpDir ( ) + " /WithCollisionPathFullKeyAndCuckooBlock " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , 2 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( keys [ i ] ) , Slice ( values [ i ] ) ) ;
@ -235,11 +318,11 @@ TEST(CuckooBuilderTest, WithCollisionPathFullKey) {
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_max_buckets = keys . size ( ) / kHashTableRatio ;
uint32_t expected_table_size = keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = GetInternalKey ( " key00 " , true ) ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( keys , values , expected_locations ,
expected_unused_bucket , expected_max_buckets , 2 , false ) ;
expected_unused_bucket , expected_table_size , 2 , false , 2 ) ;
}
TEST ( CuckooBuilderTest , WriteSuccessNoCollisionUserKey ) {
@ -258,7 +341,7 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) {
fname = test : : TmpDir ( ) + " /NoCollisionUserKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 100 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( GetInternalKey ( user_keys [ i ] , true ) ) , Slice ( values [ i ] ) ) ;
@ -268,11 +351,11 @@ TEST(CuckooBuilderTest, WriteSuccessNoCollisionUserKey) {
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_max_buckets = user_keys . size ( ) / kHashTableRatio ;
uint32_t expected_table_size = user_keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = " key00 " ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( user_keys , values , expected_locations ,
expected_unused_bucket , expected_max_buckets , 2 , true ) ;
expected_unused_bucket , expected_table_size , 2 , true ) ;
}
TEST ( CuckooBuilderTest , WriteSuccessWithCollisionUserKey ) {
@ -291,7 +374,7 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) {
fname = test : : TmpDir ( ) + " /WithCollisionUserKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 100 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( GetInternalKey ( user_keys [ i ] , true ) ) , Slice ( values [ i ] ) ) ;
@ -301,11 +384,11 @@ TEST(CuckooBuilderTest, WriteSuccessWithCollisionUserKey) {
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_max_buckets = user_keys . size ( ) / kHashTableRatio ;
uint32_t expected_table_size = user_keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = " key00 " ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( user_keys , values , expected_locations ,
expected_unused_bucket , expected_max_buckets , 4 , true ) ;
expected_unused_bucket , expected_table_size , 4 , true ) ;
}
TEST ( CuckooBuilderTest , WithCollisionPathUserKey ) {
@ -326,7 +409,7 @@ TEST(CuckooBuilderTest, WithCollisionPathUserKey) {
fname = test : : TmpDir ( ) + " /WithCollisionPathUserKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 2 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 2 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( GetInternalKey ( user_keys [ i ] , true ) ) , Slice ( values [ i ] ) ) ;
@ -336,11 +419,11 @@ TEST(CuckooBuilderTest, WithCollisionPathUserKey) {
ASSERT_OK ( builder . Finish ( ) ) ;
ASSERT_OK ( writable_file - > Close ( ) ) ;
uint32_t expected_max_buckets = user_keys . size ( ) / kHashTableRatio ;
uint32_t expected_table_size = user_keys . size ( ) / kHashTableRatio ;
std : : string expected_unused_bucket = " key00 " ;
expected_unused_bucket + = std : : string ( values [ 0 ] . size ( ) , ' a ' ) ;
CheckFileContents ( user_keys , values , expected_locations ,
expected_unused_bucket , expected_max_buckets , 2 , true ) ;
expected_unused_bucket , expected_table_size , 2 , true ) ;
}
TEST ( CuckooBuilderTest , FailWhenCollisionPathTooLong ) {
@ -362,7 +445,7 @@ TEST(CuckooBuilderTest, FailWhenCollisionPathTooLong) {
fname = test : : TmpDir ( ) + " /WithCollisionPathUserKey " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 2 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 2 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
for ( uint32_t i = 0 ; i < user_keys . size ( ) ; i + + ) {
builder . Add ( Slice ( GetInternalKey ( user_keys [ i ] , false ) ) , Slice ( " value " ) ) ;
@ -382,7 +465,7 @@ TEST(CuckooBuilderTest, FailWhenSameKeyInserted) {
fname = test : : TmpDir ( ) + " /FailWhenSameKeyInserted " ;
ASSERT_OK ( env_ - > NewWritableFile ( fname , & writable_file , env_options_ ) ) ;
CuckooTableBuilder builder ( writable_file . get ( ) , kHashTableRatio ,
num_hash_fun , 100 , BytewiseComparator ( ) , GetSliceHash ) ;
num_hash_fun , 100 , BytewiseComparator ( ) , 1 , GetSliceHash ) ;
ASSERT_OK ( builder . status ( ) ) ;
builder . Add ( Slice ( GetInternalKey ( user_key , false ) ) , Slice ( " value1 " ) ) ;