|
@@ -42,9 +42,14 @@
|
|
#include <iterator>
|
|
#include <iterator>
|
|
#include <limits> // To support Visual Studio 2008
|
|
#include <limits> // To support Visual Studio 2008
|
|
#include <map>
|
|
#include <map>
|
|
|
|
+#include <string>
|
|
#include <type_traits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <utility>
|
|
|
|
|
|
|
|
+#if defined(__cpp_lib_string_view)
|
|
|
|
+#include <string_view>
|
|
|
|
+#endif // defined(__cpp_lib_string_view)
|
|
|
|
+
|
|
#include <google/protobuf/stubs/common.h>
|
|
#include <google/protobuf/stubs/common.h>
|
|
#include <google/protobuf/arena.h>
|
|
#include <google/protobuf/arena.h>
|
|
#include <google/protobuf/generated_enum_util.h>
|
|
#include <google/protobuf/generated_enum_util.h>
|
|
@@ -186,6 +191,67 @@ using KeyForTree =
|
|
typename std::conditional<std::is_scalar<T>::value, T,
|
|
typename std::conditional<std::is_scalar<T>::value, T,
|
|
std::reference_wrapper<const T>>::type;
|
|
std::reference_wrapper<const T>>::type;
|
|
|
|
|
|
|
|
+// Default case: Not transparent.
|
|
|
|
+// We use std::hash<key_type>/std::less<key_type> and all the lookup functions
|
|
|
|
+// only accept `key_type`.
|
|
|
|
+template <typename key_type>
|
|
|
|
+struct TransparentSupport {
|
|
|
|
+ using hash = std::hash<key_type>;
|
|
|
|
+ using less = std::less<key_type>;
|
|
|
|
+
|
|
|
|
+ static bool Equals(const key_type& a, const key_type& b) { return a == b; }
|
|
|
|
+
|
|
|
|
+ template <typename K>
|
|
|
|
+ using key_arg = key_type;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#if defined(__cpp_lib_string_view)
|
|
|
|
+// If std::string_view is available, we add transparent support for std::string
|
|
|
|
+// keys. We use std::hash<std::string_view> as it supports the input types we
|
|
|
|
+// care about. The lookup functions accept arbitrary `K`. This will include any
|
|
|
|
+// key type that is convertible to std::string_view.
|
|
|
|
+template <>
|
|
|
|
+struct TransparentSupport<std::string> {
|
|
|
|
+ static std::string_view ImplicitConvert(std::string_view str) { return str; }
|
|
|
|
+ // If the element is not convertible to std::string_view, try to convert to
|
|
|
|
+ // std::string first.
|
|
|
|
+ // The template makes this overload lose resolution when both have the same
|
|
|
|
+ // rank otherwise.
|
|
|
|
+ template <typename = void>
|
|
|
|
+ static std::string_view ImplicitConvert(const std::string& str) {
|
|
|
|
+ return str;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ struct hash : private std::hash<std::string_view> {
|
|
|
|
+ using is_transparent = void;
|
|
|
|
+
|
|
|
|
+ template <typename T>
|
|
|
|
+ size_t operator()(const T& str) const {
|
|
|
|
+ return base()(ImplicitConvert(str));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private:
|
|
|
|
+ const std::hash<std::string_view>& base() const { return *this; }
|
|
|
|
+ };
|
|
|
|
+ struct less {
|
|
|
|
+ using is_transparent = void;
|
|
|
|
+
|
|
|
|
+ template <typename T, typename U>
|
|
|
|
+ bool operator()(const T& t, const U& u) const {
|
|
|
|
+ return ImplicitConvert(t) < ImplicitConvert(u);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ template <typename T, typename U>
|
|
|
|
+ static bool Equals(const T& t, const U& u) {
|
|
|
|
+ return ImplicitConvert(t) == ImplicitConvert(u);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename K>
|
|
|
|
+ using key_arg = K;
|
|
|
|
+};
|
|
|
|
+#endif // defined(__cpp_lib_string_view)
|
|
|
|
+
|
|
} // namespace internal
|
|
} // namespace internal
|
|
|
|
|
|
// This is the class for Map's internal value_type. Instead of using
|
|
// This is the class for Map's internal value_type. Instead of using
|
|
@@ -240,7 +306,7 @@ class Map {
|
|
using const_reference = const value_type&;
|
|
using const_reference = const value_type&;
|
|
|
|
|
|
using size_type = size_t;
|
|
using size_type = size_t;
|
|
- using hasher = std::hash<Key>;
|
|
|
|
|
|
+ using hasher = typename internal::TransparentSupport<Key>::hash;
|
|
|
|
|
|
Map() : arena_(nullptr), default_enum_value_(0) { Init(); }
|
|
Map() : arena_(nullptr), default_enum_value_(0) { Init(); }
|
|
explicit Map(Arena* arena) : arena_(arena), default_enum_value_(0) { Init(); }
|
|
explicit Map(Arena* arena) : arena_(arena), default_enum_value_(0) { Init(); }
|
|
@@ -357,7 +423,8 @@ class Map {
|
|
// class per key type.
|
|
// class per key type.
|
|
using TreeAllocator = typename Allocator::template rebind<
|
|
using TreeAllocator = typename Allocator::template rebind<
|
|
std::pair<const internal::KeyForTree<Key>, void*>>::other;
|
|
std::pair<const internal::KeyForTree<Key>, void*>>::other;
|
|
- using Tree = std::map<internal::KeyForTree<Key>, void*, std::less<Key>,
|
|
|
|
|
|
+ using Tree = std::map<internal::KeyForTree<Key>, void*,
|
|
|
|
+ typename internal::TransparentSupport<Key>::less,
|
|
TreeAllocator>;
|
|
TreeAllocator>;
|
|
using TreeIterator = typename Tree::iterator;
|
|
using TreeIterator = typename Tree::iterator;
|
|
|
|
|
|
@@ -541,9 +608,10 @@ class Map {
|
|
size_type size() const { return num_elements_; }
|
|
size_type size() const { return num_elements_; }
|
|
bool empty() const { return size() == 0; }
|
|
bool empty() const { return size() == 0; }
|
|
|
|
|
|
- iterator find(const Key& k) { return iterator(FindHelper(k).first); }
|
|
|
|
- const_iterator find(const Key& k) const { return find(k, nullptr); }
|
|
|
|
- bool contains(const Key& k) const { return find(k) != end(); }
|
|
|
|
|
|
+ template <typename K>
|
|
|
|
+ iterator find(const K& k) {
|
|
|
|
+ return iterator(FindHelper(k).first);
|
|
|
|
+ }
|
|
|
|
|
|
// Insert the key into the map, if not present. In that case, the value will
|
|
// Insert the key into the map, if not present. In that case, the value will
|
|
// be value initialized.
|
|
// be value initialized.
|
|
@@ -611,16 +679,18 @@ class Map {
|
|
const_iterator find(const Key& k, TreeIterator* it) const {
|
|
const_iterator find(const Key& k, TreeIterator* it) const {
|
|
return FindHelper(k, it).first;
|
|
return FindHelper(k, it).first;
|
|
}
|
|
}
|
|
- std::pair<const_iterator, size_type> FindHelper(const Key& k) const {
|
|
|
|
|
|
+ template <typename K>
|
|
|
|
+ std::pair<const_iterator, size_type> FindHelper(const K& k) const {
|
|
return FindHelper(k, nullptr);
|
|
return FindHelper(k, nullptr);
|
|
}
|
|
}
|
|
- std::pair<const_iterator, size_type> FindHelper(const Key& k,
|
|
|
|
|
|
+ template <typename K>
|
|
|
|
+ std::pair<const_iterator, size_type> FindHelper(const K& k,
|
|
TreeIterator* it) const {
|
|
TreeIterator* it) const {
|
|
size_type b = BucketNumber(k);
|
|
size_type b = BucketNumber(k);
|
|
if (TableEntryIsNonEmptyList(b)) {
|
|
if (TableEntryIsNonEmptyList(b)) {
|
|
Node* node = static_cast<Node*>(table_[b]);
|
|
Node* node = static_cast<Node*>(table_[b]);
|
|
do {
|
|
do {
|
|
- if (IsMatch(node->kv.first, k)) {
|
|
|
|
|
|
+ if (internal::TransparentSupport<Key>::Equals(node->kv.first, k)) {
|
|
return std::make_pair(const_iterator(node, this, b), b);
|
|
return std::make_pair(const_iterator(node, this, b), b);
|
|
} else {
|
|
} else {
|
|
node = node->next;
|
|
node = node->next;
|
|
@@ -675,9 +745,29 @@ class Map {
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Returns whether we should insert after the head of the list. For
|
|
|
|
+ // non-optimized builds, we randomly decide whether to insert right at the
|
|
|
|
+ // head of the list or just after the head. This helps add a little bit of
|
|
|
|
+ // non-determinism to the map ordering.
|
|
|
|
+ bool ShouldInsertAfterHead(void* node) {
|
|
|
|
+#ifdef NDEBUG
|
|
|
|
+ return false;
|
|
|
|
+#else
|
|
|
|
+ // Doing modulo with a prime mixes the bits more.
|
|
|
|
+ return (reinterpret_cast<uintptr_t>(node) ^ seed_) % 13 > 6;
|
|
|
|
+#endif
|
|
|
|
+ }
|
|
|
|
+
|
|
// Helper for InsertUnique. Handles the case where bucket b is a
|
|
// Helper for InsertUnique. Handles the case where bucket b is a
|
|
// not-too-long linked list.
|
|
// not-too-long linked list.
|
|
iterator InsertUniqueInList(size_type b, Node* node) {
|
|
iterator InsertUniqueInList(size_type b, Node* node) {
|
|
|
|
+ if (table_[b] != nullptr && ShouldInsertAfterHead(node)) {
|
|
|
|
+ Node* first = static_cast<Node*>(table_[b]);
|
|
|
|
+ node->next = first->next;
|
|
|
|
+ first->next = node;
|
|
|
|
+ return iterator(node, this, b);
|
|
|
|
+ }
|
|
|
|
+
|
|
node->next = static_cast<Node*>(table_[b]);
|
|
node->next = static_cast<Node*>(table_[b]);
|
|
table_[b] = static_cast<void*>(node);
|
|
table_[b] = static_cast<void*>(node);
|
|
return iterator(node, this, b);
|
|
return iterator(node, this, b);
|
|
@@ -767,7 +857,7 @@ class Map {
|
|
Tree* tree = static_cast<Tree*>(table[index]);
|
|
Tree* tree = static_cast<Tree*>(table[index]);
|
|
typename Tree::iterator tree_it = tree->begin();
|
|
typename Tree::iterator tree_it = tree->begin();
|
|
do {
|
|
do {
|
|
- InsertUnique(BucketNumber(tree_it->first),
|
|
|
|
|
|
+ InsertUnique(BucketNumber(std::cref(tree_it->first).get()),
|
|
NodeFromTreeIterator(tree_it));
|
|
NodeFromTreeIterator(tree_it));
|
|
} while (++tree_it != tree->end());
|
|
} while (++tree_it != tree->end());
|
|
DestroyTree(tree);
|
|
DestroyTree(tree);
|
|
@@ -848,13 +938,19 @@ class Map {
|
|
return count >= kMaxLength;
|
|
return count >= kMaxLength;
|
|
}
|
|
}
|
|
|
|
|
|
- size_type BucketNumber(const Key& k) const {
|
|
|
|
- size_type h = hash_function()(k);
|
|
|
|
- return (h + seed_) & (num_buckets_ - 1);
|
|
|
|
|
|
+ template <typename K>
|
|
|
|
+ size_type BucketNumber(const K& k) const {
|
|
|
|
+ // We xor the hash value against the random seed so that we effectively
|
|
|
|
+ // have a random hash function.
|
|
|
|
+ uint64 h = hash_function()(k) ^ seed_;
|
|
|
|
+
|
|
|
|
+ // We use the multiplication method to determine the bucket number from
|
|
|
|
+ // the hash value. The constant kPhi (suggested by Knuth) is roughly
|
|
|
|
+ // (sqrt(5) - 1) / 2 * 2^64.
|
|
|
|
+ constexpr uint64 kPhi = uint64{0x9e3779b97f4a7c15};
|
|
|
|
+ return ((kPhi * h) >> 32) & (num_buckets_ - 1);
|
|
}
|
|
}
|
|
|
|
|
|
- bool IsMatch(const Key& k0, const Key& k1) const { return k0 == k1; }
|
|
|
|
-
|
|
|
|
// Return a power of two no less than max(kMinTableSize, n).
|
|
// Return a power of two no less than max(kMinTableSize, n).
|
|
// Assumes either n < kMinTableSize or n is a power of two.
|
|
// Assumes either n < kMinTableSize or n is a power of two.
|
|
size_type TableSize(size_type n) {
|
|
size_type TableSize(size_type n) {
|
|
@@ -899,7 +995,10 @@ class Map {
|
|
|
|
|
|
// Return a randomish value.
|
|
// Return a randomish value.
|
|
size_type Seed() const {
|
|
size_type Seed() const {
|
|
- size_type s = static_cast<size_type>(reinterpret_cast<uintptr_t>(this));
|
|
|
|
|
|
+ // We get a little bit of randomness from the address of the map. The
|
|
|
|
+ // lower bits are not very random, due to alignment, so we discard them
|
|
|
|
+ // and shift the higher bits into their place.
|
|
|
|
+ size_type s = reinterpret_cast<uintptr_t>(this) >> 12;
|
|
#if defined(__x86_64__) && defined(__GNUC__) && \
|
|
#if defined(__x86_64__) && defined(__GNUC__) && \
|
|
!defined(GOOGLE_PROTOBUF_NO_RDTSC)
|
|
!defined(GOOGLE_PROTOBUF_NO_RDTSC)
|
|
uint32 hi, lo;
|
|
uint32 hi, lo;
|
|
@@ -922,6 +1021,10 @@ class Map {
|
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InnerMap);
|
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InnerMap);
|
|
}; // end of class InnerMap
|
|
}; // end of class InnerMap
|
|
|
|
|
|
|
|
+ template <typename LookupKey>
|
|
|
|
+ using key_arg = typename internal::TransparentSupport<
|
|
|
|
+ key_type>::template key_arg<LookupKey>;
|
|
|
|
+
|
|
public:
|
|
public:
|
|
// Iterators
|
|
// Iterators
|
|
class const_iterator {
|
|
class const_iterator {
|
|
@@ -1014,30 +1117,44 @@ class Map {
|
|
|
|
|
|
// Element access
|
|
// Element access
|
|
T& operator[](const key_type& key) { return (*elements_)[key].second; }
|
|
T& operator[](const key_type& key) { return (*elements_)[key].second; }
|
|
- const T& at(const key_type& key) const {
|
|
|
|
|
|
+
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ const T& at(const key_arg<K>& key) const {
|
|
const_iterator it = find(key);
|
|
const_iterator it = find(key);
|
|
- GOOGLE_CHECK(it != end()) << "key not found: " << key;
|
|
|
|
|
|
+ GOOGLE_CHECK(it != end()) << "key not found: " << static_cast<Key>(key);
|
|
return it->second;
|
|
return it->second;
|
|
}
|
|
}
|
|
- T& at(const key_type& key) {
|
|
|
|
|
|
+
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ T& at(const key_arg<K>& key) {
|
|
iterator it = find(key);
|
|
iterator it = find(key);
|
|
- GOOGLE_CHECK(it != end()) << "key not found: " << key;
|
|
|
|
|
|
+ GOOGLE_CHECK(it != end()) << "key not found: " << static_cast<Key>(key);
|
|
return it->second;
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
|
|
// Lookup
|
|
// Lookup
|
|
- size_type count(const key_type& key) const {
|
|
|
|
- const_iterator it = find(key);
|
|
|
|
- GOOGLE_DCHECK(it == end() || key == it->first);
|
|
|
|
- return it == end() ? 0 : 1;
|
|
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ size_type count(const key_arg<K>& key) const {
|
|
|
|
+ return find(key) == end() ? 0 : 1;
|
|
}
|
|
}
|
|
- const_iterator find(const key_type& key) const {
|
|
|
|
|
|
+
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ const_iterator find(const key_arg<K>& key) const {
|
|
return const_iterator(iterator(elements_->find(key)));
|
|
return const_iterator(iterator(elements_->find(key)));
|
|
}
|
|
}
|
|
- iterator find(const key_type& key) { return iterator(elements_->find(key)); }
|
|
|
|
- bool contains(const Key& key) const { return elements_->contains(key); }
|
|
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ iterator find(const key_arg<K>& key) {
|
|
|
|
+ return iterator(elements_->find(key));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ bool contains(const key_arg<K>& key) const {
|
|
|
|
+ return find(key) != end();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename K = key_type>
|
|
std::pair<const_iterator, const_iterator> equal_range(
|
|
std::pair<const_iterator, const_iterator> equal_range(
|
|
- const key_type& key) const {
|
|
|
|
|
|
+ const key_arg<K>& key) const {
|
|
const_iterator it = find(key);
|
|
const_iterator it = find(key);
|
|
if (it == end()) {
|
|
if (it == end()) {
|
|
return std::pair<const_iterator, const_iterator>(it, it);
|
|
return std::pair<const_iterator, const_iterator>(it, it);
|
|
@@ -1046,7 +1163,9 @@ class Map {
|
|
return std::pair<const_iterator, const_iterator>(begin, it);
|
|
return std::pair<const_iterator, const_iterator>(begin, it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- std::pair<iterator, iterator> equal_range(const key_type& key) {
|
|
|
|
|
|
+
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
|
|
iterator it = find(key);
|
|
iterator it = find(key);
|
|
if (it == end()) {
|
|
if (it == end()) {
|
|
return std::pair<iterator, iterator>(it, it);
|
|
return std::pair<iterator, iterator>(it, it);
|
|
@@ -1079,7 +1198,8 @@ class Map {
|
|
}
|
|
}
|
|
|
|
|
|
// Erase and clear
|
|
// Erase and clear
|
|
- size_type erase(const key_type& key) {
|
|
|
|
|
|
+ template <typename K = key_type>
|
|
|
|
+ size_type erase(const key_arg<K>& key) {
|
|
iterator it = find(key);
|
|
iterator it = find(key);
|
|
if (it == end()) {
|
|
if (it == end()) {
|
|
return 0;
|
|
return 0;
|