mirror of https://github.com/grpc/grpc.git
AVL implementation in C++ (#27782)
* avl * move-code,add-fuzzer * done * fix * Automated change: Fix sanity tests * buildifier Co-authored-by: ctiller <ctiller@users.noreply.github.com>pull/28032/head
parent
2c7f2190e6
commit
1d63bb02b7
21 changed files with 623 additions and 767 deletions
@ -1,306 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/avl/avl.h" |
||||
|
||||
#include <assert.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include <algorithm> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
grpc_avl grpc_avl_create(const grpc_avl_vtable* vtable) { |
||||
grpc_avl out; |
||||
out.vtable = vtable; |
||||
out.root = nullptr; |
||||
return out; |
||||
} |
||||
|
||||
static grpc_avl_node* ref_node(grpc_avl_node* node) { |
||||
if (node) { |
||||
gpr_ref(&node->refs); |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
static void unref_node(const grpc_avl_vtable* vtable, grpc_avl_node* node, |
||||
void* user_data) { |
||||
if (node == nullptr) { |
||||
return; |
||||
} |
||||
if (gpr_unref(&node->refs)) { |
||||
vtable->destroy_key(node->key, user_data); |
||||
vtable->destroy_value(node->value, user_data); |
||||
unref_node(vtable, node->left, user_data); |
||||
unref_node(vtable, node->right, user_data); |
||||
gpr_free(node); |
||||
} |
||||
} |
||||
|
||||
static long node_height(grpc_avl_node* node) { |
||||
return node == nullptr ? 0 : node->height; |
||||
} |
||||
|
||||
#ifndef NDEBUG |
||||
static long calculate_height(grpc_avl_node* node) { |
||||
return node == nullptr ? 0 |
||||
: 1 + std::max(calculate_height(node->left), |
||||
calculate_height(node->right)); |
||||
} |
||||
|
||||
static grpc_avl_node* assert_invariants(grpc_avl_node* n) { |
||||
if (n == nullptr) return nullptr; |
||||
assert_invariants(n->left); |
||||
assert_invariants(n->right); |
||||
assert(calculate_height(n) == n->height); |
||||
assert(labs(node_height(n->left) - node_height(n->right)) <= 1); |
||||
return n; |
||||
} |
||||
#else |
||||
static grpc_avl_node* assert_invariants(grpc_avl_node* n) { return n; } |
||||
#endif |
||||
|
||||
grpc_avl_node* new_node(void* key, void* value, grpc_avl_node* left, |
||||
grpc_avl_node* right) { |
||||
grpc_avl_node* node = static_cast<grpc_avl_node*>(gpr_malloc(sizeof(*node))); |
||||
gpr_ref_init(&node->refs, 1); |
||||
node->key = key; |
||||
node->value = value; |
||||
node->left = assert_invariants(left); |
||||
node->right = assert_invariants(right); |
||||
node->height = 1 + std::max(node_height(left), node_height(right)); |
||||
return node; |
||||
} |
||||
|
||||
static grpc_avl_node* get(const grpc_avl_vtable* vtable, grpc_avl_node* node, |
||||
void* key, void* user_data) { |
||||
long cmp; |
||||
|
||||
if (node == nullptr) { |
||||
return nullptr; |
||||
} |
||||
|
||||
cmp = vtable->compare_keys(node->key, key, user_data); |
||||
if (cmp == 0) { |
||||
return node; |
||||
} else if (cmp > 0) { |
||||
return get(vtable, node->left, key, user_data); |
||||
} else { |
||||
return get(vtable, node->right, key, user_data); |
||||
} |
||||
} |
||||
|
||||
void* grpc_avl_get(grpc_avl avl, void* key, void* user_data) { |
||||
grpc_avl_node* node = get(avl.vtable, avl.root, key, user_data); |
||||
return node ? node->value : nullptr; |
||||
} |
||||
|
||||
int grpc_avl_maybe_get(grpc_avl avl, void* key, void** value, void* user_data) { |
||||
grpc_avl_node* node = get(avl.vtable, avl.root, key, user_data); |
||||
if (node != nullptr) { |
||||
*value = node->value; |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static grpc_avl_node* rotate_left(const grpc_avl_vtable* vtable, void* key, |
||||
void* value, grpc_avl_node* left, |
||||
grpc_avl_node* right, void* user_data) { |
||||
grpc_avl_node* n = new_node(vtable->copy_key(right->key, user_data), |
||||
vtable->copy_value(right->value, user_data), |
||||
new_node(key, value, left, ref_node(right->left)), |
||||
ref_node(right->right)); |
||||
unref_node(vtable, right, user_data); |
||||
return n; |
||||
} |
||||
|
||||
static grpc_avl_node* rotate_right(const grpc_avl_vtable* vtable, void* key, |
||||
void* value, grpc_avl_node* left, |
||||
grpc_avl_node* right, void* user_data) { |
||||
grpc_avl_node* n = |
||||
new_node(vtable->copy_key(left->key, user_data), |
||||
vtable->copy_value(left->value, user_data), ref_node(left->left), |
||||
new_node(key, value, ref_node(left->right), right)); |
||||
unref_node(vtable, left, user_data); |
||||
return n; |
||||
} |
||||
|
||||
static grpc_avl_node* rotate_left_right(const grpc_avl_vtable* vtable, |
||||
void* key, void* value, |
||||
grpc_avl_node* left, |
||||
grpc_avl_node* right, void* user_data) { |
||||
/* rotate_right(..., rotate_left(left), right) */ |
||||
grpc_avl_node* n = |
||||
new_node(vtable->copy_key(left->right->key, user_data), |
||||
vtable->copy_value(left->right->value, user_data), |
||||
new_node(vtable->copy_key(left->key, user_data), |
||||
vtable->copy_value(left->value, user_data), |
||||
ref_node(left->left), ref_node(left->right->left)), |
||||
new_node(key, value, ref_node(left->right->right), right)); |
||||
unref_node(vtable, left, user_data); |
||||
return n; |
||||
} |
||||
|
||||
static grpc_avl_node* rotate_right_left(const grpc_avl_vtable* vtable, |
||||
void* key, void* value, |
||||
grpc_avl_node* left, |
||||
grpc_avl_node* right, void* user_data) { |
||||
/* rotate_left(..., left, rotate_right(right)) */ |
||||
grpc_avl_node* n = |
||||
new_node(vtable->copy_key(right->left->key, user_data), |
||||
vtable->copy_value(right->left->value, user_data), |
||||
new_node(key, value, left, ref_node(right->left->left)), |
||||
new_node(vtable->copy_key(right->key, user_data), |
||||
vtable->copy_value(right->value, user_data), |
||||
ref_node(right->left->right), ref_node(right->right))); |
||||
unref_node(vtable, right, user_data); |
||||
return n; |
||||
} |
||||
|
||||
static grpc_avl_node* rebalance(const grpc_avl_vtable* vtable, void* key, |
||||
void* value, grpc_avl_node* left, |
||||
grpc_avl_node* right, void* user_data) { |
||||
switch (node_height(left) - node_height(right)) { |
||||
case 2: |
||||
if (node_height(left->left) - node_height(left->right) == -1) { |
||||
return assert_invariants( |
||||
rotate_left_right(vtable, key, value, left, right, user_data)); |
||||
} else { |
||||
return assert_invariants( |
||||
rotate_right(vtable, key, value, left, right, user_data)); |
||||
} |
||||
case -2: |
||||
if (node_height(right->left) - node_height(right->right) == 1) { |
||||
return assert_invariants( |
||||
rotate_right_left(vtable, key, value, left, right, user_data)); |
||||
} else { |
||||
return assert_invariants( |
||||
rotate_left(vtable, key, value, left, right, user_data)); |
||||
} |
||||
default: |
||||
return assert_invariants(new_node(key, value, left, right)); |
||||
} |
||||
} |
||||
|
||||
static grpc_avl_node* add_key(const grpc_avl_vtable* vtable, |
||||
grpc_avl_node* node, void* key, void* value, |
||||
void* user_data) { |
||||
long cmp; |
||||
if (node == nullptr) { |
||||
return new_node(key, value, nullptr, nullptr); |
||||
} |
||||
cmp = vtable->compare_keys(node->key, key, user_data); |
||||
if (cmp == 0) { |
||||
return new_node(key, value, ref_node(node->left), ref_node(node->right)); |
||||
} else if (cmp > 0) { |
||||
return rebalance(vtable, vtable->copy_key(node->key, user_data), |
||||
vtable->copy_value(node->value, user_data), |
||||
add_key(vtable, node->left, key, value, user_data), |
||||
ref_node(node->right), user_data); |
||||
} else { |
||||
return rebalance( |
||||
vtable, vtable->copy_key(node->key, user_data), |
||||
vtable->copy_value(node->value, user_data), ref_node(node->left), |
||||
add_key(vtable, node->right, key, value, user_data), user_data); |
||||
} |
||||
} |
||||
|
||||
grpc_avl grpc_avl_add(grpc_avl avl, void* key, void* value, void* user_data) { |
||||
grpc_avl_node* old_root = avl.root; |
||||
avl.root = add_key(avl.vtable, avl.root, key, value, user_data); |
||||
assert_invariants(avl.root); |
||||
unref_node(avl.vtable, old_root, user_data); |
||||
return avl; |
||||
} |
||||
|
||||
static grpc_avl_node* in_order_head(grpc_avl_node* node) { |
||||
while (node->left != nullptr) { |
||||
node = node->left; |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
static grpc_avl_node* in_order_tail(grpc_avl_node* node) { |
||||
while (node->right != nullptr) { |
||||
node = node->right; |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
static grpc_avl_node* remove_key(const grpc_avl_vtable* vtable, |
||||
grpc_avl_node* node, void* key, |
||||
void* user_data) { |
||||
long cmp; |
||||
if (node == nullptr) { |
||||
return nullptr; |
||||
} |
||||
cmp = vtable->compare_keys(node->key, key, user_data); |
||||
if (cmp == 0) { |
||||
if (node->left == nullptr) { |
||||
return ref_node(node->right); |
||||
} else if (node->right == nullptr) { |
||||
return ref_node(node->left); |
||||
} else if (node->left->height < node->right->height) { |
||||
grpc_avl_node* h = in_order_head(node->right); |
||||
return rebalance( |
||||
vtable, vtable->copy_key(h->key, user_data), |
||||
vtable->copy_value(h->value, user_data), ref_node(node->left), |
||||
remove_key(vtable, node->right, h->key, user_data), user_data); |
||||
} else { |
||||
grpc_avl_node* h = in_order_tail(node->left); |
||||
return rebalance(vtable, vtable->copy_key(h->key, user_data), |
||||
vtable->copy_value(h->value, user_data), |
||||
remove_key(vtable, node->left, h->key, user_data), |
||||
ref_node(node->right), user_data); |
||||
} |
||||
} else if (cmp > 0) { |
||||
return rebalance(vtable, vtable->copy_key(node->key, user_data), |
||||
vtable->copy_value(node->value, user_data), |
||||
remove_key(vtable, node->left, key, user_data), |
||||
ref_node(node->right), user_data); |
||||
} else { |
||||
return rebalance( |
||||
vtable, vtable->copy_key(node->key, user_data), |
||||
vtable->copy_value(node->value, user_data), ref_node(node->left), |
||||
remove_key(vtable, node->right, key, user_data), user_data); |
||||
} |
||||
} |
||||
|
||||
grpc_avl grpc_avl_remove(grpc_avl avl, void* key, void* user_data) { |
||||
grpc_avl_node* old_root = avl.root; |
||||
avl.root = remove_key(avl.vtable, avl.root, key, user_data); |
||||
assert_invariants(avl.root); |
||||
unref_node(avl.vtable, old_root, user_data); |
||||
return avl; |
||||
} |
||||
|
||||
grpc_avl grpc_avl_ref(grpc_avl avl, void* /*user_data*/) { |
||||
ref_node(avl.root); |
||||
return avl; |
||||
} |
||||
|
||||
void grpc_avl_unref(grpc_avl avl, void* user_data) { |
||||
unref_node(avl.vtable, avl.root, user_data); |
||||
} |
||||
|
||||
int grpc_avl_is_empty(grpc_avl avl) { return avl.root == nullptr; } |
@ -1,94 +1,393 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 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. |
||||
* |
||||
*/ |
||||
// Copyright 2021 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_AVL_AVL_H |
||||
#define GRPC_CORE_LIB_AVL_AVL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <grpc/support/sync.h> |
||||
|
||||
/** internal node of an AVL tree */ |
||||
typedef struct grpc_avl_node { |
||||
gpr_refcount refs; |
||||
void* key; |
||||
void* value; |
||||
struct grpc_avl_node* left; |
||||
struct grpc_avl_node* right; |
||||
long height; |
||||
} grpc_avl_node; |
||||
|
||||
/** vtable for the AVL tree
|
||||
* The optional user_data is propagated from the top level grpc_avl_XXX API. |
||||
* From the same API call, multiple vtable functions may be called multiple |
||||
* times. |
||||
*/ |
||||
typedef struct grpc_avl_vtable { |
||||
/** destroy a key */ |
||||
void (*destroy_key)(void* key, void* user_data); |
||||
/** copy a key, returning new value */ |
||||
void* (*copy_key)(void* key, void* user_data); |
||||
/** compare key1, key2; return <0 if key1 < key2,
|
||||
>0 if key1 > key2, 0 if key1 == key2 */ |
||||
long (*compare_keys)(void* key1, void* key2, void* user_data); |
||||
/** destroy a value */ |
||||
void (*destroy_value)(void* value, void* user_data); |
||||
/** copy a value */ |
||||
void* (*copy_value)(void* value, void* user_data); |
||||
} grpc_avl_vtable; |
||||
|
||||
/** "pointer" to an AVL tree - this is a reference
|
||||
counted object - use grpc_avl_ref to add a reference, |
||||
grpc_avl_unref when done with a reference */ |
||||
typedef struct grpc_avl { |
||||
const grpc_avl_vtable* vtable; |
||||
grpc_avl_node* root; |
||||
} grpc_avl; |
||||
|
||||
/** Create an immutable AVL tree. */ |
||||
grpc_avl grpc_avl_create(const grpc_avl_vtable* vtable); |
||||
/** Add a reference to an existing tree - returns
|
||||
the tree as a convenience. The optional user_data will be passed to vtable |
||||
functions. */ |
||||
grpc_avl grpc_avl_ref(grpc_avl avl, void* user_data); |
||||
/** Remove a reference to a tree - destroying it if there
|
||||
are no references left. The optional user_data will be passed to vtable |
||||
functions. */ |
||||
void grpc_avl_unref(grpc_avl avl, void* user_data); |
||||
/** Return a new tree with (key, value) added to avl.
|
||||
implicitly unrefs avl to allow easy chaining. |
||||
if key exists in avl, the new tree's key entry updated |
||||
(i.e. a duplicate is not created). The optional user_data will be passed to |
||||
vtable functions. */ |
||||
grpc_avl grpc_avl_add(grpc_avl avl, void* key, void* value, void* user_data); |
||||
/** Return a new tree with key deleted
|
||||
implicitly unrefs avl to allow easy chaining. The optional user_data will be |
||||
passed to vtable functions. */ |
||||
grpc_avl grpc_avl_remove(grpc_avl avl, void* key, void* user_data); |
||||
/** Lookup key, and return the associated value.
|
||||
Does not mutate avl. |
||||
Returns NULL if key is not found. The optional user_data will be passed to |
||||
vtable functions.*/ |
||||
void* grpc_avl_get(grpc_avl avl, void* key, void* user_data); |
||||
/** Return 1 if avl contains key, 0 otherwise; if it has the key, sets *value to
|
||||
its value. The optional user_data will be passed to vtable functions. */ |
||||
int grpc_avl_maybe_get(grpc_avl avl, void* key, void** value, void* user_data); |
||||
/** Return 1 if avl is empty, 0 otherwise */ |
||||
int grpc_avl_is_empty(grpc_avl avl); |
||||
|
||||
#endif /* GRPC_CORE_LIB_AVL_AVL_H */ |
||||
#include <algorithm> |
||||
#include <memory> |
||||
|
||||
namespace grpc_core { |
||||
|
||||
template <class K, class V = void> |
||||
class AVL { |
||||
public: |
||||
AVL() {} |
||||
|
||||
AVL Add(K key, V value) const { |
||||
return AVL(AddKey(root_, std::move(key), std::move(value))); |
||||
} |
||||
AVL Remove(const K& key) const { return AVL(RemoveKey(root_, key)); } |
||||
const V* Lookup(const K& key) const { |
||||
NodePtr n = Get(root_, key); |
||||
return n ? &n->kv.second : nullptr; |
||||
} |
||||
|
||||
const std::pair<K, V>* LookupBelow(const K& key) const { |
||||
NodePtr n = GetBelow(root_, *key); |
||||
return n ? &n->kv : nullptr; |
||||
} |
||||
|
||||
bool Empty() const { return root_ == nullptr; } |
||||
|
||||
template <class F> |
||||
void ForEach(F&& f) const { |
||||
ForEachImpl(root_.get(), std::forward<F>(f)); |
||||
} |
||||
|
||||
bool SameIdentity(AVL avl) const { return root_ == avl.root_; } |
||||
|
||||
private: |
||||
struct Node; |
||||
typedef std::shared_ptr<Node> NodePtr; |
||||
struct Node : public std::enable_shared_from_this<Node> { |
||||
Node(K k, V v, NodePtr l, NodePtr r, long h) |
||||
: kv(std::move(k), std::move(v)), |
||||
left(std::move(l)), |
||||
right(std::move(r)), |
||||
height(h) {} |
||||
const std::pair<K, V> kv; |
||||
const NodePtr left; |
||||
const NodePtr right; |
||||
const long height; |
||||
}; |
||||
NodePtr root_; |
||||
|
||||
explicit AVL(NodePtr root) : root_(std::move(root)) {} |
||||
|
||||
template <class F> |
||||
static void ForEachImpl(const Node* n, F&& f) { |
||||
if (n == nullptr) return; |
||||
ForEachImpl(n->left.get(), std::forward<F>(f)); |
||||
f(const_cast<const K&>(n->kv.first), const_cast<const V&>(n->kv.second)); |
||||
ForEachImpl(n->right.get(), std::forward<F>(f)); |
||||
} |
||||
|
||||
static long Height(const NodePtr& n) { return n ? n->height : 0; } |
||||
|
||||
static NodePtr MakeNode(K key, V value, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
return std::make_shared<Node>(std::move(key), std::move(value), left, right, |
||||
1 + std::max(Height(left), Height(right))); |
||||
} |
||||
|
||||
static NodePtr Get(const NodePtr& node, const K& key) { |
||||
if (node == nullptr) { |
||||
return nullptr; |
||||
} |
||||
|
||||
if (node->kv.first > key) { |
||||
return Get(node->left, key); |
||||
} else if (node->kv.first < key) { |
||||
return Get(node->right, key); |
||||
} else { |
||||
return node; |
||||
} |
||||
} |
||||
|
||||
static NodePtr GetBelow(const NodePtr& node, const K& key) { |
||||
if (!node) return nullptr; |
||||
if (node->kv.first > key) { |
||||
return GetBelow(node->left, key); |
||||
} else if (node->kv.first < key) { |
||||
NodePtr n = GetBelow(node->right, key); |
||||
if (n == nullptr) n = node; |
||||
return n; |
||||
} else { |
||||
return node; |
||||
} |
||||
} |
||||
|
||||
static NodePtr RotateLeft(K key, V value, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
return MakeNode( |
||||
right->kv.first, right->kv.second, |
||||
MakeNode(std::move(key), std::move(value), left, right->left), |
||||
right->right); |
||||
} |
||||
|
||||
static NodePtr RotateRight(K key, V value, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
return MakeNode( |
||||
left->kv.first, left->kv.second, left->left, |
||||
MakeNode(std::move(key), std::move(value), left->right, right)); |
||||
} |
||||
|
||||
static NodePtr RotateLeftRight(K key, V value, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
/* rotate_right(..., rotate_left(left), right) */ |
||||
return MakeNode( |
||||
left->right->kv.first, left->right->kv.second, |
||||
MakeNode(left->kv.first, left->kv.second, left->left, |
||||
left->right->left), |
||||
MakeNode(std::move(key), std::move(value), left->right->right, right)); |
||||
} |
||||
|
||||
static NodePtr RotateRightLeft(K key, V value, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
/* rotate_left(..., left, rotate_right(right)) */ |
||||
return MakeNode( |
||||
right->left->kv.first, right->left->kv.second, |
||||
MakeNode(std::move(key), std::move(value), left, right->left->left), |
||||
MakeNode(right->kv.first, right->kv.second, right->left->right, |
||||
right->right)); |
||||
} |
||||
|
||||
static NodePtr Rebalance(K key, V value, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
switch (Height(left) - Height(right)) { |
||||
case 2: |
||||
if (Height(left->left) - Height(left->right) == -1) { |
||||
return RotateLeftRight(std::move(key), std::move(value), left, right); |
||||
} else { |
||||
return RotateRight(std::move(key), std::move(value), left, right); |
||||
} |
||||
case -2: |
||||
if (Height(right->left) - Height(right->right) == 1) { |
||||
return RotateRightLeft(std::move(key), std::move(value), left, right); |
||||
} else { |
||||
return RotateLeft(std::move(key), std::move(value), left, right); |
||||
} |
||||
default: |
||||
return MakeNode(key, value, left, right); |
||||
} |
||||
} |
||||
|
||||
static NodePtr AddKey(const NodePtr& node, K key, V value) { |
||||
if (!node) { |
||||
return MakeNode(std::move(key), std::move(value), nullptr, nullptr); |
||||
} |
||||
if (node->kv.first < key) { |
||||
return Rebalance(node->kv.first, node->kv.second, node->left, |
||||
AddKey(node->right, std::move(key), std::move(value))); |
||||
} |
||||
if (key < node->kv.first) { |
||||
return Rebalance(node->kv.first, node->kv.second, |
||||
AddKey(node->left, std::move(key), std::move(value)), |
||||
node->right); |
||||
} |
||||
return MakeNode(std::move(key), std::move(value), node->left, node->right); |
||||
} |
||||
|
||||
static NodePtr InOrderHead(NodePtr node) { |
||||
while (node->left != nullptr) { |
||||
node = node->left; |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
static NodePtr InOrderTail(NodePtr node) { |
||||
while (node->right != nullptr) { |
||||
node = node->right; |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
static NodePtr RemoveKey(const NodePtr& node, const K& key) { |
||||
if (node == nullptr) { |
||||
return nullptr; |
||||
} |
||||
if (key < node->kv.first) { |
||||
return Rebalance(node->kv.first, node->kv.second, |
||||
RemoveKey(node->left, key), node->right); |
||||
} else if (node->kv.first < key) { |
||||
return Rebalance(node->kv.first, node->kv.second, node->left, |
||||
RemoveKey(node->right, key)); |
||||
} else { |
||||
if (node->left == nullptr) { |
||||
return node->right; |
||||
} else if (node->right == nullptr) { |
||||
return node->left; |
||||
} else if (node->left->height < node->right->height) { |
||||
NodePtr h = InOrderHead(node->right); |
||||
return Rebalance(h->kv.first, h->kv.second, node->left, |
||||
RemoveKey(node->right, h->kv.first)); |
||||
} else { |
||||
NodePtr h = InOrderTail(node->left); |
||||
return Rebalance(h->kv.first, h->kv.second, |
||||
RemoveKey(node->left, h->kv.first), node->right); |
||||
} |
||||
} |
||||
abort(); |
||||
} |
||||
}; |
||||
|
||||
template <class K> |
||||
class AVL<K, void> { |
||||
public: |
||||
AVL() {} |
||||
|
||||
AVL Add(K key) const { return AVL(AddKey(root_, std::move(key))); } |
||||
AVL Remove(const K& key) const { return AVL(RemoveKey(root_, key)); } |
||||
bool Lookup(const K& key) const { return Get(root_, key) != nullptr; } |
||||
bool Empty() const { return root_ == nullptr; } |
||||
|
||||
template <class F> |
||||
void ForEach(F&& f) const { |
||||
ForEachImpl(root_.get(), std::forward<F>(f)); |
||||
} |
||||
|
||||
bool SameIdentity(AVL avl) const { return root_ == avl.root_; } |
||||
|
||||
private: |
||||
struct Node; |
||||
typedef std::shared_ptr<Node> NodePtr; |
||||
struct Node : public std::enable_shared_from_this<Node> { |
||||
Node(K k, NodePtr l, NodePtr r, long h) |
||||
: key(std::move(k)), |
||||
left(std::move(l)), |
||||
right(std::move(r)), |
||||
height(h) {} |
||||
const K key; |
||||
const NodePtr left; |
||||
const NodePtr right; |
||||
const long height; |
||||
}; |
||||
NodePtr root_; |
||||
|
||||
explicit AVL(NodePtr root) : root_(std::move(root)) {} |
||||
|
||||
template <class F> |
||||
static void ForEachImpl(const Node* n, F&& f) { |
||||
if (n == nullptr) return; |
||||
ForEachImpl(n->left.get(), std::forward<F>(f)); |
||||
f(const_cast<const K&>(n->key)); |
||||
ForEachImpl(n->right.get(), std::forward<F>(f)); |
||||
} |
||||
|
||||
static long Height(const NodePtr& n) { return n ? n->height : 0; } |
||||
|
||||
static NodePtr MakeNode(K key, const NodePtr& left, const NodePtr& right) { |
||||
return std::make_shared<Node>(std::move(key), left, right, |
||||
1 + std::max(Height(left), Height(right))); |
||||
} |
||||
|
||||
static NodePtr Get(const NodePtr& node, const K& key) { |
||||
if (node == nullptr) { |
||||
return nullptr; |
||||
} |
||||
|
||||
if (node->key > key) { |
||||
return Get(node->left, key); |
||||
} else if (node->key < key) { |
||||
return Get(node->right, key); |
||||
} else { |
||||
return node; |
||||
} |
||||
} |
||||
|
||||
static NodePtr RotateLeft(K key, const NodePtr& left, const NodePtr& right) { |
||||
return MakeNode(right->key, MakeNode(std::move(key), left, right->left), |
||||
right->right); |
||||
} |
||||
|
||||
static NodePtr RotateRight(K key, const NodePtr& left, const NodePtr& right) { |
||||
return MakeNode(left->key, left->left, |
||||
MakeNode(std::move(key), left->right, right)); |
||||
} |
||||
|
||||
static NodePtr RotateLeftRight(K key, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
/* rotate_right(..., rotate_left(left), right) */ |
||||
return MakeNode(left->right->key, |
||||
MakeNode(left->key, left->left, left->right->left), |
||||
MakeNode(std::move(key), left->right->right, right)); |
||||
} |
||||
|
||||
static NodePtr RotateRightLeft(K key, const NodePtr& left, |
||||
const NodePtr& right) { |
||||
/* rotate_left(..., left, rotate_right(right)) */ |
||||
return MakeNode(right->left->key, |
||||
MakeNode(std::move(key), left, right->left->left), |
||||
MakeNode(right->key, right->left->right, right->right)); |
||||
} |
||||
|
||||
static NodePtr Rebalance(K key, const NodePtr& left, const NodePtr& right) { |
||||
switch (Height(left) - Height(right)) { |
||||
case 2: |
||||
if (Height(left->left) - Height(left->right) == -1) { |
||||
return RotateLeftRight(std::move(key), left, right); |
||||
} else { |
||||
return RotateRight(std::move(key), left, right); |
||||
} |
||||
case -2: |
||||
if (Height(right->left) - Height(right->right) == 1) { |
||||
return RotateRightLeft(std::move(key), left, right); |
||||
} else { |
||||
return RotateLeft(std::move(key), left, right); |
||||
} |
||||
default: |
||||
return MakeNode(key, left, right); |
||||
} |
||||
} |
||||
|
||||
static NodePtr AddKey(const NodePtr& node, K key) { |
||||
if (!node) { |
||||
return MakeNode(std::move(key), nullptr, nullptr); |
||||
} |
||||
if (node->key < key) { |
||||
return Rebalance(node->key, node->left, |
||||
AddKey(node->right, std::move(key))); |
||||
} |
||||
if (key < node->key) { |
||||
return Rebalance(node->key, AddKey(node->left, std::move(key)), |
||||
node->right); |
||||
} |
||||
return MakeNode(std::move(key), node->left, node->right); |
||||
} |
||||
|
||||
static NodePtr InOrderHead(NodePtr node) { |
||||
while (node->left != nullptr) { |
||||
node = node->left; |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
static NodePtr InOrderTail(NodePtr node) { |
||||
while (node->right != nullptr) { |
||||
node = node->right; |
||||
} |
||||
return node; |
||||
} |
||||
|
||||
static NodePtr RemoveKey(const NodePtr& node, const K& key) { |
||||
if (node == nullptr) { |
||||
return nullptr; |
||||
} |
||||
if (key < node->key) { |
||||
return Rebalance(node->key, RemoveKey(node->left, key), node->right); |
||||
} else if (node->key < key) { |
||||
return Rebalance(node->key, node->left, RemoveKey(node->right, key)); |
||||
} else { |
||||
if (node->left == nullptr) { |
||||
return node->right; |
||||
} else if (node->right == nullptr) { |
||||
return node->left; |
||||
} else if (node->left->height < node->right->height) { |
||||
NodePtr h = InOrderHead(node->right); |
||||
return Rebalance(h->key, node->left, RemoveKey(node->right, h->key)); |
||||
} else { |
||||
NodePtr h = InOrderTail(node->left); |
||||
return Rebalance(h->key, RemoveKey(node->left, h->key), node->right); |
||||
} |
||||
} |
||||
abort(); |
||||
} |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_CORE_LIB_AVL_AVL_H
|
||||
|
@ -0,0 +1,72 @@ |
||||
// Copyright 2021 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/avl/avl.h" |
||||
#include "src/libfuzzer/libfuzzer_macro.h" |
||||
#include "test/core/avl/avl_fuzzer.pb.h" |
||||
|
||||
bool squelch = true; |
||||
bool leak_check = true; |
||||
|
||||
namespace grpc_core { |
||||
|
||||
class Fuzzer { |
||||
public: |
||||
void Run(const avl_fuzzer::Msg& msg) { |
||||
CheckEqual(); |
||||
for (const auto& action : msg.actions()) { |
||||
switch (action.action_case()) { |
||||
case avl_fuzzer::Action::kSet: |
||||
avl_ = avl_.Add(action.key(), action.set()); |
||||
map_[action.key()] = action.set(); |
||||
break; |
||||
case avl_fuzzer::Action::kDel: |
||||
avl_ = avl_.Remove(action.key()); |
||||
map_.erase(action.key()); |
||||
break; |
||||
case avl_fuzzer::Action::kGet: { |
||||
auto* p = avl_.Lookup(action.key()); |
||||
auto it = map_.find(action.key()); |
||||
if (it == map_.end() && p != nullptr) abort(); |
||||
if (it != map_.end() && p == nullptr) abort(); |
||||
if (it != map_.end() && it->second != *p) abort(); |
||||
} break; |
||||
case avl_fuzzer::Action::ACTION_NOT_SET: |
||||
break; |
||||
} |
||||
CheckEqual(); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
void CheckEqual() { |
||||
auto it = map_.begin(); |
||||
avl_.ForEach([&](int key, int value) { |
||||
if (it == map_.end()) abort(); |
||||
if (it->first != key) abort(); |
||||
if (it->second != value) abort(); |
||||
++it; |
||||
}); |
||||
if (it != map_.end()) abort(); |
||||
} |
||||
|
||||
AVL<int, int> avl_; |
||||
std::map<int, int> map_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
DEFINE_PROTO_FUZZER(const avl_fuzzer::Msg& msg) { |
||||
grpc_core::Fuzzer().Run(msg); |
||||
} |
@ -0,0 +1,32 @@ |
||||
// Copyright 2021 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. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package avl_fuzzer; |
||||
|
||||
message Empty{} |
||||
|
||||
message Action { |
||||
int32 key = 1; |
||||
oneof action { |
||||
int32 set = 2; |
||||
Empty get = 3; |
||||
Empty del = 4; |
||||
} |
||||
} |
||||
|
||||
message Msg { |
||||
repeated Action actions = 2; |
||||
} |
@ -0,0 +1 @@ |
||||
|
@ -1,300 +1,44 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 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. |
||||
* |
||||
*/ |
||||
// Copyright 2021 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/avl/avl.h" |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
namespace grpc_core { |
||||
|
||||
#include "src/core/lib/gpr/useful.h" |
||||
#include "test/core/util/test_config.h" |
||||
TEST(AvlTest, NoOp) { AVL<int, int> avl; } |
||||
|
||||
static int* box(int x) { |
||||
int* b = static_cast<int*>(gpr_malloc(sizeof(*b))); |
||||
*b = x; |
||||
return b; |
||||
TEST(AvlTest, Lookup) { |
||||
auto avl = AVL<int, int>().Add(1, 42); |
||||
EXPECT_EQ(nullptr, avl.Lookup(2)); |
||||
EXPECT_EQ(42, *avl.Lookup(1)); |
||||
avl = avl.Remove(1); |
||||
EXPECT_EQ(nullptr, avl.Lookup(1)); |
||||
avl = avl.Add(1, 42).Add(1, 1); |
||||
EXPECT_EQ(1, *avl.Lookup(1)); |
||||
avl = avl.Add(2, 2).Add(3, 3).Add(4, 4); |
||||
EXPECT_EQ(1, *avl.Lookup(1)); |
||||
EXPECT_EQ(2, *avl.Lookup(2)); |
||||
EXPECT_EQ(3, *avl.Lookup(3)); |
||||
EXPECT_EQ(4, *avl.Lookup(4)); |
||||
EXPECT_EQ(nullptr, avl.Lookup(5)); |
||||
} |
||||
|
||||
static long int_compare(void* int1, void* int2, void* /*unused*/) { |
||||
return (*static_cast<int*>(int1)) - (*static_cast<int*>(int2)); |
||||
} |
||||
static void* int_copy(void* p, void* /*unused*/) { |
||||
return box(*static_cast<int*>(p)); |
||||
} |
||||
|
||||
static void destroy(void* p, void* /*unused*/) { gpr_free(p); } |
||||
|
||||
static const grpc_avl_vtable int_int_vtable = {destroy, int_copy, int_compare, |
||||
destroy, int_copy}; |
||||
|
||||
static void check_get(grpc_avl avl, int key, int value) { |
||||
int* k = box(key); |
||||
GPR_ASSERT(*(int*)grpc_avl_get(avl, k, nullptr) == value); |
||||
gpr_free(k); |
||||
} |
||||
|
||||
static void check_negget(grpc_avl avl, int key) { |
||||
int* k = box(key); |
||||
GPR_ASSERT(grpc_avl_get(avl, k, nullptr) == nullptr); |
||||
gpr_free(k); |
||||
} |
||||
|
||||
static grpc_avl remove_int(grpc_avl avl, int key) { |
||||
int* k = box(key); |
||||
avl = grpc_avl_remove(avl, k, nullptr); |
||||
gpr_free(k); |
||||
return avl; |
||||
} |
||||
|
||||
static void test_get(void) { |
||||
grpc_avl avl; |
||||
gpr_log(GPR_DEBUG, "test_get"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(1), box(11), nullptr); |
||||
avl = grpc_avl_add(avl, box(2), box(22), nullptr); |
||||
avl = grpc_avl_add(avl, box(3), box(33), nullptr); |
||||
check_get(avl, 1, 11); |
||||
check_get(avl, 2, 22); |
||||
check_get(avl, 3, 33); |
||||
check_negget(avl, 4); |
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_ll(void) { |
||||
grpc_avl avl; |
||||
gpr_log(GPR_DEBUG, "test_ll"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(5), box(1), nullptr); |
||||
avl = grpc_avl_add(avl, box(4), box(2), nullptr); |
||||
avl = grpc_avl_add(avl, box(3), box(3), nullptr); |
||||
GPR_ASSERT(*(int*)avl.root->key == 4); |
||||
GPR_ASSERT(*(int*)avl.root->left->key == 3); |
||||
GPR_ASSERT(*(int*)avl.root->right->key == 5); |
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_lr(void) { |
||||
grpc_avl avl; |
||||
gpr_log(GPR_DEBUG, "test_lr"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(5), box(1), nullptr); |
||||
avl = grpc_avl_add(avl, box(3), box(2), nullptr); |
||||
avl = grpc_avl_add(avl, box(4), box(3), nullptr); |
||||
GPR_ASSERT(*(int*)avl.root->key == 4); |
||||
GPR_ASSERT(*(int*)avl.root->left->key == 3); |
||||
GPR_ASSERT(*(int*)avl.root->right->key == 5); |
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_rr(void) { |
||||
grpc_avl avl; |
||||
gpr_log(GPR_DEBUG, "test_rr"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(3), box(1), nullptr); |
||||
avl = grpc_avl_add(avl, box(4), box(2), nullptr); |
||||
avl = grpc_avl_add(avl, box(5), box(3), nullptr); |
||||
GPR_ASSERT(*(int*)avl.root->key == 4); |
||||
GPR_ASSERT(*(int*)avl.root->left->key == 3); |
||||
GPR_ASSERT(*(int*)avl.root->right->key == 5); |
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_rl(void) { |
||||
grpc_avl avl; |
||||
gpr_log(GPR_DEBUG, "test_rl"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(3), box(1), nullptr); |
||||
avl = grpc_avl_add(avl, box(5), box(2), nullptr); |
||||
avl = grpc_avl_add(avl, box(4), box(3), nullptr); |
||||
GPR_ASSERT(*(int*)avl.root->key == 4); |
||||
GPR_ASSERT(*(int*)avl.root->left->key == 3); |
||||
GPR_ASSERT(*(int*)avl.root->right->key == 5); |
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_unbalanced(void) { |
||||
grpc_avl avl; |
||||
gpr_log(GPR_DEBUG, "test_unbalanced"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(5), box(1), nullptr); |
||||
avl = grpc_avl_add(avl, box(4), box(2), nullptr); |
||||
avl = grpc_avl_add(avl, box(3), box(3), nullptr); |
||||
avl = grpc_avl_add(avl, box(2), box(4), nullptr); |
||||
avl = grpc_avl_add(avl, box(1), box(5), nullptr); |
||||
GPR_ASSERT(*(int*)avl.root->key == 4); |
||||
GPR_ASSERT(*(int*)avl.root->left->key == 2); |
||||
GPR_ASSERT(*(int*)avl.root->left->left->key == 1); |
||||
GPR_ASSERT(*(int*)avl.root->left->right->key == 3); |
||||
GPR_ASSERT(*(int*)avl.root->right->key == 5); |
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_replace(void) { |
||||
grpc_avl avl; |
||||
gpr_log(GPR_DEBUG, "test_replace"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(1), box(1), nullptr); |
||||
avl = grpc_avl_add(avl, box(1), box(2), nullptr); |
||||
check_get(avl, 1, 2); |
||||
check_negget(avl, 2); |
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_remove(void) { |
||||
grpc_avl avl; |
||||
grpc_avl avl3, avl4, avl5, avln; |
||||
gpr_log(GPR_DEBUG, "test_remove"); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(3), box(1), nullptr); |
||||
avl = grpc_avl_add(avl, box(4), box(2), nullptr); |
||||
avl = grpc_avl_add(avl, box(5), box(3), nullptr); |
||||
|
||||
avl3 = remove_int(grpc_avl_ref(avl, nullptr), 3); |
||||
avl4 = remove_int(grpc_avl_ref(avl, nullptr), 4); |
||||
avl5 = remove_int(grpc_avl_ref(avl, nullptr), 5); |
||||
avln = remove_int(grpc_avl_ref(avl, nullptr), 1); |
||||
|
||||
grpc_avl_unref(avl, nullptr); |
||||
|
||||
check_negget(avl3, 3); |
||||
check_get(avl3, 4, 2); |
||||
check_get(avl3, 5, 3); |
||||
grpc_avl_unref(avl3, nullptr); |
||||
|
||||
check_get(avl4, 3, 1); |
||||
check_negget(avl4, 4); |
||||
check_get(avl4, 5, 3); |
||||
grpc_avl_unref(avl4, nullptr); |
||||
|
||||
check_get(avl5, 3, 1); |
||||
check_get(avl5, 4, 2); |
||||
check_negget(avl5, 5); |
||||
grpc_avl_unref(avl5, nullptr); |
||||
|
||||
check_get(avln, 3, 1); |
||||
check_get(avln, 4, 2); |
||||
check_get(avln, 5, 3); |
||||
grpc_avl_unref(avln, nullptr); |
||||
} |
||||
|
||||
static void test_badcase1(void) { |
||||
grpc_avl avl; |
||||
|
||||
gpr_log(GPR_DEBUG, "test_badcase1"); |
||||
|
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
avl = grpc_avl_add(avl, box(88), box(1), nullptr); |
||||
avl = remove_int(avl, 643); |
||||
avl = remove_int(avl, 983); |
||||
avl = grpc_avl_add(avl, box(985), box(4), nullptr); |
||||
avl = grpc_avl_add(avl, box(640), box(5), nullptr); |
||||
avl = grpc_avl_add(avl, box(41), box(6), nullptr); |
||||
avl = grpc_avl_add(avl, box(112), box(7), nullptr); |
||||
avl = grpc_avl_add(avl, box(342), box(8), nullptr); |
||||
avl = remove_int(avl, 1013); |
||||
avl = grpc_avl_add(avl, box(434), box(10), nullptr); |
||||
avl = grpc_avl_add(avl, box(520), box(11), nullptr); |
||||
avl = grpc_avl_add(avl, box(231), box(12), nullptr); |
||||
avl = grpc_avl_add(avl, box(852), box(13), nullptr); |
||||
avl = remove_int(avl, 461); |
||||
avl = grpc_avl_add(avl, box(108), box(15), nullptr); |
||||
avl = grpc_avl_add(avl, box(806), box(16), nullptr); |
||||
avl = grpc_avl_add(avl, box(827), box(17), nullptr); |
||||
avl = remove_int(avl, 796); |
||||
avl = grpc_avl_add(avl, box(340), box(19), nullptr); |
||||
avl = grpc_avl_add(avl, box(498), box(20), nullptr); |
||||
avl = grpc_avl_add(avl, box(203), box(21), nullptr); |
||||
avl = grpc_avl_add(avl, box(751), box(22), nullptr); |
||||
avl = grpc_avl_add(avl, box(150), box(23), nullptr); |
||||
avl = remove_int(avl, 237); |
||||
avl = grpc_avl_add(avl, box(830), box(25), nullptr); |
||||
avl = remove_int(avl, 1007); |
||||
avl = remove_int(avl, 394); |
||||
avl = grpc_avl_add(avl, box(65), box(28), nullptr); |
||||
avl = remove_int(avl, 904); |
||||
avl = remove_int(avl, 123); |
||||
avl = grpc_avl_add(avl, box(238), box(31), nullptr); |
||||
avl = grpc_avl_add(avl, box(184), box(32), nullptr); |
||||
avl = remove_int(avl, 331); |
||||
avl = grpc_avl_add(avl, box(827), box(34), nullptr); |
||||
|
||||
check_get(avl, 830, 25); |
||||
|
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
static void test_stress(int amount_of_stress) { |
||||
int added[1024]; |
||||
int i, j; |
||||
int deletions = 0; |
||||
grpc_avl avl; |
||||
|
||||
unsigned seed = static_cast<unsigned>(time(nullptr)); |
||||
|
||||
gpr_log(GPR_DEBUG, "test_stress amount=%d seed=%u", amount_of_stress, seed); |
||||
|
||||
srand(static_cast<unsigned>(time(nullptr))); |
||||
avl = grpc_avl_create(&int_int_vtable); |
||||
|
||||
memset(added, 0, sizeof(added)); |
||||
|
||||
for (i = 1; deletions < amount_of_stress; i++) { |
||||
int idx = rand() % static_cast<int> GPR_ARRAY_SIZE(added); |
||||
GPR_ASSERT(i); |
||||
if (rand() < RAND_MAX / 2) { |
||||
added[idx] = i; |
||||
printf("avl = grpc_avl_add(avl, box(%d), box(%d), NULL); /* d=%d */\n", |
||||
idx, i, deletions); |
||||
avl = grpc_avl_add(avl, box(idx), box(i), nullptr); |
||||
} else { |
||||
deletions += (added[idx] != 0); |
||||
added[idx] = 0; |
||||
printf("avl = remove_int(avl, %d); /* d=%d */\n", idx, deletions); |
||||
avl = remove_int(avl, idx); |
||||
} |
||||
for (j = 0; j < static_cast<int> GPR_ARRAY_SIZE(added); j++) { |
||||
if (added[j] != 0) { |
||||
check_get(avl, j, added[j]); |
||||
} else { |
||||
check_negget(avl, j); |
||||
} |
||||
} |
||||
} |
||||
|
||||
grpc_avl_unref(avl, nullptr); |
||||
} |
||||
|
||||
int main(int argc, char* argv[]) { |
||||
grpc::testing::TestEnvironment env(argc, argv); |
||||
|
||||
test_get(); |
||||
test_ll(); |
||||
test_lr(); |
||||
test_rr(); |
||||
test_rl(); |
||||
test_unbalanced(); |
||||
test_replace(); |
||||
test_remove(); |
||||
test_badcase1(); |
||||
test_stress(10); |
||||
} // namespace grpc_core
|
||||
|
||||
return 0; |
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
||||
|
Loading…
Reference in new issue