|
@@ -41,7 +41,7 @@
|
|
#include <initializer_list>
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <iterator>
|
|
#include <limits> // To support Visual Studio 2008
|
|
#include <limits> // To support Visual Studio 2008
|
|
-#include <set>
|
|
|
|
|
|
+#include <map>
|
|
#include <type_traits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <utility>
|
|
|
|
|
|
@@ -181,39 +181,10 @@ class MapAllocator {
|
|
Arena* const arena_;
|
|
Arena* const arena_;
|
|
};
|
|
};
|
|
|
|
|
|
-template <typename Key>
|
|
|
|
-struct DerefCompare {
|
|
|
|
- bool operator()(const Key* n0, const Key* n1) const { return *n0 < *n1; }
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-// This class is used to get trivially destructible views of std::string and
|
|
|
|
-// MapKey, which are the only non-trivially destructible allowed key types.
|
|
|
|
-template <typename Key>
|
|
|
|
-class KeyView {
|
|
|
|
- public:
|
|
|
|
- KeyView(const Key& key) : key_(&key) {} // NOLINT(runtime/explicit)
|
|
|
|
-
|
|
|
|
- const Key& get() const { return *key_; }
|
|
|
|
- // Allows implicit conversions to `const Key&`, which allows us to use the
|
|
|
|
- // hasher defined for Key.
|
|
|
|
- operator const Key&() const { return get(); } // NOLINT(runtime/explicit)
|
|
|
|
-
|
|
|
|
- bool operator==(const KeyView& other) const { return get() == other.get(); }
|
|
|
|
- bool operator==(const Key& other) const { return get() == other; }
|
|
|
|
- bool operator<(const KeyView& other) const { return get() < other.get(); }
|
|
|
|
- bool operator<(const Key& other) const { return get() < other; }
|
|
|
|
-
|
|
|
|
- private:
|
|
|
|
- const Key* key_;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-// Allows the InnerMap type to support skippable destruction.
|
|
|
|
-template <typename Key>
|
|
|
|
-struct GetTrivialKey {
|
|
|
|
- using type =
|
|
|
|
- typename std::conditional<std::is_trivially_destructible<Key>::value, Key,
|
|
|
|
- KeyView<Key>>::type;
|
|
|
|
-};
|
|
|
|
|
|
+template <typename T>
|
|
|
|
+using KeyForTree =
|
|
|
|
+ typename std::conditional<std::is_scalar<T>::value, T,
|
|
|
|
+ std::reference_wrapper<const T>>::type;
|
|
|
|
|
|
} // namespace internal
|
|
} // namespace internal
|
|
|
|
|
|
@@ -313,31 +284,9 @@ class Map {
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
private:
|
|
- void Init() { elements_ = Arena::CreateMessage<InnerMap>(arena_, 0u); }
|
|
|
|
-
|
|
|
|
- // InnerMap's key type is TrivialKey and its value type is value_type*. We
|
|
|
|
- // use a custom class here and for Node, below, to ensure that k_ is at offset
|
|
|
|
- // 0, allowing safe conversion from pointer to Node to pointer to TrivialKey,
|
|
|
|
- // and vice versa when appropriate. We use GetTrivialKey to adapt Key to
|
|
|
|
- // be a trivially destructible view if Key is not already trivially
|
|
|
|
- // destructible. This view points into the Key inside v_ once it's
|
|
|
|
- // initialized.
|
|
|
|
- using TrivialKey = typename internal::GetTrivialKey<Key>::type;
|
|
|
|
- class KeyValuePair {
|
|
|
|
- public:
|
|
|
|
- KeyValuePair(const TrivialKey& k, value_type* v) : k_(k), v_(v) {}
|
|
|
|
-
|
|
|
|
- const TrivialKey& key() const { return k_; }
|
|
|
|
- TrivialKey& key() { return k_; }
|
|
|
|
- value_type* value() const { return v_; }
|
|
|
|
- value_type*& value() { return v_; }
|
|
|
|
-
|
|
|
|
- private:
|
|
|
|
- TrivialKey k_;
|
|
|
|
- value_type* v_;
|
|
|
|
- };
|
|
|
|
|
|
+ void Init() { elements_ = Arena::CreateMessage<InnerMap>(arena_, 0); }
|
|
|
|
|
|
- using Allocator = internal::MapAllocator<KeyValuePair>;
|
|
|
|
|
|
+ using Allocator = internal::MapAllocator<void*>;
|
|
|
|
|
|
// InnerMap is a generic hash-based map. It doesn't contain any
|
|
// InnerMap is a generic hash-based map. It doesn't contain any
|
|
// protocol-buffer-specific logic. It is a chaining hash map with the
|
|
// protocol-buffer-specific logic. It is a chaining hash map with the
|
|
@@ -368,14 +317,11 @@ class Map {
|
|
// 8. Mutations to a map do not invalidate the map's iterators, pointers to
|
|
// 8. Mutations to a map do not invalidate the map's iterators, pointers to
|
|
// elements, or references to elements.
|
|
// elements, or references to elements.
|
|
// 9. Except for erase(iterator), any non-const method can reorder iterators.
|
|
// 9. Except for erase(iterator), any non-const method can reorder iterators.
|
|
- // 10. InnerMap's key is TrivialKey, which is either Key, if Key is trivially
|
|
|
|
- // destructible, or a trivially destructible view of Key otherwise. This
|
|
|
|
- // allows InnerMap's destructor to be skipped when InnerMap is
|
|
|
|
- // arena-allocated.
|
|
|
|
|
|
+ // 10. InnerMap uses KeyForTree<Key> when using the Tree representation, which
|
|
|
|
+ // is either `Key`, if Key is a scalar, or `reference_wrapper<const Key>`
|
|
|
|
+ // otherwise. This avoids unncessary copies of string keys, for example.
|
|
class InnerMap : private hasher {
|
|
class InnerMap : private hasher {
|
|
public:
|
|
public:
|
|
- using Value = value_type*;
|
|
|
|
-
|
|
|
|
explicit InnerMap(size_type n) : InnerMap(nullptr, n) {}
|
|
explicit InnerMap(size_type n) : InnerMap(nullptr, n) {}
|
|
InnerMap(Arena* arena, size_type n)
|
|
InnerMap(Arena* arena, size_type n)
|
|
: hasher(),
|
|
: hasher(),
|
|
@@ -386,10 +332,6 @@ class Map {
|
|
n = TableSize(n);
|
|
n = TableSize(n);
|
|
table_ = CreateEmptyTable(n);
|
|
table_ = CreateEmptyTable(n);
|
|
num_buckets_ = index_of_first_non_null_ = n;
|
|
num_buckets_ = index_of_first_non_null_ = n;
|
|
- static_assert(
|
|
|
|
- std::is_trivially_destructible<KeyValuePair>::value,
|
|
|
|
- "We require KeyValuePair to be trivially destructible so that we can "
|
|
|
|
- "skip InnerMap's destructor when it's arena allocated.");
|
|
|
|
}
|
|
}
|
|
|
|
|
|
~InnerMap() {
|
|
~InnerMap() {
|
|
@@ -404,27 +346,25 @@ class Map {
|
|
|
|
|
|
// Linked-list nodes, as one would expect for a chaining hash table.
|
|
// Linked-list nodes, as one would expect for a chaining hash table.
|
|
struct Node {
|
|
struct Node {
|
|
- KeyValuePair kv;
|
|
|
|
|
|
+ value_type kv;
|
|
Node* next;
|
|
Node* next;
|
|
};
|
|
};
|
|
|
|
|
|
- // This is safe only if the given pointer is known to point to a Key that is
|
|
|
|
- // part of a Node.
|
|
|
|
- static Node* NodePtrFromKeyPtr(TrivialKey* k) {
|
|
|
|
- return reinterpret_cast<Node*>(k);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- static TrivialKey* KeyPtrFromNodePtr(Node* node) { return &node->kv.key(); }
|
|
|
|
-
|
|
|
|
- // Trees. The payload type is pointer to Key, so that we can query the tree
|
|
|
|
- // with Keys that are not in any particular data structure. When we insert,
|
|
|
|
- // though, the pointer is always pointing to a Key that is inside a Node.
|
|
|
|
- using KeyPtrAllocator =
|
|
|
|
- typename Allocator::template rebind<TrivialKey*>::other;
|
|
|
|
- using Tree = std::set<TrivialKey*, internal::DerefCompare<TrivialKey>,
|
|
|
|
- KeyPtrAllocator>;
|
|
|
|
|
|
+ // Trees. The payload type is a copy of Key, so that we can query the tree
|
|
|
|
+ // with Keys that are not in any particular data structure.
|
|
|
|
+ // The value is a void* pointing to Node. We use void* instead of Node* to
|
|
|
|
+ // avoid code bloat. That way there is only one instantiation of the tree
|
|
|
|
+ // class per key type.
|
|
|
|
+ using TreeAllocator = typename Allocator::template rebind<
|
|
|
|
+ std::pair<const internal::KeyForTree<Key>, void*>>::other;
|
|
|
|
+ using Tree = std::map<internal::KeyForTree<Key>, void*, std::less<Key>,
|
|
|
|
+ TreeAllocator>;
|
|
using TreeIterator = typename Tree::iterator;
|
|
using TreeIterator = typename Tree::iterator;
|
|
|
|
|
|
|
|
+ static Node* NodeFromTreeIterator(TreeIterator it) {
|
|
|
|
+ return static_cast<Node*>(it->second);
|
|
|
|
+ }
|
|
|
|
+
|
|
// iterator and const_iterator are instantiations of iterator_base.
|
|
// iterator and const_iterator are instantiations of iterator_base.
|
|
template <typename KeyValueType>
|
|
template <typename KeyValueType>
|
|
class iterator_base {
|
|
class iterator_base {
|
|
@@ -456,7 +396,7 @@ class Map {
|
|
: node_(n), m_(m), bucket_index_(index) {}
|
|
: node_(n), m_(m), bucket_index_(index) {}
|
|
|
|
|
|
iterator_base(TreeIterator tree_it, const InnerMap* m, size_type index)
|
|
iterator_base(TreeIterator tree_it, const InnerMap* m, size_type index)
|
|
- : node_(NodePtrFromKeyPtr(*tree_it)), m_(m), bucket_index_(index) {
|
|
|
|
|
|
+ : node_(NodeFromTreeIterator(tree_it)), m_(m), bucket_index_(index) {
|
|
// Invariant: iterators that use buckets with trees have an even
|
|
// Invariant: iterators that use buckets with trees have an even
|
|
// bucket_index_.
|
|
// bucket_index_.
|
|
GOOGLE_DCHECK_EQ(bucket_index_ % 2, 0u);
|
|
GOOGLE_DCHECK_EQ(bucket_index_ % 2, 0u);
|
|
@@ -476,7 +416,7 @@ class Map {
|
|
} else if (m_->TableEntryIsTree(bucket_index_)) {
|
|
} else if (m_->TableEntryIsTree(bucket_index_)) {
|
|
Tree* tree = static_cast<Tree*>(m_->table_[bucket_index_]);
|
|
Tree* tree = static_cast<Tree*>(m_->table_[bucket_index_]);
|
|
GOOGLE_DCHECK(!tree->empty());
|
|
GOOGLE_DCHECK(!tree->empty());
|
|
- node_ = NodePtrFromKeyPtr(*tree->begin());
|
|
|
|
|
|
+ node_ = NodeFromTreeIterator(tree->begin());
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -504,7 +444,7 @@ class Map {
|
|
if (++tree_it == tree->end()) {
|
|
if (++tree_it == tree->end()) {
|
|
SearchFrom(bucket_index_ + 2);
|
|
SearchFrom(bucket_index_ + 2);
|
|
} else {
|
|
} else {
|
|
- node_ = NodePtrFromKeyPtr(*tree_it);
|
|
|
|
|
|
+ node_ = NodeFromTreeIterator(tree_it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
@@ -542,8 +482,8 @@ class Map {
|
|
// Well, bucket_index_ still might be correct, but probably
|
|
// Well, bucket_index_ still might be correct, but probably
|
|
// not. Revalidate just to be sure. This case is rare enough that we
|
|
// not. Revalidate just to be sure. This case is rare enough that we
|
|
// don't worry about potential optimizations, such as having a custom
|
|
// don't worry about potential optimizations, such as having a custom
|
|
- // find-like method that compares Node* instead of TrivialKey.
|
|
|
|
- iterator_base i(m_->find(*KeyPtrFromNodePtr(node_), it));
|
|
|
|
|
|
+ // find-like method that compares Node* instead of the key.
|
|
|
|
+ iterator_base i(m_->find(node_->kv.first, it));
|
|
bucket_index_ = i.bucket_index_;
|
|
bucket_index_ = i.bucket_index_;
|
|
return m_->TableEntryIsList(bucket_index_);
|
|
return m_->TableEntryIsList(bucket_index_);
|
|
}
|
|
}
|
|
@@ -554,8 +494,8 @@ class Map {
|
|
};
|
|
};
|
|
|
|
|
|
public:
|
|
public:
|
|
- using iterator = iterator_base<KeyValuePair>;
|
|
|
|
- using const_iterator = iterator_base<const KeyValuePair>;
|
|
|
|
|
|
+ using iterator = iterator_base<value_type>;
|
|
|
|
+ using const_iterator = iterator_base<const value_type>;
|
|
|
|
|
|
iterator begin() { return iterator(this); }
|
|
iterator begin() { return iterator(this); }
|
|
iterator end() { return iterator(); }
|
|
iterator end() { return iterator(); }
|
|
@@ -578,7 +518,7 @@ class Map {
|
|
table_[b] = table_[b + 1] = nullptr;
|
|
table_[b] = table_[b + 1] = nullptr;
|
|
typename Tree::iterator tree_it = tree->begin();
|
|
typename Tree::iterator tree_it = tree->begin();
|
|
do {
|
|
do {
|
|
- Node* node = NodePtrFromKeyPtr(*tree_it);
|
|
|
|
|
|
+ Node* node = NodeFromTreeIterator(tree_it);
|
|
typename Tree::iterator next = tree_it;
|
|
typename Tree::iterator next = tree_it;
|
|
++next;
|
|
++next;
|
|
tree->erase(tree_it);
|
|
tree->erase(tree_it);
|
|
@@ -601,31 +541,13 @@ 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 TrivialKey& k) { return iterator(FindHelper(k).first); }
|
|
|
|
- const_iterator find(const TrivialKey& k) const { return find(k, nullptr); }
|
|
|
|
- bool contains(const TrivialKey& k) const { return find(k) != end(); }
|
|
|
|
-
|
|
|
|
- // In traditional C++ style, this performs "insert if not present."
|
|
|
|
- std::pair<iterator, bool> insert(const KeyValuePair& kv) {
|
|
|
|
- std::pair<const_iterator, size_type> p = FindHelper(kv.key());
|
|
|
|
- // Case 1: key was already present.
|
|
|
|
- if (p.first.node_ != nullptr)
|
|
|
|
- return std::make_pair(iterator(p.first), false);
|
|
|
|
- // Case 2: insert.
|
|
|
|
- if (ResizeIfLoadIsOutOfRange(num_elements_ + 1)) {
|
|
|
|
- p = FindHelper(kv.key());
|
|
|
|
- }
|
|
|
|
- const size_type b = p.second; // bucket number
|
|
|
|
- Node* node = Alloc<Node>(1);
|
|
|
|
- alloc_.construct(&node->kv, kv);
|
|
|
|
- iterator result = InsertUnique(b, node);
|
|
|
|
- ++num_elements_;
|
|
|
|
- return std::make_pair(result, true);
|
|
|
|
- }
|
|
|
|
|
|
+ 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(); }
|
|
|
|
|
|
- // The same, but if an insertion is necessary then the value portion of the
|
|
|
|
- // inserted key-value pair is left uninitialized.
|
|
|
|
- std::pair<iterator, bool> insert(const TrivialKey& k) {
|
|
|
|
|
|
+ // Insert the key into the map, if not present. In that case, the value will
|
|
|
|
+ // be value initialized.
|
|
|
|
+ std::pair<iterator, bool> insert(const Key& k) {
|
|
std::pair<const_iterator, size_type> p = FindHelper(k);
|
|
std::pair<const_iterator, size_type> p = FindHelper(k);
|
|
// Case 1: key was already present.
|
|
// Case 1: key was already present.
|
|
if (p.first.node_ != nullptr)
|
|
if (p.first.node_ != nullptr)
|
|
@@ -635,21 +557,22 @@ class Map {
|
|
p = FindHelper(k);
|
|
p = FindHelper(k);
|
|
}
|
|
}
|
|
const size_type b = p.second; // bucket number
|
|
const size_type b = p.second; // bucket number
|
|
- Node* node = Alloc<Node>(1);
|
|
|
|
- using KeyAllocator =
|
|
|
|
- typename Allocator::template rebind<TrivialKey>::other;
|
|
|
|
- KeyAllocator(alloc_).construct(&node->kv.key(), k);
|
|
|
|
|
|
+ Node* node;
|
|
|
|
+ if (alloc_.arena() == nullptr) {
|
|
|
|
+ node = new Node{value_type(k), nullptr};
|
|
|
|
+ } else {
|
|
|
|
+ node = Alloc<Node>(1);
|
|
|
|
+ Arena::CreateInArenaStorage(const_cast<Key*>(&node->kv.first),
|
|
|
|
+ alloc_.arena(), k);
|
|
|
|
+ Arena::CreateInArenaStorage(&node->kv.second, alloc_.arena());
|
|
|
|
+ }
|
|
|
|
+
|
|
iterator result = InsertUnique(b, node);
|
|
iterator result = InsertUnique(b, node);
|
|
++num_elements_;
|
|
++num_elements_;
|
|
return std::make_pair(result, true);
|
|
return std::make_pair(result, true);
|
|
}
|
|
}
|
|
|
|
|
|
- // Returns iterator so that outer map can update the TrivialKey to point to
|
|
|
|
- // the Key inside value_type in case TrivialKey is a view type.
|
|
|
|
- iterator operator[](const TrivialKey& k) {
|
|
|
|
- KeyValuePair kv(k, Value());
|
|
|
|
- return insert(kv).first;
|
|
|
|
- }
|
|
|
|
|
|
+ value_type& operator[](const Key& k) { return *insert(k).first; }
|
|
|
|
|
|
void erase(iterator it) {
|
|
void erase(iterator it) {
|
|
GOOGLE_DCHECK_EQ(it.m_, this);
|
|
GOOGLE_DCHECK_EQ(it.m_, this);
|
|
@@ -665,7 +588,7 @@ class Map {
|
|
} else {
|
|
} else {
|
|
GOOGLE_DCHECK(TableEntryIsTree(b));
|
|
GOOGLE_DCHECK(TableEntryIsTree(b));
|
|
Tree* tree = static_cast<Tree*>(table_[b]);
|
|
Tree* tree = static_cast<Tree*>(table_[b]);
|
|
- tree->erase(*tree_it);
|
|
|
|
|
|
+ tree->erase(tree_it);
|
|
if (tree->empty()) {
|
|
if (tree->empty()) {
|
|
// Force b to be the minimum of b and b ^ 1. This is important
|
|
// Force b to be the minimum of b and b ^ 1. This is important
|
|
// only because we want index_of_first_non_null_ to be correct.
|
|
// only because we want index_of_first_non_null_ to be correct.
|
|
@@ -685,19 +608,19 @@ class Map {
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
private:
|
|
- const_iterator find(const TrivialKey& 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 TrivialKey& k) const {
|
|
|
|
|
|
+ std::pair<const_iterator, size_type> FindHelper(const Key& k) const {
|
|
return FindHelper(k, nullptr);
|
|
return FindHelper(k, nullptr);
|
|
}
|
|
}
|
|
- std::pair<const_iterator, size_type> FindHelper(const TrivialKey& k,
|
|
|
|
|
|
+ std::pair<const_iterator, size_type> FindHelper(const Key& 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(*KeyPtrFromNodePtr(node), k)) {
|
|
|
|
|
|
+ if (IsMatch(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;
|
|
@@ -707,8 +630,7 @@ class Map {
|
|
GOOGLE_DCHECK_EQ(table_[b], table_[b ^ 1]);
|
|
GOOGLE_DCHECK_EQ(table_[b], table_[b ^ 1]);
|
|
b &= ~static_cast<size_t>(1);
|
|
b &= ~static_cast<size_t>(1);
|
|
Tree* tree = static_cast<Tree*>(table_[b]);
|
|
Tree* tree = static_cast<Tree*>(table_[b]);
|
|
- TrivialKey* key = const_cast<TrivialKey*>(&k);
|
|
|
|
- typename Tree::iterator tree_it = tree->find(key);
|
|
|
|
|
|
+ auto tree_it = tree->find(k);
|
|
if (tree_it != tree->end()) {
|
|
if (tree_it != tree->end()) {
|
|
if (it != nullptr) *it = tree_it;
|
|
if (it != nullptr) *it = tree_it;
|
|
return std::make_pair(const_iterator(tree_it, this, b), b);
|
|
return std::make_pair(const_iterator(tree_it, this, b), b);
|
|
@@ -729,7 +651,7 @@ class Map {
|
|
// or whatever. But it's probably cheap enough to recompute that here;
|
|
// or whatever. But it's probably cheap enough to recompute that here;
|
|
// it's likely that we're inserting into an empty or short list.
|
|
// it's likely that we're inserting into an empty or short list.
|
|
iterator result;
|
|
iterator result;
|
|
- GOOGLE_DCHECK(find(*KeyPtrFromNodePtr(node)) == end());
|
|
|
|
|
|
+ GOOGLE_DCHECK(find(node->kv.first) == end());
|
|
if (TableEntryIsEmpty(b)) {
|
|
if (TableEntryIsEmpty(b)) {
|
|
result = InsertUniqueInList(b, node);
|
|
result = InsertUniqueInList(b, node);
|
|
} else if (TableEntryIsNonEmptyList(b)) {
|
|
} else if (TableEntryIsNonEmptyList(b)) {
|
|
@@ -768,7 +690,7 @@ class Map {
|
|
// Maintain the invariant that node->next is null for all Nodes in Trees.
|
|
// Maintain the invariant that node->next is null for all Nodes in Trees.
|
|
node->next = nullptr;
|
|
node->next = nullptr;
|
|
return iterator(
|
|
return iterator(
|
|
- static_cast<Tree*>(table_[b])->insert(KeyPtrFromNodePtr(node)).first,
|
|
|
|
|
|
+ static_cast<Tree*>(table_[b])->insert({node->kv.first, node}).first,
|
|
this, b & ~static_cast<size_t>(1));
|
|
this, b & ~static_cast<size_t>(1));
|
|
}
|
|
}
|
|
|
|
|
|
@@ -836,7 +758,7 @@ class Map {
|
|
Node* node = static_cast<Node*>(table[index]);
|
|
Node* node = static_cast<Node*>(table[index]);
|
|
do {
|
|
do {
|
|
Node* next = node->next;
|
|
Node* next = node->next;
|
|
- InsertUnique(BucketNumber(*KeyPtrFromNodePtr(node)), node);
|
|
|
|
|
|
+ InsertUnique(BucketNumber(node->kv.first), node);
|
|
node = next;
|
|
node = next;
|
|
} while (node != nullptr);
|
|
} while (node != nullptr);
|
|
}
|
|
}
|
|
@@ -845,8 +767,8 @@ 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 {
|
|
- Node* node = NodePtrFromKeyPtr(*tree_it);
|
|
|
|
- InsertUnique(BucketNumber(**tree_it), node);
|
|
|
|
|
|
+ InsertUnique(BucketNumber(tree_it->first),
|
|
|
|
+ NodeFromTreeIterator(tree_it));
|
|
} while (++tree_it != tree->end());
|
|
} while (++tree_it != tree->end());
|
|
DestroyTree(tree);
|
|
DestroyTree(tree);
|
|
}
|
|
}
|
|
@@ -888,15 +810,9 @@ class Map {
|
|
|
|
|
|
void TreeConvert(size_type b) {
|
|
void TreeConvert(size_type b) {
|
|
GOOGLE_DCHECK(!TableEntryIsTree(b) && !TableEntryIsTree(b ^ 1));
|
|
GOOGLE_DCHECK(!TableEntryIsTree(b) && !TableEntryIsTree(b ^ 1));
|
|
- typename Allocator::template rebind<Tree>::other tree_allocator(alloc_);
|
|
|
|
- Tree* tree = tree_allocator.allocate(1);
|
|
|
|
- // We want to use the three-arg form of construct, if it exists, but we
|
|
|
|
- // create a temporary and use the two-arg construct that's known to exist.
|
|
|
|
- // It's clunky, but the compiler should be able to generate more-or-less
|
|
|
|
- // the same code.
|
|
|
|
- tree_allocator.construct(
|
|
|
|
- tree, Tree(typename Tree::key_compare(), KeyPtrAllocator(alloc_)));
|
|
|
|
- // Now the tree is ready to use.
|
|
|
|
|
|
+ Tree* tree =
|
|
|
|
+ Arena::Create<Tree>(alloc_.arena(), typename Tree::key_compare(),
|
|
|
|
+ typename Tree::allocator_type(alloc_));
|
|
size_type count = CopyListToTree(b, tree) + CopyListToTree(b ^ 1, tree);
|
|
size_type count = CopyListToTree(b, tree) + CopyListToTree(b ^ 1, tree);
|
|
GOOGLE_DCHECK_EQ(count, tree->size());
|
|
GOOGLE_DCHECK_EQ(count, tree->size());
|
|
table_[b] = table_[b ^ 1] = static_cast<void*>(tree);
|
|
table_[b] = table_[b ^ 1] = static_cast<void*>(tree);
|
|
@@ -908,7 +824,7 @@ class Map {
|
|
size_type count = 0;
|
|
size_type count = 0;
|
|
Node* node = static_cast<Node*>(table_[b]);
|
|
Node* node = static_cast<Node*>(table_[b]);
|
|
while (node != nullptr) {
|
|
while (node != nullptr) {
|
|
- tree->insert(KeyPtrFromNodePtr(node));
|
|
|
|
|
|
+ tree->insert({node->kv.first, node});
|
|
++count;
|
|
++count;
|
|
Node* next = node->next;
|
|
Node* next = node->next;
|
|
node->next = nullptr;
|
|
node->next = nullptr;
|
|
@@ -932,14 +848,12 @@ class Map {
|
|
return count >= kMaxLength;
|
|
return count >= kMaxLength;
|
|
}
|
|
}
|
|
|
|
|
|
- size_type BucketNumber(const TrivialKey& k) const {
|
|
|
|
|
|
+ size_type BucketNumber(const Key& k) const {
|
|
size_type h = hash_function()(k);
|
|
size_type h = hash_function()(k);
|
|
return (h + seed_) & (num_buckets_ - 1);
|
|
return (h + seed_) & (num_buckets_ - 1);
|
|
}
|
|
}
|
|
|
|
|
|
- bool IsMatch(const TrivialKey& k0, const TrivialKey& k1) const {
|
|
|
|
- return k0 == k1;
|
|
|
|
- }
|
|
|
|
|
|
+ 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.
|
|
@@ -964,14 +878,15 @@ class Map {
|
|
}
|
|
}
|
|
|
|
|
|
void DestroyNode(Node* node) {
|
|
void DestroyNode(Node* node) {
|
|
- alloc_.destroy(&node->kv);
|
|
|
|
- Dealloc<Node>(node, 1);
|
|
|
|
|
|
+ if (alloc_.arena() == nullptr) {
|
|
|
|
+ delete node;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
void DestroyTree(Tree* tree) {
|
|
void DestroyTree(Tree* tree) {
|
|
- typename Allocator::template rebind<Tree>::other tree_allocator(alloc_);
|
|
|
|
- tree_allocator.destroy(tree);
|
|
|
|
- tree_allocator.deallocate(tree, 1);
|
|
|
|
|
|
+ if (alloc_.arena() == nullptr) {
|
|
|
|
+ delete tree;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
void** CreateEmptyTable(size_type n) {
|
|
void** CreateEmptyTable(size_type n) {
|
|
@@ -1022,7 +937,7 @@ class Map {
|
|
const_iterator() {}
|
|
const_iterator() {}
|
|
explicit const_iterator(const InnerIt& it) : it_(it) {}
|
|
explicit const_iterator(const InnerIt& it) : it_(it) {}
|
|
|
|
|
|
- const_reference operator*() const { return *it_->value(); }
|
|
|
|
|
|
+ const_reference operator*() const { return *it_; }
|
|
const_pointer operator->() const { return &(operator*()); }
|
|
const_pointer operator->() const { return &(operator*()); }
|
|
|
|
|
|
const_iterator& operator++() {
|
|
const_iterator& operator++() {
|
|
@@ -1055,7 +970,7 @@ class Map {
|
|
iterator() {}
|
|
iterator() {}
|
|
explicit iterator(const InnerIt& it) : it_(it) {}
|
|
explicit iterator(const InnerIt& it) : it_(it) {}
|
|
|
|
|
|
- reference operator*() const { return *it_->value(); }
|
|
|
|
|
|
+ reference operator*() const { return *it_; }
|
|
pointer operator->() const { return &(operator*()); }
|
|
pointer operator->() const { return &(operator*()); }
|
|
|
|
|
|
iterator& operator++() {
|
|
iterator& operator++() {
|
|
@@ -1098,18 +1013,7 @@ class Map {
|
|
bool empty() const { return size() == 0; }
|
|
bool empty() const { return size() == 0; }
|
|
|
|
|
|
// Element access
|
|
// Element access
|
|
- T& operator[](const key_type& key) {
|
|
|
|
- typename InnerMap::iterator it = (*elements_)[key];
|
|
|
|
- value_type** value = &it->value();
|
|
|
|
- if (*value == nullptr) {
|
|
|
|
- *value = CreateValueTypeInternal(key);
|
|
|
|
- // We need to update the key in case it's a view type.
|
|
|
|
- it->key() = (*value)->first;
|
|
|
|
- internal::MapValueInitializer<is_proto_enum<T>::value, T>::Initialize(
|
|
|
|
- (*value)->second, default_enum_value_);
|
|
|
|
- }
|
|
|
|
- return (*value)->second;
|
|
|
|
- }
|
|
|
|
|
|
+ T& operator[](const key_type& key) { return (*elements_)[key].second; }
|
|
const T& at(const key_type& key) const {
|
|
const T& at(const key_type& 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: " << key;
|
|
@@ -1157,9 +1061,7 @@ class Map {
|
|
std::pair<typename InnerMap::iterator, bool> p =
|
|
std::pair<typename InnerMap::iterator, bool> p =
|
|
elements_->insert(value.first);
|
|
elements_->insert(value.first);
|
|
if (p.second) {
|
|
if (p.second) {
|
|
- p.first->value() = CreateValueTypeInternal(value);
|
|
|
|
- // We need to update the key in case it's a view type.
|
|
|
|
- p.first->key() = p.first->value()->first;
|
|
|
|
|
|
+ p.first->second = value.second;
|
|
}
|
|
}
|
|
return std::pair<iterator, bool>(iterator(p.first), p.second);
|
|
return std::pair<iterator, bool>(iterator(p.first), p.second);
|
|
}
|
|
}
|
|
@@ -1187,12 +1089,8 @@ class Map {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
iterator erase(iterator pos) {
|
|
iterator erase(iterator pos) {
|
|
- value_type* value = pos.operator->();
|
|
|
|
iterator i = pos++;
|
|
iterator i = pos++;
|
|
elements_->erase(i.it_);
|
|
elements_->erase(i.it_);
|
|
- // Note: we need to delete the value after erasing from the inner map
|
|
|
|
- // because the inner map's key may be a view of the value's key.
|
|
|
|
- if (arena_ == nullptr) delete value;
|
|
|
|
return pos;
|
|
return pos;
|
|
}
|
|
}
|
|
void erase(iterator first, iterator last) {
|
|
void erase(iterator first, iterator last) {
|
|
@@ -1235,32 +1133,6 @@ class Map {
|
|
default_enum_value_ = default_enum_value;
|
|
default_enum_value_ = default_enum_value;
|
|
}
|
|
}
|
|
|
|
|
|
- value_type* CreateValueTypeInternal(const Key& key) {
|
|
|
|
- if (arena_ == nullptr) {
|
|
|
|
- return new value_type(key);
|
|
|
|
- } else {
|
|
|
|
- value_type* value = reinterpret_cast<value_type*>(
|
|
|
|
- Arena::CreateArray<uint8>(arena_, sizeof(value_type)));
|
|
|
|
- Arena::CreateInArenaStorage(const_cast<Key*>(&value->first), arena_, key);
|
|
|
|
- Arena::CreateInArenaStorage(&value->second, arena_);
|
|
|
|
- return value;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- value_type* CreateValueTypeInternal(const value_type& value) {
|
|
|
|
- if (arena_ == nullptr) {
|
|
|
|
- return new value_type(value);
|
|
|
|
- } else {
|
|
|
|
- value_type* p = reinterpret_cast<value_type*>(
|
|
|
|
- Arena::CreateArray<uint8>(arena_, sizeof(value_type)));
|
|
|
|
- Arena::CreateInArenaStorage(const_cast<Key*>(&p->first), arena_,
|
|
|
|
- value.first);
|
|
|
|
- Arena::CreateInArenaStorage(&p->second, arena_);
|
|
|
|
- p->second = value.second;
|
|
|
|
- return p;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
Arena* arena_;
|
|
Arena* arena_;
|
|
int default_enum_value_;
|
|
int default_enum_value_;
|
|
InnerMap* elements_;
|
|
InnerMap* elements_;
|