tint/utils: Add utils::Vector
A small-object-optimized vector container. Bug: tint:1613 Change-Id: I1c482027db4624d4a2fd00ddeec02cfd0bb9b27f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96281 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
3fce08f298
commit
3bc20e3786
|
@ -564,6 +564,7 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"utils/string.h",
|
||||
"utils/unique_allocator.h",
|
||||
"utils/unique_vector.h",
|
||||
"utils/vector.h",
|
||||
"writer/append_vector.cc",
|
||||
"writer/append_vector.h",
|
||||
"writer/array_length_from_uniform_options.cc",
|
||||
|
@ -1227,6 +1228,7 @@ if (tint_build_unittests) {
|
|||
"utils/transform_test.cc",
|
||||
"utils/unique_allocator_test.cc",
|
||||
"utils/unique_vector_test.cc",
|
||||
"utils/vector_test.cc",
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -474,6 +474,7 @@ set(TINT_LIB_SRCS
|
|||
utils/string.h
|
||||
utils/unique_allocator.h
|
||||
utils/unique_vector.h
|
||||
utils/vector.h
|
||||
writer/append_vector.cc
|
||||
writer/append_vector.h
|
||||
writer/array_length_from_uniform_options.cc
|
||||
|
@ -857,6 +858,7 @@ if(TINT_BUILD_TESTS)
|
|||
utils/transform_test.cc
|
||||
utils/unique_allocator_test.cc
|
||||
utils/unique_vector_test.cc
|
||||
utils/vector_test.cc
|
||||
writer/append_vector_test.cc
|
||||
writer/flatten_bindings_test.cc
|
||||
writer/float_to_string_test.cc
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::utils {
|
||||
namespace detail {
|
||||
|
||||
|
@ -66,6 +68,15 @@ void HashCombine(size_t* hash, const std::vector<T>& vector) {
|
|||
}
|
||||
}
|
||||
|
||||
/// HashCombine "hashes" together an existing hash and hashable values.
|
||||
template <typename T, size_t N>
|
||||
void HashCombine(size_t* hash, const utils::Vector<T, N>& list) {
|
||||
HashCombine(hash, list.Length());
|
||||
for (auto& el : list) {
|
||||
HashCombine(hash, el);
|
||||
}
|
||||
}
|
||||
|
||||
/// HashCombine "hashes" together an existing hash and hashable values.
|
||||
template <typename... TYPES>
|
||||
void HashCombine(size_t* hash, const std::tuple<TYPES...>& tuple) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::utils {
|
||||
namespace {
|
||||
|
@ -35,13 +36,22 @@ TEST(HashTests, Basic) {
|
|||
EXPECT_NE(Hash(std::string("hello")), Hash(std::string("world")));
|
||||
}
|
||||
|
||||
TEST(HashTests, Vector) {
|
||||
TEST(HashTests, StdVector) {
|
||||
EXPECT_EQ(Hash(std::vector<int>({})), Hash(std::vector<int>({})));
|
||||
EXPECT_EQ(Hash(std::vector<int>({1, 2, 3})), Hash(std::vector<int>({1, 2, 3})));
|
||||
EXPECT_NE(Hash(std::vector<int>({1, 2, 3})), Hash(std::vector<int>({1, 2, 4})));
|
||||
EXPECT_NE(Hash(std::vector<int>({1, 2, 3})), Hash(std::vector<int>({1, 2, 3, 4})));
|
||||
}
|
||||
|
||||
TEST(HashTests, TintVector) {
|
||||
EXPECT_EQ(Hash(Vector<int>({})), Hash(Vector<int>({})));
|
||||
EXPECT_EQ(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 3})));
|
||||
EXPECT_NE(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 4})));
|
||||
EXPECT_NE(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 3, 4})));
|
||||
EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 4>({1, 2, 3})));
|
||||
EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 2>({1, 2, 3})));
|
||||
}
|
||||
|
||||
TEST(HashTests, Tuple) {
|
||||
EXPECT_EQ(Hash(std::make_tuple(1)), Hash(std::make_tuple(1)));
|
||||
EXPECT_EQ(Hash(std::make_tuple(1, 2, 3)), Hash(std::make_tuple(1, 2, 3)));
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "src/tint/traits.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
||||
|
@ -52,6 +53,94 @@ auto Transform(const std::vector<IN>& in, TRANSFORMER&& transform)
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector.
|
||||
/// @param in the input vector.
|
||||
/// @param transform the transformation function with signature: `OUT(IN)`
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <typename IN, size_t N, typename TRANSFORMER>
|
||||
auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
|
||||
-> Vector<decltype(transform(in[0]))> {
|
||||
Vector<decltype(transform(in[0])), N> result(in.Length());
|
||||
for (size_t i = 0; i < result.Length(); ++i) {
|
||||
result[i] = transform(in[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector.
|
||||
/// @param in the input vector.
|
||||
/// @param transform the transformation function with signature: `OUT(IN, size_t)`
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <typename IN, size_t N, typename TRANSFORMER>
|
||||
auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
|
||||
-> Vector<decltype(transform(in[0], 1u)), N> {
|
||||
Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
|
||||
for (size_t i = 0; i < result.Length(); ++i) {
|
||||
result[i] = transform(in[i], i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector reference.
|
||||
/// @param in the input vector.
|
||||
/// @param transform the transformation function with signature: `OUT(IN)`
|
||||
/// @tparam N the small-array size of the returned Vector
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(const VectorRef<IN>& in, TRANSFORMER&& transform)
|
||||
-> Vector<decltype(transform(in[0])), N> {
|
||||
Vector<decltype(transform(in[0])), N> result(in.Length());
|
||||
for (size_t i = 0; i < result.Length(); ++i) {
|
||||
result[i] = transform(in[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector reference.
|
||||
/// @param in the input vector.
|
||||
/// @param transform the transformation function with signature: `OUT(IN, size_t)`
|
||||
/// @tparam N the small-array size of the returned Vector
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(const VectorRef<IN>& in, TRANSFORMER&& transform)
|
||||
-> Vector<decltype(transform(in[0], 1u)), N> {
|
||||
Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
|
||||
for (size_t i = 0; i < result.Length(); ++i) {
|
||||
result[i] = transform(in[i], i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector reference.
|
||||
/// @param in the input vector.
|
||||
/// @param transform the transformation function with signature: `OUT(IN)`
|
||||
/// @tparam N the small-array size of the returned Vector
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
|
||||
-> Vector<decltype(transform(in[0])), N> {
|
||||
Vector<decltype(transform(in[0])), N> result(in.Length());
|
||||
for (size_t i = 0; i < result.Length(); ++i) {
|
||||
result[i] = transform(in[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector reference.
|
||||
/// @param in the input vector.
|
||||
/// @param transform the transformation function with signature: `OUT(IN, size_t)`
|
||||
/// @tparam N the small-array size of the returned Vector
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
|
||||
-> Vector<decltype(transform(in[0], 1u)), N> {
|
||||
Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
|
||||
for (size_t i = 0; i < result.Length(); ++i) {
|
||||
result[i] = transform(in[i], i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// TransformN performs an element-wise transformation of a vector, transforming and returning at
|
||||
/// most `n` elements.
|
||||
/// @param in the input vector.
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
namespace tint::utils {
|
||||
namespace {
|
||||
|
||||
TEST(TransformTest, Empty) {
|
||||
TEST(TransformTest, StdVectorEmpty) {
|
||||
const std::vector<int> empty{};
|
||||
{
|
||||
auto transformed = Transform(empty, [](int) -> int {
|
||||
|
@ -46,21 +46,21 @@ TEST(TransformTest, Empty) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, Identity) {
|
||||
TEST(TransformTest, StdVectorIdentity) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
auto transformed = Transform(input, [](int i) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, IdentityWithIndex) {
|
||||
TEST(TransformTest, StdVectorIdentityWithIndex) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
auto transformed = Transform(input, [](int i, size_t) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, Index) {
|
||||
TEST(TransformTest, StdVectorIndex) {
|
||||
const std::vector<int> input{10, 20, 30, 40};
|
||||
{
|
||||
auto transformed = Transform(input, [](int, size_t idx) { return idx; });
|
||||
|
@ -69,7 +69,7 @@ TEST(TransformTest, Index) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformSameType) {
|
||||
TEST(TransformTest, TransformStdVectorSameType) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = Transform(input, [](int i) { return i * 10; });
|
||||
|
@ -78,7 +78,7 @@ TEST(TransformTest, TransformSameType) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformDifferentType) {
|
||||
TEST(TransformTest, TransformStdVectorDifferentType) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = Transform(input, [](int i) { return std::to_string(i); });
|
||||
|
@ -87,7 +87,7 @@ TEST(TransformTest, TransformDifferentType) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformNTest, Empty) {
|
||||
TEST(TransformNTest, StdVectorEmpty) {
|
||||
const std::vector<int> empty{};
|
||||
{
|
||||
auto transformed = TransformN(empty, 4u, [](int) -> int {
|
||||
|
@ -107,7 +107,7 @@ TEST(TransformNTest, Empty) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformNTest, Identity) {
|
||||
TEST(TransformNTest, StdVectorIdentity) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = TransformN(input, 0u, [](int) {
|
||||
|
@ -129,7 +129,7 @@ TEST(TransformNTest, Identity) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformNTest, IdentityWithIndex) {
|
||||
TEST(TransformNTest, StdVectorIdentityWithIndex) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = TransformN(input, 0u, [](int, size_t) {
|
||||
|
@ -151,7 +151,7 @@ TEST(TransformNTest, IdentityWithIndex) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformNTest, Index) {
|
||||
TEST(TransformNTest, StdVectorIndex) {
|
||||
const std::vector<int> input{10, 20, 30, 40};
|
||||
{
|
||||
auto transformed = TransformN(input, 0u, [](int, size_t) {
|
||||
|
@ -173,7 +173,7 @@ TEST(TransformNTest, Index) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformNTest, TransformSameType) {
|
||||
TEST(TransformNTest, StdVectorTransformSameType) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = TransformN(input, 0u, [](int, size_t) {
|
||||
|
@ -195,7 +195,7 @@ TEST(TransformNTest, TransformSameType) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformNTest, TransformDifferentType) {
|
||||
TEST(TransformNTest, StdVectorTransformDifferentType) {
|
||||
const std::vector<int> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = TransformN(input, 0u, [](int) {
|
||||
|
@ -217,5 +217,200 @@ TEST(TransformNTest, TransformDifferentType) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TintVectorEmpty) {
|
||||
const Vector<int, 4> empty{};
|
||||
{
|
||||
auto transformed = Transform(empty, [](int) -> int {
|
||||
[] { FAIL() << "Callback should not be called for empty vector"; }();
|
||||
return 0;
|
||||
});
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_EQ(transformed.Length(), 0u);
|
||||
}
|
||||
{
|
||||
auto transformed = Transform(empty, [](int, size_t) -> int {
|
||||
[] { FAIL() << "Callback should not be called for empty vector"; }();
|
||||
return 0;
|
||||
});
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_EQ(transformed.Length(), 0u);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TintVectorIdentity) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
auto transformed = Transform(input, [](int i) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, TintVectorIdentityWithIndex) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
auto transformed = Transform(input, [](int i, size_t) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, TintVectorIndex) {
|
||||
const Vector<int, 4> input{10, 20, 30, 40};
|
||||
{
|
||||
auto transformed = Transform(input, [](int, size_t idx) { return idx; });
|
||||
CHECK_ELEMENT_TYPE(transformed, size_t);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(0u, 1u, 2u, 3u));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformTintVectorSameType) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = Transform(input, [](int i) { return i * 10; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(10, 20, 30, 40));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformTintVectorDifferentType) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
{
|
||||
auto transformed = Transform(input, [](int i) { return std::to_string(i); });
|
||||
CHECK_ELEMENT_TYPE(transformed, std::string);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre("1", "2", "3", "4"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, VectorRefEmpty) {
|
||||
Vector<int, 4> empty{};
|
||||
VectorRef<int> ref(empty);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int) -> int {
|
||||
[] { FAIL() << "Callback should not be called for empty vector"; }();
|
||||
return 0;
|
||||
});
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_EQ(transformed.Length(), 0u);
|
||||
}
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int, size_t) -> int {
|
||||
[] { FAIL() << "Callback should not be called for empty vector"; }();
|
||||
return 0;
|
||||
});
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_EQ(transformed.Length(), 0u);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, VectorRefIdentity) {
|
||||
Vector<int, 4> input{1, 2, 3, 4};
|
||||
VectorRef<int> ref(input);
|
||||
auto transformed = Transform<8>(ref, [](int i) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, VectorRefIdentityWithIndex) {
|
||||
Vector<int, 4> input{1, 2, 3, 4};
|
||||
VectorRef<int> ref(input);
|
||||
auto transformed = Transform<2>(ref, [](int i, size_t) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, VectorRefIndex) {
|
||||
Vector<int, 4> input{10, 20, 30, 40};
|
||||
VectorRef<int> ref(input);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int, size_t idx) { return idx; });
|
||||
CHECK_ELEMENT_TYPE(transformed, size_t);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(0u, 1u, 2u, 3u));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformVectorRefSameType) {
|
||||
Vector<int, 4> input{1, 2, 3, 4};
|
||||
VectorRef<int> ref(input);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int i) { return i * 10; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(10, 20, 30, 40));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformVectorRefDifferentType) {
|
||||
Vector<int, 4> input{1, 2, 3, 4};
|
||||
VectorRef<int> ref(input);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int i) { return std::to_string(i); });
|
||||
CHECK_ELEMENT_TYPE(transformed, std::string);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre("1", "2", "3", "4"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, ConstVectorRefEmpty) {
|
||||
const Vector<int, 4> empty{};
|
||||
ConstVectorRef<int> ref(empty);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int) -> int {
|
||||
[] { FAIL() << "Callback should not be called for empty vector"; }();
|
||||
return 0;
|
||||
});
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_EQ(transformed.Length(), 0u);
|
||||
}
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int, size_t) -> int {
|
||||
[] { FAIL() << "Callback should not be called for empty vector"; }();
|
||||
return 0;
|
||||
});
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_EQ(transformed.Length(), 0u);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, ConstVectorRefIdentity) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
ConstVectorRef<int> ref(input);
|
||||
auto transformed = Transform<8>(ref, [](int i) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, ConstVectorRefIdentityWithIndex) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
ConstVectorRef<int> ref(input);
|
||||
auto transformed = Transform<2>(ref, [](int i, size_t) { return i; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(TransformTest, ConstVectorRefIndex) {
|
||||
const Vector<int, 4> input{10, 20, 30, 40};
|
||||
ConstVectorRef<int> ref(input);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int, size_t idx) { return idx; });
|
||||
CHECK_ELEMENT_TYPE(transformed, size_t);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(0u, 1u, 2u, 3u));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformConstVectorRefSameType) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
ConstVectorRef<int> ref(input);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int i) { return i * 10; });
|
||||
CHECK_ELEMENT_TYPE(transformed, int);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre(10, 20, 30, 40));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformTest, TransformConstVectorRefDifferentType) {
|
||||
const Vector<int, 4> input{1, 2, 3, 4};
|
||||
ConstVectorRef<int> ref(input);
|
||||
{
|
||||
auto transformed = Transform<4>(ref, [](int i) { return std::to_string(i); });
|
||||
CHECK_ELEMENT_TYPE(transformed, std::string);
|
||||
EXPECT_THAT(transformed, testing::ElementsAre("1", "2", "3", "4"));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::utils
|
||||
|
|
|
@ -0,0 +1,695 @@
|
|||
// Copyright 2022 The Tint 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 SRC_TINT_UTILS_VECTOR_H_
|
||||
#define SRC_TINT_UTILS_VECTOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/utils/bitcast.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
||||
/// Forward declarations
|
||||
template <typename>
|
||||
class VectorRef;
|
||||
template <typename>
|
||||
class ConstVectorRef;
|
||||
|
||||
} // namespace tint::utils
|
||||
|
||||
namespace tint::utils {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// A slice represents a contigious array of elements of type T.
|
||||
template <typename T>
|
||||
struct Slice {
|
||||
/// The pointer to the first element in the slice
|
||||
T* data = nullptr;
|
||||
|
||||
/// The total number of elements in the slice
|
||||
size_t len = 0;
|
||||
|
||||
/// The total capacity of the backing store for the slice
|
||||
size_t cap = 0;
|
||||
|
||||
/// Index operator
|
||||
/// @param i the element index. Must be less than `len`.
|
||||
/// @returns a reference to the i'th element.
|
||||
T& operator[](size_t i) { return data[i]; }
|
||||
|
||||
/// Index operator
|
||||
/// @param i the element index. Must be less than `len`.
|
||||
/// @returns a reference to the i'th element.
|
||||
const T& operator[](size_t i) const { return data[i]; }
|
||||
|
||||
/// @returns a reference to the first element in the vector
|
||||
T& Front() { return data[0]; }
|
||||
|
||||
/// @returns a reference to the first element in the vector
|
||||
const T& Front() const { return data[0]; }
|
||||
|
||||
/// @returns a reference to the last element in the vector
|
||||
T& Back() { return data[len - 1]; }
|
||||
|
||||
/// @returns a reference to the last element in the vector
|
||||
const T& Back() const { return data[len - 1]; }
|
||||
|
||||
/// @returns a pointer to the first element in the vector
|
||||
T* begin() { return data; }
|
||||
|
||||
/// @returns a pointer to the first element in the vector
|
||||
const T* begin() const { return data; }
|
||||
|
||||
/// @returns a pointer to one past the last element in the vector
|
||||
T* end() { return data + len; }
|
||||
|
||||
/// @returns a pointer to one past the last element in the vector
|
||||
const T* end() const { return data + len; }
|
||||
|
||||
/// @returns a reverse iterator starting with the last element in the vector
|
||||
auto rbegin() { return std::reverse_iterator<T*>(end()); }
|
||||
|
||||
/// @returns a reverse iterator starting with the last element in the vector
|
||||
auto rbegin() const { return std::reverse_iterator<const T*>(end()); }
|
||||
|
||||
/// @returns the end for a reverse iterator
|
||||
auto rend() { return std::reverse_iterator<T*>(begin()); }
|
||||
|
||||
/// @returns the end for a reverse iterator
|
||||
auto rend() const { return std::reverse_iterator<const T*>(begin()); }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Vector is a small-object-optimized, dynamically-sized vector of contigious elements of type T.
|
||||
///
|
||||
/// Vector will fit `N` elements internally before spilling to heap allocations. If `N` is greater
|
||||
/// than zero, the internal elements are stored in a 'small array' held internally by the Vector.
|
||||
///
|
||||
/// Vectors can be copied or moved.
|
||||
///
|
||||
/// Copying a vector will either copy to the 'small array' if the number of elements is equal to or
|
||||
/// less than N, otherwise elements will be copied into a new heap allocation.
|
||||
///
|
||||
/// Moving a vector will reassign ownership of the heap-allocation memory, if the source vector
|
||||
/// holds its elements in a heap allocation, otherwise a copy will be made as described above.
|
||||
///
|
||||
/// Vector is optimized for CPU performance over memory efficiency. For example:
|
||||
/// * Moving a vector that stores its elements in a heap allocation to another vector will simply
|
||||
/// assign the heap allocation, even if the target vector can hold the elements in its 'small
|
||||
/// array'. This reduces memory copying, but may incur additional memory usage.
|
||||
/// * Resizing, or popping elements from a vector that has spilled to a heap allocation does not
|
||||
/// revert back to using the 'small array'. Again, this is to reduce memory copying.
|
||||
template <typename T, size_t N = 0>
|
||||
class Vector {
|
||||
public:
|
||||
/// Type of `T`.
|
||||
using value_type = T;
|
||||
|
||||
/// Constructor
|
||||
Vector() = default;
|
||||
|
||||
/// Constructor
|
||||
/// @param length the initial length of the vector. Elements will be zero-initialized.
|
||||
explicit Vector(size_t length) {
|
||||
Reserve(length);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
new (&impl_.slice.data[i]) T{};
|
||||
}
|
||||
impl_.slice.len = length;
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
/// @param length the initial length of the vector
|
||||
/// @param value the value to copy into each element of the vector
|
||||
Vector(size_t length, const T& value) {
|
||||
Reserve(length);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
new (&impl_.slice.data[i]) T{value};
|
||||
}
|
||||
impl_.slice.len = length;
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
/// @param elements the elements to place into the vector
|
||||
explicit Vector(std::initializer_list<T> elements) {
|
||||
Reserve(elements.size());
|
||||
for (auto& el : elements) {
|
||||
new (&impl_.slice.data[impl_.slice.len++]) T{el};
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
/// @param other the vector to copy
|
||||
Vector(const Vector& other) { Copy(other.impl_.slice); }
|
||||
|
||||
/// Move constructor
|
||||
/// @param other the vector to move
|
||||
Vector(Vector&& other) { MoveOrCopy(VectorRef<T>(std::move(other))); }
|
||||
|
||||
/// Copy constructor (differing N length)
|
||||
/// @param other the vector to copy
|
||||
template <size_t N2>
|
||||
Vector(const Vector<T, N2>& other) {
|
||||
Copy(other.impl_.slice);
|
||||
}
|
||||
|
||||
/// Move constructor (differing N length)
|
||||
/// @param other the vector to move
|
||||
template <size_t N2>
|
||||
Vector(Vector<T, N2>&& other) {
|
||||
MoveOrCopy(VectorRef<T>(std::move(other)));
|
||||
}
|
||||
|
||||
/// Move constructor from a mutable vector reference
|
||||
/// @param other the vector reference to move
|
||||
Vector(VectorRef<T>&& other) { // NOLINT(runtime/explicit)
|
||||
MoveOrCopy(std::move(other));
|
||||
}
|
||||
|
||||
/// Copy constructor from an immutable vector reference
|
||||
/// @param other the vector reference to copy
|
||||
Vector(const ConstVectorRef<T>& other) { // NOLINT(runtime/explicit)
|
||||
Copy(other.slice_);
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
~Vector() { ClearAndFree(); }
|
||||
|
||||
/// Assignment operator
|
||||
/// @param other the vector to copy
|
||||
/// @returns this vector so calls can be chained
|
||||
Vector& operator=(const Vector& other) {
|
||||
if (&other != this) {
|
||||
Copy(other.impl_.slice);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Move operator
|
||||
/// @param other the vector to move
|
||||
/// @returns this vector so calls can be chained
|
||||
Vector& operator=(Vector&& other) {
|
||||
if (&other != this) {
|
||||
MoveOrCopy(VectorRef<T>(std::move(other)));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assignment operator (differing N length)
|
||||
/// @param other the vector to copy
|
||||
/// @returns this vector so calls can be chained
|
||||
template <size_t N2>
|
||||
Vector& operator=(const Vector<T, N2>& other) {
|
||||
Copy(other.impl_.slice);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Move operator (differing N length)
|
||||
/// @param other the vector to copy
|
||||
/// @returns this vector so calls can be chained
|
||||
template <size_t N2>
|
||||
Vector& operator=(Vector<T, N2>&& other) {
|
||||
MoveOrCopy(VectorRef<T>(std::move(other)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Index operator
|
||||
/// @param i the element index. Must be less than `len`.
|
||||
/// @returns a reference to the i'th element.
|
||||
T& operator[](size_t i) { return impl_.slice[i]; }
|
||||
|
||||
/// Index operator
|
||||
/// @param i the element index. Must be less than `len`.
|
||||
/// @returns a reference to the i'th element.
|
||||
const T& operator[](size_t i) const { return impl_.slice[i]; }
|
||||
|
||||
/// @return the number of elements in the vector
|
||||
size_t Length() const { return impl_.slice.len; }
|
||||
|
||||
/// @return the number of elements that the vector could hold before a heap allocation needs to
|
||||
/// be made
|
||||
size_t Capacity() const { return impl_.slice.cap; }
|
||||
|
||||
/// Reserves memory to hold at least `new_cap` elements
|
||||
/// @param new_cap the new vector capacity
|
||||
void Reserve(size_t new_cap) {
|
||||
if (new_cap > impl_.slice.cap) {
|
||||
auto* old_data = impl_.slice.data;
|
||||
impl_.Allocate(new_cap);
|
||||
for (size_t i = 0; i < impl_.slice.len; i++) {
|
||||
new (&impl_.slice.data[i]) T(std::move(old_data[i]));
|
||||
old_data[i].~T();
|
||||
}
|
||||
impl_.Free(old_data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resizes the vector to the given length, expanding capacity if necessary.
|
||||
/// New elements are zero-initialized
|
||||
/// @param new_len the new vector length
|
||||
void Resize(size_t new_len) {
|
||||
Reserve(new_len);
|
||||
for (size_t i = impl_.slice.len; i > new_len; i--) { // Shrink
|
||||
impl_.slice.data[i - 1].~T();
|
||||
}
|
||||
for (size_t i = impl_.slice.len; i < new_len; i++) { // Grow
|
||||
new (&impl_.slice.data[i]) T{};
|
||||
}
|
||||
impl_.slice.len = new_len;
|
||||
}
|
||||
|
||||
/// Copies all the elements from `other` to this vector, replacing the content of this vector.
|
||||
/// @param other the
|
||||
template <typename T2, size_t N2>
|
||||
void Copy(const Vector<T2, N2>& other) {
|
||||
Copy(other.impl_.slice);
|
||||
}
|
||||
|
||||
/// Clears all elements from the vector, keeping the capacity the same.
|
||||
void Clear() {
|
||||
for (size_t i = 0; i < impl_.slice.len; i++) {
|
||||
impl_.slice.data[i].~T();
|
||||
}
|
||||
impl_.slice.len = 0;
|
||||
}
|
||||
|
||||
/// Appends a new element to the vector.
|
||||
/// @param el the element to copy to the vector.
|
||||
void Push(const T& el) {
|
||||
if (impl_.slice.len >= impl_.slice.cap) {
|
||||
Grow();
|
||||
}
|
||||
new (&impl_.slice.data[impl_.slice.len++]) T(el);
|
||||
}
|
||||
|
||||
/// Appends a new element to the vector.
|
||||
/// @param el the element to move to the vector.
|
||||
void Push(T&& el) {
|
||||
if (impl_.slice.len >= impl_.slice.cap) {
|
||||
Grow();
|
||||
}
|
||||
new (&impl_.slice.data[impl_.slice.len++]) T(std::move(el));
|
||||
}
|
||||
|
||||
/// Appends a new element to the vector.
|
||||
/// @param args the arguments to pass to the element constructor.
|
||||
template <typename... ARGS>
|
||||
void Emplace(ARGS&&... args) {
|
||||
if (impl_.slice.len >= impl_.slice.cap) {
|
||||
Grow();
|
||||
}
|
||||
new (&impl_.slice.data[impl_.slice.len++]) T(std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
/// Removes and returns the last element from the vector.
|
||||
/// @returns the popped element
|
||||
T Pop() { return std::move(impl_.slice.data[--impl_.slice.len]); }
|
||||
|
||||
/// @returns true if the vector is empty.
|
||||
bool IsEmpty() const { return impl_.slice.len == 0; }
|
||||
|
||||
/// @returns a reference to the first element in the vector
|
||||
T& Front() { return impl_.slice.Front(); }
|
||||
|
||||
/// @returns a reference to the first element in the vector
|
||||
const T& Front() const { return impl_.slice.Front(); }
|
||||
|
||||
/// @returns a reference to the last element in the vector
|
||||
T& Back() { return impl_.slice.Back(); }
|
||||
|
||||
/// @returns a reference to the last element in the vector
|
||||
const T& Back() const { return impl_.slice.Back(); }
|
||||
|
||||
/// @returns a pointer to the first element in the vector
|
||||
T* begin() { return impl_.slice.begin(); }
|
||||
|
||||
/// @returns a pointer to the first element in the vector
|
||||
const T* begin() const { return impl_.slice.begin(); }
|
||||
|
||||
/// @returns a pointer to one past the last element in the vector
|
||||
T* end() { return impl_.slice.end(); }
|
||||
|
||||
/// @returns a pointer to one past the last element in the vector
|
||||
const T* end() const { return impl_.slice.end(); }
|
||||
|
||||
/// @returns a reverse iterator starting with the last element in the vector
|
||||
auto rbegin() { return impl_.slice.rbegin(); }
|
||||
|
||||
/// @returns a reverse iterator starting with the last element in the vector
|
||||
auto rbegin() const { return impl_.slice.rbegin(); }
|
||||
|
||||
/// @returns the end for a reverse iterator
|
||||
auto rend() { return impl_.slice.rend(); }
|
||||
|
||||
/// @returns the end for a reverse iterator
|
||||
auto rend() const { return impl_.slice.rend(); }
|
||||
|
||||
private:
|
||||
/// Friend class (differing specializations of this class)
|
||||
template <typename, size_t>
|
||||
friend class Vector;
|
||||
|
||||
/// Friend class
|
||||
template <typename>
|
||||
friend class VectorRef;
|
||||
|
||||
/// Friend class
|
||||
template <typename>
|
||||
friend class ConstVectorRef;
|
||||
|
||||
/// The slice type used by this vector
|
||||
using Slice = detail::Slice<T>;
|
||||
|
||||
/// Expands the capacity of the vector
|
||||
void Grow() { Reserve(impl_.slice.cap * 2); }
|
||||
|
||||
/// Moves 'other' to this vector, if possible, otherwise performs a copy.
|
||||
void MoveOrCopy(VectorRef<T>&& other) {
|
||||
if (other.can_move_) {
|
||||
ClearAndFree();
|
||||
impl_.slice = other.slice_;
|
||||
other.slice_ = {};
|
||||
} else {
|
||||
Copy(other.slice_);
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies all the elements from `other` to this vector, replacing the content of this vector.
|
||||
/// @param other the
|
||||
void Copy(const Slice& other) {
|
||||
if (impl_.slice.cap < other.len) {
|
||||
ClearAndFree();
|
||||
impl_.Allocate(other.len);
|
||||
} else {
|
||||
Clear();
|
||||
}
|
||||
|
||||
impl_.slice.len = other.len;
|
||||
for (size_t i = 0; i < impl_.slice.len; i++) {
|
||||
new (&impl_.slice.data[i]) T{other.data[i]};
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the vector, then frees the slice data.
|
||||
void ClearAndFree() {
|
||||
Clear();
|
||||
impl_.Free(impl_.slice.data);
|
||||
}
|
||||
|
||||
/// True if this vector uses a small array for small object optimization.
|
||||
constexpr static bool HasSmallArray = N > 0;
|
||||
|
||||
/// A structure that has the same size and alignment as T.
|
||||
/// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
|
||||
struct alignas(alignof(T)) TStorage {
|
||||
/// @returns the storage reinterpreted as a T*
|
||||
T* Get() { return Bitcast<T*>(&data[0]); }
|
||||
/// @returns the storage reinterpreted as a T*
|
||||
const T* Get() const { return Bitcast<const T*>(&data[0]); }
|
||||
/// Byte array of length sizeof(T)
|
||||
uint8_t data[sizeof(T)];
|
||||
};
|
||||
|
||||
/// The internal structure for the vector with a small array.
|
||||
struct ImplWithSmallArray {
|
||||
std::array<TStorage, N> small_arr;
|
||||
Slice slice = {small_arr[0].Get(), 0, N};
|
||||
|
||||
/// Allocates a new vector of `T` either from #small_arr, or from the heap, then assigns the
|
||||
/// pointer it to #slice.data, and updates #slice.cap.
|
||||
void Allocate(size_t new_cap) {
|
||||
if (new_cap < N) {
|
||||
slice.data = small_arr[0].Get();
|
||||
slice.cap = N;
|
||||
} else {
|
||||
slice.data = Bitcast<T*>(new TStorage[new_cap]);
|
||||
slice.cap = new_cap;
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees `data`, if not nullptr and isn't a pointer to #small_arr
|
||||
void Free(T* data) const {
|
||||
if (data && data != small_arr[0].Get()) {
|
||||
delete[] Bitcast<TStorage*>(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates whether the slice structure can be std::move()d.
|
||||
/// @returns true if #slice.data does not point to #small_arr
|
||||
bool CanMove() const { return slice.data != small_arr[0].Get(); }
|
||||
};
|
||||
|
||||
/// The internal structure for the vector without a small array.
|
||||
struct ImplWithoutSmallArray {
|
||||
Slice slice = {nullptr, 0, 0};
|
||||
|
||||
/// Allocates a new vector of `T` and assigns it to #slice.data, and updates #slice.cap.
|
||||
void Allocate(size_t new_cap) {
|
||||
slice.data = Bitcast<T*>(new TStorage[new_cap]);
|
||||
slice.cap = new_cap;
|
||||
}
|
||||
|
||||
/// Frees `data`, if not nullptr.
|
||||
void Free(T* data) const {
|
||||
if (data) {
|
||||
delete[] Bitcast<TStorage*>(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates whether the slice structure can be std::move()d.
|
||||
/// @returns true
|
||||
bool CanMove() const { return true; }
|
||||
};
|
||||
|
||||
/// Either a ImplWithSmallArray or ImplWithoutSmallArray based on N.
|
||||
std::conditional_t<HasSmallArray, ImplWithSmallArray, ImplWithoutSmallArray> impl_;
|
||||
};
|
||||
|
||||
/// VectorRef is a weak reference to a Vector, used to pass vectors as parameters, avoiding copies
|
||||
/// between the caller and the callee. VectorRef can accept a Vector of any 'N' value, decoupling
|
||||
/// the caller's vector internal size from the callee's vector size.
|
||||
///
|
||||
/// A VectorRef tracks the usage of moves either side of the call. If at the call site, a Vector
|
||||
/// argument is moved to a VectorRef parameter, and within the callee, the VectorRef parameter is
|
||||
/// moved to a Vector, then the Vector heap allocation will be moved. For example:
|
||||
///
|
||||
/// ```
|
||||
/// void func_a() {
|
||||
/// Vector<std::string, 4> vec;
|
||||
/// // logic to populate 'vec'.
|
||||
/// func_b(std::move(vec)); // Constructs a VectorRef tracking the move here.
|
||||
/// }
|
||||
///
|
||||
/// void func_b(VectorRef<std::string> vec_ref) {
|
||||
/// // A move was made when calling func_b, so the vector can be moved instead of copied.
|
||||
/// Vector<std::string, 2> vec(std::move(vec_ref));
|
||||
/// }
|
||||
/// ```
|
||||
template <typename T>
|
||||
class VectorRef {
|
||||
/// The slice type used by this vector reference
|
||||
using Slice = detail::Slice<T>;
|
||||
|
||||
public:
|
||||
/// Constructor from a Vector.
|
||||
/// @param vector the vector reference
|
||||
template <size_t N>
|
||||
VectorRef(Vector<T, N>& vector) // NOLINT(runtime/explicit)
|
||||
: slice_(vector.impl_.slice), can_move_(false) {}
|
||||
|
||||
/// Constructor from a std::move()'d Vector
|
||||
/// @param vector the vector being moved
|
||||
template <size_t N>
|
||||
VectorRef(Vector<T, N>&& vector) // NOLINT(runtime/explicit)
|
||||
: slice_(vector.impl_.slice), can_move_(vector.impl_.CanMove()) {}
|
||||
|
||||
/// Copy constructor
|
||||
/// @param other the vector reference
|
||||
VectorRef(const VectorRef& other) : slice_(other.slice_), can_move_(false) {}
|
||||
|
||||
/// Move constructor
|
||||
/// @param other the vector reference
|
||||
VectorRef(VectorRef&& other) = default;
|
||||
|
||||
/// Index operator
|
||||
/// @param i the element index. Must be less than `len`.
|
||||
/// @returns a reference to the i'th element.
|
||||
T& operator[](size_t i) { return slice_[i]; }
|
||||
|
||||
/// Index operator
|
||||
/// @param i the element index. Must be less than `len`.
|
||||
/// @returns a reference to the i'th element.
|
||||
const T& operator[](size_t i) const { return slice_[i]; }
|
||||
|
||||
/// @return the number of elements in the vector
|
||||
size_t Length() const { return slice_.len; }
|
||||
|
||||
/// @return the number of elements that the vector could hold before a heap allocation needs to
|
||||
/// be made
|
||||
size_t Capacity() const { return slice_.cap; }
|
||||
|
||||
/// @returns true if the vector is empty.
|
||||
bool IsEmpty() const { return slice_.len == 0; }
|
||||
|
||||
/// @returns a reference to the first element in the vector
|
||||
T& Front() { return slice_.Front(); }
|
||||
|
||||
/// @returns a reference to the first element in the vector
|
||||
const T& Front() const { return slice_.Front(); }
|
||||
|
||||
/// @returns a reference to the last element in the vector
|
||||
T& Back() { return slice_.Back(); }
|
||||
|
||||
/// @returns a reference to the last element in the vector
|
||||
const T& Back() const { return slice_.Back(); }
|
||||
|
||||
/// @returns a pointer to the first element in the vector
|
||||
T* begin() { return slice_.begin(); }
|
||||
|
||||
/// @returns a pointer to the first element in the vector
|
||||
const T* begin() const { return slice_.begin(); }
|
||||
|
||||
/// @returns a pointer to one past the last element in the vector
|
||||
T* end() { return slice_.end(); }
|
||||
|
||||
/// @returns a pointer to one past the last element in the vector
|
||||
const T* end() const { return slice_.end(); }
|
||||
|
||||
/// @returns a reverse iterator starting with the last element in the vector
|
||||
auto rbegin() { return slice_.rbegin(); }
|
||||
|
||||
/// @returns a reverse iterator starting with the last element in the vector
|
||||
auto rbegin() const { return slice_.rbegin(); }
|
||||
|
||||
/// @returns the end for a reverse iterator
|
||||
auto rend() { return slice_.rend(); }
|
||||
|
||||
/// @returns the end for a reverse iterator
|
||||
auto rend() const { return slice_.rend(); }
|
||||
|
||||
private:
|
||||
/// Friend classes
|
||||
template <typename, size_t>
|
||||
friend class Vector;
|
||||
|
||||
/// The slice of the vector being referenced.
|
||||
Slice& slice_;
|
||||
/// Whether the slice data is passed by r-value reference, and can be moved.
|
||||
bool can_move_ = false;
|
||||
};
|
||||
|
||||
/// ConstVectorRef is a weak, immutable reference to a Vector, used to pass vectors as parameters,
|
||||
/// avoiding copies between the caller and the callee. VectorRef can accept a Vector of any 'N'
|
||||
/// value, decoupling the caller's vector internal size from the callee's vector size.
|
||||
template <typename T>
|
||||
class ConstVectorRef {
|
||||
/// The slice type used by this vector reference
|
||||
using Slice = detail::Slice<T>;
|
||||
|
||||
public:
|
||||
/// Constructor from a Vector.
|
||||
/// @param vector the vector reference
|
||||
template <size_t N>
|
||||
ConstVectorRef(const Vector<T, N>& vector) // NOLINT(runtime/explicit)
|
||||
: slice_(vector.impl_.slice) {}
|
||||
|
||||
/// Copy constructor
|
||||
/// @param other the vector reference
|
||||
ConstVectorRef(const ConstVectorRef& other) = default;
|
||||
|
||||
/// Index operator
|
||||
/// @param i the element index. Must be less than `len`.
|
||||
/// @returns a reference to the i'th element.
|
||||
const T& operator[](size_t i) const { return slice_[i]; }
|
||||
|
||||
/// @return the number of elements in the vector
|
||||
size_t Length() const { return slice_.len; }
|
||||
|
||||
/// @return the number of elements that the vector could hold before a heap allocation needs to
|
||||
/// be made
|
||||
size_t Capacity() const { return slice_.cap; }
|
||||
|
||||
/// @returns true if the vector is empty.
|
||||
bool IsEmpty() const { return slice_.len == 0; }
|
||||
|
||||
/// @returns a reference to the first element in the vector
|
||||
const T& Front() const { return slice_.Front(); }
|
||||
|
||||
/// @returns a reference to the last element in the vector
|
||||
const T& Back() const { return slice_.Back(); }
|
||||
|
||||
/// @returns a pointer to the first element in the vector
|
||||
const T* begin() const { return slice_.begin(); }
|
||||
|
||||
/// @returns a pointer to one past the last element in the vector
|
||||
const T* end() const { return slice_.end(); }
|
||||
|
||||
/// @returns a reverse iterator starting with the last element in the vector
|
||||
auto rbegin() const { return slice_.rbegin(); }
|
||||
|
||||
/// @returns the end for a reverse iterator
|
||||
auto rend() const { return slice_.rend(); }
|
||||
|
||||
private:
|
||||
/// Friend classes
|
||||
template <typename, size_t>
|
||||
friend class Vector;
|
||||
|
||||
/// The slice of the vector being referenced.
|
||||
const Slice& slice_;
|
||||
};
|
||||
|
||||
/// Helper for converting a Vector to a std::vector.
|
||||
/// @note This helper exists to help code migration. Avoid if possible.
|
||||
template <typename T, size_t N>
|
||||
std::vector<T> ToStdVector(const Vector<T, N>& vector) {
|
||||
std::vector<T> out;
|
||||
out.reserve(vector.Length());
|
||||
for (auto& el : vector) {
|
||||
out.emplace_back(el);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Helper for converting a std::vector to a Vector.
|
||||
/// @note This helper exists to help code migration. Avoid if possible.
|
||||
template <typename T, size_t N = 0>
|
||||
Vector<T, N> ToVector(const std::vector<T>& vector) {
|
||||
Vector<T, N> out;
|
||||
out.Reserve(vector.size());
|
||||
for (auto& el : vector) {
|
||||
out.Push(el);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Helper for constructing a Vector from a set of elements.
|
||||
/// The returned Vector's small-array size (`N`) is equal to the number of provided elements.
|
||||
/// @param elements the elements used to construct the vector.
|
||||
template <typename T, typename... Ts>
|
||||
auto MakeVector(Ts&&... elements) {
|
||||
return Vector<T, sizeof...(Ts)>({std::forward<Ts>(elements)...});
|
||||
}
|
||||
|
||||
} // namespace tint::utils
|
||||
|
||||
#endif // SRC_TINT_UTILS_VECTOR_H_
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue