mirror of https://github.com/grpc/grpc.git
Merge pull request #18052 from mhaidrygoog/xds_work
Support for LocalityMap for XDS LB policypull/18588/head
commit
5e26c4339d
19 changed files with 1403 additions and 198 deletions
@ -0,0 +1,419 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_GPRPP_MAP_H |
||||
#define GRPC_CORE_LIB_GPRPP_MAP_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <string.h> |
||||
#include <functional> |
||||
#include <iterator> |
||||
#include "src/core/lib/gpr/useful.h" |
||||
#include "src/core/lib/gprpp/memory.h" |
||||
#include "src/core/lib/gprpp/pair.h" |
||||
|
||||
namespace grpc_core { |
||||
struct StringLess { |
||||
bool operator()(const char* a, const char* b) const { |
||||
return strcmp(a, b) < 0; |
||||
} |
||||
bool operator()(const UniquePtr<char>& k1, const UniquePtr<char>& k2) { |
||||
return strcmp(k1.get(), k2.get()) < 0; |
||||
} |
||||
}; |
||||
|
||||
namespace testing { |
||||
class MapTest; |
||||
} |
||||
|
||||
// Alternative map implementation for grpc_core
|
||||
template <class Key, class T, class Compare = std::less<Key>> |
||||
class Map { |
||||
public: |
||||
typedef Key key_type; |
||||
typedef T mapped_type; |
||||
typedef Pair<key_type, mapped_type> value_type; |
||||
typedef Compare key_compare; |
||||
class iterator; |
||||
|
||||
~Map() { clear(); } |
||||
|
||||
T& operator[](key_type&& key); |
||||
T& operator[](const key_type& key); |
||||
iterator find(const key_type& k); |
||||
size_t erase(const key_type& key); |
||||
// Removes the current entry and points to the next one
|
||||
iterator erase(iterator iter); |
||||
|
||||
size_t size() { return size_; } |
||||
|
||||
template <class... Args> |
||||
Pair<iterator, bool> emplace(Args&&... args); |
||||
|
||||
Pair<iterator, bool> insert(value_type&& pair) { |
||||
return emplace(std::move(pair)); |
||||
} |
||||
|
||||
Pair<iterator, bool> insert(const value_type& pair) { return emplace(pair); } |
||||
|
||||
bool empty() const { return root_ == nullptr; } |
||||
|
||||
void clear() { |
||||
auto iter = begin(); |
||||
while (!empty()) { |
||||
iter = erase(iter); |
||||
} |
||||
} |
||||
|
||||
iterator begin() { |
||||
Entry* curr = GetMinEntry(root_); |
||||
return iterator(this, curr); |
||||
} |
||||
|
||||
iterator end() { return iterator(this, nullptr); } |
||||
|
||||
private: |
||||
friend class testing::MapTest; |
||||
struct Entry { |
||||
explicit Entry(value_type&& pair) : pair(std::move(pair)) {} |
||||
value_type pair; |
||||
Entry* left = nullptr; |
||||
Entry* right = nullptr; |
||||
int32_t height = 1; |
||||
}; |
||||
|
||||
static int32_t EntryHeight(const Entry* e) { |
||||
return e == nullptr ? 0 : e->height; |
||||
} |
||||
|
||||
static Entry* GetMinEntry(Entry* e); |
||||
Entry* InOrderSuccessor(const Entry* e) const; |
||||
static Entry* RotateLeft(Entry* e); |
||||
static Entry* RotateRight(Entry* e); |
||||
static Entry* RebalanceTreeAfterInsertion(Entry* root, const key_type& k); |
||||
static Entry* RebalanceTreeAfterDeletion(Entry* root); |
||||
// Returns a pair with the first value being an iterator pointing to the
|
||||
// inserted entry and the second value being the new root of the subtree
|
||||
// after a rebalance
|
||||
Pair<iterator, Entry*> InsertRecursive(Entry* root, value_type&& p); |
||||
static Entry* RemoveRecursive(Entry* root, const key_type& k); |
||||
// Return 0 if lhs = rhs
|
||||
// 1 if lhs > rhs
|
||||
// -1 if lhs < rhs
|
||||
static int CompareKeys(const Key& lhs, const Key& rhs); |
||||
|
||||
Entry* root_ = nullptr; |
||||
size_t size_ = 0; |
||||
}; |
||||
|
||||
template <class Key, class T, class Compare> |
||||
class Map<Key, T, Compare>::iterator |
||||
: public std::iterator<std::input_iterator_tag, Pair<Key, T>, int32_t, |
||||
Pair<Key, T>*, Pair<Key, T>&> { |
||||
public: |
||||
iterator(const iterator& iter) : curr_(iter.curr_), map_(iter.map_) {} |
||||
bool operator==(const iterator& rhs) const { return (curr_ == rhs.curr_); } |
||||
bool operator!=(const iterator& rhs) const { return (curr_ != rhs.curr_); } |
||||
|
||||
iterator& operator++() { |
||||
curr_ = map_->InOrderSuccessor(curr_); |
||||
return *this; |
||||
} |
||||
|
||||
iterator operator++(int) { |
||||
Entry* prev = curr_; |
||||
curr_ = map_->InOrderSuccessor(curr_); |
||||
return iterator(map_, prev); |
||||
} |
||||
|
||||
iterator& operator=(const iterator& other) { |
||||
if (this != &other) { |
||||
this->curr_ = other.curr_; |
||||
this->map_ = other.map_; |
||||
} |
||||
return *this; |
||||
} |
||||
|
||||
// operator*()
|
||||
value_type& operator*() { return curr_->pair; } |
||||
const value_type& operator*() const { return curr_->pair; } |
||||
|
||||
// operator->()
|
||||
value_type* operator->() { return &curr_->pair; } |
||||
value_type const* operator->() const { return &curr_->pair; } |
||||
|
||||
private: |
||||
friend class Map<key_type, mapped_type, key_compare>; |
||||
using GrpcMap = typename ::grpc_core::Map<Key, T, Compare>; |
||||
iterator(GrpcMap* map, Entry* curr) : curr_(curr), map_(map) {} |
||||
Entry* curr_; |
||||
GrpcMap* map_; |
||||
}; |
||||
|
||||
template <class Key, class T, class Compare> |
||||
T& Map<Key, T, Compare>::operator[](key_type&& key) { |
||||
auto iter = find(key); |
||||
if (iter == end()) { |
||||
return emplace(std::move(key), T()).first->second; |
||||
} |
||||
return iter->second; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
T& Map<Key, T, Compare>::operator[](const key_type& key) { |
||||
auto iter = find(key); |
||||
if (iter == end()) { |
||||
return emplace(key, T()).first->second; |
||||
} |
||||
return iter->second; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::iterator Map<Key, T, Compare>::find( |
||||
const key_type& k) { |
||||
Entry* iter = root_; |
||||
while (iter != nullptr) { |
||||
int comp = CompareKeys(iter->pair.first, k); |
||||
if (comp == 0) { |
||||
return iterator(this, iter); |
||||
} else if (comp < 0) { |
||||
iter = iter->right; |
||||
} else { |
||||
iter = iter->left; |
||||
} |
||||
} |
||||
return end(); |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
template <class... Args> |
||||
typename ::grpc_core::Pair<typename Map<Key, T, Compare>::iterator, bool> |
||||
Map<Key, T, Compare>::emplace(Args&&... args) { |
||||
Pair<key_type, mapped_type> pair(std::forward<Args>(args)...); |
||||
iterator ret = find(pair.first); |
||||
bool insertion = false; |
||||
if (ret == end()) { |
||||
Pair<iterator, Entry*> p = InsertRecursive(root_, std::move(pair)); |
||||
root_ = p.second; |
||||
ret = p.first; |
||||
insertion = true; |
||||
size_++; |
||||
} |
||||
return MakePair(ret, insertion); |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
size_t Map<Key, T, Compare>::erase(const key_type& key) { |
||||
iterator it = find(key); |
||||
if (it == end()) return 0; |
||||
erase(it); |
||||
return 1; |
||||
} |
||||
|
||||
// TODO(mhaidry): Modify erase to use the iterator location
|
||||
// to create an efficient erase method
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::iterator Map<Key, T, Compare>::erase( |
||||
iterator iter) { |
||||
if (iter == end()) return iter; |
||||
key_type& del_key = iter->first; |
||||
iter++; |
||||
root_ = RemoveRecursive(root_, del_key); |
||||
size_--; |
||||
return iter; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::InOrderSuccessor( |
||||
const Entry* e) const { |
||||
if (e->right != nullptr) { |
||||
return GetMinEntry(e->right); |
||||
} |
||||
Entry* successor = nullptr; |
||||
Entry* iter = root_; |
||||
while (iter != nullptr) { |
||||
int comp = CompareKeys(iter->pair.first, e->pair.first); |
||||
if (comp > 0) { |
||||
successor = iter; |
||||
iter = iter->left; |
||||
} else if (comp < 0) { |
||||
iter = iter->right; |
||||
} else |
||||
break; |
||||
} |
||||
return successor; |
||||
} |
||||
|
||||
// Returns a pair with the first value being an iterator pointing to the
|
||||
// inserted entry and the second value being the new root of the subtree
|
||||
// after a rebalance
|
||||
template <class Key, class T, class Compare> |
||||
typename ::grpc_core::Pair<typename Map<Key, T, Compare>::iterator, |
||||
typename Map<Key, T, Compare>::Entry*> |
||||
Map<Key, T, Compare>::InsertRecursive(Entry* root, value_type&& p) { |
||||
if (root == nullptr) { |
||||
Entry* e = New<Entry>(std::move(p)); |
||||
return MakePair(iterator(this, e), e); |
||||
} |
||||
int comp = CompareKeys(root->pair.first, p.first); |
||||
if (comp > 0) { |
||||
Pair<iterator, Entry*> ret = InsertRecursive(root->left, std::move(p)); |
||||
root->left = ret.second; |
||||
ret.second = RebalanceTreeAfterInsertion(root, ret.first->first); |
||||
return ret; |
||||
} else if (comp < 0) { |
||||
Pair<iterator, Entry*> ret = InsertRecursive(root->right, std::move(p)); |
||||
root->right = ret.second; |
||||
ret.second = RebalanceTreeAfterInsertion(root, ret.first->first); |
||||
return ret; |
||||
} else { |
||||
root->pair = std::move(p); |
||||
return MakePair(iterator(this, root), root); |
||||
} |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::GetMinEntry( |
||||
Entry* e) { |
||||
if (e != nullptr) { |
||||
while (e->left != nullptr) { |
||||
e = e->left; |
||||
} |
||||
} |
||||
return e; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::RotateLeft( |
||||
Entry* e) { |
||||
Entry* rightChild = e->right; |
||||
Entry* rightLeftChild = rightChild->left; |
||||
rightChild->left = e; |
||||
e->right = rightLeftChild; |
||||
e->height = 1 + GPR_MAX(EntryHeight(e->left), EntryHeight(e->right)); |
||||
rightChild->height = 1 + GPR_MAX(EntryHeight(rightChild->left), |
||||
EntryHeight(rightChild->right)); |
||||
return rightChild; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::RotateRight( |
||||
Entry* e) { |
||||
Entry* leftChild = e->left; |
||||
Entry* leftRightChild = leftChild->right; |
||||
leftChild->right = e; |
||||
e->left = leftRightChild; |
||||
e->height = 1 + GPR_MAX(EntryHeight(e->left), EntryHeight(e->right)); |
||||
leftChild->height = |
||||
1 + GPR_MAX(EntryHeight(leftChild->left), EntryHeight(leftChild->right)); |
||||
return leftChild; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::Entry* |
||||
Map<Key, T, Compare>::RebalanceTreeAfterInsertion(Entry* root, |
||||
const key_type& k) { |
||||
root->height = 1 + GPR_MAX(EntryHeight(root->left), EntryHeight(root->right)); |
||||
int32_t heightDifference = EntryHeight(root->left) - EntryHeight(root->right); |
||||
if (heightDifference > 1 && CompareKeys(root->left->pair.first, k) > 0) { |
||||
return RotateRight(root); |
||||
} |
||||
if (heightDifference < -1 && CompareKeys(root->right->pair.first, k) < 0) { |
||||
return RotateLeft(root); |
||||
} |
||||
if (heightDifference > 1 && CompareKeys(root->left->pair.first, k) < 0) { |
||||
root->left = RotateLeft(root->left); |
||||
return RotateRight(root); |
||||
} |
||||
if (heightDifference < -1 && CompareKeys(root->right->pair.first, k) > 0) { |
||||
root->right = RotateRight(root->right); |
||||
return RotateLeft(root); |
||||
} |
||||
return root; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::Entry* |
||||
Map<Key, T, Compare>::RebalanceTreeAfterDeletion(Entry* root) { |
||||
root->height = 1 + GPR_MAX(EntryHeight(root->left), EntryHeight(root->right)); |
||||
int32_t heightDifference = EntryHeight(root->left) - EntryHeight(root->right); |
||||
if (heightDifference > 1) { |
||||
int leftHeightDifference = |
||||
EntryHeight(root->left->left) - EntryHeight(root->left->right); |
||||
if (leftHeightDifference < 0) { |
||||
root->left = RotateLeft(root->left); |
||||
} |
||||
return RotateRight(root); |
||||
} |
||||
if (heightDifference < -1) { |
||||
int rightHeightDifference = |
||||
EntryHeight(root->right->left) - EntryHeight(root->right->right); |
||||
if (rightHeightDifference > 0) { |
||||
root->right = RotateRight(root->right); |
||||
} |
||||
return RotateLeft(root); |
||||
} |
||||
return root; |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
typename Map<Key, T, Compare>::Entry* Map<Key, T, Compare>::RemoveRecursive( |
||||
Entry* root, const key_type& k) { |
||||
if (root == nullptr) return root; |
||||
int comp = CompareKeys(root->pair.first, k); |
||||
if (comp > 0) { |
||||
root->left = RemoveRecursive(root->left, k); |
||||
} else if (comp < 0) { |
||||
root->right = RemoveRecursive(root->right, k); |
||||
} else { |
||||
Entry* ret; |
||||
if (root->left == nullptr) { |
||||
ret = root->right; |
||||
Delete(root); |
||||
return ret; |
||||
} else if (root->right == nullptr) { |
||||
ret = root->left; |
||||
Delete(root); |
||||
return ret; |
||||
} else { |
||||
ret = root->right; |
||||
while (ret->left != nullptr) { |
||||
ret = ret->left; |
||||
} |
||||
root->pair.swap(ret->pair); |
||||
root->right = RemoveRecursive(root->right, ret->pair.first); |
||||
} |
||||
} |
||||
return RebalanceTreeAfterDeletion(root); |
||||
} |
||||
|
||||
template <class Key, class T, class Compare> |
||||
int Map<Key, T, Compare>::CompareKeys(const key_type& lhs, |
||||
const key_type& rhs) { |
||||
key_compare compare; |
||||
bool left_comparison = compare(lhs, rhs); |
||||
bool right_comparison = compare(rhs, lhs); |
||||
// Both values are equal
|
||||
if (!left_comparison && !right_comparison) { |
||||
return 0; |
||||
} |
||||
return left_comparison ? -1 : 1; |
||||
} |
||||
} // namespace grpc_core
|
||||
#endif /* GRPC_CORE_LIB_GPRPP_MAP_H */ |
@ -0,0 +1,38 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_GPRPP_PAIR_H |
||||
#define GRPC_CORE_LIB_GPRPP_PAIR_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <utility> |
||||
|
||||
namespace grpc_core { |
||||
template <class T1, class T2> |
||||
using Pair = std::pair<T1, T2>; |
||||
|
||||
template <class T1, class T2> |
||||
inline Pair<typename std::decay<T1>::type, typename std::decay<T2>::type> |
||||
MakePair(T1&& u, T2&& v) { |
||||
typedef typename std::decay<T1>::type V1; |
||||
typedef typename std::decay<T2>::type V2; |
||||
return Pair<V1, V2>(std::forward<T1>(u), std::forward<T2>(v)); |
||||
} |
||||
} // namespace grpc_core
|
||||
#endif /* GRPC_CORE_LIB_GPRPP_PAIR_H */ |
@ -0,0 +1,409 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/lib/gprpp/map.h" |
||||
#include <gtest/gtest.h> |
||||
#include "include/grpc/support/string_util.h" |
||||
#include "src/core/lib/gprpp/inlined_vector.h" |
||||
#include "src/core/lib/gprpp/memory.h" |
||||
#include "src/core/lib/gprpp/orphanable.h" |
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace testing { |
||||
class Payload { |
||||
public: |
||||
Payload() : data_(-1) {} |
||||
explicit Payload(int data) : data_(data) {} |
||||
Payload(const Payload& other) : data_(other.data_) {} |
||||
Payload& operator=(const Payload& other) { |
||||
if (this != &other) { |
||||
data_ = other.data_; |
||||
} |
||||
return *this; |
||||
} |
||||
int data() { return data_; } |
||||
|
||||
private: |
||||
int data_; |
||||
}; |
||||
|
||||
inline UniquePtr<char> CopyString(const char* string) { |
||||
return UniquePtr<char>(gpr_strdup(string)); |
||||
} |
||||
|
||||
static constexpr char kKeys[][4] = {"abc", "efg", "hij", "klm", "xyz"}; |
||||
|
||||
class MapTest : public ::testing::Test { |
||||
public: |
||||
template <class Key, class T, class Compare> |
||||
typename ::grpc_core::Map<Key, T, Compare>::Entry* Root( |
||||
typename ::grpc_core::Map<Key, T, Compare>* map) { |
||||
return map->root_; |
||||
} |
||||
}; |
||||
|
||||
// Test insertion of Payload
|
||||
TEST_F(MapTest, EmplaceAndFind) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], Payload(i)); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map.find(kKeys[i])->second.data()); |
||||
} |
||||
} |
||||
|
||||
// Test insertion of Payload Unique Ptrs
|
||||
TEST_F(MapTest, EmplaceAndFindWithUniquePtrValue) { |
||||
Map<const char*, UniquePtr<Payload>, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], MakeUnique<Payload>(i)); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map.find(kKeys[i])->second->data()); |
||||
} |
||||
} |
||||
|
||||
// Test insertion of Unique Ptr kKeys and Payload
|
||||
TEST_F(MapTest, EmplaceAndFindWithUniquePtrKey) { |
||||
Map<UniquePtr<char>, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(CopyString(kKeys[i]), Payload(i)); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map.find(CopyString(kKeys[i]))->second.data()); |
||||
} |
||||
} |
||||
|
||||
// Test insertion of Payload
|
||||
TEST_F(MapTest, InsertAndFind) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.insert(MakePair(kKeys[i], Payload(i))); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map.find(kKeys[i])->second.data()); |
||||
} |
||||
} |
||||
|
||||
// Test insertion of Payload Unique Ptrs
|
||||
TEST_F(MapTest, InsertAndFindWithUniquePtrValue) { |
||||
Map<const char*, UniquePtr<Payload>, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.insert(MakePair(kKeys[i], MakeUnique<Payload>(i))); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map.find(kKeys[i])->second->data()); |
||||
} |
||||
} |
||||
|
||||
// Test insertion of Unique Ptr kKeys and Payload
|
||||
TEST_F(MapTest, InsertAndFindWithUniquePtrKey) { |
||||
Map<UniquePtr<char>, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.insert(MakePair(CopyString(kKeys[i]), Payload(i))); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map.find(CopyString(kKeys[i]))->second.data()); |
||||
} |
||||
} |
||||
|
||||
// Test bracket operators
|
||||
TEST_F(MapTest, BracketOperator) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map[kKeys[i]] = Payload(i); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map[kKeys[i]].data()); |
||||
} |
||||
} |
||||
|
||||
// Test bracket operators with unique pointer to payload
|
||||
TEST_F(MapTest, BracketOperatorWithUniquePtrValue) { |
||||
Map<const char*, UniquePtr<Payload>, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map[kKeys[i]] = MakeUnique<Payload>(i); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map[kKeys[i]]->data()); |
||||
} |
||||
} |
||||
|
||||
// Test bracket operators with unique pointer to payload
|
||||
TEST_F(MapTest, BracketOperatorWithUniquePtrKey) { |
||||
Map<UniquePtr<char>, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map[CopyString(kKeys[i])] = Payload(i); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map[CopyString(kKeys[i])].data()); |
||||
} |
||||
} |
||||
|
||||
// Test removal of a single value
|
||||
TEST_F(MapTest, Erase) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], Payload(i)); |
||||
} |
||||
EXPECT_EQ(test_map.size(), 5UL); |
||||
EXPECT_EQ(test_map.erase(kKeys[3]), 1UL); // Remove "hij"
|
||||
for (int i = 0; i < 5; i++) { |
||||
if (i == 3) { // "hij" should not be present
|
||||
EXPECT_TRUE(test_map.find(kKeys[i]) == test_map.end()); |
||||
} else { |
||||
EXPECT_EQ(i, test_map.find(kKeys[i])->second.data()); |
||||
} |
||||
} |
||||
EXPECT_EQ(test_map.size(), 4UL); |
||||
} |
||||
|
||||
// Test removal of a single value with unique ptr to payload
|
||||
TEST_F(MapTest, EraseWithUniquePtrValue) { |
||||
Map<const char*, UniquePtr<Payload>, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], MakeUnique<Payload>(i)); |
||||
} |
||||
EXPECT_EQ(test_map.size(), 5UL); |
||||
test_map.erase(kKeys[3]); // Remove "hij"
|
||||
for (int i = 0; i < 5; i++) { |
||||
if (i == 3) { // "hij" should not be present
|
||||
EXPECT_TRUE(test_map.find(kKeys[i]) == test_map.end()); |
||||
} else { |
||||
EXPECT_EQ(i, test_map.find(kKeys[i])->second->data()); |
||||
} |
||||
} |
||||
EXPECT_EQ(test_map.size(), 4UL); |
||||
} |
||||
|
||||
// Test removal of a single value
|
||||
TEST_F(MapTest, EraseWithUniquePtrKey) { |
||||
Map<UniquePtr<char>, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(CopyString(kKeys[i]), Payload(i)); |
||||
} |
||||
EXPECT_EQ(test_map.size(), 5UL); |
||||
test_map.erase(CopyString(kKeys[3])); // Remove "hij"
|
||||
for (int i = 0; i < 5; i++) { |
||||
if (i == 3) { // "hij" should not be present
|
||||
EXPECT_TRUE(test_map.find(CopyString(kKeys[i])) == test_map.end()); |
||||
} else { |
||||
EXPECT_EQ(i, test_map.find(CopyString(kKeys[i]))->second.data()); |
||||
} |
||||
} |
||||
EXPECT_EQ(test_map.size(), 4UL); |
||||
} |
||||
|
||||
// Test clear
|
||||
TEST_F(MapTest, SizeAndClear) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], Payload(i)); |
||||
} |
||||
EXPECT_EQ(test_map.size(), 5UL); |
||||
EXPECT_FALSE(test_map.empty()); |
||||
test_map.clear(); |
||||
EXPECT_EQ(test_map.size(), 0UL); |
||||
EXPECT_TRUE(test_map.empty()); |
||||
} |
||||
|
||||
// Test clear with unique ptr payload
|
||||
TEST_F(MapTest, SizeAndClearWithUniquePtrValue) { |
||||
Map<const char*, UniquePtr<Payload>, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], MakeUnique<Payload>(i)); |
||||
} |
||||
EXPECT_EQ(test_map.size(), 5UL); |
||||
EXPECT_FALSE(test_map.empty()); |
||||
test_map.clear(); |
||||
EXPECT_EQ(test_map.size(), 0UL); |
||||
EXPECT_TRUE(test_map.empty()); |
||||
} |
||||
|
||||
// Test clear with unique ptr char key
|
||||
TEST_F(MapTest, SizeAndClearWithUniquePtrKey) { |
||||
Map<UniquePtr<char>, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(CopyString(kKeys[i]), Payload(i)); |
||||
} |
||||
EXPECT_EQ(test_map.size(), 5UL); |
||||
EXPECT_FALSE(test_map.empty()); |
||||
test_map.clear(); |
||||
EXPECT_EQ(test_map.size(), 0UL); |
||||
EXPECT_TRUE(test_map.empty()); |
||||
} |
||||
|
||||
// Test correction of Left-Left Tree imbalance
|
||||
TEST_F(MapTest, MapLL) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 2; i >= 0; i--) { |
||||
test_map.emplace(kKeys[i], Payload(i)); |
||||
} |
||||
EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[2]), 0); |
||||
} |
||||
|
||||
// Test correction of Left-Right tree imbalance
|
||||
TEST_F(MapTest, MapLR) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
int insertion_key_index[] = {2, 0, 1}; |
||||
for (int i = 0; i < 3; i++) { |
||||
int key_index = insertion_key_index[i]; |
||||
test_map.emplace(kKeys[key_index], Payload(key_index)); |
||||
} |
||||
EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[2]), 0); |
||||
} |
||||
|
||||
// Test correction of Right-Left tree imbalance
|
||||
TEST_F(MapTest, MapRL) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
int insertion_key_index[] = {0, 2, 1}; |
||||
for (int i = 0; i < 3; i++) { |
||||
int key_index = insertion_key_index[i]; |
||||
test_map.emplace(kKeys[key_index], Payload(key_index)); |
||||
} |
||||
EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[2]), 0); |
||||
} |
||||
|
||||
// Test correction of Right-Right tree imbalance
|
||||
TEST_F(MapTest, MapRR) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], Payload(i)); |
||||
} |
||||
EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[1]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[0]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[3]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->right->left->pair.first, kKeys[2]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->right->right->pair.first, kKeys[4]), 0); |
||||
} |
||||
|
||||
// Test correction after random insertion
|
||||
TEST_F(MapTest, MapRandomInsertions) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
int insertion_key_index[] = {1, 4, 3, 0, 2}; |
||||
for (int i = 0; i < 5; i++) { |
||||
int key_index = insertion_key_index[i]; |
||||
test_map.emplace(kKeys[key_index], Payload(key_index)); |
||||
} |
||||
EXPECT_EQ(strcmp(Root(&test_map)->pair.first, kKeys[3]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->left->pair.first, kKeys[1]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->right->pair.first, kKeys[4]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->left->right->pair.first, kKeys[2]), 0); |
||||
EXPECT_EQ(strcmp(Root(&test_map)->left->left->pair.first, kKeys[0]), 0); |
||||
} |
||||
|
||||
// Test Map iterator
|
||||
TEST_F(MapTest, Iteration) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], Payload(i)); |
||||
} |
||||
int count = 0; |
||||
for (auto iter = test_map.begin(); iter != test_map.end(); iter++) { |
||||
EXPECT_EQ(iter->second.data(), count); |
||||
count++; |
||||
} |
||||
EXPECT_EQ(count, 5); |
||||
} |
||||
|
||||
// Test Map iterator with unique ptr payload
|
||||
TEST_F(MapTest, IterationWithUniquePtrValue) { |
||||
Map<const char*, UniquePtr<Payload>, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], MakeUnique<Payload>(i)); |
||||
} |
||||
int count = 0; |
||||
for (auto iter = test_map.begin(); iter != test_map.end(); iter++) { |
||||
EXPECT_EQ(iter->second->data(), count); |
||||
count++; |
||||
} |
||||
EXPECT_EQ(count, 5); |
||||
} |
||||
|
||||
// Test Map iterator with unique ptr to char key
|
||||
TEST_F(MapTest, IterationWithUniquePtrKey) { |
||||
Map<UniquePtr<char>, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(CopyString(kKeys[i]), Payload(i)); |
||||
} |
||||
int count = 0; |
||||
for (auto iter = test_map.begin(); iter != test_map.end(); iter++) { |
||||
EXPECT_EQ(iter->second.data(), count); |
||||
count++; |
||||
} |
||||
EXPECT_EQ(count, 5); |
||||
} |
||||
|
||||
// Test removing entries while iterating the map
|
||||
TEST_F(MapTest, EraseUsingIterator) { |
||||
Map<const char*, Payload, StringLess> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(kKeys[i], Payload(i)); |
||||
} |
||||
int count = 0; |
||||
for (auto iter = test_map.begin(); iter != test_map.end();) { |
||||
EXPECT_EQ(iter->second.data(), count); |
||||
iter = test_map.erase(iter); |
||||
count++; |
||||
} |
||||
EXPECT_EQ(count, 5); |
||||
EXPECT_TRUE(test_map.empty()); |
||||
} |
||||
|
||||
// Random ops on a Map with Integer key of Payload value,
|
||||
// tests default comparator
|
||||
TEST_F(MapTest, RandomOpsWithIntKey) { |
||||
Map<int, Payload> test_map; |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map.emplace(i, Payload(i)); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i, test_map.find(i)->second.data()); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
test_map[i] = Payload(i + 10); |
||||
} |
||||
for (int i = 0; i < 5; i++) { |
||||
EXPECT_EQ(i + 10, test_map[i].data()); |
||||
} |
||||
EXPECT_EQ(test_map.erase(3), 1UL); |
||||
EXPECT_TRUE(test_map.find(3) == test_map.end()); |
||||
EXPECT_FALSE(test_map.empty()); |
||||
EXPECT_EQ(test_map.size(), 4UL); |
||||
test_map.clear(); |
||||
EXPECT_EQ(test_map.size(), 0UL); |
||||
EXPECT_TRUE(test_map.empty()); |
||||
} |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc::testing::TestEnvironment env(argc, argv); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue