[utils] Add [Byte]SpanFromRef and SpanAs[Writable]Bytes Also adds tests, no_compile tests and fix the TODOs to use the helpers in WireMemoryTransferServiceTests.cpp Fixed: 526549345 Change-Id: I49438226e8fc135b6df59ea79692a3e069ca406a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/319855 Reviewed-by: Loko Kung <lokokung@google.com> Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp b/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp index 4a87b35..99b315a 100644 --- a/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp +++ b/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
@@ -130,9 +130,7 @@ } std::span<std::byte> GetSpanToClientBufferContent() { - // TODO(https://crbug.com/526549345) Use byte_span_from_ref. - return DAWN_UNSAFE_TODO( - {reinterpret_cast<std::byte*>(&mClientBufferContent), sizeof(mClientBufferContent)}); + return {ByteSpanFromRef(mClientBufferContent)}; } std::tuple<WGPUBuffer, wgpu::Buffer, MockClientMemoryHandle*, MockServerMemoryHandle*> @@ -245,9 +243,7 @@ void ExpectServerDeserializeData(bool success, MockServerMemoryHandle* serverHandle) { DAWN_ASSERT(serverHandle != nullptr); - // TODO(https://crbug.com/526549345) use byte_span_from_ref - std::span<std::byte> DAWN_UNSAFE_TODO(target( - reinterpret_cast<std::byte*>(&mServerBufferContent), sizeof(mServerBufferContent))); + std::span<std::byte> target{ByteSpanFromRef(mServerBufferContent)}; EXPECT_CALL( *serverHandle, DeserializeDataUpdate(
diff --git a/src/utils/span.h b/src/utils/span.h index 682d77f..91b0cf7 100644 --- a/src/utils/span.h +++ b/src/utils/span.h
@@ -303,6 +303,52 @@ } // namespace detail +// Converts a `Span<[const] T>` to a `Span<[const] std::byte>`. +// Mirrors Chromium's base::as_[writable_]bytes but with std::byte. +// TODO(https://crbug.com/524405497): Support `Span<T, N> to `Span<std::byte, ...>` once fixed +// extent spans are supported. +template <typename T, typename Index, typename PtrType> +constexpr auto SpanAsBytes(detail::SpanBase<T, Index, PtrType> s) { + // SAFETY: `s.data()` points to at least `s.size_bytes` bytes of data. + return DAWN_UNSAFE_BUFFERS( + Span<const std::byte>{reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()}); +} +template <typename T, typename Index, typename PtrType> + requires(!std::is_const_v<T>) +constexpr auto SpanAsWritableBytes(detail::SpanBase<T, Index, PtrType> s) { + // SAFETY: `s.data()` points to at least `s.size_bytes` bytes of data. + return DAWN_UNSAFE_BUFFERS( + Span<std::byte>{reinterpret_cast<std::byte*>(s.data()), s.size_bytes()}); +} + +// Converts a `[const] T&` to a `Span<[const] T>`. +// Mirrors Chromium's base::span_from_ref. +// TODO(https://crbug.com/524405497): Make it return Span<T, 1> once fixed extent spans are +// supported. +template <typename T> +constexpr Span<const T> SpanFromRef(const T& t) { + // SAFETY: A reference always points at a valid allocation of one element. + return DAWN_UNSAFE_BUFFERS({std::addressof(t), 1u}); +} +template <typename T> +constexpr Span<T> SpanFromRef(T& t) { + // SAFETY: A reference always points at a valid allocation of one element. + return DAWN_UNSAFE_BUFFERS({std::addressof(t), 1u}); +} + +// Converts a `[const] T&` to a `Span<[const] std::byte>`. +// Mirrors Chromium's base::byte_span_from_ref but with std::byte. +// TODO(https://crbug.com/524405497): Make it return Span<T, sizeof(T)> once fixed extent spans are +// supported. +template <typename T> +constexpr Span<const std::byte> ByteSpanFromRef(const T& t) { + return SpanAsBytes(SpanFromRef(t)); +} +template <typename T> +constexpr Span<std::byte> ByteSpanFromRef(T& t) { + return SpanAsWritableBytes(SpanFromRef(t)); +} + } // namespace dawn #endif // SRC_UTILS_SPAN_H_
diff --git a/src/utils/span_nocompile.nc b/src/utils/span_nocompile.nc index 34f3160..cbf555a 100644 --- a/src/utils/span_nocompile.nc +++ b/src/utils/span_nocompile.nc
@@ -116,4 +116,12 @@ (void) sp.subspan(2, 2); // expected-error {{no matching member function for call to}} } +void TestAsWriteableBytesRequiresNonConst() { + auto sp = Span<const int>{FakeRange()}; + + SpanAsBytes(sp); // Control case + SpanAsWritableBytes(sp); // expected-error {{no matching function for call}} + +} + } // namespace dawn
diff --git a/src/utils/span_tests.cc b/src/utils/span_tests.cc index 3e234fc..39226c7 100644 --- a/src/utils/span_tests.cc +++ b/src/utils/span_tests.cc
@@ -602,5 +602,82 @@ EXPECT_DEATH_IF_SUPPORTED(sp8.subspan(kOne, std::numeric_limits<Index8>::max()), ""); } +TEST(SpanTest, SpanAsBytes) { + // Empty spans. + { + Span<int> sp; + + auto bsp = SpanAsBytes(sp); + static_assert(std::is_same_v<Span<const std::byte>, decltype(bsp)>); + EXPECT_TRUE(bsp.empty()); + + auto wbsp = SpanAsWritableBytes(sp); + static_assert(std::is_same_v<Span<std::byte>, decltype(wbsp)>); + EXPECT_TRUE(wbsp.empty()); + } + + // Non-empty span. + { + std::array<int, 3> ints{}; + Span<int> sp{ints}; + + auto bsp = SpanAsBytes(sp); + static_assert(std::is_same_v<Span<const std::byte>, decltype(bsp)>); + EXPECT_EQ(bsp.size(), sp.size_bytes()); + EXPECT_EQ(bsp.data(), reinterpret_cast<const std::byte*>(sp.data())); + + auto wbsp = SpanAsWritableBytes(sp); + static_assert(std::is_same_v<Span<std::byte>, decltype(wbsp)>); + EXPECT_EQ(wbsp.size(), sp.size_bytes()); + EXPECT_EQ(wbsp.data(), reinterpret_cast<std::byte*>(sp.data())); + } + + // Span with an index. + { + std::array<int, 3> ints{}; + // SAFETY: This is viewing ints, just with typed indices. + ityp::span<Index, int> DAWN_UNSAFE_BUFFERS(sp{ints.data(), Index(3u)}); + + auto bsp = SpanAsBytes(sp); + static_assert(std::is_same_v<Span<const std::byte>, decltype(bsp)>); + EXPECT_EQ(bsp.size(), sp.size_bytes()); + EXPECT_EQ(bsp.data(), reinterpret_cast<const std::byte*>(sp.data())); + + auto wbsp = SpanAsWritableBytes(sp); + static_assert(std::is_same_v<Span<std::byte>, decltype(wbsp)>); + EXPECT_EQ(wbsp.size(), sp.size_bytes()); + EXPECT_EQ(wbsp.data(), reinterpret_cast<std::byte*>(sp.data())); + } +} + +TEST(SpanTest, SpanFromRef) { + { + uint32_t i = 0; + + auto sp = SpanFromRef(i); + static_assert(std::is_same_v<Span<uint32_t>, decltype(sp)>); + EXPECT_EQ(sp.size(), 1u); + EXPECT_EQ(sp.data(), &i); + + auto bsp = ByteSpanFromRef(i); + static_assert(std::is_same_v<Span<std::byte>, decltype(bsp)>); + EXPECT_EQ(bsp.size(), sizeof(uint32_t)); + EXPECT_EQ(bsp.data(), reinterpret_cast<std::byte*>(&i)); + } + { + const uint32_t i = 0; + + auto sp = SpanFromRef(i); + static_assert(std::is_same_v<Span<const uint32_t>, decltype(sp)>); + EXPECT_EQ(sp.size(), 1u); + EXPECT_EQ(sp.data(), &i); + + auto bsp = ByteSpanFromRef(i); + static_assert(std::is_same_v<Span<const std::byte>, decltype(bsp)>); + EXPECT_EQ(bsp.size(), sizeof(uint32_t)); + EXPECT_EQ(bsp.data(), reinterpret_cast<const std::byte*>(&i)); + } +} + } // anonymous namespace } // namespace dawn