-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NetKAT] Add PagedStableVector class that provides pointer stability.
PiperOrigin-RevId: 706058715
- Loading branch information
1 parent
1aabd36
commit efdcd02
Showing
3 changed files
with
200 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |