@ -2,17 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
# if defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_ANDROID)
# include <unordered_set>
# elif defined(LEVELDB_PLATFORM_OSX)
# include <ext/hash_set>
# elif defined(LEVELDB_PLATFORM_CHROMIUM)
# include "base/hash_tables.h"
# else
# include <hash_set> // TODO(sanjay): Switch to unordered_set when possible.
# endif
# include <assert.h>
# include <stdio.h>
# include <stdlib.h>
# include "leveldb/cache.h"
# include "port/port.h"
@ -33,6 +25,7 @@ namespace {
struct LRUHandle {
void * value ;
void ( * deleter ) ( const Slice & , void * value ) ;
LRUHandle * next_hash ;
LRUHandle * next ;
LRUHandle * prev ;
size_t charge ; // TODO(opt): Only allow uint32_t?
@ -51,43 +44,93 @@ struct LRUHandle {
}
} ;
// Pick a platform specific hash_set instantiation
# if defined(LEVELDB_PLATFORM_CHROMIUM) && defined(OS_WIN)
// Microsoft's hash_set deviates from the standard. See
// http://msdn.microsoft.com/en-us/library/1t4xas78(v=vs.80).aspx
// for details. Basically the 2 param () operator is a less than and
// the 1 param () operator is a hash function.
struct HandleHashCompare : public stdext : : hash_compare < LRUHandle * > {
size_t operator ( ) ( LRUHandle * h ) const {
Slice k = h - > key ( ) ;
return Hash ( k . data ( ) , k . size ( ) , 0 ) ;
// We provide our own simple hash table since it removes a whole bunch
// of porting hacks and is also faster than some of the built-in hash
// table implementations in some of the compiler/runtime combinations
// we have tested. E.g., readrandom speeds up by ~5% over the g++
// 4.4.3's builtin hashtable.
class HandleTable {
public :
HandleTable ( ) : length_ ( 0 ) , elems_ ( 0 ) , list_ ( NULL ) { Resize ( ) ; }
~ HandleTable ( ) { delete [ ] list_ ; }
LRUHandle * Lookup ( LRUHandle * h ) {
return * FindPointer ( h ) ;
}
LRUHandle * Insert ( LRUHandle * h ) {
LRUHandle * * ptr = FindPointer ( h ) ;
LRUHandle * old = * ptr ;
h - > next_hash = ( old = = NULL ? NULL : old - > next_hash ) ;
* ptr = h ;
if ( old = = NULL ) {
+ + elems_ ;
if ( elems_ > length_ ) {
// Since each cache entry is fairly large, we aim for a small
// average linked list length (<= 1).
Resize ( ) ;
}
}
bool operator ( ) ( LRUHandle * a , LRUHandle * b ) const {
return a - > key ( ) . compare ( b - > key ( ) ) < 0 ;
return old ;
}
LRUHandle * Remove ( LRUHandle * h ) {
LRUHandle * * ptr = FindPointer ( h ) ;
LRUHandle * result = * ptr ;
if ( result ! = NULL ) {
* ptr = result - > next_hash ;
- - elems_ ;
}
} ;
typedef base : : hash_set < LRUHandle * , HandleHashCompare > HandleTable ;
# else
struct HandleHash {
inline size_t operator ( ) ( LRUHandle * h ) const {
Slice k = h - > key ( ) ;
return Hash ( k . data ( ) , k . size ( ) , 0 ) ;
return result ;
}
private :
// The table consists of an array of buckets where each bucket is
// a linked list of cache entries that hash into the bucket.
uint32_t length_ ;
uint32_t elems_ ;
LRUHandle * * list_ ;
// Return a pointer to slot that points to a cache entry that
// matches *h. If there is no such cache entry, return a pointer to
// the trailing slot in the corresponding linked list.
LRUHandle * * FindPointer ( LRUHandle * h ) {
Slice key = h - > key ( ) ;
uint32_t hash = Hash ( key . data ( ) , key . size ( ) , 0 ) ;
LRUHandle * * ptr = & list_ [ hash & ( length_ - 1 ) ] ;
while ( * ptr ! = NULL & & key ! = ( * ptr ) - > key ( ) ) {
ptr = & ( * ptr ) - > next_hash ;
}
} ;
return ptr ;
}
struct HandleEq {
inline bool operator ( ) ( LRUHandle * a , LRUHandle * b ) const {
return a - > key ( ) = = b - > key ( ) ;
void Resize ( ) {
uint32_t new_length = 4 ;
while ( new_length < elems_ ) {
new_length * = 2 ;
}
LRUHandle * * new_list = new LRUHandle * [ new_length ] ;
memset ( new_list , 0 , sizeof ( new_list [ 0 ] ) * new_length ) ;
uint32_t count = 0 ;
for ( int i = 0 ; i < length_ ; i + + ) {
LRUHandle * h = list_ [ i ] ;
while ( h ! = NULL ) {
LRUHandle * next = h - > next_hash ;
Slice key = h - > key ( ) ;
uint32_t hash = Hash ( key . data ( ) , key . size ( ) , 0 ) ;
LRUHandle * * ptr = & new_list [ hash & ( new_length - 1 ) ] ;
h - > next_hash = * ptr ;
* ptr = h ;
h = next ;
count + + ;
}
}
} ;
# if defined(LEVELDB_PLATFORM_CHROMIUM)
typedef base : : hash_set < LRUHandle * , HandleHash , HandleEq > HandleTable ;
# elif defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_ANDROID)
typedef std : : unordered_set < LRUHandle * , HandleHash , HandleEq > HandleTable ;
# else
typedef __gnu_cxx : : hash_set < LRUHandle * , HandleHash , HandleEq > HandleTable ;
# endif
# endif
assert ( elems_ = = count ) ;
delete [ ] list_ ;
list_ = new_list ;
length_ = new_length ;
}
} ;
class LRUCache : public Cache {
public :
@ -132,7 +175,6 @@ LRUCache::LRUCache(size_t capacity)
}
LRUCache : : ~ LRUCache ( ) {
table_ . clear ( ) ;
for ( LRUHandle * e = lru_ . next ; e ! = & lru_ ; ) {
LRUHandle * next = e - > next ;
assert ( e - > refs = = 1 ) ; // Error if caller has an unreleased handle
@ -170,16 +212,13 @@ Cache::Handle* LRUCache::Lookup(const Slice& key) {
LRUHandle dummy ;
dummy . next = & dummy ;
dummy . value = const_cast < Slice * > ( & key ) ;
HandleTable : : iterator iter = table_ . find ( & dummy ) ;
if ( iter = = table_ . end ( ) ) {
return NULL ;
} else {
LRUHandle * e = const_cast < LRUHandle * > ( * iter ) ;
LRUHandle * e = table_ . Lookup ( & dummy ) ;
if ( e ! = NULL ) {
e - > refs + + ;
LRU_Remove ( e ) ;
LRU_Append ( e ) ;
return reinterpret_cast < Handle * > ( e ) ;
}
return reinterpret_cast < Handle * > ( e ) ;
}
void * LRUCache : : Value ( Handle * handle ) {
@ -206,20 +245,16 @@ Cache::Handle* LRUCache::Insert(const Slice& key, void* value, size_t charge,
LRU_Append ( e ) ;
usage_ + = charge ;
std : : pair < HandleTable : : iterator , bool > p = table_ . insert ( e ) ;
if ( ! p . second ) {
// Kill existing entry
LRUHandle * old = const_cast < LRUHandle * > ( * ( p . first ) ) ;
LRUHandle * old = table_ . Insert ( e ) ;
if ( old ! = NULL ) {
LRU_Remove ( old ) ;
table_ . erase ( p . first ) ;
table_ . insert ( e ) ;
Unref ( old ) ;
}
while ( usage_ > capacity_ & & lru_ . next ! = & lru_ ) {
LRUHandle * old = lru_ . next ;
LRU_Remove ( old ) ;
table_ . eras e( old ) ;
table_ . Remov e( old ) ;
Unref ( old ) ;
}
@ -232,11 +267,9 @@ void LRUCache::Erase(const Slice& key) {
LRUHandle dummy ;
dummy . next = & dummy ;
dummy . value = const_cast < Slice * > ( & key ) ;
HandleTable : : iterator iter = table_ . find ( & dummy ) ;
if ( iter ! = table_ . end ( ) ) {
LRUHandle * e = const_cast < LRUHandle * > ( * iter ) ;
LRUHandle * e = table_ . Remove ( & dummy ) ;
if ( e ! = NULL ) {
LRU_Remove ( e ) ;
table_ . erase ( iter ) ;
Unref ( e ) ;
}
}