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
parent
04ce30a1f8
commit
253d7076fc
10 changed files with 877 additions and 0 deletions
@ -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
|
@ -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(); |
||||||
|
} |
Loading…
Reference in new issue