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
Craig Tiller 3 years ago committed by GitHub
parent 2c7f2190e6
commit 1d63bb02b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      BUILD
  2. 65
      CMakeLists.txt
  3. 2
      Makefile
  4. 21
      build_autogenerated.yaml
  5. 2
      config.m4
  6. 2
      config.w32
  7. 1
      gRPC-Core.podspec
  8. 1
      grpc.gemspec
  9. 2
      grpc.gyp
  10. 1
      package.xml
  11. 306
      src/core/lib/avl/avl.cc
  12. 475
      src/core/lib/avl/avl.h
  13. 1
      src/python/grpcio/grpc_core_dependencies.py
  14. 19
      test/core/avl/BUILD
  15. 72
      test/core/avl/avl_fuzzer.cc
  16. 32
      test/core/avl/avl_fuzzer.proto
  17. 1
      test/core/avl/avl_fuzzer_corpus/0
  18. 324
      test/core/avl/avl_test.cc
  19. 1
      tools/doxygen/Doxyfile.c++.internal
  20. 1
      tools/doxygen/Doxyfile.core.internal
  21. 48
      tools/run_tests/generated/tests.json

13
BUILD

@ -1590,6 +1590,16 @@ grpc_cc_library(
], ],
) )
grpc_cc_library(
name = "avl",
hdrs = [
"src/core/lib/avl/avl.h",
],
deps = [
"gpr_platform",
],
)
grpc_cc_library( grpc_cc_library(
name = "event_engine_base", name = "event_engine_base",
srcs = [ srcs = [
@ -1631,7 +1641,6 @@ grpc_cc_library(
srcs = [ srcs = [
"src/core/lib/address_utils/parse_address.cc", "src/core/lib/address_utils/parse_address.cc",
"src/core/lib/address_utils/sockaddr_utils.cc", "src/core/lib/address_utils/sockaddr_utils.cc",
"src/core/lib/avl/avl.cc",
"src/core/lib/backoff/backoff.cc", "src/core/lib/backoff/backoff.cc",
"src/core/lib/channel/channel_args.cc", "src/core/lib/channel/channel_args.cc",
"src/core/lib/channel/channel_stack.cc", "src/core/lib/channel/channel_stack.cc",
@ -1793,7 +1802,6 @@ grpc_cc_library(
"src/core/lib/transport/http2_errors.h", "src/core/lib/transport/http2_errors.h",
"src/core/lib/address_utils/parse_address.h", "src/core/lib/address_utils/parse_address.h",
"src/core/lib/address_utils/sockaddr_utils.h", "src/core/lib/address_utils/sockaddr_utils.h",
"src/core/lib/avl/avl.h",
"src/core/lib/backoff/backoff.h", "src/core/lib/backoff/backoff.h",
"src/core/lib/channel/call_tracer.h", "src/core/lib/channel/call_tracer.h",
"src/core/lib/channel/channel_args.h", "src/core/lib/channel/channel_args.h",
@ -1955,6 +1963,7 @@ grpc_cc_library(
public_hdrs = GRPC_PUBLIC_HDRS + GRPC_PUBLIC_EVENT_ENGINE_HDRS, public_hdrs = GRPC_PUBLIC_HDRS + GRPC_PUBLIC_EVENT_ENGINE_HDRS,
visibility = ["@grpc:alt_grpc_base_legacy"], visibility = ["@grpc:alt_grpc_base_legacy"],
deps = [ deps = [
"avl",
"bitset", "bitset",
"channel_stack_type", "channel_stack_type",
"chunked_vector", "chunked_vector",

65
CMakeLists.txt generated

@ -560,7 +560,6 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_c alts_zero_copy_grpc_protector_test) add_dependencies(buildtests_c alts_zero_copy_grpc_protector_test)
add_dependencies(buildtests_c arena_test) add_dependencies(buildtests_c arena_test)
add_dependencies(buildtests_c auth_context_test) add_dependencies(buildtests_c auth_context_test)
add_dependencies(buildtests_c avl_test)
add_dependencies(buildtests_c b64_test) add_dependencies(buildtests_c b64_test)
add_dependencies(buildtests_c bad_server_response_test) add_dependencies(buildtests_c bad_server_response_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -743,6 +742,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx auth_property_iterator_test) add_dependencies(buildtests_cxx auth_property_iterator_test)
add_dependencies(buildtests_cxx authorization_matchers_test) add_dependencies(buildtests_cxx authorization_matchers_test)
add_dependencies(buildtests_cxx authorization_policy_provider_test) add_dependencies(buildtests_cxx authorization_policy_provider_test)
add_dependencies(buildtests_cxx avl_test)
add_dependencies(buildtests_cxx aws_request_signer_test) add_dependencies(buildtests_cxx aws_request_signer_test)
add_dependencies(buildtests_cxx backoff_test) add_dependencies(buildtests_cxx backoff_test)
add_dependencies(buildtests_cxx bad_streaming_id_bad_client_test) add_dependencies(buildtests_cxx bad_streaming_id_bad_client_test)
@ -1807,7 +1807,6 @@ add_library(grpc
src/core/ext/xds/xds_server_config_fetcher.cc src/core/ext/xds/xds_server_config_fetcher.cc
src/core/lib/address_utils/parse_address.cc src/core/lib/address_utils/parse_address.cc
src/core/lib/address_utils/sockaddr_utils.cc src/core/lib/address_utils/sockaddr_utils.cc
src/core/lib/avl/avl.cc
src/core/lib/backoff/backoff.cc src/core/lib/backoff/backoff.cc
src/core/lib/channel/channel_args.cc src/core/lib/channel/channel_args.cc
src/core/lib/channel/channel_stack.cc src/core/lib/channel/channel_stack.cc
@ -2446,7 +2445,6 @@ add_library(grpc_unsecure
src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c
src/core/lib/address_utils/parse_address.cc src/core/lib/address_utils/parse_address.cc
src/core/lib/address_utils/sockaddr_utils.cc src/core/lib/address_utils/sockaddr_utils.cc
src/core/lib/avl/avl.cc
src/core/lib/backoff/backoff.cc src/core/lib/backoff/backoff.cc
src/core/lib/channel/channel_args.cc src/core/lib/channel/channel_args.cc
src/core/lib/channel/channel_stack.cc src/core/lib/channel/channel_stack.cc
@ -4436,33 +4434,6 @@ target_link_libraries(auth_context_test
) )
endif()
if(gRPC_BUILD_TESTS)
add_executable(avl_test
test/core/avl/avl_test.cc
)
target_include_directories(avl_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
)
target_link_libraries(avl_test
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif() endif()
if(gRPC_BUILD_TESTS) if(gRPC_BUILD_TESTS)
@ -8114,6 +8085,40 @@ target_link_libraries(authorization_policy_provider_test
) )
endif()
if(gRPC_BUILD_TESTS)
add_executable(avl_test
test/core/avl/avl_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(avl_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(avl_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
)
endif() endif()
if(gRPC_BUILD_TESTS) if(gRPC_BUILD_TESTS)

2
Makefile generated

@ -1357,7 +1357,6 @@ LIBGRPC_SRC = \
src/core/ext/xds/xds_server_config_fetcher.cc \ src/core/ext/xds/xds_server_config_fetcher.cc \
src/core/lib/address_utils/parse_address.cc \ src/core/lib/address_utils/parse_address.cc \
src/core/lib/address_utils/sockaddr_utils.cc \ src/core/lib/address_utils/sockaddr_utils.cc \
src/core/lib/avl/avl.cc \
src/core/lib/backoff/backoff.cc \ src/core/lib/backoff/backoff.cc \
src/core/lib/channel/channel_args.cc \ src/core/lib/channel/channel_args.cc \
src/core/lib/channel/channel_stack.cc \ src/core/lib/channel/channel_stack.cc \
@ -1844,7 +1843,6 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c \ src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c \
src/core/lib/address_utils/parse_address.cc \ src/core/lib/address_utils/parse_address.cc \
src/core/lib/address_utils/sockaddr_utils.cc \ src/core/lib/address_utils/sockaddr_utils.cc \
src/core/lib/avl/avl.cc \
src/core/lib/backoff/backoff.cc \ src/core/lib/backoff/backoff.cc \
src/core/lib/channel/channel_args.cc \ src/core/lib/channel/channel_args.cc \
src/core/lib/channel/channel_stack.cc \ src/core/lib/channel/channel_stack.cc \

@ -1273,7 +1273,6 @@ libs:
- src/core/ext/xds/xds_server_config_fetcher.cc - src/core/ext/xds/xds_server_config_fetcher.cc
- src/core/lib/address_utils/parse_address.cc - src/core/lib/address_utils/parse_address.cc
- src/core/lib/address_utils/sockaddr_utils.cc - src/core/lib/address_utils/sockaddr_utils.cc
- src/core/lib/avl/avl.cc
- src/core/lib/backoff/backoff.cc - src/core/lib/backoff/backoff.cc
- src/core/lib/channel/channel_args.cc - src/core/lib/channel/channel_args.cc
- src/core/lib/channel/channel_stack.cc - src/core/lib/channel/channel_stack.cc
@ -2046,7 +2045,6 @@ libs:
- src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c - src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c
- src/core/lib/address_utils/parse_address.cc - src/core/lib/address_utils/parse_address.cc
- src/core/lib/address_utils/sockaddr_utils.cc - src/core/lib/address_utils/sockaddr_utils.cc
- src/core/lib/avl/avl.cc
- src/core/lib/backoff/backoff.cc - src/core/lib/backoff/backoff.cc
- src/core/lib/channel/channel_args.cc - src/core/lib/channel/channel_args.cc
- src/core/lib/channel/channel_stack.cc - src/core/lib/channel/channel_stack.cc
@ -3087,15 +3085,6 @@ targets:
deps: deps:
- grpc_test_util - grpc_test_util
uses_polling: false uses_polling: false
- name: avl_test
build: test
language: c
headers: []
src:
- test/core/avl/avl_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: b64_test - name: b64_test
build: test build: test
language: c language: c
@ -4588,6 +4577,16 @@ targets:
deps: deps:
- grpc++ - grpc++
- grpc_test_util - grpc_test_util
- name: avl_test
gtest: true
build: test
language: c++
headers:
- src/core/lib/avl/avl.h
src:
- test/core/avl/avl_test.cc
deps: []
uses_polling: false
- name: aws_request_signer_test - name: aws_request_signer_test
gtest: true gtest: true
build: test build: test

2
config.m4 generated

@ -365,7 +365,6 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/xds/xds_server_config_fetcher.cc \ src/core/ext/xds/xds_server_config_fetcher.cc \
src/core/lib/address_utils/parse_address.cc \ src/core/lib/address_utils/parse_address.cc \
src/core/lib/address_utils/sockaddr_utils.cc \ src/core/lib/address_utils/sockaddr_utils.cc \
src/core/lib/avl/avl.cc \
src/core/lib/backoff/backoff.cc \ src/core/lib/backoff/backoff.cc \
src/core/lib/channel/channel_args.cc \ src/core/lib/channel/channel_args.cc \
src/core/lib/channel/channel_stack.cc \ src/core/lib/channel/channel_stack.cc \
@ -1190,7 +1189,6 @@ if test "$PHP_GRPC" != "no"; then
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/upbdefs-generated/xds/type/v3) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/upbdefs-generated/xds/type/v3)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/xds) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/xds)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/address_utils) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/address_utils)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/avl)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/backoff) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/backoff)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/channel) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/channel)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/compression) PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/compression)

