encoding: treat TLV-TYPE zero as invalid

 * Use tlv::Invalid to indicate an invalid Block, instead of
   UINT32_MAX (which is a valid type), thus fixing bug #4726
 * Introduce Block::isValid as a replacement for Block::empty
   and soft-deprecate the latter
 * Improve test coverage of Block and tlv::readType

Refs: #4726, #4895
Change-Id: I1cd3336fcbfe83555f3111738da67041dfae64f3
diff --git a/tests/unit/encoding/block.t.cpp b/tests/unit/encoding/block.t.cpp
index 18126e8..f6b245e 100644
--- a/tests/unit/encoding/block.t.cpp
+++ b/tests/unit/encoding/block.t.cpp
@@ -25,6 +25,8 @@
 #include "tests/boost-test.hpp"
 
 #include <boost/lexical_cast.hpp>
+#include <boost/mpl/vector.hpp>
+
 #include <cstring>
 #include <sstream>
 
@@ -37,15 +39,27 @@
 BOOST_AUTO_TEST_SUITE(Construction)
 
 static const uint8_t TEST_BUFFER[] = {
-  0x00, 0x01, 0xfa, // ok
-  0x01, 0x01, 0xfb, // ok
-  0x03, 0x02, 0xff // bad: TLV-LENGTH is 2 but there's only 1-octet TLV-VALUE
+  0x42, 0x01, 0xfa,
+  0x01, 0x01, 0xfb,
+  0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, // bug #4726
 };
 
-BOOST_AUTO_TEST_CASE(Empty)
+BOOST_AUTO_TEST_CASE(Default)
 {
   Block b;
+
+  BOOST_CHECK_EQUAL(b.isValid(), false);
   BOOST_CHECK_EQUAL(b.empty(), true);
+  BOOST_CHECK_EQUAL(b.type(), tlv::Invalid);
+  BOOST_CHECK_EQUAL(b.hasValue(), false);
+  BOOST_CHECK_EQUAL(b.value_size(), 0);
+  BOOST_CHECK(b.value() == nullptr);
+
+  BOOST_CHECK_THROW(b.size(), Block::Error);
+  BOOST_CHECK_THROW(b.begin(), Block::Error);
+  BOOST_CHECK_THROW(b.end(), Block::Error);
+  BOOST_CHECK_THROW(b.wire(), Block::Error);
+  BOOST_CHECK_THROW(b.blockFromValue(), Block::Error);
 }
 
 BOOST_AUTO_TEST_CASE(FromEncodingBuffer)
@@ -134,18 +148,25 @@
 BOOST_AUTO_TEST_CASE(FromType)
 {
   Block b1(4);
-  BOOST_CHECK_EQUAL(b1.empty(), false);
+  BOOST_CHECK_EQUAL(b1.isValid(), true);
   BOOST_CHECK_EQUAL(b1.type(), 4);
   BOOST_CHECK_EQUAL(b1.size(), 2); // 1-octet TLV-TYPE and 1-octet TLV-LENGTH
   BOOST_CHECK_EQUAL(b1.hasValue(), false);
   BOOST_CHECK_EQUAL(b1.value_size(), 0);
 
   Block b2(258);
-  BOOST_CHECK_EQUAL(b2.empty(), false);
+  BOOST_CHECK_EQUAL(b2.isValid(), true);
   BOOST_CHECK_EQUAL(b2.type(), 258);
   BOOST_CHECK_EQUAL(b2.size(), 4); // 3-octet TLV-TYPE and 1-octet TLV-LENGTH
   BOOST_CHECK_EQUAL(b2.hasValue(), false);
   BOOST_CHECK_EQUAL(b2.value_size(), 0);
+
+  Block b3(tlv::Invalid);
+  BOOST_CHECK_EQUAL(b3.isValid(), false);
+  BOOST_CHECK_EQUAL(b3.type(), tlv::Invalid);
+  BOOST_CHECK_EQUAL(b3.hasValue(), false);
+  BOOST_CHECK_EQUAL(b3.value_size(), 0);
+  BOOST_CHECK(b3.value() == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(FromTypeAndBuffer)
@@ -154,7 +175,7 @@
   auto bufferPtr = make_shared<Buffer>(VALUE, sizeof(VALUE));
 
   Block b(42, bufferPtr);
-  BOOST_CHECK_EQUAL(b.empty(), false);
+  BOOST_CHECK_EQUAL(b.isValid(), true);
   BOOST_CHECK_EQUAL(b.type(), 42);
   BOOST_CHECK_EQUAL(b.size(), 6);
   BOOST_CHECK_EQUAL(b.hasValue(), true);
@@ -167,7 +188,7 @@
   Block nested(BUFFER, sizeof(BUFFER));
 
   Block b(84, nested);
-  BOOST_CHECK_EQUAL(b.empty(), false);
+  BOOST_CHECK_EQUAL(b.isValid(), true);
   BOOST_CHECK_EQUAL(b.type(), 84);
   BOOST_CHECK_EQUAL(b.size(), 10);
   BOOST_CHECK_EQUAL(b.hasValue(), true);
@@ -181,10 +202,10 @@
   stream.seekg(0);
 
   Block b = Block::fromStream(stream);
-  BOOST_CHECK_EQUAL(b.type(), 0);
+  BOOST_CHECK_EQUAL(b.type(), 66);
   BOOST_CHECK_EQUAL(b.size(), 3);
   BOOST_CHECK_EQUAL(b.value_size(), 1);
-  BOOST_CHECK_EQUAL(*b.wire(),  0x00);
+  BOOST_CHECK_EQUAL(*b.wire(),  0x42);
   BOOST_CHECK_EQUAL(*b.value(), 0xfa);
 
   b = Block::fromStream(stream);
@@ -194,6 +215,13 @@
   BOOST_CHECK_EQUAL(*b.wire(),  0x01);
   BOOST_CHECK_EQUAL(*b.value(), 0xfb);
 
+  b = Block::fromStream(stream);
+  BOOST_CHECK_EQUAL(b.type(), 0xffffffff);
+  BOOST_CHECK_EQUAL(b.size(), 6);
+  BOOST_CHECK_EQUAL(b.value_size(), 0);
+  BOOST_CHECK_EQUAL(*b.wire(),  0xfe);
+
+  BOOST_CHECK(stream.eof());
   BOOST_CHECK_THROW(Block::fromStream(stream), tlv::Error);
 }
 
@@ -222,6 +250,7 @@
   BOOST_CHECK_EQUAL(b.type(), 6);
   BOOST_CHECK_EQUAL(b.value_size(), 32);
   b.parse();
+  BOOST_CHECK_EQUAL(b.elements_size(), 5);
 }
 
 BOOST_AUTO_TEST_CASE(FromStreamZeroLength)
@@ -268,17 +297,17 @@
 
 BOOST_AUTO_TEST_CASE(FromWireBuffer)
 {
-  ConstBufferPtr buffer = make_shared<Buffer>(TEST_BUFFER, sizeof(TEST_BUFFER));
+  auto buffer = make_shared<Buffer>(TEST_BUFFER, sizeof(TEST_BUFFER));
 
   size_t offset = 0;
   bool isOk = false;
   Block b;
   std::tie(isOk, b) = Block::fromBuffer(buffer, offset);
   BOOST_CHECK(isOk);
-  BOOST_CHECK_EQUAL(b.type(), 0);
+  BOOST_CHECK_EQUAL(b.type(), 66);
   BOOST_CHECK_EQUAL(b.size(), 3);
   BOOST_CHECK_EQUAL(b.value_size(), 1);
-  BOOST_CHECK_EQUAL(*b.wire(),  0x00);
+  BOOST_CHECK_EQUAL(*b.wire(),  0x42);
   BOOST_CHECK_EQUAL(*b.value(), 0xfa);
   offset += b.size();
 
@@ -292,7 +321,11 @@
   offset += b.size();
 
   std::tie(isOk, b) = Block::fromBuffer(buffer, offset);
-  BOOST_CHECK(!isOk);
+  BOOST_CHECK(isOk);
+  BOOST_CHECK_EQUAL(b.type(), 0xffffffff);
+  BOOST_CHECK_EQUAL(b.size(), 6);
+  BOOST_CHECK_EQUAL(b.value_size(), 0);
+  BOOST_CHECK_EQUAL(*b.wire(),  0xfe);
 }
 
 BOOST_AUTO_TEST_CASE(FromRawBuffer)
@@ -302,10 +335,10 @@
   Block b;
   std::tie(isOk, b) = Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset);
   BOOST_CHECK(isOk);
-  BOOST_CHECK_EQUAL(b.type(), 0);
+  BOOST_CHECK_EQUAL(b.type(), 66);
   BOOST_CHECK_EQUAL(b.size(), 3);
   BOOST_CHECK_EQUAL(b.value_size(), 1);
-  BOOST_CHECK_EQUAL(*b.wire(),  0x00);
+  BOOST_CHECK_EQUAL(*b.wire(),  0x42);
   BOOST_CHECK_EQUAL(*b.value(), 0xfa);
   offset += b.size();
 
@@ -319,7 +352,66 @@
   offset += b.size();
 
   std::tie(isOk, b) = Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset);
+  BOOST_CHECK(isOk);
+  BOOST_CHECK_EQUAL(b.type(), 0xffffffff);
+  BOOST_CHECK_EQUAL(b.size(), 6);
+  BOOST_CHECK_EQUAL(b.value_size(), 0);
+  BOOST_CHECK_EQUAL(*b.wire(),  0xfe);
+}
+
+template<typename T>
+struct MalformedInput
+{
+  static const std::vector<uint8_t> INPUT;
+};
+
+template<>
+const std::vector<uint8_t> MalformedInput<struct TlvTypeZero>::INPUT{
+  0x00, 0x00
+};
+template<>
+const std::vector<uint8_t> MalformedInput<struct TlvTypeTooLarge>::INPUT{
+  0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+template<>
+const std::vector<uint8_t> MalformedInput<struct BadTlvLength>::INPUT{
+  0x01, 0xff, 0x42, 0x42
+};
+template<>
+const std::vector<uint8_t> MalformedInput<struct TruncatedTlvValue>::INPUT{
+  0x01, 0x02, 0x03
+};
+
+using MalformedInputs = boost::mpl::vector<
+  MalformedInput<TlvTypeZero>,
+  MalformedInput<TlvTypeTooLarge>,
+  MalformedInput<BadTlvLength>,
+  MalformedInput<TruncatedTlvValue>
+>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Malformed, T, MalformedInputs)
+{
+  // constructor from raw buffer
+  BOOST_CHECK_THROW(Block(T::INPUT.data(), T::INPUT.size()), tlv::Error);
+
+  // fromStream()
+  std::stringstream stream;
+  stream.write(reinterpret_cast<const char*>(T::INPUT.data()), T::INPUT.size());
+  stream.seekg(0);
+  BOOST_CHECK_THROW(Block::fromStream(stream), tlv::Error);
+
+  // fromBuffer(), ConstBufferPtr overload
+  auto buf = make_shared<Buffer>(T::INPUT.begin(), T::INPUT.end());
+  bool isOk;
+  Block b;
+  std::tie(isOk, b) = Block::fromBuffer(buf, 0);
   BOOST_CHECK(!isOk);
+  BOOST_CHECK(!b.isValid());
+
+  // fromBuffer(), raw buffer overload
+  std::tie(isOk, b) = Block::fromBuffer(T::INPUT.data(), T::INPUT.size());
+  BOOST_CHECK(!isOk);
+  BOOST_CHECK(!b.isValid());
 }
 
 BOOST_AUTO_TEST_SUITE_END() // Construction
@@ -341,7 +433,6 @@
                 0x1c, 0x00, // KeyLocator empty
           0x17, 0x00 // SignatureValue empty
   };
-
   Block data(PACKET, sizeof(PACKET));
   data.parse();
 
@@ -354,6 +445,13 @@
 
   BOOST_CHECK(data.find(0x15) == data.elements_begin() + 2);
   BOOST_CHECK(data.find(0x01) == data.elements_end());
+
+  const uint8_t MALFORMED[] = {
+    // TLV-LENGTH of nested element is greater than TLV-LENGTH of enclosing element
+    0x05, 0x05, 0x07, 0x07, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f
+  };
+  Block bad(MALFORMED, sizeof(MALFORMED));
+  BOOST_CHECK_THROW(bad.parse(), Block::Error);
 }
 
 BOOST_AUTO_TEST_CASE(InsertBeginning)
@@ -483,15 +581,14 @@
   block.push_back(makeNonNegativeIntegerBlock(tlv::ContentType, 1));
 
   BOOST_CHECK_EQUAL(5, block.elements_size());
-  BOOST_REQUIRE_NO_THROW(block.remove(tlv::ContentType));
-  BOOST_CHECK_EQUAL(2, block.elements_size());
+  BOOST_CHECK_NO_THROW(block.remove(tlv::ContentType));
+  BOOST_REQUIRE_EQUAL(2, block.elements_size());
 
-  Block::element_container elements = block.elements();
-
+  auto elements = block.elements();
   BOOST_CHECK_EQUAL(tlv::FreshnessPeriod, elements[0].type());
   BOOST_CHECK_EQUAL(123, readNonNegativeInteger(elements[0]));
   BOOST_CHECK_EQUAL(tlv::Name, elements[1].type());
-  BOOST_CHECK(readString(elements[1]).compare("ndn:/test-prefix") == 0);
+  BOOST_CHECK_EQUAL(readString(elements[1]).compare("ndn:/test-prefix"), 0);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // SubElements
@@ -557,8 +654,8 @@
 
 BOOST_AUTO_TEST_CASE(Simple)
 {
-  Block b0 = "0000"_block;
-  BOOST_CHECK_EQUAL(b0.type(), 0x00);
+  Block b0 = "4200"_block;
+  BOOST_CHECK_EQUAL(b0.type(), 0x42);
   BOOST_CHECK_EQUAL(b0.value_size(), 0);
 
   Block b1 = "0101A0"_block;
@@ -581,9 +678,11 @@
   BOOST_CHECK_THROW(""_block, std::invalid_argument);
   BOOST_CHECK_THROW("1"_block, std::invalid_argument);
   BOOST_CHECK_THROW("333"_block, std::invalid_argument);
+  BOOST_CHECK_THROW("xx yy zz"_block, std::invalid_argument); // only comments
 
-  BOOST_CHECK_THROW("0202C0"_block, tlv::Error);
-  BOOST_CHECK_THROW("0201C0C1"_block, tlv::Error);
+  BOOST_CHECK_THROW("0000"_block, tlv::Error); // invalid type
+  BOOST_CHECK_THROW("0202C0"_block, tlv::Error); // truncated value
+  BOOST_CHECK_THROW("0201C0C1"_block, tlv::Error); // trailing garbage
 }
 
 BOOST_AUTO_TEST_SUITE_END() // BlockLiteral
diff --git a/tests/unit/encoding/tlv.t.cpp b/tests/unit/encoding/tlv.t.cpp
index 8c73267..1810f01 100644
--- a/tests/unit/encoding/tlv.t.cpp
+++ b/tests/unit/encoding/tlv.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -112,6 +112,7 @@
 }
 
 static const uint8_t BUFFER[] = {
+  0x00, // == 0
   0x01, // == 1
   0xfc, // == 252
   0xfd, 0x00, 0xfd, // == 253
@@ -121,10 +122,12 @@
 
 BOOST_AUTO_TEST_CASE(SizeOf)
 {
+  BOOST_CHECK_EQUAL(sizeOfVarNumber(0), 1);
   BOOST_CHECK_EQUAL(sizeOfVarNumber(1), 1);
   BOOST_CHECK_EQUAL(sizeOfVarNumber(252), 1);
   BOOST_CHECK_EQUAL(sizeOfVarNumber(253), 3);
   BOOST_CHECK_EQUAL(sizeOfVarNumber(65536), 5);
+  BOOST_CHECK_EQUAL(sizeOfVarNumber(4294967295), 5);
   BOOST_CHECK_EQUAL(sizeOfVarNumber(4294967296), 9);
 }
 
@@ -132,6 +135,7 @@
 {
   std::ostringstream os;
 
+  writeVarNumber(os, 0);
   writeVarNumber(os, 1);
   writeVarNumber(os, 252);
   writeVarNumber(os, 253);
@@ -139,7 +143,7 @@
   writeVarNumber(os, 4294967296);
 
   std::string buffer = os.str();
-  const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.c_str());
+  const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.data());
 
   BOOST_CHECK_EQUAL(buffer.size(), sizeof(BUFFER));
   BOOST_CHECK_EQUAL_COLLECTIONS(BUFFER, BUFFER + sizeof(BUFFER),
@@ -155,60 +159,65 @@
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), true);
   begin = BUFFER;
   BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 1));
-  BOOST_CHECK_EQUAL(value, 1);
+  BOOST_CHECK_EQUAL(value, 0);
 
   begin = BUFFER + 1;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), true);
   begin = BUFFER + 1;
   BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 1));
+  BOOST_CHECK_EQUAL(value, 1);
+
+  begin = BUFFER + 2;
+  BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), true);
+  begin = BUFFER + 2;
+  BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 1));
   BOOST_CHECK_EQUAL(value, 252);
 
-  begin = BUFFER + 2;
+  begin = BUFFER + 3;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), false);
-  begin = BUFFER + 2;
+  begin = BUFFER + 3;
   BOOST_CHECK_THROW(readVarNumber(begin, begin + 1), Error);
 
-  begin = BUFFER + 2;
+  begin = BUFFER + 3;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 2, value), false);
-  begin = BUFFER + 2;
+  begin = BUFFER + 3;
   BOOST_CHECK_THROW(readVarNumber(begin, begin + 2), Error);
 
-  begin = BUFFER + 2;
+  begin = BUFFER + 3;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 3, value), true);
-  begin = BUFFER + 2;
+  begin = BUFFER + 3;
   BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 3));
   BOOST_CHECK_EQUAL(value, 253);
 
-
-  begin = BUFFER + 5;
+  begin = BUFFER + 6;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), false);
-  begin = BUFFER + 5;
+  begin = BUFFER + 6;
   BOOST_CHECK_THROW(readVarNumber(begin, begin + 1), Error);
 
-  begin = BUFFER + 5;
+  begin = BUFFER + 6;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 4, value), false);
-  begin = BUFFER + 5;
+  begin = BUFFER + 6;
   BOOST_CHECK_THROW(readVarNumber(begin, begin + 4), Error);
 
-  begin = BUFFER + 5;
+  begin = BUFFER + 6;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 5, value), true);
-  begin = BUFFER + 5;
+  begin = BUFFER + 6;
   BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 5));
   BOOST_CHECK_EQUAL(value, 65536);
 
-  begin = BUFFER + 10;
+  begin = BUFFER + 11;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), false);
-  begin = BUFFER + 10;
+  begin = BUFFER + 11;
   BOOST_CHECK_THROW(readVarNumber(begin, begin + 1), Error);
 
-  begin = BUFFER + 10;
+  begin = BUFFER + 11;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 8, value), false);
-  begin = BUFFER + 10;
+  begin = BUFFER + 11;
   BOOST_CHECK_THROW(readVarNumber(begin, begin + 8), Error);
 
-  begin = BUFFER + 10;
+  begin = BUFFER + 11;
   BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 9, value), true);
-  begin = BUFFER + 10;
+  begin = BUFFER + 11;
   BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 9));
   BOOST_CHECK_EQUAL(value, 4294967296);
 }
@@ -217,6 +226,7 @@
 {
   StreamIterator end; // end of stream
   uint64_t value;
+
   {
     ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
     StreamIterator begin(stream);
@@ -226,118 +236,130 @@
     ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
     StreamIterator begin(stream);
     BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
+    BOOST_CHECK_EQUAL(value, 0);
+  }
+
+  {
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
+    StreamIterator begin(stream);
+    BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
+  }
+  {
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
+    StreamIterator begin(stream);
+    BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
     BOOST_CHECK_EQUAL(value, 1);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
     BOOST_CHECK_EQUAL(value, 252);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 3, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 3, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 2);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 3, 2);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 2);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 3, 2);
     StreamIterator begin(stream);
     BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 3);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 3, 3);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 3);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 3, 3);
     StreamIterator begin(stream);
     BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
     BOOST_CHECK_EQUAL(value, 253);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 6, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 6, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 4);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 6, 4);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 4);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 6, 4);
     StreamIterator begin(stream);
     BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 5);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 6, 5);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 5);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 6, 5);
     StreamIterator begin(stream);
     BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
     BOOST_CHECK_EQUAL(value, 65536);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 11, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 1);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 11, 1);
     StreamIterator begin(stream);
     BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 8);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 11, 8);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 8);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 11, 8);
     StreamIterator begin(stream);
     BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
   }
 
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 9);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 11, 9);
     StreamIterator begin(stream);
     BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
   }
   {
-    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 9);
+    ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 11, 9);
     StreamIterator begin(stream);
     BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
     BOOST_CHECK_EQUAL(value, 4294967296);
@@ -346,6 +368,72 @@
 
 BOOST_AUTO_TEST_SUITE_END() // VarNumber
 
+BOOST_AUTO_TEST_SUITE(Type)
+
+static const uint8_t BUFFER[] = {
+  0x00, // == 0 (illegal)
+  0x01, // == 1
+  0xfd, 0x00, 0xfd, // == 253
+  0xfe, 0xff, 0xff, 0xff, 0xff, // == 4294967295
+  0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 // == 4294967296 (illegal)
+};
+
+BOOST_AUTO_TEST_CASE(Read)
+{
+  const uint8_t* begin;
+  uint32_t type;
+
+  begin = BUFFER;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 1, type), false);
+  begin = BUFFER;
+  BOOST_CHECK_THROW(readType(begin, begin + 1), Error);
+
+  begin = BUFFER + 1;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 1, type), true);
+  begin = BUFFER + 1;
+  BOOST_CHECK_NO_THROW(readType(begin, begin + 1));
+  BOOST_CHECK_EQUAL(type, 1);
+
+  begin = BUFFER + 2;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 1, type), false);
+  begin = BUFFER + 2;
+  BOOST_CHECK_THROW(readType(begin, begin + 1), Error);
+
+  begin = BUFFER + 2;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 2, type), false);
+  begin = BUFFER + 2;
+  BOOST_CHECK_THROW(readType(begin, begin + 2), Error);
+
+  begin = BUFFER + 2;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 3, type), true);
+  begin = BUFFER + 2;
+  BOOST_CHECK_NO_THROW(readType(begin, begin + 3));
+  BOOST_CHECK_EQUAL(type, 253);
+
+  begin = BUFFER + 5;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 1, type), false);
+  begin = BUFFER + 5;
+  BOOST_CHECK_THROW(readType(begin, begin + 1), Error);
+
+  begin = BUFFER + 5;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 5, type), true);
+  begin = BUFFER + 5;
+  BOOST_CHECK_NO_THROW(readType(begin, begin + 5));
+  BOOST_CHECK_EQUAL(type, 4294967295);
+
+  begin = BUFFER + 10;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 1, type), false);
+  begin = BUFFER + 10;
+  BOOST_CHECK_THROW(readType(begin, begin + 1), Error);
+
+  begin = BUFFER + 10;
+  BOOST_CHECK_EQUAL(readType(begin, begin + 9, type), false);
+  begin = BUFFER + 10;
+  BOOST_CHECK_THROW(readType(begin, begin + 9), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Type
+
 BOOST_AUTO_TEST_SUITE(NonNegativeInteger)
 
 // This check ensures readNonNegativeInteger only requires InputIterator concept and nothing more.
@@ -388,7 +476,7 @@
   writeNonNegativeInteger(os, 72340172838076674);
 
   std::string buffer = os.str();
-  const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.c_str());
+  const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.data());
 
   BOOST_CHECK_EQUAL_COLLECTIONS(BUFFER, BUFFER + sizeof(BUFFER),
                                 actual, actual + sizeof(BUFFER));
diff --git a/tests/unit/name-component.t.cpp b/tests/unit/name-component.t.cpp
index 6f5bffc..4ee25ea 100644
--- a/tests/unit/name-component.t.cpp
+++ b/tests/unit/name-component.t.cpp
@@ -141,7 +141,7 @@
 BOOST_AUTO_TEST_CASE(InvalidType)
 {
   Component comp;
-  BOOST_CHECK_THROW(comp.wireDecode("0001 80"_block), Component::Error);
+  BOOST_CHECK_THROW(comp.wireDecode(Block{}), Component::Error);
   BOOST_CHECK_THROW(comp.wireDecode("FE0001000001 80"_block), Component::Error);
 
   BOOST_CHECK_THROW(Component::fromEscapedString("0=A"), Component::Error);