[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