Chunked vector type (#27517)

* chunked vector type

* Automated change: Fix sanity tests

* compile fix

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
reviewable/pr27526/r1
Craig Tiller 3 years ago committed by GitHub
parent 04ce30a1f8
commit 253d7076fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      BUILD
  2. 103
      CMakeLists.txt
  3. 118
      build_autogenerated.yaml
  4. 207
      src/core/lib/gprpp/chunked_vector.h
  5. 29
      test/core/gprpp/BUILD
  6. 14
      test/core/gprpp/chunked_vector_corpora/crash-a0868ce3a0f76feefcc715148ed9e71fa0738c2a
  7. 153
      test/core/gprpp/chunked_vector_fuzzer.cc
  8. 50
      test/core/gprpp/chunked_vector_fuzzer.proto
  9. 169
      test/core/gprpp/chunked_vector_test.cc
  10. 24
      tools/run_tests/generated/tests.json

10
BUILD

@ -749,6 +749,16 @@ grpc_cc_library(
deps = ["gpr_platform"],
)
grpc_cc_library(
name = "chunked_vector",
hdrs = ["src/core/lib/gprpp/chunked_vector.h"],
external_deps = ["absl/utility"],
deps = [
# TODO(ctiller): weaken this to just arena when that splits into its own target
"gpr_base",
],
)
grpc_cc_library(
name = "capture",
external_deps = ["absl/utility"],

103
CMakeLists.txt generated

@ -834,6 +834,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx channelz_registry_test)
add_dependencies(buildtests_cxx channelz_service_test)
add_dependencies(buildtests_cxx channelz_test)
add_dependencies(buildtests_cxx chunked_vector_test)
add_dependencies(buildtests_cxx cli_call_test)
add_dependencies(buildtests_cxx client_callback_end2end_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -9928,6 +9929,108 @@ target_link_libraries(channelz_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(chunked_vector_test
src/core/ext/upb-generated/google/api/annotations.upb.c
src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.c
src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.c
src/core/ext/upb-generated/google/api/http.upb.c
src/core/ext/upb-generated/google/protobuf/any.upb.c
src/core/ext/upb-generated/google/protobuf/duration.upb.c
src/core/ext/upb-generated/google/protobuf/empty.upb.c
src/core/ext/upb-generated/google/protobuf/struct.upb.c
src/core/ext/upb-generated/google/protobuf/timestamp.upb.c
src/core/ext/upb-generated/google/protobuf/wrappers.upb.c
src/core/ext/upb-generated/google/rpc/status.upb.c
src/core/lib/gpr/alloc.cc
src/core/lib/gpr/atm.cc
src/core/lib/gpr/cpu_iphone.cc
src/core/lib/gpr/cpu_linux.cc
src/core/lib/gpr/cpu_posix.cc
src/core/lib/gpr/cpu_windows.cc
src/core/lib/gpr/env_linux.cc
src/core/lib/gpr/env_posix.cc
src/core/lib/gpr/env_windows.cc
src/core/lib/gpr/log.cc
src/core/lib/gpr/log_android.cc
src/core/lib/gpr/log_linux.cc
src/core/lib/gpr/log_posix.cc
src/core/lib/gpr/log_windows.cc
src/core/lib/gpr/murmur_hash.cc
src/core/lib/gpr/string.cc
src/core/lib/gpr/string_posix.cc
src/core/lib/gpr/string_util_windows.cc
src/core/lib/gpr/string_windows.cc
src/core/lib/gpr/sync.cc
src/core/lib/gpr/sync_abseil.cc
src/core/lib/gpr/sync_posix.cc
src/core/lib/gpr/sync_windows.cc
src/core/lib/gpr/time.cc
src/core/lib/gpr/time_posix.cc
src/core/lib/gpr/time_precise.cc
src/core/lib/gpr/time_windows.cc
src/core/lib/gpr/tmpfile_msys.cc
src/core/lib/gpr/tmpfile_posix.cc
src/core/lib/gpr/tmpfile_windows.cc
src/core/lib/gpr/wrap_memcpy.cc
src/core/lib/gprpp/arena.cc
src/core/lib/gprpp/examine_stack.cc
src/core/lib/gprpp/fork.cc
src/core/lib/gprpp/global_config_env.cc
src/core/lib/gprpp/host_port.cc
src/core/lib/gprpp/mpscq.cc
src/core/lib/gprpp/stat_posix.cc
src/core/lib/gprpp/stat_windows.cc
src/core/lib/gprpp/status_helper.cc
src/core/lib/gprpp/thd_posix.cc
src/core/lib/gprpp/thd_windows.cc
src/core/lib/gprpp/time_util.cc
src/core/lib/profiling/basic_timers.cc
src/core/lib/profiling/stap_timers.cc
test/core/gprpp/chunked_vector_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(chunked_vector_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(chunked_vector_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::base
absl::core_headers
absl::memory
absl::status
absl::cord
absl::str_format
absl::strings
absl::synchronization
absl::time
absl::optional
absl::utility
upb
)
endif()
if(gRPC_BUILD_TESTS)

@ -5228,6 +5228,124 @@ targets:
deps:
- grpc++
- grpc_test_util
- name: chunked_vector_test
gtest: true
build: test
language: c++
headers:
- src/core/ext/upb-generated/google/api/annotations.upb.h
- src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.h
- src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.h
- src/core/ext/upb-generated/google/api/http.upb.h
- src/core/ext/upb-generated/google/protobuf/any.upb.h
- src/core/ext/upb-generated/google/protobuf/duration.upb.h
- src/core/ext/upb-generated/google/protobuf/empty.upb.h
- src/core/ext/upb-generated/google/protobuf/struct.upb.h
- src/core/ext/upb-generated/google/protobuf/timestamp.upb.h
- src/core/ext/upb-generated/google/protobuf/wrappers.upb.h
- src/core/ext/upb-generated/google/rpc/status.upb.h
- src/core/lib/gpr/alloc.h
- src/core/lib/gpr/env.h
- src/core/lib/gpr/murmur_hash.h
- src/core/lib/gpr/spinlock.h
- src/core/lib/gpr/string.h
- src/core/lib/gpr/string_windows.h
- src/core/lib/gpr/time_precise.h
- src/core/lib/gpr/tls.h
- src/core/lib/gpr/tmpfile.h
- src/core/lib/gpr/useful.h
- src/core/lib/gprpp/arena.h
- src/core/lib/gprpp/chunked_vector.h
- src/core/lib/gprpp/construct_destruct.h
- src/core/lib/gprpp/debug_location.h
- src/core/lib/gprpp/examine_stack.h
- src/core/lib/gprpp/fork.h
- src/core/lib/gprpp/global_config.h
- src/core/lib/gprpp/global_config_custom.h
- src/core/lib/gprpp/global_config_env.h
- src/core/lib/gprpp/global_config_generic.h
- src/core/lib/gprpp/host_port.h
- src/core/lib/gprpp/manual_constructor.h
- src/core/lib/gprpp/memory.h
- src/core/lib/gprpp/mpscq.h
- src/core/lib/gprpp/stat.h
- src/core/lib/gprpp/status_helper.h
- src/core/lib/gprpp/sync.h
- src/core/lib/gprpp/thd.h
- src/core/lib/gprpp/time_util.h
- src/core/lib/profiling/timers.h
src:
- src/core/ext/upb-generated/google/api/annotations.upb.c
- src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.c
- src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.c
- src/core/ext/upb-generated/google/api/http.upb.c
- src/core/ext/upb-generated/google/protobuf/any.upb.c
- src/core/ext/upb-generated/google/protobuf/duration.upb.c
- src/core/ext/upb-generated/google/protobuf/empty.upb.c
- src/core/ext/upb-generated/google/protobuf/struct.upb.c
- src/core/ext/upb-generated/google/protobuf/timestamp.upb.c
- src/core/ext/upb-generated/google/protobuf/wrappers.upb.c
- src/core/ext/upb-generated/google/rpc/status.upb.c
- src/core/lib/gpr/alloc.cc
- src/core/lib/gpr/atm.cc
- src/core/lib/gpr/cpu_iphone.cc
- src/core/lib/gpr/cpu_linux.cc
- src/core/lib/gpr/cpu_posix.cc
- src/core/lib/gpr/cpu_windows.cc
- src/core/lib/gpr/env_linux.cc
- src/core/lib/gpr/env_posix.cc
- src/core/lib/gpr/env_windows.cc
- src/core/lib/gpr/log.cc
- src/core/lib/gpr/log_android.cc
- src/core/lib/gpr/log_linux.cc
- src/core/lib/gpr/log_posix.cc
- src/core/lib/gpr/log_windows.cc
- src/core/lib/gpr/murmur_hash.cc
- src/core/lib/gpr/string.cc
- src/core/lib/gpr/string_posix.cc
- src/core/lib/gpr/string_util_windows.cc
- src/core/lib/gpr/string_windows.cc
- src/core/lib/gpr/sync.cc
- src/core/lib/gpr/sync_abseil.cc
- src/core/lib/gpr/sync_posix.cc
- src/core/lib/gpr/sync_windows.cc
- src/core/lib/gpr/time.cc
- src/core/lib/gpr/time_posix.cc
- src/core/lib/gpr/time_precise.cc
- src/core/lib/gpr/time_windows.cc
- src/core/lib/gpr/tmpfile_msys.cc
- src/core/lib/gpr/tmpfile_posix.cc
- src/core/lib/gpr/tmpfile_windows.cc
- src/core/lib/gpr/wrap_memcpy.cc
- src/core/lib/gprpp/arena.cc
- src/core/lib/gprpp/examine_stack.cc
- src/core/lib/gprpp/fork.cc
- src/core/lib/gprpp/global_config_env.cc
- src/core/lib/gprpp/host_port.cc
- src/core/lib/gprpp/mpscq.cc
- src/core/lib/gprpp/stat_posix.cc
- src/core/lib/gprpp/stat_windows.cc
- src/core/lib/gprpp/status_helper.cc
- src/core/lib/gprpp/thd_posix.cc
- src/core/lib/gprpp/thd_windows.cc
- src/core/lib/gprpp/time_util.cc
- src/core/lib/profiling/basic_timers.cc
- src/core/lib/profiling/stap_timers.cc
- test/core/gprpp/chunked_vector_test.cc
deps:
- absl/base:base
- absl/base:core_headers
- absl/memory:memory
- absl/status:status
- absl/strings:cord
- absl/strings:str_format
- absl/strings:strings
- absl/synchronization:synchronization
- absl/time:time
- absl/types:optional
- absl/utility:utility
- upb
uses_polling: false
- name: cli_call_test
gtest: true
build: test

@ -0,0 +1,207 @@
// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H
#define GRPC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/gprpp/arena.h"
#include "src/core/lib/gprpp/manual_constructor.h"
namespace grpc_core {
// Arena-friendly vector type.
// This "vector" allocates non-contiguous runs of kChunkSize T's at a time.
// Expectation is that most usage will fit in one chunk, sometimes two will be
// needed, and very rarely three. Appending is constant time, calculating the
// size is O(n_chunks).
template <typename T, size_t kChunkSize>
class ChunkedVector {
private:
// One chunk of allocated memory.
struct Chunk {
Chunk() = default;
Chunk* next = nullptr;
size_t count = 0;
ManualConstructor<T> data[kChunkSize];
};
public:
explicit ChunkedVector(Arena* arena) : arena_(arena) {}
template <class Iterator>
ChunkedVector(Arena* arena, Iterator begin, Iterator end) : arena_(arena) {
for (; begin != end; ++begin) {
EmplaceBack(*begin);
}
}
ChunkedVector(const ChunkedVector& other)
: ChunkedVector(other.arena_, other.begin(), other.end()) {}
ChunkedVector& operator=(ChunkedVector other) {
Swap(&other);
return *this;
}
ChunkedVector(ChunkedVector&& other) noexcept
: arena_(other.arena_), first_(other.first_), append_(other.append_) {
other.first_ = nullptr;
other.append_ = nullptr;
}
ChunkedVector& operator=(ChunkedVector&& other) noexcept {
Swap(&other);
return *this;
}
~ChunkedVector() { Clear(); }
void Swap(ChunkedVector* other) {
std::swap(other->arena_, arena_);
std::swap(other->first_, first_);
std::swap(other->append_, append_);
}
// Append a new element to the end of the vector.
template <typename... Args>
void EmplaceBack(Args&&... args) {
AppendSlot()->Init(std::forward<Args>(args)...);
}
// Remove the last element and return it.
T PopBack() {
GPR_ASSERT(append_ != nullptr);
if (append_->count == 0) {
GPR_ASSERT(first_ != append_);
Chunk* chunk = first_;
while (chunk->next != append_) {
chunk = chunk->next;
}
append_ = chunk;
}
const auto last = append_->count - 1;
T result = std::move(*append_->data[last]);
append_->data[last].Destroy();
append_->count = last;
return result;
}
void Clear() {
Chunk* chunk = first_;
while (chunk != nullptr && chunk->count != 0) {
for (size_t i = 0; i < chunk->count; i++) {
chunk->data[i].Destroy();
}
chunk->count = 0;
chunk = chunk->next;
}
append_ = first_;
}
// Forward-only iterator.
class ForwardIterator {
public:
ForwardIterator(Chunk* chunk, size_t n) : chunk_(chunk), n_(n) {}
T& operator*() const { return *chunk_->data[n_]; }
T* operator->() const { return &*chunk_->data[n_]; }
ForwardIterator& operator++() {
++n_;
if (n_ == chunk_->count) {
chunk_ = chunk_->next;
n_ = 0;
}
return *this;
}
bool operator==(const ForwardIterator& other) const {
return chunk_ == other.chunk_ && n_ == other.n_;
}
bool operator!=(const ForwardIterator& other) const {
return !(*this == other);
}
private:
Chunk* chunk_;
size_t n_;
};
// Const Forward-only iterator.
class ConstForwardIterator {
public:
ConstForwardIterator(const Chunk* chunk, size_t n) : chunk_(chunk), n_(n) {}
const T& operator*() const { return *chunk_->data[n_]; }
const T* operator->() const { return &*chunk_->data[n_]; }
ConstForwardIterator& operator++() {
++n_;
if (n_ == chunk_->count) {
chunk_ = chunk_->next;
n_ = 0;
}
return *this;
}
bool operator==(const ConstForwardIterator& other) const {
return chunk_ == other.chunk_ && n_ == other.n_;
}
bool operator!=(const ConstForwardIterator& other) const {
return !(*this == other);
}
private:
const Chunk* chunk_;
size_t n_;
};
ForwardIterator begin() {
if (first_ != nullptr && first_->count == 0) return end();
return ForwardIterator(first_, 0);
}
ForwardIterator end() { return ForwardIterator(nullptr, 0); }
ConstForwardIterator begin() const {
if (first_ != nullptr && first_->count == 0) return cend();
return ConstForwardIterator(first_, 0);
}
ConstForwardIterator end() const { return ConstForwardIterator(nullptr, 0); }
ConstForwardIterator cbegin() const { return begin(); }
ConstForwardIterator cend() const { return end(); }
// Count the number of elements in the vector.
size_t size() const {
size_t n = 0;
for (const Chunk* chunk = first_; chunk != nullptr; chunk = chunk->next) {
n += chunk->count;
}
return n;
}
private:
ManualConstructor<T>* AppendSlot() {
if (append_ == nullptr) {
GPR_ASSERT(first_ == nullptr);
first_ = arena_->New<Chunk>();
append_ = first_;
} else if (append_->count == kChunkSize) {
if (append_->next == nullptr) {
append_->next = arena_->New<Chunk>();
}
append_ = append_->next;
}
return &append_->data[append_->count++];
}
Arena* arena_;
Chunk* first_ = nullptr;
Chunk* append_ = nullptr;
};
} // namespace grpc_core
#endif // GRPC_CORE_LIB_GPRPP_CHUNKED_VECTOR_H

@ -19,6 +19,8 @@ licenses(["notice"]) # Apache v2
grpc_package(name = "test/core/gprpp")
load("//test/core/util:grpc_fuzzer.bzl", "grpc_proto_fuzzer")
grpc_cc_test(
name = "examine_stack_test",
srcs = ["examine_stack_test.cc"],
@ -283,3 +285,30 @@ grpc_cc_test(
"//test/core/util:grpc_suppressions",
],
)
grpc_cc_test(
name = "chunked_vector_test",
srcs = ["chunked_vector_test.cc"],
external_deps = ["gtest"],
language = "c++",
uses_polling = False,
deps = [
"//:chunked_vector",
"//:gpr_base",
"//test/core/util:grpc_suppressions",
],
)
grpc_proto_fuzzer(
name = "chunked_vector_fuzzer",
srcs = ["chunked_vector_fuzzer.cc"],
corpus = "chunked_vector_corpora",
language = "C++",
proto = "chunked_vector_fuzzer.proto",
tags = ["no_windows"],
uses_polling = False,
deps = [
"//:chunked_vector",
"//test/core/util:grpc_test_util",
],
)

@ -0,0 +1,14 @@
actions {
move {
from: 875700282
to: 3473408
}
}
actions {
emplace_back {
}
}
actions {
clear {
}
}

@ -0,0 +1,153 @@
// 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 <vector>
#include "src/core/lib/gprpp/chunked_vector.h"
#include "src/libfuzzer/libfuzzer_macro.h"
#include "test/core/gprpp/chunked_vector_fuzzer.pb.h"
bool squelch = true;
bool leak_check = true;
static constexpr size_t kChunkSize = 17;
using IntHdl = std::shared_ptr<int>;
namespace grpc_core {
struct Comparison {
explicit Comparison(Arena* arena) : chunked(arena) {}
ChunkedVector<IntHdl, kChunkSize> chunked;
std::vector<IntHdl> std;
// Check that both chunked and std are equivalent.
void AssertOk() const {
GPR_ASSERT(std.size() == chunked.size());
auto it_chunked = chunked.cbegin();
auto it_std = std.cbegin();
while (it_std != std.cend()) {
GPR_ASSERT(**it_std == **it_chunked);
++it_chunked;
++it_std;
}
GPR_ASSERT(it_chunked == chunked.cend());
}
};
class Fuzzer {
public:
Fuzzer() : arena_(Arena::Create(128)) {}
~Fuzzer() {
vectors_.clear();
arena_->Destroy();
}
void Act(const chunked_vector_fuzzer::Action& action) {
switch (action.action_type_case()) {
case chunked_vector_fuzzer::Action::kEmplaceBack: {
// Add some value to the back of a comparison, assert that both vectors
// are equivalent.
auto* c = Mutate(action.emplace_back().vector());
c->chunked.EmplaceBack(
std::make_shared<int>(action.emplace_back().value()));
c->std.emplace_back(
std::make_shared<int>(action.emplace_back().value()));
c->AssertOk();
} break;
case chunked_vector_fuzzer::Action::kPopBack: {
// Remove some value to the back of a comparison, assert that both
// vectors are equivalent.
auto* c = Mutate(action.pop_back().vector());
if (c->chunked.size() > 0) {
c->chunked.PopBack();
c->std.pop_back();
c->AssertOk();
}
} break;
case chunked_vector_fuzzer::Action::kCopy: {
// Copy one vector into another, assert both everything stays
// equivalent.
auto it_from = vectors_.find(action.copy().from());
if (it_from == vectors_.end()) {
it_from =
vectors_.emplace(action.copy().from(), Comparison(arena_)).first;
}
auto it_to = vectors_.find(action.copy().to());
if (it_to == vectors_.end()) {
it_to = vectors_.emplace(action.copy().to(), it_from->second).first;
} else {
it_to->second = it_from->second;
}
it_from->second.AssertOk();
it_to->second.AssertOk();
} break;
case chunked_vector_fuzzer::Action::kMove: {
// Move one vector into another, assert both everything stays
// equivalent.
auto it_from = vectors_.find(action.move().from());
if (it_from == vectors_.end()) {
it_from =
vectors_.emplace(action.move().from(), Comparison(arena_)).first;
}
auto it_to = vectors_.find(action.move().to());
if (it_to == vectors_.end()) {
it_to =
vectors_.emplace(action.move().to(), std::move(it_from->second))
.first;
} else {
it_to->second = it_from->second;
}
it_from->second.AssertOk();
it_to->second.AssertOk();
} break;
case chunked_vector_fuzzer::Action::kClear: {
// Clear a vector, assert that both underlying vectors are equivalent.
auto* c = Mutate(action.clear().vector());
c->chunked.Clear();
c->std.clear();
c->AssertOk();
} break;
case chunked_vector_fuzzer::Action::kSwap: {
// Swap two vectors, assert that both underlying vectors are equivalent.
auto* from = Mutate(action.swap().from());
auto* to = Mutate(action.swap().to());
from->chunked.Swap(&to->chunked);
from->std.swap(to->std);
from->AssertOk();
} break;
case chunked_vector_fuzzer::Action::ACTION_TYPE_NOT_SET:
break;
}
}
private:
Comparison* Mutate(int index) {
auto it = vectors_.find(index);
if (it != vectors_.end()) {
return &it->second;
}
return &vectors_.emplace(index, Comparison(arena_)).first->second;
}
Arena* arena_;
std::map<int, Comparison> vectors_;
};
} // namespace grpc_core
DEFINE_PROTO_FUZZER(const chunked_vector_fuzzer::Msg& msg) {
grpc_core::Fuzzer fuzzer;
for (int i = 0; i < msg.actions_size(); i++) {
fuzzer.Act(msg.actions(i));
}
}

@ -0,0 +1,50 @@
// 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 chunked_vector_fuzzer;
message EmplaceBack {
int32 vector = 1;
int32 value = 2;
}
message PopBack {
int32 vector = 1;
int32 value = 2;
}
message Copy {
int32 from = 1;
int32 to = 2;
}
message ClearVector {
int32 vector = 1;
}
message Action {
oneof action_type {
EmplaceBack emplace_back = 1;
PopBack pop_back = 2;
Copy copy = 3;
Copy move = 4;
ClearVector clear = 5;
Copy swap = 6;
}
}
message Msg {
repeated Action actions = 1;
}

@ -0,0 +1,169 @@
// 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/gprpp/chunked_vector.h"
#include <gtest/gtest.h>
namespace grpc_core {
namespace testing {
static constexpr size_t kInitialArenaSize = 1024;
static constexpr size_t kChunkSize = 3;
TEST(ChunkedVector, Noop) {
auto* arena = Arena::Create(kInitialArenaSize);
{
ChunkedVector<int, kChunkSize> v(arena);
EXPECT_EQ(0, v.size());
}
arena->Destroy();
}
TEST(ChunkedVector, Stack) {
auto* arena = Arena::Create(kInitialArenaSize);
{
ChunkedVector<int, kChunkSize> v(arena);
// Populate 2 chunks of memory, and 2/3 of a final chunk.
EXPECT_EQ(0, v.size());
v.EmplaceBack(1);
EXPECT_EQ(1, v.size());
v.EmplaceBack(2);
EXPECT_EQ(2, v.size());
v.EmplaceBack(3);
EXPECT_EQ(3, v.size());
v.EmplaceBack(4);
EXPECT_EQ(4, v.size());
v.EmplaceBack(5);
EXPECT_EQ(5, v.size());
v.EmplaceBack(6);
EXPECT_EQ(6, v.size());
v.EmplaceBack(7);
EXPECT_EQ(7, v.size());
v.EmplaceBack(8);
EXPECT_EQ(8, v.size());
// Now pop all of them out and check the expected ordering.
EXPECT_EQ(8, v.PopBack());
EXPECT_EQ(7, v.size());
EXPECT_EQ(7, v.PopBack());
EXPECT_EQ(6, v.size());
EXPECT_EQ(6, v.PopBack());
EXPECT_EQ(5, v.size());
EXPECT_EQ(5, v.PopBack());
EXPECT_EQ(4, v.size());
EXPECT_EQ(4, v.PopBack());
EXPECT_EQ(3, v.size());
EXPECT_EQ(3, v.PopBack());
EXPECT_EQ(2, v.size());
EXPECT_EQ(2, v.PopBack());
EXPECT_EQ(1, v.size());
EXPECT_EQ(1, v.PopBack());
EXPECT_EQ(0, v.size());
}
arena->Destroy();
}
TEST(ChunkedVector, Iterate) {
auto* arena = Arena::Create(kInitialArenaSize);
{
ChunkedVector<int, kChunkSize> v(arena);
v.EmplaceBack(1);
v.EmplaceBack(2);
v.EmplaceBack(3);
v.EmplaceBack(4);
v.EmplaceBack(5);
v.EmplaceBack(6);
v.EmplaceBack(7);
v.EmplaceBack(8);
auto it = v.begin();
EXPECT_EQ(1, *it);
++it;
EXPECT_EQ(2, *it);
++it;
EXPECT_EQ(3, *it);
++it;
EXPECT_EQ(4, *it);
++it;
EXPECT_EQ(5, *it);
++it;
EXPECT_EQ(6, *it);
++it;
EXPECT_EQ(7, *it);
++it;
EXPECT_EQ(8, *it);
++it;
EXPECT_EQ(v.end(), it);
}
arena->Destroy();
}
TEST(ChunkedVector, ConstIterate) {
auto* arena = Arena::Create(kInitialArenaSize);
{
ChunkedVector<int, kChunkSize> v(arena);
v.EmplaceBack(1);
v.EmplaceBack(2);
v.EmplaceBack(3);
v.EmplaceBack(4);
v.EmplaceBack(5);
v.EmplaceBack(6);
v.EmplaceBack(7);
v.EmplaceBack(8);
auto it = v.cbegin();
EXPECT_EQ(1, *it);
++it;
EXPECT_EQ(2, *it);
++it;
EXPECT_EQ(3, *it);
++it;
EXPECT_EQ(4, *it);
++it;
EXPECT_EQ(5, *it);
++it;
EXPECT_EQ(6, *it);
++it;
EXPECT_EQ(7, *it);
++it;
EXPECT_EQ(8, *it);
++it;
EXPECT_EQ(v.cend(), it);
}
arena->Destroy();
}
TEST(ChunkedVector, Clear) {
auto* arena = Arena::Create(kInitialArenaSize);
{
ChunkedVector<int, kChunkSize> v(arena);
v.EmplaceBack(1);
EXPECT_EQ(v.size(), 1);
v.Clear();
EXPECT_EQ(v.size(), 0);
EXPECT_EQ(v.begin(), v.end());
}
arena->Destroy();
}
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -4197,6 +4197,30 @@
],
"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": "chunked_vector_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save