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 2021 gRPC authors.
|
||||||
* |
//
|
||||||
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
// You may obtain a copy of the License at
|
||||||
* 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
|
||||||
* |
//
|
||||||
* 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,
|
||||||
* Unless required by applicable law or agreed to in writing, software |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
// See the License for the specific language governing permissions and
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
// limitations under the License.
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
* |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef GRPC_CORE_LIB_AVL_AVL_H |
#ifndef GRPC_CORE_LIB_AVL_AVL_H |
||||||
#define GRPC_CORE_LIB_AVL_AVL_H |
#define GRPC_CORE_LIB_AVL_AVL_H |
||||||
|
|
||||||
#include <grpc/support/port_platform.h> |
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
#include <grpc/support/sync.h> |
#include <algorithm> |
||||||
|
#include <memory> |
||||||
/** internal node of an AVL tree */ |
|
||||||
typedef struct grpc_avl_node { |
namespace grpc_core { |
||||||
gpr_refcount refs; |
|
||||||
void* key; |
template <class K, class V = void> |
||||||
void* value; |
class AVL { |
||||||
struct grpc_avl_node* left; |
public: |
||||||
struct grpc_avl_node* right; |
AVL() {} |
||||||
long height; |
|
||||||
} grpc_avl_node; |
AVL Add(K key, V value) const { |
||||||
|
return AVL(AddKey(root_, std::move(key), std::move(value))); |
||||||
/** vtable for the AVL tree
|
} |
||||||
* The optional user_data is propagated from the top level grpc_avl_XXX API. |
AVL Remove(const K& key) const { return AVL(RemoveKey(root_, key)); } |
||||||
* From the same API call, multiple vtable functions may be called multiple |
const V* Lookup(const K& key) const { |
||||||
* times. |
NodePtr n = Get(root_, key); |
||||||
*/ |
return n ? &n->kv.second : nullptr; |
||||||
typedef struct grpc_avl_vtable { |
} |
||||||
/** destroy a key */ |
|
||||||
void (*destroy_key)(void* key, void* user_data); |
const std::pair<K, V>* LookupBelow(const K& key) const { |
||||||
/** copy a key, returning new value */ |
NodePtr n = GetBelow(root_, *key); |
||||||
void* (*copy_key)(void* key, void* user_data); |
return n ? &n->kv : nullptr; |
||||||
/** 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); |
bool Empty() const { return root_ == nullptr; } |
||||||
/** destroy a value */ |
|
||||||
void (*destroy_value)(void* value, void* user_data); |
template <class F> |
||||||
/** copy a value */ |
void ForEach(F&& f) const { |
||||||
void* (*copy_value)(void* value, void* user_data); |
ForEachImpl(root_.get(), std::forward<F>(f)); |
||||||
} grpc_avl_vtable; |
} |
||||||
|
|
||||||
/** "pointer" to an AVL tree - this is a reference
|
bool SameIdentity(AVL avl) const { return root_ == avl.root_; } |
||||||
counted object - use grpc_avl_ref to add a reference, |
|
||||||
grpc_avl_unref when done with a reference */ |
private: |
||||||
typedef struct grpc_avl { |
struct Node; |
||||||
const grpc_avl_vtable* vtable; |
typedef std::shared_ptr<Node> NodePtr; |
||||||
grpc_avl_node* root; |
struct Node : public std::enable_shared_from_this<Node> { |
||||||
} grpc_avl; |
Node(K k, V v, NodePtr l, NodePtr r, long h) |
||||||
|
: kv(std::move(k), std::move(v)), |
||||||
/** Create an immutable AVL tree. */ |
left(std::move(l)), |
||||||
grpc_avl grpc_avl_create(const grpc_avl_vtable* vtable); |
right(std::move(r)), |
||||||
/** Add a reference to an existing tree - returns
|
height(h) {} |
||||||
the tree as a convenience. The optional user_data will be passed to vtable |
const std::pair<K, V> kv; |
||||||
functions. */ |
const NodePtr left; |
||||||
grpc_avl grpc_avl_ref(grpc_avl avl, void* user_data); |
const NodePtr right; |
||||||
/** Remove a reference to a tree - destroying it if there
|
const long height; |
||||||
are no references left. The optional user_data will be passed to vtable |
}; |
||||||
functions. */ |
NodePtr root_; |
||||||
void grpc_avl_unref(grpc_avl avl, void* user_data); |
|
||||||
/** Return a new tree with (key, value) added to avl.
|
explicit AVL(NodePtr root) : root_(std::move(root)) {} |
||||||
implicitly unrefs avl to allow easy chaining. |
|
||||||
if key exists in avl, the new tree's key entry updated |
template <class F> |
||||||
(i.e. a duplicate is not created). The optional user_data will be passed to |
static void ForEachImpl(const Node* n, F&& f) { |
||||||
vtable functions. */ |
if (n == nullptr) return; |
||||||
grpc_avl grpc_avl_add(grpc_avl avl, void* key, void* value, void* user_data); |
ForEachImpl(n->left.get(), std::forward<F>(f)); |
||||||
/** Return a new tree with key deleted
|
f(const_cast<const K&>(n->kv.first), const_cast<const V&>(n->kv.second)); |
||||||
implicitly unrefs avl to allow easy chaining. The optional user_data will be |
ForEachImpl(n->right.get(), std::forward<F>(f)); |
||||||
passed to vtable functions. */ |
} |
||||||
grpc_avl grpc_avl_remove(grpc_avl avl, void* key, void* user_data); |
|
||||||
/** Lookup key, and return the associated value.
|
static long Height(const NodePtr& n) { return n ? n->height : 0; } |
||||||
Does not mutate avl. |
|
||||||
Returns NULL if key is not found. The optional user_data will be passed to |
static NodePtr MakeNode(K key, V value, const NodePtr& left, |
||||||
vtable functions.*/ |
const NodePtr& right) { |
||||||
void* grpc_avl_get(grpc_avl avl, void* key, void* user_data); |
return std::make_shared<Node>(std::move(key), std::move(value), left, right, |
||||||
/** Return 1 if avl contains key, 0 otherwise; if it has the key, sets *value to
|
1 + std::max(Height(left), Height(right))); |
||||||
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 */ |
static NodePtr Get(const NodePtr& node, const K& key) { |
||||||
int grpc_avl_is_empty(grpc_avl avl); |
if (node == nullptr) { |
||||||
|
return nullptr; |
||||||
#endif /* GRPC_CORE_LIB_AVL_AVL_H */ |
} |
||||||
|
|
||||||
|
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 2021 gRPC authors.
|
||||||
* |
//
|
||||||
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
// You may obtain a copy of the License at
|
||||||
* 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
|
||||||
* |
//
|
||||||
* 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,
|
||||||
* Unless required by applicable law or agreed to in writing, software |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
// See the License for the specific language governing permissions and
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
// limitations under the License.
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
* |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "src/core/lib/avl/avl.h" |
#include "src/core/lib/avl/avl.h" |
||||||
|
|
||||||
#include <stdio.h> |
#include <gtest/gtest.h> |
||||||
#include <string.h> |
|
||||||
|
|
||||||
#include <grpc/support/alloc.h> |
namespace grpc_core { |
||||||
#include <grpc/support/log.h> |
|
||||||
|
|
||||||
#include "src/core/lib/gpr/useful.h" |
TEST(AvlTest, NoOp) { AVL<int, int> avl; } |
||||||
#include "test/core/util/test_config.h" |
|
||||||
|
|
||||||
static int* box(int x) { |
TEST(AvlTest, Lookup) { |
||||||
int* b = static_cast<int*>(gpr_malloc(sizeof(*b))); |
auto avl = AVL<int, int>().Add(1, 42); |
||||||
*b = x; |
EXPECT_EQ(nullptr, avl.Lookup(2)); |
||||||
return b; |
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*/) { |
} // namespace grpc_core
|
||||||
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); |
|
||||||
|
|
||||||
return 0; |
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue