Adds length-prefixed read to BigEndianReader

A common task when parsing encoded structs from a network is to
read a serialization of an object with (possibly nested) length-prefixed
members.

//third_party/boringssl's Crypto ByteString bakes
support for this in by having a "length-prefixed read" API that
initially reads a big-endian length L from the buffer, then constructs
and returns a new reader on the L many bytes immediately following
the length, skipping the parent reader over this substring. However,
this API is C-style and, as a consequence, perhaps not suitable for
wide use in Chromium code.

This patch adds similar functionality to base::BigEndianReader
by adding ReadU8LengthPrefixed and ReadU16LengthPrefixed methods.

R=mark

Change-Id: Idaf0b04b414a3484099a861ef02501e5f8a83a47
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1860518
Commit-Queue: David Van Cleve <[email protected]>
Reviewed-by: Mark Mentovai <[email protected]>
Cr-Commit-Position: refs/heads/master@{#706074}
diff --git a/base/big_endian_unittest.cc b/base/big_endian_unittest.cc
index 7f8d9a5..07c208a 100644
--- a/base/big_endian_unittest.cc
+++ b/base/big_endian_unittest.cc
@@ -42,6 +42,64 @@
   EXPECT_EQ(expected.data(), piece.data());
 }
 
+TEST(BigEndianReaderTest, ReadsLengthPrefixedValues) {
+  {
+    char u8_prefixed_data[] = {8,   8,   9,    0xA,  0xB,  0xC,  0xD,
+                               0xE, 0xF, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E};
+    BigEndianReader reader(u8_prefixed_data, sizeof(u8_prefixed_data));
+
+    base::StringPiece piece;
+    ASSERT_TRUE(reader.ReadU8LengthPrefixed(&piece));
+    // |reader| should skip both a u8 and the length-8 length-prefixed field.
+    EXPECT_EQ(reader.ptr(), u8_prefixed_data + 9);
+    EXPECT_EQ(piece.size(), 8u);
+    EXPECT_EQ(piece.data(), u8_prefixed_data + 1);
+  }
+
+  {
+    char u16_prefixed_data[] = {0,    8,    0xD,  0xE,  0xF,
+                                0x1A, 0x2B, 0x3C, 0x4D, 0x5E};
+    BigEndianReader reader(u16_prefixed_data, sizeof(u16_prefixed_data));
+    base::StringPiece piece;
+    ASSERT_TRUE(reader.ReadU16LengthPrefixed(&piece));
+    // |reader| should skip both a u16 and the length-8 length-prefixed field.
+    EXPECT_EQ(reader.ptr(), u16_prefixed_data + 10);
+    EXPECT_EQ(piece.size(), 8u);
+    EXPECT_EQ(piece.data(), u16_prefixed_data + 2);
+
+    // With no data left, we shouldn't be able to
+    // read another u8 length prefix (or a u16 length prefix,
+    // for that matter).
+    EXPECT_FALSE(reader.ReadU8LengthPrefixed(&piece));
+    EXPECT_FALSE(reader.ReadU16LengthPrefixed(&piece));
+  }
+
+  {
+    // Make sure there's no issue reading a zero-value length prefix.
+    char u16_prefixed_data[3] = {};
+    BigEndianReader reader(u16_prefixed_data, sizeof(u16_prefixed_data));
+    base::StringPiece piece;
+    ASSERT_TRUE(reader.ReadU16LengthPrefixed(&piece));
+    EXPECT_EQ(reader.ptr(), u16_prefixed_data + 2);
+    EXPECT_EQ(piece.data(), u16_prefixed_data + 2);
+    EXPECT_EQ(piece.size(), 0u);
+  }
+}
+
+TEST(BigEndianReaderTest, LengthPrefixedReadsFailGracefully) {
+  // We can't read 0xF (or, for that matter, 0xF8) bytes after the length
+  // prefix: there isn't enough data.
+  char data[] = {0xF, 8,   9,    0xA,  0xB,  0xC,  0xD,
+                 0xE, 0xF, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E};
+  BigEndianReader reader(data, sizeof(data));
+  base::StringPiece piece;
+  EXPECT_FALSE(reader.ReadU8LengthPrefixed(&piece));
+  EXPECT_EQ(data, reader.ptr());
+
+  EXPECT_FALSE(reader.ReadU16LengthPrefixed(&piece));
+  EXPECT_EQ(data, reader.ptr());
+}
+
 TEST(BigEndianReaderTest, RespectsLength) {
   char data[8];
   char buf[2];