2
config.w32 generated

@ -331,7 +331,6 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\xds\\xds_server_config_fetcher.cc " + "src\\core\\ext\\xds\\xds_server_config_fetcher.cc " +
"src\\core\\lib\\address_utils\\parse_address.cc " + "src\\core\\lib\\address_utils\\parse_address.cc " +
"src\\core\\lib\\address_utils\\sockaddr_utils.cc " + "src\\core\\lib\\address_utils\\sockaddr_utils.cc " +
"src\\core\\lib\\avl\\avl.cc " +
"src\\core\\lib\\backoff\\backoff.cc " + "src\\core\\lib\\backoff\\backoff.cc " +
"src\\core\\lib\\channel\\channel_args.cc " + "src\\core\\lib\\channel\\channel_args.cc " +
"src\\core\\lib\\channel\\channel_stack.cc " + "src\\core\\lib\\channel\\channel_stack.cc " +
@ -1294,7 +1293,6 @@ if (PHP_GRPC != "no") {
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\xds"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\xds");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\address_utils"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\address_utils");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\avl");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\backoff"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\backoff");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\channel"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\channel");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\compression"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\compression");

1
gRPC-Core.podspec generated

@ -818,7 +818,6 @@ Pod::Spec.new do |s|
'src/core/lib/address_utils/parse_address.h', 'src/core/lib/address_utils/parse_address.h',
'src/core/lib/address_utils/sockaddr_utils.cc', 'src/core/lib/address_utils/sockaddr_utils.cc',
'src/core/lib/address_utils/sockaddr_utils.h', 'src/core/lib/address_utils/sockaddr_utils.h',
'src/core/lib/avl/avl.cc',
'src/core/lib/avl/avl.h', 'src/core/lib/avl/avl.h',
'src/core/lib/backoff/backoff.cc', 'src/core/lib/backoff/backoff.cc',
'src/core/lib/backoff/backoff.h', 'src/core/lib/backoff/backoff.h',

