@ -209,10 +209,16 @@ class HashLinkListRep : public MemTableRep {
bool LinkListContains ( Node * head , const Slice & key ) const ;
bool LinkListContains ( Node * head , const Slice & key ) const ;
SkipListBucketHeader * GetSkipListBucketHeader ( Pointer * first_next_pointer )
bool IsEmptyBucket ( Pointer & bucket_pointer ) const {
const ;
return bucket_pointer . load ( std : : memory_order_acquire ) = = nullptr ;
}
// Precondition: GetLinkListFirstNode() must have been called first and return
// null so that it must be a skip list bucket
SkipListBucketHeader * GetSkipListBucketHeader ( Pointer & bucket_pointer ) const ;
Node * GetLinkListFirstNode ( Pointer * first_next_pointer ) const ;
// Returning nullptr indicates it is a skip list bucket.
Node * GetLinkListFirstNode ( Pointer & bucket_pointer ) const ;
Slice GetPrefix ( const Slice & internal_key ) const {
Slice GetPrefix ( const Slice & internal_key ) const {
return transform_ - > Transform ( ExtractUserKey ( internal_key ) ) ;
return transform_ - > Transform ( ExtractUserKey ( internal_key ) ) ;
@ -222,11 +228,9 @@ class HashLinkListRep : public MemTableRep {
return GetSliceRangedNPHash ( slice , bucket_size_ ) ;
return GetSliceRangedNPHash ( slice , bucket_size_ ) ;
}
}
Pointer * GetBucket ( size_t i ) const {
Pointer & GetBucket ( size_t i ) const { return buckets_ [ i ] ; }
return static_cast < Pointer * > ( buckets_ [ i ] . load ( std : : memory_order_acquire ) ) ;
}
Pointer * GetBucket ( const Slice & slice ) const {
Pointer & GetBucket ( const Slice & slice ) const {
return GetBucket ( GetHash ( slice ) ) ;
return GetBucket ( GetHash ( slice ) ) ;
}
}
@ -414,11 +418,24 @@ class HashLinkListRep : public MemTableRep {
// Advance to the first entry with a key >= target
// Advance to the first entry with a key >= target
void Seek ( const Slice & k , const char * memtable_key ) override {
void Seek ( const Slice & k , const char * memtable_key ) override {
auto transformed = memtable_rep_ . GetPrefix ( k ) ;
auto transformed = memtable_rep_ . GetPrefix ( k ) ;
auto * bucket = memtable_rep_ . GetBucket ( transformed ) ;
Pointer & bucket = memtable_rep_ . GetBucket ( transformed ) ;
if ( memtable_rep_ . IsEmptyBucket ( bucket ) ) {
skip_list_iter_ . reset ( ) ;
Reset ( nullptr ) ;
} else {
Node * first_linked_list_node =
memtable_rep_ . GetLinkListFirstNode ( bucket ) ;
if ( first_linked_list_node ! = nullptr ) {
// The bucket is organized as a linked list
skip_list_iter_ . reset ( ) ;
Reset ( first_linked_list_node ) ;
HashLinkListRep : : LinkListIterator : : Seek ( k , memtable_key ) ;
} else {
SkipListBucketHeader * skip_list_header =
SkipListBucketHeader * skip_list_header =
memtable_rep_ . GetSkipListBucketHeader ( bucket ) ;
memtable_rep_ . GetSkipListBucketHeader ( bucket ) ;
if ( skip_list_header ! = nullptr ) {
assert ( skip_list_header ! = nullptr ) ;
// The bucket is organized as a skip list
// The bucket is organized as a skip list
if ( ! skip_list_iter_ ) {
if ( ! skip_list_iter_ ) {
skip_list_iter_ . reset (
skip_list_iter_ . reset (
@ -433,11 +450,7 @@ class HashLinkListRep : public MemTableRep {
encoded_key . EncodeLengthPrefixedKey ( k ) ;
encoded_key . EncodeLengthPrefixedKey ( k ) ;
skip_list_iter_ - > Seek ( encoded_key . GetUserKey ( ) . data ( ) ) ;
skip_list_iter_ - > Seek ( encoded_key . GetUserKey ( ) . data ( ) ) ;
}
}
} else {
}
// The bucket is organized as a linked list
skip_list_iter_ . reset ( ) ;
Reset ( memtable_rep_ . GetLinkListFirstNode ( bucket ) ) ;
HashLinkListRep : : LinkListIterator : : Seek ( k , memtable_key ) ;
}
}
}
}
@ -528,36 +541,38 @@ KeyHandle HashLinkListRep::Allocate(const size_t len, char** buf) {
}
}
SkipListBucketHeader * HashLinkListRep : : GetSkipListBucketHeader (
SkipListBucketHeader * HashLinkListRep : : GetSkipListBucketHeader (
Pointer * first_next_pointer ) const {
Pointer & bucket_pointer ) const {
if ( first_next_pointer = = nullptr ) {
Pointer * first_next_pointer =
return nullptr ;
static_cast < Pointer * > ( bucket_pointer . load ( std : : memory_order_acquire ) ) ;
}
assert ( first_next_pointer ! = nullptr ) ;
if ( first_next_pointer - > load ( std : : memory_order_relaxed ) = = nullptr ) {
assert ( first_next_pointer - > load ( std : : memory_order_relaxed ) ! = nullptr ) ;
// Single entry bucket
return nullptr ;
}
// Counting header
// Counting header
BucketHeader * header = reinterpret_cast < BucketHeader * > ( first_next_pointer ) ;
BucketHeader * header = reinterpret_cast < BucketHeader * > ( first_next_pointer ) ;
if ( header - > IsSkipListBucket ( ) ) {
assert ( header - > IsSkipListBucket ( ) ) ;
assert ( header - > GetNumEntries ( ) > threshold_use_skiplist_ ) ;
assert ( header - > GetNumEntries ( ) > threshold_use_skiplist_ ) ;
auto * skip_list_bucket_header =
auto * skip_list_bucket_header =
reinterpret_cast < SkipListBucketHeader * > ( header ) ;
reinterpret_cast < SkipListBucketHeader * > ( header ) ;
assert ( skip_list_bucket_header - > Counting_header . next . load (
assert ( skip_list_bucket_header - > Counting_header . next . load (
std : : memory_order_relaxed ) = = header ) ;
std : : memory_order_relaxed ) = = header ) ;
return skip_list_bucket_header ;
return skip_list_bucket_header ;
}
assert ( header - > GetNumEntries ( ) < = threshold_use_skiplist_ ) ;
return nullptr ;
}
}
Node * HashLinkListRep : : GetLinkListFirstNode ( Pointer * first_nex t_pointer) const {
Node * HashLinkListRep : : GetLinkListFirstNode ( Pointer & bucket_pointer ) const {
if ( first_next_pointer = = nullptr ) {
Pointer * first_next_pointer =
return nullptr ;
static_cast < Pointer * > ( bucket_pointer . load ( std : : memory_order_acquire ) ) ;
}
assert ( first_next_pointer ! = nullptr ) ;
if ( first_next_pointer - > load ( std : : memory_order_relaxed ) = = nullptr ) {
if ( first_next_pointer - > load ( std : : memory_order_relaxed ) = = nullptr ) {
// Single entry bucket
// Single entry bucket
return reinterpret_cast < Node * > ( first_next_pointer ) ;
return reinterpret_cast < Node * > ( first_next_pointer ) ;
}
}
// It is possible that after we fetch first_next_pointer it is modified
// and the next is not null anymore. In this case, the bucket should have been
// modified to a counting header, so we should reload the first_next_pointer
// to make sure we see the update.
first_next_pointer =
static_cast < Pointer * > ( bucket_pointer . load ( std : : memory_order_acquire ) ) ;
// Counting header
// Counting header
BucketHeader * header = reinterpret_cast < BucketHeader * > ( first_next_pointer ) ;
BucketHeader * header = reinterpret_cast < BucketHeader * > ( first_next_pointer ) ;
if ( ! header - > IsSkipListBucket ( ) ) {
if ( ! header - > IsSkipListBucket ( ) ) {
@ -695,17 +710,21 @@ bool HashLinkListRep::Contains(const char* key) const {
Slice internal_key = GetLengthPrefixedSlice ( key ) ;
Slice internal_key = GetLengthPrefixedSlice ( key ) ;
auto transformed = GetPrefix ( internal_key ) ;
auto transformed = GetPrefix ( internal_key ) ;
auto bucket = GetBucket ( transformed ) ;
Pointer & bucket = GetBucket ( transformed ) ;
if ( bucket = = nullptr ) {
if ( IsEmptyBucket ( bucket ) ) {
return false ;
return false ;
}
}
Node * linked_list_node = GetLinkListFirstNode ( bucket ) ;
if ( linked_list_node ! = nullptr ) {
return LinkListContains ( linked_list_node , internal_key ) ;
}
SkipListBucketHeader * skip_list_header = GetSkipListBucketHeader ( bucket ) ;
SkipListBucketHeader * skip_list_header = GetSkipListBucketHeader ( bucket ) ;
if ( skip_list_header ! = nullptr ) {
if ( skip_list_header ! = nullptr ) {
return skip_list_header - > skip_list . Contains ( key ) ;
return skip_list_header - > skip_list . Contains ( key ) ;
} else {
return LinkListContains ( GetLinkListFirstNode ( bucket ) , internal_key ) ;
}
}
return false ;
}
}
size_t HashLinkListRep : : ApproximateMemoryUsage ( ) {
size_t HashLinkListRep : : ApproximateMemoryUsage ( ) {
@ -716,17 +735,12 @@ size_t HashLinkListRep::ApproximateMemoryUsage() {
void HashLinkListRep : : Get ( const LookupKey & k , void * callback_args ,
void HashLinkListRep : : Get ( const LookupKey & k , void * callback_args ,
bool ( * callback_func ) ( void * arg , const char * entry ) ) {
bool ( * callback_func ) ( void * arg , const char * entry ) ) {
auto transformed = transform_ - > Transform ( k . user_key ( ) ) ;
auto transformed = transform_ - > Transform ( k . user_key ( ) ) ;
auto bucket = GetBucket ( transformed ) ;
Pointer & bucket = GetBucket ( transformed ) ;
auto * skip_list_header = GetSkipListBucketHeader ( bucket ) ;
if ( IsEmptyBucket ( bucket ) ) {
if ( skip_list_header ! = nullptr ) {
return ;
// Is a skip list
MemtableSkipList : : Iterator iter ( & skip_list_header - > skip_list ) ;
for ( iter . Seek ( k . memtable_key ( ) . data ( ) ) ;
iter . Valid ( ) & & callback_func ( callback_args , iter . key ( ) ) ;
iter . Next ( ) ) {
}
}
} else {
auto * link_list_head = GetLinkListFirstNode ( bucket ) ;
auto * link_list_head = GetLinkListFirstNode ( bucket ) ;
if ( link_list_head ! = nullptr ) {
if ( link_list_head ! = nullptr ) {
LinkListIterator iter ( this , link_list_head ) ;
LinkListIterator iter ( this , link_list_head ) ;
@ -734,6 +748,15 @@ void HashLinkListRep::Get(const LookupKey& k, void* callback_args,
iter . Valid ( ) & & callback_func ( callback_args , iter . key ( ) ) ;
iter . Valid ( ) & & callback_func ( callback_args , iter . key ( ) ) ;
iter . Next ( ) ) {
iter . Next ( ) ) {
}
}
} else {
auto * skip_list_header = GetSkipListBucketHeader ( bucket ) ;
if ( skip_list_header ! = nullptr ) {
// Is a skip list
MemtableSkipList : : Iterator iter ( & skip_list_header - > skip_list ) ;
for ( iter . Seek ( k . memtable_key ( ) . data ( ) ) ;
iter . Valid ( ) & & callback_func ( callback_args , iter . key ( ) ) ;
iter . Next ( ) ) {
}
}
}
}
}
}
}
@ -746,17 +769,8 @@ MemTableRep::Iterator* HashLinkListRep::GetIterator(Arena* alloc_arena) {
for ( size_t i = 0 ; i < bucket_size_ ; + + i ) {
for ( size_t i = 0 ; i < bucket_size_ ; + + i ) {
int count = 0 ;
int count = 0 ;
auto * bucket = GetBucket ( i ) ;
Pointer & bucket = GetBucket ( i ) ;
if ( bucket ! = nullptr ) {
if ( ! IsEmptyBucket ( bucket ) ) {
auto * skip_list_header = GetSkipListBucketHeader ( bucket ) ;
if ( skip_list_header ! = nullptr ) {
// Is a skip list
MemtableSkipList : : Iterator itr ( & skip_list_header - > skip_list ) ;
for ( itr . SeekToFirst ( ) ; itr . Valid ( ) ; itr . Next ( ) ) {
list - > Insert ( itr . key ( ) ) ;
count + + ;
}
} else {
auto * link_list_head = GetLinkListFirstNode ( bucket ) ;
auto * link_list_head = GetLinkListFirstNode ( bucket ) ;
if ( link_list_head ! = nullptr ) {
if ( link_list_head ! = nullptr ) {
LinkListIterator itr ( this , link_list_head ) ;
LinkListIterator itr ( this , link_list_head ) ;
@ -764,6 +778,14 @@ MemTableRep::Iterator* HashLinkListRep::GetIterator(Arena* alloc_arena) {
list - > Insert ( itr . key ( ) ) ;
list - > Insert ( itr . key ( ) ) ;
count + + ;
count + + ;
}
}
} else {
auto * skip_list_header = GetSkipListBucketHeader ( bucket ) ;
assert ( skip_list_header ! = nullptr ) ;
// Is a skip list
MemtableSkipList : : Iterator itr ( & skip_list_header - > skip_list ) ;
for ( itr . SeekToFirst ( ) ; itr . Valid ( ) ; itr . Next ( ) ) {
list - > Insert ( itr . key ( ) ) ;
count + + ;
}
}
}
}
}
}