| // |
| // Copyright 2019 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // span_unittests.cpp: Unit tests for the angle::Span class. |
| // |
| |
| #include "common/span.h" |
| #include "common/unsafe_buffers.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include <array> |
| #include <string> |
| #include <string_view> |
| #include <type_traits> |
| #include <vector> |
| |
| namespace angle |
| { |
| namespace |
| { |
| |
| constexpr size_t kSpanDataSize = 16; |
| constexpr unsigned int kSpanData[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7, |
| 8, 9, 10, 11, 12, 13, 14, 15}; |
| constexpr std::array<const unsigned int, kSpanDataSize> kSpanArr = { |
| {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; |
| |
| class FakeRange |
| { |
| public: |
| size_t size() const { return kSpanDataSize; } |
| const unsigned int *data() { return kSpanData; } |
| }; |
| |
| // Test that comparing spans work |
| TEST(SpanTest, Comparison) |
| { |
| // Duplicate data to make sure comparison is being done on values (and not addresses). |
| static constexpr unsigned int kSpanDataDup[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7, |
| 8, 9, 10, 11, 12, 13, 14, 15}; |
| |
| // Don't use ASSERT_EQ at first because the == is more hidden |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_TRUE(Span<const unsigned int>() == Span(kSpanData, 0)); |
| ASSERT_TRUE(Span(kSpanData + 3, 4) != Span(kSpanDataDup + 5, 4)); |
| }) |
| |
| // Check ASSERT_EQ and ASSERT_NE work correctly |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_EQ(Span(kSpanData, kSpanDataSize), Span(kSpanDataDup, kSpanDataSize)); |
| ASSERT_NE(Span(kSpanData, kSpanDataSize - 1), Span(kSpanDataDup + 1, kSpanDataSize - 1)); |
| ASSERT_NE(Span(kSpanData, kSpanDataSize), Span(kSpanDataDup, kSpanDataSize - 1)); |
| ASSERT_NE(Span(kSpanData, kSpanDataSize - 1), Span(kSpanDataDup, kSpanDataSize)); |
| ASSERT_NE(Span(kSpanData, 0), Span(kSpanDataDup, 1)); |
| ASSERT_NE(Span(kSpanData, 1), Span(kSpanDataDup, 0)); |
| }) |
| } |
| |
| // Test indexing |
| TEST(SpanTest, Indexing) |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| |
| for (size_t i = 0; i < kSpanDataSize; ++i) |
| { |
| ASSERT_EQ(sp[i], i); |
| } |
| |
| unsigned int storage[kSpanDataSize] = {}; |
| auto writableSpan = ANGLE_UNSAFE_TODO(angle::Span<unsigned int>(storage, kSpanDataSize)); |
| |
| for (size_t i = 0; i < kSpanDataSize; ++i) |
| { |
| writableSpan[i] = i; |
| } |
| for (size_t i = 0; i < kSpanDataSize; ++i) |
| { |
| ASSERT_EQ(writableSpan[i], i); |
| } |
| for (size_t i = 0; i < kSpanDataSize; ++i) |
| { |
| ANGLE_UNSAFE_TODO(ASSERT_EQ(storage[i], i)); |
| } |
| } |
| |
| // Test for the various constructors |
| TEST(SpanTest, Constructors) |
| { |
| // Default constructor |
| { |
| Span<const unsigned int> sp; |
| static_assert(std::is_same_v<decltype(sp), Span<const unsigned int, dynamic_extent>>); |
| ASSERT_TRUE(sp.size() == 0); |
| ASSERT_TRUE(sp.empty()); |
| } |
| |
| // Constexpr construct from pointer and size |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| static_assert(std::is_same_v<decltype(sp), const Span<const unsigned int, dynamic_extent>>); |
| ASSERT_EQ(sp.data(), kSpanData); |
| ASSERT_EQ(sp.size(), kSpanDataSize); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Constexpr construct from fixed C-style array |
| { |
| constexpr Span sp(kSpanData); |
| static_assert(std::is_same_v<decltype(sp), const Span<const unsigned int, kSpanDataSize>>); |
| ASSERT_EQ(sp.data(), kSpanData); |
| ASSERT_EQ(sp.size(), kSpanDataSize); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Constexpr construct from constexpr std::array<const> |
| { |
| constexpr Span sp(kSpanArr); |
| static_assert(std::is_same_v<decltype(sp), const Span<const unsigned int, kSpanDataSize>>); |
| ASSERT_EQ(sp.data(), kSpanArr.data()); |
| ASSERT_EQ(sp.size(), kSpanArr.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from const std::array<non-const>. |
| { |
| const std::array<int, 2> kArr = {1, 2}; |
| Span sp(kArr); |
| static_assert(std::is_same_v<decltype(sp), Span<const int, 2u>>); |
| ASSERT_EQ(sp.data(), kArr.data()); |
| ASSERT_EQ(sp.size(), kArr.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from std::array<const>. |
| { |
| std::array<const int, 2> arr = {1, 2}; |
| Span sp(arr); |
| static_assert(std::is_same_v<decltype(sp), Span<const int, 2u>>); |
| ASSERT_EQ(sp.data(), arr.data()); |
| ASSERT_EQ(sp.size(), arr.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from std::array<non-const>. |
| { |
| std::array<int, 2> arr = {1, 2}; |
| Span sp(arr); |
| static_assert(std::is_same_v<decltype(sp), Span<int, 2u>>); |
| ASSERT_EQ(sp.data(), arr.data()); |
| ASSERT_EQ(sp.size(), arr.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from std::vector |
| { |
| std::vector<unsigned int> vec({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); |
| Span sp(vec); |
| static_assert(std::is_same_v<decltype(sp), Span<unsigned int, dynamic_extent>>); |
| ASSERT_EQ(sp.data(), vec.data()); |
| ASSERT_EQ(sp.size(), vec.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from const std::vector |
| { |
| const std::vector<unsigned int> vec({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); |
| Span sp(vec); |
| static_assert(std::is_same_v<decltype(sp), Span<const unsigned int, dynamic_extent>>); |
| ASSERT_EQ(sp.data(), vec.data()); |
| ASSERT_EQ(sp.size(), vec.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from std::string |
| { |
| std::string str = "hooray"; |
| Span sp(str); |
| static_assert(std::is_same_v<decltype(sp), Span<char, dynamic_extent>>); |
| ASSERT_EQ(sp.data(), str.data()); |
| ASSERT_EQ(sp.size(), str.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from const std::string |
| { |
| const std::string str = "hooray"; |
| Span sp(str); |
| static_assert(std::is_same_v<decltype(sp), Span<const char, dynamic_extent>>); |
| ASSERT_EQ(sp.data(), str.data()); |
| ASSERT_EQ(sp.size(), str.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construct from std::string_view |
| { |
| std::string_view view = "hooray"; |
| Span sp(view); |
| static_assert(std::is_same_v<decltype(sp), Span<const char, dynamic_extent>>); |
| ASSERT_EQ(sp.data(), view.data()); |
| ASSERT_EQ(sp.size(), view.size()); |
| ASSERT_FALSE(sp.empty()); |
| } |
| |
| // Construction from any class that provides data() and size(). |
| { |
| FakeRange range; |
| Span sp(range); |
| ASSERT_EQ(sp.data(), kSpanData); |
| ASSERT_EQ(sp.size(), kSpanDataSize); |
| } |
| |
| // Copy constructor and copy assignment |
| { |
| Span sp(kSpanData); |
| Span sp2(sp); |
| Span<const unsigned int> sp3; |
| |
| ASSERT_EQ(sp, sp2); |
| ASSERT_EQ(sp2.data(), kSpanData); |
| ASSERT_EQ(sp2.size(), kSpanDataSize); |
| ASSERT_FALSE(sp2.empty()); |
| |
| sp3 = sp; |
| |
| ASSERT_EQ(sp3, sp); |
| ASSERT_EQ(sp3.data(), kSpanData); |
| ASSERT_EQ(sp3.size(), kSpanDataSize); |
| ASSERT_FALSE(sp3.empty()); |
| } |
| } |
| |
| // Test accessing the data directly |
| TEST(SpanTest, DataAccess) |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| const unsigned int *data = sp.data(); |
| |
| for (size_t i = 0; i < kSpanDataSize; ++i) |
| { |
| ANGLE_UNSAFE_TODO(ASSERT_EQ(data[i], i)); |
| } |
| } |
| |
| // Test front and back |
| TEST(SpanTest, FrontAndBack) |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| ASSERT_TRUE(sp.front() == 0); |
| ASSERT_EQ(sp.back(), kSpanDataSize - 1); |
| } |
| |
| // Test begin and end |
| TEST(SpanTest, BeginAndEnd) |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| |
| size_t currentIndex = 0; |
| for (unsigned int value : sp) |
| { |
| ASSERT_EQ(value, currentIndex); |
| ++currentIndex; |
| } |
| } |
| |
| // Test reverse begin and end |
| TEST(SpanTest, RbeginAndRend) |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| |
| size_t currentIndex = 0; |
| for (auto iter = sp.rbegin(); iter != sp.rend(); ++iter) |
| { |
| ASSERT_EQ(*iter, kSpanDataSize - 1 - currentIndex); |
| ++currentIndex; |
| } |
| } |
| |
| // Test first and last |
| TEST(SpanTest, FirstAndLast) |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| constexpr size_t kSplitSize = kSpanDataSize / 4; |
| { |
| constexpr Span first = sp.first(kSplitSize); |
| constexpr Span last = sp.last(kSplitSize); |
| |
| static_assert( |
| std::is_same_v<decltype(first), const Span<const unsigned int, dynamic_extent>>); |
| ANGLE_UNSAFE_TODO(ASSERT_EQ(first, Span(kSpanData, kSplitSize))); |
| ASSERT_EQ(first.data(), kSpanData); |
| ASSERT_EQ(first.size(), kSplitSize); |
| |
| static_assert( |
| std::is_same_v<decltype(last), const Span<const unsigned int, dynamic_extent>>); |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_EQ(last, Span(kSpanData + kSpanDataSize - kSplitSize, kSplitSize)); |
| ASSERT_EQ(last.data(), kSpanData + kSpanDataSize - kSplitSize); |
| }) |
| ASSERT_EQ(last.size(), kSplitSize); |
| } |
| |
| { |
| constexpr Span first = sp.first<kSplitSize>(); |
| constexpr Span last = sp.last<kSplitSize>(); |
| |
| static_assert(std::is_same_v<decltype(first), const Span<const unsigned int, kSplitSize>>); |
| ANGLE_UNSAFE_TODO(ASSERT_EQ(first, Span(kSpanData, kSplitSize))); |
| ASSERT_EQ(first.data(), kSpanData); |
| ASSERT_EQ(first.size(), kSplitSize); |
| |
| static_assert(std::is_same_v<decltype(last), const Span<const unsigned int, kSplitSize>>); |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_EQ(last, Span(kSpanData + kSpanDataSize - kSplitSize, kSplitSize)); |
| ASSERT_EQ(last.data(), kSpanData + kSpanDataSize - kSplitSize); |
| }) |
| ASSERT_EQ(last.size(), kSplitSize); |
| } |
| } |
| |
| // Test subspan |
| TEST(SpanTest, Subspan) |
| { |
| constexpr auto sp = ANGLE_UNSAFE_TODO(Span(kSpanData, kSpanDataSize)); |
| constexpr size_t kSplitOffset = kSpanDataSize / 4; |
| constexpr size_t kSplitSize = kSpanDataSize / 2; |
| |
| // Test one-arg subspan |
| { |
| constexpr Span subspan = sp.subspan(kSplitOffset); |
| static_assert( |
| std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>); |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSpanDataSize - kSplitOffset)); |
| ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset); |
| }) |
| ASSERT_EQ(subspan.size(), kSpanDataSize - kSplitOffset); |
| } |
| |
| // Test two-arg subspan |
| { |
| constexpr Span subspan = sp.subspan(kSplitOffset, kSplitSize); |
| static_assert( |
| std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>); |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSplitSize)); |
| ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset); |
| }) |
| ASSERT_EQ(subspan.size(), kSplitSize); |
| } |
| |
| // Templated one-arg subspan just sugar until C++20 fixed spans. |
| { |
| constexpr Span subspan = sp.subspan<kSplitOffset>(); |
| static_assert( |
| std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>); |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSpanDataSize - kSplitOffset)); |
| ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset); |
| }) |
| ASSERT_EQ(subspan.size(), kSpanDataSize - kSplitOffset); |
| } |
| |
| // Templated two-arg subspan just sugar until C++20 fixed spans. |
| { |
| constexpr Span subspan = sp.subspan<kSplitOffset, kSplitSize>(); |
| static_assert( |
| std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>); |
| ANGLE_UNSAFE_TODO({ |
| ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSplitSize)); |
| ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset); |
| }) |
| ASSERT_EQ(subspan.size(), kSplitSize); |
| } |
| } |
| |
| // Test conversions from non-const to const spans. |
| TEST(SpanTest, ConstConversions) |
| { |
| const unsigned int kStorage[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7, |
| 8, 9, 10, 11, 12, 13, 14, 15}; |
| unsigned int storage[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; |
| auto readable_span = ANGLE_UNSAFE_TODO(angle::Span(kStorage, kSpanDataSize)); |
| auto writable_span = ANGLE_UNSAFE_TODO(angle::Span(storage, kSpanDataSize)); |
| |
| static_assert( |
| std::is_same_v<decltype(readable_span), Span<const unsigned int, dynamic_extent>>); |
| |
| static_assert(std::is_same_v<decltype(writable_span), Span<unsigned int, dynamic_extent>>); |
| |
| // Direct comparisons allowed. |
| EXPECT_TRUE(readable_span == writable_span); |
| EXPECT_FALSE(readable_span != writable_span); |
| |
| writable_span[0] = 1234; |
| EXPECT_FALSE(readable_span == writable_span); |
| |
| // Assignment allowed from non-const to const. |
| readable_span = writable_span; |
| EXPECT_TRUE(readable_span == writable_span); |
| } |
| |
| // Test conversions from fixed to dynamic spans. |
| TEST(SpanTest, FixedConversions) |
| { |
| unsigned int storage[kSpanDataSize] = {}; |
| std::vector<unsigned int> vec(kSpanDataSize, 42); |
| Span<unsigned int> dynamic_span(vec); |
| Span<unsigned int, kSpanDataSize> static_span(storage); |
| |
| // Direct comparisons allowed. |
| EXPECT_NE(static_span, dynamic_span); |
| |
| // Assignment allowed from fixed to dynamic. |
| dynamic_span = static_span; |
| EXPECT_EQ(static_span, dynamic_span); |
| |
| // Other way around prohibited without fixed conversion. |
| dynamic_span[0] = 1234; |
| static_span = dynamic_span.first<kSpanDataSize>(); |
| EXPECT_EQ(static_span, dynamic_span); |
| } |
| |
| // Test non-member functions. |
| TEST(SpanTest, Helpers) |
| { |
| // Test as_bytes. |
| { |
| constexpr int kArray[] = {2, 3, 5, 7, 11, 13}; |
| auto bytes_span = as_bytes(Span(kArray)); |
| EXPECT_EQ(reinterpret_cast<const uint8_t *>(kArray), bytes_span.data()); |
| EXPECT_EQ(sizeof(kArray), bytes_span.size()); |
| EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes()); |
| } |
| |
| // Test as_writble_bytes. |
| { |
| std::vector<int> vec = {1, 1, 2, 3, 5, 8}; |
| Span<int> mutable_span(vec); |
| auto writable_bytes_span = as_writable_bytes(mutable_span); |
| static_assert(std::is_same_v<decltype(writable_bytes_span), Span<uint8_t>>); |
| EXPECT_EQ(reinterpret_cast<uint8_t *>(vec.data()), writable_bytes_span.data()); |
| EXPECT_EQ(sizeof(int) * vec.size(), writable_bytes_span.size()); |
| EXPECT_EQ(writable_bytes_span.size(), writable_bytes_span.size_bytes()); |
| } |
| |
| // Test as_chars. |
| { |
| constexpr int kArray[] = {2, 3, 5, 7, 11, 13}; |
| auto chars_span = as_chars(Span(kArray)); |
| EXPECT_EQ(reinterpret_cast<const char *>(kArray), chars_span.data()); |
| EXPECT_EQ(sizeof(kArray), chars_span.size()); |
| EXPECT_EQ(chars_span.size(), chars_span.size_bytes()); |
| } |
| |
| // Test as writable chars. |
| { |
| std::vector<int> vec = {1, 1, 2, 3, 5, 8}; |
| Span<int> mutable_span(vec); |
| auto writable_chars_span = as_writable_chars(mutable_span); |
| static_assert(std::is_same_v<decltype(writable_chars_span), Span<char>>); |
| EXPECT_EQ(reinterpret_cast<char *>(vec.data()), writable_chars_span.data()); |
| EXPECT_EQ(sizeof(int) * vec.size(), writable_chars_span.size()); |
| EXPECT_EQ(writable_chars_span.size(), writable_chars_span.size_bytes()); |
| } |
| |
| // Test span_from_ref. |
| { |
| int x = 123; |
| auto s = span_from_ref(x); |
| EXPECT_EQ(&x, s.data()); |
| EXPECT_EQ(1u, s.size()); |
| EXPECT_EQ(sizeof(int), s.size_bytes()); |
| EXPECT_EQ(123, s[0]); |
| } |
| |
| // Test byte_span_from_ref. |
| { |
| int x = 123; |
| auto b = byte_span_from_ref(x); |
| EXPECT_EQ(reinterpret_cast<uint8_t *>(&x), b.data()); |
| EXPECT_EQ(sizeof(int), b.size()); |
| } |
| |
| // Test as_byte_span. |
| { |
| const std::vector<int> kVec({2, 3, 5, 7, 11, 13}); |
| auto byte_span = as_byte_span(kVec); |
| static_assert(std::is_same_v<decltype(byte_span), Span<const uint8_t>>); |
| EXPECT_EQ(byte_span.data(), reinterpret_cast<const uint8_t *>(kVec.data())); |
| EXPECT_EQ(byte_span.size(), kVec.size() * sizeof(int)); |
| } |
| |
| // Test as_writable_byte_span |
| { |
| int kMutArray[] = {2, 3, 5, 7}; |
| auto byte_span = as_writable_byte_span(kMutArray); |
| EXPECT_EQ(byte_span.data(), reinterpret_cast<uint8_t *>(kMutArray)); |
| EXPECT_EQ(byte_span.size(), sizeof(kMutArray)); |
| } |
| |
| // Test copy_from. |
| { |
| unsigned int data1[4] = {1, 2, 3, 4}; |
| unsigned int data2[4] = {0, 0, 0, 0}; |
| |
| Span<unsigned int> sp1(data1); |
| Span<unsigned int> sp2(data2); |
| |
| sp2.copy_from(sp1); |
| for (size_t i = 0; i < 4; ++i) |
| { |
| ANGLE_UNSAFE_TODO(EXPECT_EQ(data2[i], data1[i])); |
| } |
| } |
| |
| // Test copy_from with container. |
| { |
| std::vector<unsigned int> vec = {10, 20, 30, 40}; |
| unsigned int data[4] = {0, 0, 0, 0}; |
| Span<unsigned int, 4> sp(data); |
| |
| sp.copy_from(vec); |
| EXPECT_EQ(data[0], 10u); |
| EXPECT_EQ(data[1], 20u); |
| EXPECT_EQ(data[2], 30u); |
| EXPECT_EQ(data[3], 40u); |
| } |
| |
| // Test copy_from with overlapping memory (forward move). |
| { |
| unsigned int data[4] = {1, 2, 3, 4}; |
| Span<unsigned int> sp(data); |
| |
| // Copy [1, 2, 3] to [2, 3, 4] -> Result should be [1, 1, 2, 3] |
| // This exercises the std::copy_backward path in the overlap logic. |
| sp.subspan(1).copy_from(sp.first(3)); |
| EXPECT_EQ(data[0], 1u); |
| EXPECT_EQ(data[1], 1u); |
| EXPECT_EQ(data[2], 2u); |
| EXPECT_EQ(data[3], 3u); |
| } |
| |
| // Test copy_from with overlapping memory (backward move). |
| { |
| unsigned int data[4] = {1, 2, 3, 4}; |
| Span<unsigned int> sp(data); |
| |
| // Copy [2, 3, 4] to [1, 2, 3] -> Result should be [2, 3, 4, 4] |
| // This exercises the std::copy path in the overlap logic. |
| sp.first(3).copy_from(sp.subspan(1)); |
| EXPECT_EQ(data[0], 2u); |
| EXPECT_EQ(data[1], 3u); |
| EXPECT_EQ(data[2], 4u); |
| EXPECT_EQ(data[3], 4u); |
| } |
| |
| // Verify Extent Safety (Compile-time) |
| { |
| using FixedSpan = Span<int, 4>; |
| using DynamicSpan = Span<int, dynamic_extent>; |
| |
| // Identical fixed extents are allowed. |
| static_assert(std::is_invocable_v<decltype(&FixedSpan::copy_from<int, 4, int *>), FixedSpan, |
| const FixedSpan &>); |
| // Const sources are allowed. |
| static_assert( |
| std::is_invocable_v<decltype(&FixedSpan::copy_from<const int, 4, const int *>), |
| FixedSpan, const Span<const int, 4> &>); |
| // Fixed from Dynamic is allowed (runtime CHECK handles mismatch). |
| static_assert( |
| std::is_invocable_v<decltype(&FixedSpan::copy_from<int, dynamic_extent, int *>), |
| FixedSpan, const DynamicSpan &>); |
| // Dynamic from Fixed is allowed. |
| static_assert(std::is_invocable_v<decltype(&DynamicSpan::copy_from<int, 4, int *>), |
| DynamicSpan, const FixedSpan &>); |
| // Containers are allowed. |
| static_assert(std::is_invocable_v<decltype(&FixedSpan::copy_from<std::vector<int>>), |
| FixedSpan, const std::vector<int> &>); |
| } |
| |
| // Test to_fixed_extent. |
| { |
| unsigned int data[4] = {1, 2, 3, 4}; |
| Span<unsigned int> sp(data); |
| |
| auto fixed_sp = sp.to_fixed_extent<4>(); |
| ASSERT_TRUE(fixed_sp.has_value()); |
| EXPECT_EQ(fixed_sp->size(), 4u); |
| EXPECT_EQ((*fixed_sp)[0], 1u); |
| |
| auto fixed_sp_wrong = sp.to_fixed_extent<5>(); |
| ASSERT_FALSE(fixed_sp_wrong.has_value()); |
| } |
| |
| // Test split_at. |
| { |
| unsigned int data[4] = {1, 2, 3, 4}; |
| Span<unsigned int> sp(data); |
| |
| { |
| auto [front, back] = sp.split_at<2>(); |
| static_assert(std::is_same_v<decltype(front), Span<unsigned int, 2>>); |
| EXPECT_EQ(front.size(), 2u); |
| EXPECT_EQ(back.size(), 2u); |
| EXPECT_EQ(front[0], 1u); |
| EXPECT_EQ(back[0], 3u); |
| } |
| |
| { |
| auto [front, back] = sp.split_at(1); |
| EXPECT_EQ(front.size(), 1u); |
| EXPECT_EQ(back.size(), 3u); |
| EXPECT_EQ(front[0], 1u); |
| EXPECT_EQ(back[0], 2u); |
| } |
| } |
| |
| // Test copy_from on a const Span handle. |
| { |
| unsigned int data1[4] = {1, 2, 3, 4}; |
| unsigned int data2[4] = {0, 0, 0, 0}; |
| |
| const Span<unsigned int> sp1(data1); |
| const Span<unsigned int> sp2(data2); |
| |
| sp2.copy_from(sp1); |
| |
| for (size_t i = 0; i < 4; ++i) |
| { |
| ANGLE_UNSAFE_TODO(EXPECT_EQ(data2[i], data1[i])); |
| } |
| } |
| } |
| |
| } // anonymous namespace |
| } // namespace angle |