1
grpc.gemspec generated

@ -738,7 +738,6 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/address_utils/parse_address.h ) s.files += %w( src/core/lib/address_utils/parse_address.h )
s.files += %w( src/core/lib/address_utils/sockaddr_utils.cc ) s.files += %w( src/core/lib/address_utils/sockaddr_utils.cc )
s.files += %w( src/core/lib/address_utils/sockaddr_utils.h ) s.files += %w( src/core/lib/address_utils/sockaddr_utils.h )
s.files += %w( src/core/lib/avl/avl.cc )
s.files += %w( src/core/lib/avl/avl.h ) s.files += %w( src/core/lib/avl/avl.h )
s.files += %w( src/core/lib/backoff/backoff.cc ) s.files += %w( src/core/lib/backoff/backoff.cc )
s.files += %w( src/core/lib/backoff/backoff.h ) s.files += %w( src/core/lib/backoff/backoff.h )

2
grpc.gyp generated

@ -798,7 +798,6 @@
'src/core/ext/xds/xds_server_config_fetcher.cc', 'src/core/ext/xds/xds_server_config_fetcher.cc',
'src/core/lib/address_utils/parse_address.cc', 'src/core/lib/address_utils/parse_address.cc',
'src/core/lib/address_utils/sockaddr_utils.cc', 'src/core/lib/address_utils/sockaddr_utils.cc',
'src/core/lib/avl/avl.cc',
'src/core/lib/backoff/backoff.cc', 'src/core/lib/backoff/backoff.cc',
'src/core/lib/channel/channel_args.cc', 'src/core/lib/channel/channel_args.cc',
'src/core/lib/channel/channel_stack.cc', 'src/core/lib/channel/channel_stack.cc',
@ -1258,7 +1257,6 @@
'src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c', 'src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c',
'src/core/lib/address_utils/parse_address.cc', 'src/core/lib/address_utils/parse_address.cc',
'src/core/lib/address_utils/sockaddr_utils.cc', 'src/core/lib/address_utils/sockaddr_utils.cc',
'src/core/lib/avl/avl.cc',
'src/core/lib/backoff/backoff.cc', 'src/core/lib/backoff/backoff.cc',
'src/core/lib/channel/channel_args.cc', 'src/core/lib/channel/channel_args.cc',
'src/core/lib/channel/channel_stack.cc', 'src/core/lib/channel/channel_stack.cc',

1
package.xml generated

@ -718,7 +718,6 @@
<file baseinstalldir="/" name="src/core/lib/address_utils/parse_address.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/address_utils/parse_address.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/address_utils/sockaddr_utils.cc" role="src" /> <file baseinstalldir="/" name="src/core/lib/address_utils/sockaddr_utils.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/address_utils/sockaddr_utils.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/address_utils/sockaddr_utils.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/avl/avl.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/avl/avl.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/avl/avl.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/backoff/backoff.cc" role="src" /> <file baseinstalldir="/" name="src/core/lib/backoff/backoff.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/backoff/backoff.h" role="src" /> <file baseinstalldir="/" name="src/core/lib/backoff/backoff.h" role="src" />

@ -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

@ -340,7 +340,6 @@ CORE_SOURCE_FILES = [
'src/core/ext/xds/xds_server_config_fetcher.cc', 'src/core/ext/xds/xds_server_config_fetcher.cc',
'src/core/lib/address_utils/parse_address.cc', 'src/core/lib/address_utils/parse_address.cc',
'src/core/lib/address_utils/sockaddr_utils.cc', 'src/core/lib/address_utils/sockaddr_utils.cc',
'src/core/lib/avl/avl.cc',
'src/core/lib/backoff/backoff.cc', 'src/core/lib/backoff/backoff.cc',
'src/core/lib/channel/channel_args.cc', 'src/core/lib/channel/channel_args.cc',
'src/core/lib/channel/channel_stack.cc', 'src/core/lib/channel/channel_stack.cc',

@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package") load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
load("//test/core/util:grpc_fuzzer.bzl", "grpc_proto_fuzzer")
licenses(["notice"]) licenses(["notice"])
@ -21,11 +22,25 @@ grpc_package(name = "test/core/avl")
grpc_cc_test( grpc_cc_test(
name = "avl_test", name = "avl_test",
srcs = ["avl_test.cc"], srcs = ["avl_test.cc"],
external_deps = ["gtest"],
language = "C++", language = "C++",
uses_polling = False, uses_polling = False,
deps = [ deps = [
"//:gpr", "//:avl",
"//:grpc", "//test/core/util:grpc_suppressions",
],
)
grpc_proto_fuzzer(
name = "avl_fuzzer",
srcs = ["avl_fuzzer.cc"],
corpus = "avl_fuzzer_corpus",
language = "C++",
proto = "avl_fuzzer.proto",
tags = ["no_windows"],
uses_polling = False,
deps = [
"//:avl",
"//test/core/util:grpc_test_util", "//test/core/util:grpc_test_util",
], ],
) )

@ -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;
}

