[tint][utils] Make it more likely for fuzzers to decode containers

Instead of relying on a 'stop' byte to parse correctly, also treat an EOF as a successful decode.
Should increase the likelihood of the fuzzer decoding out-of-band data from WGSL comments.

Bug: tint:2223
Change-Id: Ife4fcf47c16d82cf50ff70649a5e1ed0d1c3dd93
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/185362
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/utils/bytes/decoder.h b/src/tint/utils/bytes/decoder.h
index 606ce41..9f29bbf 100644
--- a/src/tint/utils/bytes/decoder.h
+++ b/src/tint/utils/bytes/decoder.h
@@ -133,7 +133,7 @@
     static Result<std::unordered_map<K, V>> Decode(Reader& reader) {
         std::unordered_map<K, V> out;
 
-        while (true) {
+        while (!reader.IsEOF()) {
             auto stop = bytes::Decode<bool>(reader);
             if (stop != Success) {
                 return stop.Failure();
@@ -165,7 +165,7 @@
     static Result<std::unordered_set<V>> Decode(Reader& reader) {
         std::unordered_set<V> out;
 
-        while (true) {
+        while (!reader.IsEOF()) {
             auto stop = bytes::Decode<bool>(reader);
             if (stop != Success) {
                 return stop.Failure();
@@ -193,7 +193,7 @@
     static Result<std::vector<V>> Decode(Reader& reader) {
         std::vector<V> out;
 
-        while (true) {
+        while (!reader.IsEOF()) {
             auto stop = bytes::Decode<bool>(reader);
             if (stop != Success) {
                 return stop.Failure();
diff --git a/src/tint/utils/bytes/decoder_test.cc b/src/tint/utils/bytes/decoder_test.cc
index d272b0d..125d7e6 100644
--- a/src/tint/utils/bytes/decoder_test.cc
+++ b/src/tint/utils/bytes/decoder_test.cc
@@ -34,6 +34,7 @@
 #include <utility>
 
 #include "gmock/gmock.h"
+#include "src/tint/utils/result/result.h"
 
 namespace tint {
 namespace {
@@ -145,14 +146,21 @@
                      0x00, 0x70, 0x08, 0x80,  //
                      0x01);
     auto reader = BufferReader{Slice{data}};
-    auto got = Decode<M>(reader);
-    EXPECT_THAT(got.Get(), testing::ContainerEq(M{
-                               std::pair<uint8_t, uint16_t>(0x10u, 0x2002u),
-                               std::pair<uint8_t, uint16_t>(0x30u, 0x4004u),
-                               std::pair<uint8_t, uint16_t>(0x50u, 0x6006u),
-                               std::pair<uint8_t, uint16_t>(0x70u, 0x8008u),
-                           }));
-    EXPECT_NE(Decode<M>(reader), Success);
+    {
+        auto got = Decode<M>(reader);
+        ASSERT_EQ(got, Success);
+        EXPECT_THAT(got.Get(), testing::ContainerEq(M{
+                                   std::pair<uint8_t, uint16_t>(0x10u, 0x2002u),
+                                   std::pair<uint8_t, uint16_t>(0x30u, 0x4004u),
+                                   std::pair<uint8_t, uint16_t>(0x50u, 0x6006u),
+                                   std::pair<uint8_t, uint16_t>(0x70u, 0x8008u),
+                               }));
+    }
+    {
+        auto got = Decode<M>(reader);
+        ASSERT_EQ(got, Success);
+        EXPECT_THAT(got.Get(), testing::IsEmpty());
+    }
 }
 
 TEST(BytesDecoderTest, UnorderedSet) {
@@ -163,14 +171,21 @@
                      0x00, 0x08, 0x80,  //
                      0x01);
     auto reader = BufferReader{Slice{data}};
-    auto got = Decode<S>(reader);
-    EXPECT_THAT(got.Get(), testing::ContainerEq(S{
-                               0x2002u,
-                               0x4004u,
-                               0x6006u,
-                               0x8008u,
-                           }));
-    EXPECT_NE(Decode<S>(reader), Success);
+    {
+        auto got = Decode<S>(reader);
+        ASSERT_EQ(got, Success);
+        EXPECT_THAT(got.Get(), testing::ContainerEq(S{
+                                   0x2002u,
+                                   0x4004u,
+                                   0x6006u,
+                                   0x8008u,
+                               }));
+    }
+    {
+        auto got = Decode<S>(reader);
+        ASSERT_EQ(got, Success);
+        EXPECT_THAT(got.Get(), testing::IsEmpty());
+    }
 }
 
 TEST(BytesDecoderTest, Vector) {
@@ -181,14 +196,21 @@
                      0x00, 0x70,  //
                      0x01);
     auto reader = BufferReader{Slice{data}};
-    auto got = Decode<M>(reader);
-    EXPECT_THAT(got.Get(), testing::ContainerEq(M{
-                               0x10u,
-                               0x30u,
-                               0x50u,
-                               0x70u,
-                           }));
-    EXPECT_NE(Decode<M>(reader), Success);
+    {
+        auto got = Decode<M>(reader);
+        ASSERT_EQ(got, Success);
+        EXPECT_THAT(got.Get(), testing::ContainerEq(M{
+                                   0x10u,
+                                   0x30u,
+                                   0x50u,
+                                   0x70u,
+                               }));
+    }
+    {
+        auto got = Decode<M>(reader);
+        ASSERT_EQ(got, Success);
+        EXPECT_THAT(got.Get(), testing::IsEmpty());
+    }
 }
 
 TEST(BytesDecoderTest, Optional) {
diff --git a/src/tint/utils/bytes/reader.cc b/src/tint/utils/bytes/reader.cc
index 9c28d1d..fc2dfc2 100644
--- a/src/tint/utils/bytes/reader.cc
+++ b/src/tint/utils/bytes/reader.cc
@@ -41,4 +41,8 @@
     return n;
 }
 
+bool BufferReader::IsEOF() const {
+    return bytes_remaining_ == 0;
+}
+
 }  // namespace tint::bytes
diff --git a/src/tint/utils/bytes/reader.h b/src/tint/utils/bytes/reader.h
index 59e1864..3b5f51a 100644
--- a/src/tint/utils/bytes/reader.h
+++ b/src/tint/utils/bytes/reader.h
@@ -54,6 +54,9 @@
     /// then the end of the stream has been reached.
     virtual size_t Read(std::byte* out, size_t count) = 0;
 
+    /// @returns true if the Reader has no more bytes to read.
+    virtual bool IsEOF() const = 0;
+
     /// 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 a failure is returned.
@@ -140,6 +143,9 @@
     /// @copydoc Reader::Read
     size_t Read(std::byte* out, size_t count) override;
 
+    /// @copydoc Reader::IsEOF
+    bool IsEOF() const override;
+
   private:
     /// The data to read from
     const std::byte* data_ = nullptr;
diff --git a/src/tint/utils/bytes/reader_test.cc b/src/tint/utils/bytes/reader_test.cc
index 04ad6af..2e90e88 100644
--- a/src/tint/utils/bytes/reader_test.cc
+++ b/src/tint/utils/bytes/reader_test.cc
@@ -39,47 +39,87 @@
 
 TEST(BufferReaderTest, IntegerBigEndian) {
     auto data = Data(0x10, 0x20, 0x30, 0x40);
-    auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kBig);
+
+    BufferReader u32_reader{Slice{data}};
+    EXPECT_FALSE(u32_reader.IsEOF());
+    auto u32 = u32_reader.Int<uint32_t>(Endianness::kBig);
     EXPECT_EQ(u32, 0x10203040u);
-    auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kBig);
+    EXPECT_TRUE(u32_reader.IsEOF());
+
+    BufferReader i32_reader{Slice{data}};
+    EXPECT_FALSE(i32_reader.IsEOF());
+    auto i32 = i32_reader.Int<int32_t>(Endianness::kBig);
     EXPECT_EQ(i32, 0x10203040);
+    EXPECT_TRUE(i32_reader.IsEOF());
 }
 
 TEST(BufferReaderTest, IntegerBigEndian_TooShort) {
     auto data = Data(0x10, 0x20);
-    auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kBig);
+
+    BufferReader u32_reader{Slice{data}};
+    EXPECT_FALSE(u32_reader.IsEOF());
+    auto u32 = u32_reader.Int<uint32_t>(Endianness::kBig);
     EXPECT_NE(u32, Success);
-    auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kBig);
+    EXPECT_TRUE(u32_reader.IsEOF());
+
+    BufferReader i32_reader{Slice{data}};
+    EXPECT_FALSE(i32_reader.IsEOF());
+    auto i32 = i32_reader.Int<int32_t>(Endianness::kBig);
     EXPECT_NE(i32, Success);
+    EXPECT_TRUE(i32_reader.IsEOF());
 }
 
 TEST(BufferReaderTest, IntegerLittleEndian) {
     auto data = Data(0x10, 0x20, 0x30, 0x40);
-    auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kLittle);
+
+    BufferReader u32_reader{Slice{data}};
+    EXPECT_FALSE(u32_reader.IsEOF());
+    auto u32 = u32_reader.Int<uint32_t>(Endianness::kLittle);
     EXPECT_EQ(u32, 0x40302010u);
-    auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kLittle);
+    EXPECT_TRUE(u32_reader.IsEOF());
+
+    BufferReader i32_reader{Slice{data}};
+    EXPECT_FALSE(i32_reader.IsEOF());
+    auto i32 = i32_reader.Int<int32_t>(Endianness::kLittle);
     EXPECT_EQ(i32, 0x40302010);
+    EXPECT_TRUE(i32_reader.IsEOF());
 }
 
 TEST(BufferReaderTest, IntegerLittleEndian_TooShort) {
     auto data = Data(0x30, 0x40);
-    auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kLittle);
+
+    BufferReader u32_reader{Slice{data}};
+    EXPECT_FALSE(u32_reader.IsEOF());
+    auto u32 = u32_reader.Int<uint32_t>(Endianness::kLittle);
     EXPECT_NE(u32, Success);
-    auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kLittle);
+    EXPECT_TRUE(u32_reader.IsEOF());
+
+    BufferReader i32_reader{Slice{data}};
+    EXPECT_FALSE(i32_reader.IsEOF());
+    auto i32 = i32_reader.Int<int32_t>(Endianness::kLittle);
     EXPECT_NE(i32, Success);
+    EXPECT_TRUE(i32_reader.IsEOF());
 }
 
 TEST(BufferReaderTest, Float) {
     auto data = Data(0x00, 0x00, 0x08, 0x41);
-    auto f32 = BufferReader{Slice{data}}.Float<float>();
+
+    BufferReader f32_reader{Slice{data}};
+    EXPECT_FALSE(f32_reader.IsEOF());
+    auto f32 = f32_reader.Float<float>();
     ASSERT_EQ(f32, Success);
     EXPECT_EQ(f32.Get(), 8.5f);
+    EXPECT_TRUE(f32_reader.IsEOF());
 }
 
 TEST(BufferReaderTest, Float_TooShort) {
     auto data = Data(0x08, 0x41);
-    auto f32 = BufferReader{Slice{data}}.Float<float>();
+
+    BufferReader f32_reader{Slice{data}};
+    EXPECT_FALSE(f32_reader.IsEOF());
+    auto f32 = f32_reader.Float<float>();
     EXPECT_NE(f32, Success);
+    EXPECT_TRUE(f32_reader.IsEOF());
 }
 
 }  // namespace