Commit 4e5cb0ef authored by Daniel Andersson's avatar Daniel Andersson Committed by facebook-github-bot-0

Enable find/emplace for key types other than KeyT.

Summary: Add template parameters to support arbitrary types when looking up a key.

This is useful to avoid the cost of 'materializing' a key which is likely to be present in the array (or, alternatively, switching on a tagged union KeyT).

Use the new capability to greatly simplify the lookup logic in HHVMs static string table.

Reviewed By: nbronson

Differential Revision: D2662451

fb-gh-sync-id: 707fa033f350b80ca8080af17f1a8a74c59f2e88
parent defa24cf
......@@ -18,15 +18,18 @@
#error "This should only be included by AtomicHashArray.h"
#endif
#include <type_traits>
#include <folly/Bits.h>
#include <folly/detail/AtomicHashUtils.h>
namespace folly {
// AtomicHashArray private constructor --
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
AtomicHashArray(size_t capacity, KeyT emptyKey, KeyT lockedKey,
KeyT erasedKey, double _maxLoadFactor, size_t cacheSize)
: capacity_(capacity),
......@@ -43,20 +46,22 @@ AtomicHashArray(size_t capacity, KeyT emptyKey, KeyT lockedKey,
* of key and returns true, or if key does not exist returns false and
* ret.index is set to capacity_.
*/
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
typename AtomicHashArray<KeyT, ValueT,
HashFcn, EqualFcn, Allocator, ProbeFcn>::SimpleRetT
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
findInternal(const KeyT key_in) {
DCHECK_NE(key_in, kEmptyKey_);
DCHECK_NE(key_in, kLockedKey_);
DCHECK_NE(key_in, kErasedKey_);
for (size_t idx = keyToAnchorIdx(key_in), numProbes = 0;
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
template <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn>
typename AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::SimpleRetT
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
findInternal(const LookupKeyT key_in) {
checkLegalKeyIfKey<LookupKeyT>(key_in);
for (size_t idx = keyToAnchorIdx<LookupKeyT, LookupHashFcn>(key_in),
numProbes = 0;
;
idx = ProbeFcn()(idx, numProbes, capacity_)) {
const KeyT key = acquireLoadKey(cells_[idx]);
if (LIKELY(EqualFcn()(key, key_in))) {
if (LIKELY(LookupEqualFcn()(key, key_in))) {
return SimpleRetT(idx, true);
}
if (UNLIKELY(key == kEmptyKey_)) {
......@@ -83,20 +88,23 @@ findInternal(const KeyT key_in) {
* this will be the previously inserted value, and if the map is full it is
* default.
*/
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
template <typename... ArgTs>
typename AtomicHashArray<KeyT, ValueT,
HashFcn, EqualFcn, Allocator, ProbeFcn>::SimpleRetT
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
insertInternal(KeyT key_in, ArgTs&&... vCtorArgs) {
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
template <typename LookupKeyT,
typename LookupHashFcn,
typename LookupEqualFcn,
typename LookupKeyToKeyFcn,
typename... ArgTs>
typename AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::SimpleRetT
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
insertInternal(LookupKeyT key_in, ArgTs&&... vCtorArgs) {
const short NO_NEW_INSERTS = 1;
const short NO_PENDING_INSERTS = 2;
CHECK_NE(key_in, kEmptyKey_);
CHECK_NE(key_in, kLockedKey_);
CHECK_NE(key_in, kErasedKey_);
checkLegalKeyIfKey<LookupKeyT>(key_in);
size_t idx = keyToAnchorIdx(key_in);
size_t idx = keyToAnchorIdx<LookupKeyT, LookupHashFcn>(key_in);
size_t numProbes = 0;
for (;;) {
DCHECK_LT(idx, capacity_);
......@@ -132,11 +140,20 @@ insertInternal(KeyT key_in, ArgTs&&... vCtorArgs) {
// If we fail, fall through to comparison below; maybe the insert that
// just beat us was for this very key....
if (tryLockCell(cell)) {
KeyT key_new;
// Write the value - done before unlocking
try {
key_new = LookupKeyToKeyFcn()(key_in);
typedef typename std::remove_const<LookupKeyT>::type
LookupKeyTNoConst;
constexpr bool kAlreadyChecked =
std::is_same<KeyT, LookupKeyTNoConst>::value;
if (!kAlreadyChecked) {
checkLegalKeyIfKey(key_new);
}
DCHECK(relaxedLoadKey(*cell) == kLockedKey_);
new (&cell->second) ValueT(std::forward<ArgTs>(vCtorArgs)...);
unlockCell(cell, key_in); // Sets the new key
unlockCell(cell, key_new); // Sets the new key
} catch (...) {
// Transition back to empty key---requires handling
// locked->empty below.
......@@ -147,7 +164,7 @@ insertInternal(KeyT key_in, ArgTs&&... vCtorArgs) {
// An erase() can race here and delete right after our insertion
// Direct comparison rather than EqualFcn ok here
// (we just inserted it)
DCHECK(relaxedLoadKey(*cell) == key_in ||
DCHECK(relaxedLoadKey(*cell) == key_new ||
relaxedLoadKey(*cell) == kErasedKey_);
--numPendingEntries_;
++numEntries_; // This is a thread cached atomic increment :)
......@@ -167,7 +184,7 @@ insertInternal(KeyT key_in, ArgTs&&... vCtorArgs) {
}
const KeyT thisKey = acquireLoadKey(*cell);
if (EqualFcn()(thisKey, key_in)) {
if (LookupEqualFcn()(thisKey, key_in)) {
// Found an existing entry for our key, but we don't overwrite the
// previous value.
return SimpleRetT(idx, false);
......@@ -191,7 +208,6 @@ insertInternal(KeyT key_in, ArgTs&&... vCtorArgs) {
}
}
/*
* erase --
*
......@@ -202,9 +218,10 @@ insertInternal(KeyT key_in, ArgTs&&... vCtorArgs) {
* erased key will never be reused. If there's an associated value, we won't
* touch it either.
*/
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
size_t AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
size_t AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
erase(KeyT key_in) {
CHECK_NE(key_in, kEmptyKey_);
CHECK_NE(key_in, kLockedKey_);
......@@ -250,11 +267,12 @@ erase(KeyT key_in) {
}
}
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
typename AtomicHashArray<KeyT, ValueT,
HashFcn, EqualFcn, Allocator, ProbeFcn>::SmartPtr
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
typename AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::SmartPtr
AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
create(size_t maxSize, const Config& c) {
CHECK_LE(c.maxLoadFactor, 1.0);
CHECK_GT(c.maxLoadFactor, 0.0);
......@@ -292,9 +310,10 @@ create(size_t maxSize, const Config& c) {
return map;
}
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
void AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
void AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
destroy(AtomicHashArray* p) {
assert(p);
......@@ -311,9 +330,10 @@ destroy(AtomicHashArray* p) {
}
// clear -- clears all keys and values in the map and resets all counters
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
void AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
void AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
clear() {
FOR_EACH_RANGE(i, 0, capacity_) {
if (cells_[i].first != kEmptyKey_) {
......@@ -331,10 +351,11 @@ clear() {
// Iterator implementation
template <class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
template <class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
template <class ContT, class IterVal>
struct AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>::
struct AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>::
aha_iterator
: boost::iterator_facade<aha_iterator<ContT,IterVal>,
IterVal,
......
......@@ -62,18 +62,46 @@ struct AtomicHashArrayQuadraticProbeFcn
}
};
// Enables specializing checkLegalKey without specializing its class.
namespace detail {
// Local copy of folly::gen::Identity, to avoid heavy dependencies.
class AHAIdentity {
public:
template<class Value>
auto operator()(Value&& value) const ->
decltype(std::forward<Value>(value)) {
return std::forward<Value>(value);
}
};
template <typename NotKeyT, typename KeyT>
inline void checkLegalKeyIfKeyTImpl(NotKeyT ignored, KeyT emptyKey,
KeyT lockedKey, KeyT erasedKey) {
}
template <typename KeyT>
inline void checkLegalKeyIfKeyTImpl(KeyT key_in, KeyT emptyKey,
KeyT lockedKey, KeyT erasedKey) {
DCHECK_NE(key_in, emptyKey);
DCHECK_NE(key_in, lockedKey);
DCHECK_NE(key_in, erasedKey);
}
} // namespace detail
template <class KeyT, class ValueT,
class HashFcn = std::hash<KeyT>,
class EqualFcn = std::equal_to<KeyT>,
class Allocator = std::allocator<char>,
class ProbeFcn = AtomicHashArrayLinearProbeFcn>
class ProbeFcn = AtomicHashArrayLinearProbeFcn,
class KeyConvertFcn = detail::AHAIdentity>
class AtomicHashMap;
template <class KeyT, class ValueT,
class HashFcn = std::hash<KeyT>,
class EqualFcn = std::equal_to<KeyT>,
class Allocator = std::allocator<char>,
class ProbeFcn = AtomicHashArrayLinearProbeFcn>
class ProbeFcn = AtomicHashArrayLinearProbeFcn,
class KeyConvertFcn = detail::AHAIdentity>
class AtomicHashArray : boost::noncopyable {
static_assert((std::is_convertible<KeyT,int32_t>::value ||
std::is_convertible<KeyT,int64_t>::value ||
......@@ -84,6 +112,9 @@ class AtomicHashArray : boost::noncopyable {
public:
typedef KeyT key_type;
typedef ValueT mapped_type;
typedef HashFcn hasher;
typedef EqualFcn key_equal;
typedef KeyConvertFcn key_convert;
typedef std::pair<const KeyT, ValueT> value_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
......@@ -164,11 +195,37 @@ class AtomicHashArray : boost::noncopyable {
// Cannot have pre-instantiated const Config instance because of SIOF.
static SmartPtr create(size_t maxSize, const Config& c = Config());
iterator find(KeyT k) {
return iterator(this, findInternal(k).idx);
/*
* find --
*
*
* Returns the iterator to the element if found, otherwise end().
*
* As an optional feature, the type of the key to look up (LookupKeyT) is
* allowed to be different from the type of keys actually stored (KeyT).
*
* This enables use cases where materializing the key is costly and usually
* redudant, e.g., canonicalizing/interning a set of strings and being able
* to look up by StringPiece. To use this feature, LookupHashFcn must take
* a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first
* and second parameter, respectively.
*
* See folly/test/ArrayHashArrayTest.cpp for sample usage.
*/
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
iterator find(LookupKeyT k) {
return iterator(this,
findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k).idx);
}
const_iterator find(KeyT k) const {
return const_cast<AtomicHashArray*>(this)->find(k);
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
const_iterator find(LookupKeyT k) const {
return const_cast<AtomicHashArray*>(this)->
find<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k);
}
/*
......@@ -194,10 +251,24 @@ class AtomicHashArray : boost::noncopyable {
*
* Same contract as insert(), but performs in-place construction
* of the value type using the specified arguments.
*
* Also, like find(), this method optionally allows 'key_in' to have a type
* different from that stored in the table; see find(). If and only if no
* equal key is already present, this method converts 'key_in' to a key of
* type KeyT using the provided LookupKeyToKeyFcn.
*/
template <typename... ArgTs>
std::pair<iterator,bool> emplace(KeyT key_in, ArgTs&&... vCtorArgs) {
SimpleRetT ret = insertInternal(key_in, std::forward<ArgTs>(vCtorArgs)...);
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal,
typename LookupKeyToKeyFcn = key_convert,
typename... ArgTs>
std::pair<iterator,bool> emplace(LookupKeyT key_in, ArgTs&&... vCtorArgs) {
SimpleRetT ret = insertInternal<LookupKeyT,
LookupHashFcn,
LookupEqualFcn,
LookupKeyToKeyFcn>(
key_in,
std::forward<ArgTs>(vCtorArgs)...);
return std::make_pair(iterator(this, ret.idx), ret.success);
}
......@@ -276,10 +347,22 @@ friend class AtomicHashMap<KeyT,
template <typename... ArgTs>
SimpleRetT insertInternal(KeyT key, ArgTs&&... vCtorArgs);
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal,
typename LookupKeyToKeyFcn = detail::AHAIdentity,
typename... ArgTs>
SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... vCtorArgs);
SimpleRetT findInternal(const KeyT key);
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
SimpleRetT findInternal(const LookupKeyT key);
template <typename MaybeKeyT>
void checkLegalKeyIfKey(MaybeKeyT key) {
detail::checkLegalKeyIfKeyTImpl(key, kEmptyKey_, kLockedKey_, kErasedKey_);
}
static std::atomic<KeyT>* cellKeyPtr(const value_type& r) {
// We need some illegal casting here in order to actually store
......@@ -330,8 +413,9 @@ friend class AtomicHashMap<KeyT,
std::memory_order_acq_rel);
}
inline size_t keyToAnchorIdx(const KeyT k) const {
const size_t hashVal = HashFcn()(k);
template <class LookupKeyT = key_type, class LookupHashFcn = hasher>
inline size_t keyToAnchorIdx(const LookupKeyT k) const {
const size_t hashVal = LookupHashFcn()(k);
const size_t probe = hashVal & kAnchorMask_;
return LIKELY(probe < capacity_) ? probe : hashVal % capacity_;
}
......
This diff is collapsed.
......@@ -155,10 +155,11 @@ struct AtomicHashMapFullError : std::runtime_error {
{}
};
template<class KeyT, class ValueT,
class HashFcn, class EqualFcn, class Allocator, class ProbeFcn>
template<class KeyT, class ValueT, class HashFcn, class EqualFcn,
class Allocator, class ProbeFcn, class KeyConvertFcn>
class AtomicHashMap : boost::noncopyable {
typedef AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>
typedef AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn,
Allocator, ProbeFcn, KeyConvertFcn>
SubMap;
public:
......@@ -167,6 +168,7 @@ typedef AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>
typedef std::pair<const KeyT, ValueT> value_type;
typedef HashFcn hasher;
typedef EqualFcn key_equal;
typedef KeyConvertFcn key_convert;
typedef value_type* pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
......@@ -239,19 +241,44 @@ typedef AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>
*
* Same contract as insert(), but performs in-place construction
* of the value type using the specified arguments.
*
* Also, like find(), this method optionally allows 'key_in' to have a type
* different from that stored in the table; see find(). If and only if no
* equal key is already present, this method converts 'key_in' to a key of
* type KeyT using the provided LookupKeyToKeyFcn.
*/
template <typename... ArgTs>
std::pair<iterator,bool> emplace(key_type k, ArgTs&&... vCtorArg);
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal,
typename LookupKeyToKeyFcn = key_convert,
typename... ArgTs>
std::pair<iterator,bool> emplace(LookupKeyT k, ArgTs&&... vCtorArg);
/*
* find --
*
* Returns an iterator into the map.
* Returns the iterator to the element if found, otherwise end().
*
* As an optional feature, the type of the key to look up (LookupKeyT) is
* allowed to be different from the type of keys actually stored (KeyT).
*
* If the key is not found, returns end().
* This enables use cases where materializing the key is costly and usually
* redudant, e.g., canonicalizing/interning a set of strings and being able
* to look up by StringPiece. To use this feature, LookupHashFcn must take
* a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first
* and second parameter, respectively.
*
* See folly/test/ArrayHashMapTest.cpp for sample usage.
*/
iterator find(key_type k);
const_iterator find(key_type k) const;
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
iterator find(LookupKeyT k);
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
const_iterator find(LookupKeyT k) const;
/*
* erase --
......@@ -403,10 +430,17 @@ typedef AtomicHashArray<KeyT, ValueT, HashFcn, EqualFcn, Allocator, ProbeFcn>
SimpleRetT() = default;
};
template <typename... ArgTs>
SimpleRetT insertInternal(KeyT key, ArgTs&&... value);
SimpleRetT findInternal(const KeyT k) const;
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal,
typename LookupKeyToKeyFcn = key_convert,
typename... ArgTs>
SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... value);
template <typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
SimpleRetT findInternal(const LookupKeyT k) const;
SimpleRetT findAtInternal(uint32_t idx) const;
......
......@@ -247,3 +247,91 @@ TEST(Aha, InsertErase_i64_str) {
TEST(Aha, Create_cstr_i64) {
auto obj = AtomicHashArray<const char*, int64_t>::create(12);
}
static bool legalKey(char* a);
// Support two additional key lookup types (char and StringPiece) using
// one set of traits.
struct EqTraits {
bool operator()(char* a, char* b) {
return legalKey(a) && (strcmp(a, b) == 0);
}
bool operator()(char* a, const char& b) {
return legalKey(a) && (a[0] != '\0') && (a[0] == b);
}
bool operator()(char* a, const StringPiece b) {
return legalKey(a) &&
(strlen(a) == b.size()) && (strncmp(a, b.begin(), b.size()) == 0);
}
};
struct HashTraits {
size_t operator()(char* a) {
size_t result = 0;
while (a[0] != 0) result += static_cast<size_t>(*(a++));
return result;
}
size_t operator()(const char& a) {
return static_cast<size_t>(a);
}
size_t operator()(const StringPiece a) {
size_t result = 0;
for (const auto& ch : a) result += static_cast<size_t>(ch);
return result;
}
};
// Creates malloc'ed null-terminated strings.
struct KeyConvertTraits {
char* operator()(const char& a) {
return strndup(&a, 1);
}
char* operator()(const StringPiece a) {
return strndup(a.begin(), a.size());
}
};
typedef AtomicHashArray<char*, int64_t, HashTraits, EqTraits,
MmapAllocator<char>, AtomicHashArrayQuadraticProbeFcn,
KeyConvertTraits>
AHACstrInt;
AHACstrInt::Config cstrIntCfg;
static bool legalKey(char* a) {
return a != cstrIntCfg.emptyKey &&
a != cstrIntCfg.lockedKey &&
a != cstrIntCfg.erasedKey;
}
TEST(Aha, LookupAny) {
auto arr = AHACstrInt::create(12);
arr->insert(std::make_pair(strdup("f"), 42));
EXPECT_EQ(42, arr->find("f")->second);
{
// Look up a single char, successfully.
auto it = arr->find('f');
EXPECT_EQ(42, it->second);
}
{
// Look up a single char, unsuccessfully.
auto it = arr->find('g');
EXPECT_TRUE(it == arr->end());
}
{
// Insert a new char key.
auto res = arr->emplace('h', static_cast<int64_t>(123));
EXPECT_TRUE(res.second);
EXPECT_TRUE(res.first != arr->end());
// Look up the string version.
EXPECT_EQ(123, arr->find("h")->second);
}
{
// Fail to emplace an existing key.
auto res = arr->emplace('f', static_cast<int64_t>(123));
EXPECT_FALSE(res.second);
EXPECT_TRUE(res.first != arr->end());
}
for (auto it : *arr) free(it.first);
}
......@@ -29,6 +29,7 @@ using std::vector;
using std::string;
using folly::AtomicHashMap;
using folly::AtomicHashArray;
using folly::StringPiece;
// Tunables:
DEFINE_double(targetLoadFactor, 0.75, "Target memory utilization fraction.");
......@@ -113,6 +114,69 @@ static int genVal(int key) {
return key / 3;
}
static bool legalKey(const char* a);
struct EqTraits {
bool operator()(const char* a, const char* b) {
return legalKey(a) && (strcmp(a, b) == 0);
}
bool operator()(const char* a, const char& b) {
return legalKey(a) && (a[0] != '\0') && (a[0] == b);
}
bool operator()(const char* a, const StringPiece b) {
return legalKey(a) &&
(strlen(a) == b.size()) && (strcmp(a, b.begin()) == 0);
}
};
struct HashTraits {
size_t operator()(const char* a) {
size_t result = 0;
while (a[0] != 0) result += static_cast<size_t>(*(a++));
return result;
}
size_t operator()(const char& a) {
return static_cast<size_t>(a);
}
size_t operator()(const StringPiece a) {
size_t result = 0;
for (const auto& ch : a) result += static_cast<size_t>(ch);
return result;
}
};
typedef AtomicHashMap<const char*, int64_t, HashTraits, EqTraits> AHMCstrInt;
AHMCstrInt::Config cstrIntCfg;
static bool legalKey(const char* a) {
return a != cstrIntCfg.emptyKey &&
a != cstrIntCfg.lockedKey &&
a != cstrIntCfg.erasedKey;
}
TEST(Ahm, BasicLookup) {
AHMCstrInt myMap(1024, cstrIntCfg);
EXPECT_TRUE(myMap.begin() == myMap.end());
myMap.insert(std::make_pair("f", 42));
EXPECT_EQ(42, myMap.find("f")->second);
{
// Look up a single char, successfully.
auto it = myMap.find<char>('f');
EXPECT_EQ(42, it->second);
}
{
// Look up a single char, unsuccessfully.
auto it = myMap.find<char>('g');
EXPECT_TRUE(it == myMap.end());
}
{
// Look up a string piece, successfully.
const StringPiece piece("f");
auto it = myMap.find(piece);
EXPECT_EQ(42, it->second);
}
}
TEST(Ahm, grow) {
VLOG(1) << "Overhead: " << sizeof(AHArrayT) << " (array) " <<
sizeof(AHMapT) + sizeof(AHArrayT) << " (map/set) Bytes.";
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment