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