[tint][utils] Add byte reader utility

Change-Id: I0c6e1f14212071984147ab50edc26095431e16d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/162201
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/cmd/test/BUILD.bazel b/src/tint/cmd/test/BUILD.bazel
index c883c12..6c3f01e 100644
--- a/src/tint/cmd/test/BUILD.bazel
+++ b/src/tint/cmd/test/BUILD.bazel
@@ -60,6 +60,7 @@
     "//src/tint/lang/wgsl/writer/ir_to_program:test",
     "//src/tint/lang/wgsl/writer/raise:test",
     "//src/tint/lang/wgsl:test",
+    "//src/tint/utils/bytes:test",
     "//src/tint/utils/cli:test",
     "//src/tint/utils/command:test",
     "//src/tint/utils/containers:test",
diff --git a/src/tint/cmd/test/BUILD.cmake b/src/tint/cmd/test/BUILD.cmake
index f994d98..8dac742 100644
--- a/src/tint/cmd/test/BUILD.cmake
+++ b/src/tint/cmd/test/BUILD.cmake
@@ -61,6 +61,7 @@
   tint_lang_wgsl_writer_ir_to_program_test
   tint_lang_wgsl_writer_raise_test
   tint_lang_wgsl_test
+  tint_utils_bytes_test
   tint_utils_cli_test
   tint_utils_command_test
   tint_utils_containers_test
diff --git a/src/tint/cmd/test/BUILD.gn b/src/tint/cmd/test/BUILD.gn
index 774b68a..9a45716 100644
--- a/src/tint/cmd/test/BUILD.gn
+++ b/src/tint/cmd/test/BUILD.gn
@@ -66,6 +66,7 @@
       "${tint_src_dir}/lang/wgsl/sem:unittests",
       "${tint_src_dir}/lang/wgsl/writer/ir_to_program:unittests",
       "${tint_src_dir}/lang/wgsl/writer/raise:unittests",
+      "${tint_src_dir}/utils/bytes:unittests",
       "${tint_src_dir}/utils/cli:unittests",
       "${tint_src_dir}/utils/command:unittests",
       "${tint_src_dir}/utils/containers:unittests",
diff --git a/src/tint/utils/BUILD.cmake b/src/tint/utils/BUILD.cmake
index 659c2a2..badeb73 100644
--- a/src/tint/utils/BUILD.cmake
+++ b/src/tint/utils/BUILD.cmake
@@ -34,6 +34,7 @@
 #                       Do not modify this file directly
 ################################################################################
 
+include(utils/bytes/BUILD.cmake)
 include(utils/cli/BUILD.cmake)
 include(utils/command/BUILD.cmake)
 include(utils/containers/BUILD.cmake)
