Skip to content

Commit

Permalink
[NetKAT] Add PagedStableVector class that provides pointer stability.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 706058715
  • Loading branch information
smolkaj authored and copybara-github committed Dec 14, 2024
1 parent 1aabd36 commit efdcd02
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 6 deletions.
27 changes: 21 additions & 6 deletions netkat/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ cc_library(
],
)

cc_test(
name = "evaluator_test",
srcs = ["evaluator_test.cc"],
deps = [
":evaluator",
":netkat_cc_proto",
":netkat_proto_constructors",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_fuzztest//fuzztest",
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "netkat_proto_constructors",
srcs = ["netkat_proto_constructors.cc"],
Expand All @@ -82,14 +95,16 @@ cc_test(
],
)

cc_library(
name = "paged_stable_vector",
hdrs = ["paged_stable_vector.h"],
)

cc_test(
name = "evaluator_test",
srcs = ["evaluator_test.cc"],
name = "paged_stable_vector_test",
srcs = ["paged_stable_vector_test.cc"],
deps = [
":evaluator",
":netkat_cc_proto",
":netkat_proto_constructors",
"@com_google_absl//absl/container:flat_hash_set",
":paged_stable_vector",
"@com_google_fuzztest//fuzztest",
"@com_google_googletest//:gtest_main",
],
Expand Down
74 changes: 74 additions & 0 deletions netkat/paged_stable_vector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2024 The NetKAT 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
//
// https://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.
//
// -----------------------------------------------------------------------------
// File: paged_stable_vector.h
// -----------------------------------------------------------------------------

#ifndef GOOGLE_NETKAT_NETKAT_PAGED_STABLE_VECTOR_H_
#define GOOGLE_NETKAT_NETKAT_PAGED_STABLE_VECTOR_H_

#include <cstddef>
#include <utility>
#include <vector>

namespace netkat {

// A variant of `std::vector` that allocates memory in pages (or "chunks") of
// fixed `PageSize`. This introduces an extra level of indirection and
// introduces some level of discontiguity (depending on `PageSize`), but allows
// the class to guarantee pointer stability: calls to `push_back`/`emplace_back`
// never invalidate pointers/iterators/references to elements previously added
// to the vector.
//
// Allocating memory in pages also avoids the cost of relocation, which may be
// significant for very large vectors in performance-sensitive applications.
//
// The API of this class is kept just large enough to cover our use cases.
template <class T, size_t PageSize>
class PagedStableVector {
public:
PagedStableVector() = default;

size_t size() const {
return data_.empty() ? 0
: (data_.size() - 1) * PageSize + data_.back().size();
}

template <class Value>
void push_back(Value&& value) {
if (size() % PageSize == 0) data_.emplace_back().reserve(PageSize);
data_.back().push_back(std::forward<Value>(value));
}

template <class... Args>
void emplace_back(Args&&... value) {
if (size() % PageSize == 0) data_.emplace_back().reserve(PageSize);
data_.back().emplace_back(std::forward<Args>(value)...);
}

T& operator[](size_t index) {
return data_[index / PageSize][index % PageSize];
}
const T& operator[](size_t index) const {
return data_[index / PageSize][index % PageSize];
}

private:
std::vector<std::vector<T>> data_;
};

} // namespace netkat

#endif // GOOGLE_NETKAT_NETKAT_PAGED_STABLE_VECTOR_H_
105 changes: 105 additions & 0 deletions netkat/paged_stable_vector_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2024 The NetKAT 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
//
// https://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 "netkat/paged_stable_vector.h"

#include <string>
#include <vector>

#include "fuzztest/fuzztest.h"
#include "gtest/gtest.h"

namespace netkat {
namespace {

// A small, but otherwise random page size used throughout the tests.
// Using a small page size is useful for exercising the page replacement logic.
static constexpr int kSmallPageSize = 3;

void PushBackInreasesSize(std::vector<std::string> elements) {
PagedStableVector<std::string, kSmallPageSize> vector;
for (const auto& element : elements) {
vector.push_back(element);
}
EXPECT_EQ(vector.size(), elements.size());
}
FUZZ_TEST(PagedStableVectorTest, PushBackInreasesSize);

void EmplaceBackInreasesSize(std::vector<int> elements) {
PagedStableVector<int, kSmallPageSize> vector;
for (const auto& element : elements) {
vector.emplace_back(element);
}
EXPECT_EQ(vector.size(), elements.size());
}
FUZZ_TEST(PagedStableVectorTest, EmplaceBackInreasesSize);

void PushBackAddsElementToBack(std::vector<std::string> elements) {
PagedStableVector<std::string, kSmallPageSize> vector;
for (int i = 0; i < elements.size(); ++i) {
vector.push_back(elements[i]);
for (int j = 0; j < i; ++j) {
EXPECT_EQ(vector[j], elements[j]);
}
}
}
FUZZ_TEST(PagedStableVectorTest, PushBackAddsElementToBack);

void EmplaceBackAddsElementToBack(std::vector<int> elements) {
PagedStableVector<int, kSmallPageSize> vector;
for (int i = 0; i < elements.size(); ++i) {
vector.emplace_back(elements[i]);
for (int j = 0; j < i; ++j) {
EXPECT_EQ(vector[j], elements[j]);
}
}
}
FUZZ_TEST(PagedStableVectorTest, EmplaceBackAddsElementToBack);

void BracketAssigmentWorks(std::vector<std::string> elements) {
PagedStableVector<std::string, kSmallPageSize> vector;
for (int i = 0; i < elements.size(); ++i) {
vector.push_back("initial value");
}
for (int i = 0; i < elements.size(); ++i) {
vector[i] = elements[i];
}
for (int i = 0; i < elements.size(); ++i) {
EXPECT_EQ(vector[i], elements[i]);
}
}
FUZZ_TEST(PagedStableVectorTest, BracketAssigmentWorks);

TEST(PagedStableVectorTest, ReferencesDontGetInvalidated) {
PagedStableVector<std::string, kSmallPageSize> vector;

// Store a few references.
vector.push_back("first element");
std::string* first_element_ptr = &vector[0];
vector.push_back("second element");
std::string* second_element_ptr = &vector[1];

// Push a ton of elements to trigger page allocation.
// If this were a regular std::vector, the references would be invalidated.
for (int i = 0; i < 100 * kSmallPageSize; ++i) {
vector.push_back("dummy");
}

// Check that the references are still valid.
EXPECT_EQ(&vector[0], first_element_ptr);
EXPECT_EQ(&vector[1], second_element_ptr);
};

} // namespace
} // namespace netkat

0 comments on commit efdcd02

Please sign in to comment.