@ -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();
} }

@ -1713,7 +1713,6 @@ src/core/lib/address_utils/parse_address.cc \
src/core/lib/address_utils/parse_address.h \ src/core/lib/address_utils/parse_address.h \
src/core/lib/address_utils/sockaddr_utils.cc \ src/core/lib/address_utils/sockaddr_utils.cc \
src/core/lib/address_utils/sockaddr_utils.h \ src/core/lib/address_utils/sockaddr_utils.h \
src/core/lib/avl/avl.cc \
src/core/lib/avl/avl.h \ src/core/lib/avl/avl.h \
src/core/lib/backoff/backoff.cc \ src/core/lib/backoff/backoff.cc \
src/core/lib/backoff/backoff.h \ src/core/lib/backoff/backoff.h \

@ -1508,7 +1508,6 @@ src/core/lib/address_utils/parse_address.cc \
src/core/lib/address_utils/parse_address.h \ src/core/lib/address_utils/parse_address.h \
src/core/lib/address_utils/sockaddr_utils.cc \ src/core/lib/address_utils/sockaddr_utils.cc \
src/core/lib/address_utils/sockaddr_utils.h \ src/core/lib/address_utils/sockaddr_utils.h \
src/core/lib/avl/avl.cc \
src/core/lib/avl/avl.h \ src/core/lib/avl/avl.h \
src/core/lib/backoff/backoff.cc \ src/core/lib/backoff/backoff.cc \
src/core/lib/backoff/backoff.h \ src/core/lib/backoff/backoff.h \

@ -385,30 +385,6 @@
], ],
"uses_polling": false "uses_polling": false
}, },
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": false,
"language": "c",
"name": "avl_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{ {
"args": [], "args": [],
"benchmark": false, "benchmark": false,
@ -3237,6 +3213,30 @@
], ],
"uses_polling": true "uses_polling": true
}, },
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "avl_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{ {
"args": [], "args": [],
"benchmark": false, "benchmark": false,

Loading…
Cancel
Save