diff --git a/src/tint/utils/bytes/BUILD.bazel b/src/tint/utils/bytes/BUILD.bazel
new file mode 100644
index 0000000..5fcc9cf
--- /dev/null
+++ b/src/tint/utils/bytes/BUILD.bazel
@@ -0,0 +1,84 @@
+# Copyright 2023 The Dawn & Tint Authors
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+#   tools/src/cmd/gen/build/BUILD.bazel.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+#                       Do not modify this file directly
+################################################################################
+
+load("//src/tint:flags.bzl", "COPTS")
+load("@bazel_skylib//lib:selects.bzl", "selects")
+cc_library(
+  name = "bytes",
+  srcs = [
+    "bytes.cc",
+  ],
+  hdrs = [
+    "endianness.h",
+    "reader.h",
+    "swap.h",
+  ],
+  deps = [
+    "//src/tint/utils/containers",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/reflection",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/traits",
+  ],
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
+cc_library(
+  name = "test",
+  alwayslink = True,
+  srcs = [
+    "reader_test.cc",
+    "swap_test.cc",
+  ],
+  deps = [
+    "//src/tint/utils/bytes",
+    "//src/tint/utils/containers",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/reflection",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/traits",
+    "@gtest",
+  ],
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
+
diff --git a/src/tint/utils/bytes/BUILD.cmake b/src/tint/utils/bytes/BUILD.cmake
new file mode 100644
index 0000000..9322ea7
--- /dev/null
+++ b/src/tint/utils/bytes/BUILD.cmake
@@ -0,0 +1,82 @@
+# Copyright 2023 The Dawn & Tint Authors
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+#   tools/src/cmd/gen/build/BUILD.cmake.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+#                       Do not modify this file directly
+################################################################################
+
+################################################################################
+# Target:    tint_utils_bytes
+# Kind:      lib
+################################################################################
+tint_add_target(tint_utils_bytes lib
+  utils/bytes/bytes.cc
+  utils/bytes/endianness.h
+  utils/bytes/reader.h
+  utils/bytes/swap.h
+)
+
+tint_target_add_dependencies(tint_utils_bytes lib
+  tint_utils_containers
+  tint_utils_ice
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_reflection
+  tint_utils_rtti
+  tint_utils_traits
+)
+
+################################################################################
+# Target:    tint_utils_bytes_test
+# Kind:      test
+################################################################################
+tint_add_target(tint_utils_bytes_test test
+  utils/bytes/reader_test.cc
+  utils/bytes/swap_test.cc
+)
+
+tint_target_add_dependencies(tint_utils_bytes_test test
+  tint_utils_bytes
+  tint_utils_containers
+  tint_utils_ice
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_reflection
+  tint_utils_rtti
+  tint_utils_traits
+)
+
+tint_target_add_external_dependencies(tint_utils_bytes_test test
+  "gtest"
+)
diff --git a/src/tint/utils/bytes/BUILD.gn b/src/tint/utils/bytes/BUILD.gn
new file mode 100644
index 0000000..c6fb515
--- /dev/null
+++ b/src/tint/utils/bytes/BUILD.gn
@@ -0,0 +1,82 @@
+# Copyright 2023 The Dawn & Tint Authors
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+#   tools/src/cmd/gen/build/BUILD.gn.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+#                       Do not modify this file directly
+################################################################################
+
+import("../../../../scripts/tint_overrides_with_defaults.gni")
+
+import("${tint_src_dir}/tint.gni")
+
+if (tint_build_unittests || tint_build_benchmarks) {
+  import("//testing/test.gni")
+}
+
+libtint_source_set("bytes") {
+  sources = [
+    "bytes.cc",
+    "endianness.h",
+    "reader.h",
+    "swap.h",
+  ]
+  deps = [
+    "${tint_src_dir}/utils/containers",
+    "${tint_src_dir}/utils/ice",
+    "${tint_src_dir}/utils/macros",
+    "${tint_src_dir}/utils/math",
+    "${tint_src_dir}/utils/memory",
+    "${tint_src_dir}/utils/reflection",
+    "${tint_src_dir}/utils/rtti",
+    "${tint_src_dir}/utils/traits",
+  ]
+}
+if (tint_build_unittests) {
+  tint_unittests_source_set("unittests") {
+    sources = [
+      "reader_test.cc",
+      "swap_test.cc",
+    ]
+    deps = [
+      "${tint_src_dir}:gmock_and_gtest",
+      "${tint_src_dir}/utils/bytes",
+      "${tint_src_dir}/utils/containers",
+      "${tint_src_dir}/utils/ice",
+      "${tint_src_dir}/utils/macros",
+      "${tint_src_dir}/utils/math",
+      "${tint_src_dir}/utils/memory",
+      "${tint_src_dir}/utils/reflection",
+      "${tint_src_dir}/utils/rtti",
+      "${tint_src_dir}/utils/traits",
+    ]
+  }
+}
diff --git a/src/tint/utils/bytes/bytes.cc b/src/tint/utils/bytes/bytes.cc
new file mode 100644
index 0000000..392f0ea
--- /dev/null
+++ b/src/tint/utils/bytes/bytes.cc
@@ -0,0 +1,33 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
+#endif
+
+// A placeholder symbol used to emit a symbol for this lib target.
+int tint_utils_bytes_symbol = 1;
diff --git a/src/tint/utils/bytes/endianness.h b/src/tint/utils/bytes/endianness.h
new file mode 100644
index 0000000..81db319
--- /dev/null
+++ b/src/tint/utils/bytes/endianness.h
@@ -0,0 +1,47 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_UTILS_BYTES_ENDIANNESS_H_
+#define SRC_TINT_UTILS_BYTES_ENDIANNESS_H_
+
+#include <cstdint>
+#include <cstring>
+
+namespace tint::bytes {
+
+enum class Endianness : uint8_t { kBig, kLittle };
+
+inline Endianness NativeEndianness() {
+    uint8_t u8[4];
+    uint32_t u32 = 0x01020304;
+    memcpy(u8, &u32, 4);
+    return u8[0] == 1 ? Endianness::kBig : Endianness::kLittle;
+}
+
+}  // namespace tint::bytes
+
+#endif  // SRC_TINT_UTILS_BYTES_ENDIANNESS_H_
diff --git a/src/tint/utils/bytes/reader.h b/src/tint/utils/bytes/reader.h
new file mode 100644
index 0000000..38bbf4e
--- /dev/null
+++ b/src/tint/utils/bytes/reader.h
@@ -0,0 +1,140 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_UTILS_BYTES_READER_H_
+#define SRC_TINT_UTILS_BYTES_READER_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+
+#include "src/tint/utils/bytes/endianness.h"
+#include "src/tint/utils/bytes/swap.h"
+#include "src/tint/utils/containers/slice.h"
+#include "src/tint/utils/reflection/reflection.h"
+
+namespace tint::bytes {
+
+/// A binary stream reader.
+struct Reader {
+    /// @returns true if there are no more bytes remaining
+    bool IsEOF() const { return offset >= bytes.len; }
+
+    /// @returns the number of bytes remaining in the stream
+    size_t BytesRemaining() const { return IsEOF() ? 0 : bytes.len - offset; }
+
+    /// Reads an integer from the stream, performing byte swapping if the stream's endianness
+    /// differs from the native endianness. If there are too few bytes remaining in the stream, then
+    /// the missing data will be substituted with zeros.
+    /// @return the deserialized integer
+    template <typename T>
+    T Int() {
+        static_assert(std::is_integral_v<T>);
+        T out = 0;
+        if (!IsEOF()) {
+            size_t n = std::min(sizeof(T), BytesRemaining());
+            memcpy(&out, &bytes[offset], n);
+            offset += n;
+            if (NativeEndianness() != endianness) {
+                out = Swap(out);
+            }
+        }
+        return out;
+    }
+
+    /// Reads a float from the stream. If there are too few bytes remaining in the stream, then
+    /// the missing data will be substituted with zeros.
+    /// @return the deserialized floating point number
+    template <typename T>
+    T Float() {
+        static_assert(std::is_floating_point_v<T>);
+        T out = 0;
+        if (!IsEOF()) {
+            size_t n = std::min(sizeof(T), BytesRemaining());
+            memcpy(&out, &bytes[offset], n);
+            offset += n;
+        }
+        return out;
+    }
+
+    /// Reads a boolean from the stream
+    /// @returns true if the next byte is non-zero
+    bool Bool() {
+        if (IsEOF()) {
+            return false;
+        }
+        return bytes[offset++] != std::byte{0};
+    }
+
+    /// Reads a string of @p len bytes from the stream. If there are too few bytes remaining in the
+    /// stream, then the returned string will be truncated.
+    /// @param len the length of the returned string in bytes
+    /// @return the deserialized string
+    std::string String(size_t len) {
+        if (IsEOF()) {
+            return "";
+        }
+        size_t n = std::min(len, BytesRemaining());
+        std::string out(reinterpret_cast<const char*>(&bytes[offset]), n);
+        offset += n;
+        return out;
+    }
+
+    /// The data to read from
+    Slice<const std::byte> bytes;
+
+    /// The current byte offset
+    size_t offset = 0;
+
+    /// The endianness of integers serialized in the stream
+    Endianness endianness = Endianness::kLittle;
+};
+
+/// Reads the templated type from the reader and assigns it to @p out
+/// @note This function does not
+template <typename T>
+Reader& operator>>(Reader& reader, T& out) {
+    constexpr bool is_numeric = std::is_integral_v<T> || std::is_floating_point_v<T>;
+    static_assert(is_numeric);
+
+    if constexpr (std::is_integral_v<T>) {
+        out = reader.Int<T>();
+        return reader;
+    }
+
+    if constexpr (std::is_floating_point_v<T>) {
+        out = reader.Float<T>();
+        return reader;
+    }
+
+    // Unreachable
+    return reader;
+}
+
+}  // namespace tint::bytes
+
+#endif  // SRC_TINT_UTILS_BYTES_READER_H_
diff --git a/src/tint/utils/bytes/reader_test.cc b/src/tint/utils/bytes/reader_test.cc
new file mode 100644
index 0000000..e8af39d
--- /dev/null
+++ b/src/tint/utils/bytes/reader_test.cc
@@ -0,0 +1,107 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/utils/bytes/reader.h"
+
+#include "gtest/gtest.h"
+
+namespace tint::bytes {
+namespace {
+
+template <typename... ARGS>
+auto Data(ARGS&&... args) {
+    return std::array{std::byte{static_cast<uint8_t>(args)}...};
+}
+
+TEST(BytesReaderTest, IntegerBigEndian) {
+    auto data = Data(0x10, 0x20, 0x30, 0x40);
+    auto u32 = Reader{Slice{data}, 0, Endianness::kBig}.Int<uint32_t>();
+    EXPECT_EQ(u32, 0x10203040u);
+    auto i32 = Reader{Slice{data}, 0, Endianness::kBig}.Int<int32_t>();
+    EXPECT_EQ(i32, 0x10203040);
+}
+
+TEST(BytesReaderTest, IntegerBigEndian_Offset) {
+    auto data = Data(0x10, 0x20, 0x30, 0x40, 0x50, 0x60);
+    auto u32 = Reader{Slice{data}, 2, Endianness::kBig}.Int<uint32_t>();
+    EXPECT_EQ(u32, 0x30405060u);
+    auto i32 = Reader{Slice{data}, 2, Endianness::kBig}.Int<int32_t>();
+    EXPECT_EQ(i32, 0x30405060);
+}
+
+TEST(BytesReaderTest, IntegerBigEndian_Clipped) {
+    auto data = Data(0x10, 0x20, 0x30, 0x40);
+    auto u32 = Reader{Slice{data}, 2, Endianness::kBig}.Int<uint32_t>();
+    EXPECT_EQ(u32, 0x30400000u);
+    auto i32 = Reader{Slice{data}, 2, Endianness::kBig}.Int<int32_t>();
+    EXPECT_EQ(i32, 0x30400000);
+}
+
+TEST(BytesReaderTest, IntegerLittleEndian) {
+    auto data = Data(0x10, 0x20, 0x30, 0x40);
+    auto u32 = Reader{Slice{data}, 0, Endianness::kLittle}.Int<uint32_t>();
+    EXPECT_EQ(u32, 0x40302010u);
+    auto i32 = Reader{Slice{data}, 0, Endianness::kLittle}.Int<int32_t>();
+    EXPECT_EQ(i32, 0x40302010);
+}
+
+TEST(BytesReaderTest, IntegerLittleEndian_Offset) {
+    auto data = Data(0x10, 0x20, 0x30, 0x40, 0x50, 0x60);
+    auto u32 = Reader{Slice{data}, 2, Endianness::kLittle}.Int<uint32_t>();
+    EXPECT_EQ(u32, 0x60504030u);
+    auto i32 = Reader{Slice{data}, 2, Endianness::kLittle}.Int<int32_t>();
+    EXPECT_EQ(i32, 0x60504030);
+}
+
+TEST(BytesReaderTest, IntegerLittleEndian_Clipped) {
+    auto data = Data(0x10, 0x20, 0x30, 0x40);
+    auto u32 = Reader{Slice{data}, 2, Endianness::kLittle}.Int<uint32_t>();
+    EXPECT_EQ(u32, 0x00004030u);
+    auto i32 = Reader{Slice{data}, 2, Endianness::kLittle}.Int<int32_t>();
+    EXPECT_EQ(i32, 0x00004030);
+}
+
+TEST(BytesReaderTest, Float) {
+    auto data = Data(0x00, 0x00, 0x08, 0x41);
+    float f32 = Reader{Slice{data}}.Float<float>();
+    EXPECT_EQ(f32, 8.5f);
+}
+
+TEST(BytesReaderTest, Float_Offset) {
+    auto data = Data(0x00, 0x00, 0x08, 0x41, 0x80, 0x3e);
+    float f32 = Reader{Slice{data}, 2}.Float<float>();
+    EXPECT_EQ(f32, 0.25049614f);
+}
+
+TEST(BytesReaderTest, Float_Clipped) {
+    auto data = Data(0x00, 0x00, 0x08, 0x41);
+    float f32 = Reader{Slice{data}, 2}.Float<float>();
+    EXPECT_EQ(f32, 2.3329e-41f);
+}
+
+}  // namespace
+}  // namespace tint::bytes
diff --git a/src/tint/utils/bytes/swap.h b/src/tint/utils/bytes/swap.h
new file mode 100644
index 0000000..da4c6e6
--- /dev/null
+++ b/src/tint/utils/bytes/swap.h
@@ -0,0 +1,55 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_UTILS_BYTES_SWAP_H_
+#define SRC_TINT_UTILS_BYTES_SWAP_H_
+
+#include <cstdint>
+#include <cstring>
+#include <type_traits>
+#include <utility>
+
+namespace tint::bytes {
+
+/// @return the input integer value with all bytes reversed
+/// @param value the input value
+template <typename T>
+[[nodiscard]] inline T Swap(T value) {
+    static_assert(std::is_integral_v<T>);
+    uint8_t bytes[sizeof(T)];
+    memcpy(bytes, &value, sizeof(T));
+    for (size_t i = 0; i < sizeof(T) / 2; i++) {
+        std::swap(bytes[i], bytes[sizeof(T) - i - 1]);
+    }
+    T out;
+    memcpy(&out, bytes, sizeof(T));
+    return out;
+}
+
+}  // namespace tint::bytes
+
+#endif  // SRC_TINT_UTILS_BYTES_SWAP_H_
diff --git a/src/tint/utils/bytes/swap_test.cc b/src/tint/utils/bytes/swap_test.cc
new file mode 100644
index 0000000..5d950c0
--- /dev/null
+++ b/src/tint/utils/bytes/swap_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/utils/bytes/swap.h"
+
+#include "gtest/gtest.h"
+
+namespace tint::bytes {
+namespace {
+
+TEST(BytesSwapTest, Uint) {
+    EXPECT_EQ(Swap<uint8_t>(0x41), static_cast<uint8_t>(0x41));
+    EXPECT_EQ(Swap<uint16_t>(0x4152), static_cast<uint16_t>(0x5241));
+    EXPECT_EQ(Swap<uint32_t>(0x41526374), static_cast<uint32_t>(0x74635241));
+    EXPECT_EQ(Swap<uint64_t>(0x415263748596A7B8), static_cast<uint64_t>(0xB8A7968574635241));
+}
+
+TEST(BytesSwapTest, Sint) {
+    EXPECT_EQ(Swap<int8_t>(0x41), static_cast<int8_t>(0x41));
+    EXPECT_EQ(Swap<int8_t>(-0x41), static_cast<int8_t>(-0x41));
+    EXPECT_EQ(Swap<int16_t>(0x4152), static_cast<int16_t>(0x5241));
+    EXPECT_EQ(Swap<int16_t>(-0x4152), static_cast<int16_t>(0xAEBE));
+    EXPECT_EQ(Swap<int32_t>(0x41526374), static_cast<int32_t>(0x74635241));
+    EXPECT_EQ(Swap<int32_t>(-0x41526374), static_cast<int32_t>(0x8C9CADBE));
+    EXPECT_EQ(Swap<int64_t>(0x415263748596A7B8), static_cast<int64_t>(0xB8A7968574635241));
+    EXPECT_EQ(Swap<int64_t>(-0x415263748596A7B8), static_cast<int64_t>(0x4858697A8B9CADBE));
+}
+
+}  // namespace
+}  // namespace tint::bytes
diff --git a/src/tint/utils/containers/slice.h b/src/tint/utils/containers/slice.h
index f2cf7c3..3c60913 100644
--- a/src/tint/utils/containers/slice.h
+++ b/src/tint/utils/containers/slice.h
@@ -28,6 +28,7 @@
 #ifndef SRC_TINT_UTILS_CONTAINERS_SLICE_H_
 #define SRC_TINT_UTILS_CONTAINERS_SLICE_H_
 
+#include <array>
 #include <cstdint>
 #include <iterator>
 
@@ -158,6 +159,12 @@
     constexpr Slice(T (&elements)[N])  // NOLINT
         : data(elements), len(N), cap(N) {}
 
+    /// Constructor
+    /// @param array std::array of elements
+    template <size_t N>
+    constexpr Slice(std::array<T, N>& array)  // NOLINT
+        : data(array.data()), len(N), cap(N) {}
+
     /// Reinterprets this slice as `const Slice<TO>&`
     /// @returns the reinterpreted slice
     /// @see CanReinterpretSlice
diff --git a/src/tint/utils/containers/slice_test.cc b/src/tint/utils/containers/slice_test.cc
index 2432aeb..01932fd 100644
--- a/src/tint/utils/containers/slice_test.cc
+++ b/src/tint/utils/containers/slice_test.cc
@@ -118,6 +118,16 @@
     EXPECT_FALSE(slice.IsEmpty());
 }
 
+TEST(TintSliceTest, CtorStdArray) {
+    std::array elements{1, 2, 3};
+
+    auto slice = Slice{elements};
+    EXPECT_EQ(slice.data, &elements[0]);
+    EXPECT_EQ(slice.len, 3u);
+    EXPECT_EQ(slice.cap, 3u);
+    EXPECT_FALSE(slice.IsEmpty());
+}
+
 TEST(TintSliceTest, Index) {
     int elements[] = {1, 2, 3};