name: reorganize Name class

* Categorize class methods.
* Make append component methods inline.
* Make comparison operators non-member functions.
* Improve Doxygen.
* Simplify Name::at implementation.
* Reorder test cases.
* Add test coverage for Name::empty() and iterators.
* Make naming convention test case more readable,
  and move it to TestNameComponent test suite.

refs #4171

Change-Id: I5f7deff2535f8265ac4942f892879dd7e56f6414
diff --git a/src/name.cpp b/src/name.cpp
index ad15d08..58b1cc3 100644
--- a/src/name.cpp
+++ b/src/name.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
@@ -25,10 +25,11 @@
 
 #include "name.hpp"
 
-#include "util/time.hpp"
 #include "encoding/block.hpp"
 #include "encoding/encoding-buffer.hpp"
+#include "util/time.hpp"
 
+#include <sstream>
 #include <boost/algorithm/string/trim.hpp>
 #include <boost/functional/hash.hpp>
 
@@ -43,15 +44,17 @@
 
 const size_t Name::npos = std::numeric_limits<size_t>::max();
 
+// ---- constructors, encoding, decoding ----
+
 Name::Name()
-  : m_nameBlock(tlv::Name)
+  : m_wire(tlv::Name)
 {
 }
 
 Name::Name(const Block& wire)
 {
-  m_nameBlock = wire;
-  m_nameBlock.parse();
+  m_wire = wire;
+  m_wire.parse();
 }
 
 Name::Name(const char* uri)
@@ -108,13 +111,12 @@
   }
 }
 
-Name
-Name::deepCopy() const
+std::string
+Name::toUri() const
 {
-  Name copiedName(*this);
-  copiedName.m_nameBlock.resetWire();
-  copiedName.wireEncode(); // "compress" the underlying buffer
-  return copiedName;
+  std::ostringstream os;
+  os << *this;
+  return os.str();
 }
 
 template<encoding::Tag TAG>
@@ -123,10 +125,9 @@
 {
   size_t totalLength = 0;
 
-  for (const_reverse_iterator i = rbegin(); i != rend(); ++i)
-    {
-      totalLength += i->wireEncode(encoder);
-    }
+  for (const_reverse_iterator i = rbegin(); i != rend(); ++i) {
+    totalLength += i->wireEncode(encoder);
+  }
 
   totalLength += encoder.prependVarNumber(totalLength);
   totalLength += encoder.prependVarNumber(tlv::Name);
@@ -142,8 +143,8 @@
 const Block&
 Name::wireEncode() const
 {
-  if (m_nameBlock.hasWire())
-    return m_nameBlock;
+  if (m_wire.hasWire())
+    return m_wire;
 
   EncodingEstimator estimator;
   size_t estimatedSize = wireEncode(estimator);
@@ -151,10 +152,10 @@
   EncodingBuffer buffer(estimatedSize, 0);
   wireEncode(buffer);
 
-  m_nameBlock = buffer.block();
-  m_nameBlock.parse();
+  m_wire = buffer.block();
+  m_wire.parse();
 
-  return m_nameBlock;
+  return m_wire;
 }
 
 void
@@ -163,99 +164,33 @@
   if (wire.type() != tlv::Name)
     BOOST_THROW_EXCEPTION(tlv::Error("Unexpected TLV type when decoding Name"));
 
-  m_nameBlock = wire;
-  m_nameBlock.parse();
+  m_wire = wire;
+  m_wire.parse();
 }
 
-std::string
-Name::toUri() const
+Name
+Name::deepCopy() const
 {
-  std::ostringstream os;
-  os << *this;
-  return os.str();
+  Name copiedName(*this);
+  copiedName.m_wire.resetWire();
+  copiedName.wireEncode(); // "compress" the underlying buffer
+  return copiedName;
 }
 
-Name&
-Name::append(const PartialName& name)
+// ---- accessors ----
+
+const name::Component&
+Name::at(ssize_t i) const
 {
-  if (&name == this)
-    // Copying from this name, so need to make a copy first.
-    return append(PartialName(name));
+  if (i < 0) {
+    i = size() + i;
+  }
 
-  for (size_t i = 0; i < name.size(); ++i)
-    append(name.at(i));
+  if (i < 0 || static_cast<size_t>(i) >= size()) {
+    BOOST_THROW_EXCEPTION(Error("Requested component does not exist (out of bounds)"));
+  }
 
-  return *this;
-}
-
-Name&
-Name::appendNumber(uint64_t number)
-{
-  m_nameBlock.push_back(Component::fromNumber(number));
-  return *this;
-}
-
-Name&
-Name::appendNumberWithMarker(uint8_t marker, uint64_t number)
-{
-  m_nameBlock.push_back(Component::fromNumberWithMarker(marker, number));
-  return *this;
-}
-
-Name&
-Name::appendVersion(uint64_t version)
-{
-  m_nameBlock.push_back(Component::fromVersion(version));
-  return *this;
-}
-
-Name&
-Name::appendVersion()
-{
-  appendVersion(time::toUnixTimestamp(time::system_clock::now()).count());
-  return *this;
-}
-
-Name&
-Name::appendSegment(uint64_t segmentNo)
-{
-  m_nameBlock.push_back(Component::fromSegment(segmentNo));
-  return *this;
-}
-
-Name&
-Name::appendSegmentOffset(uint64_t offset)
-{
-  m_nameBlock.push_back(Component::fromSegmentOffset(offset));
-  return *this;
-}
-
-Name&
-Name::appendTimestamp(const time::system_clock::TimePoint& timePoint)
-{
-  m_nameBlock.push_back(Component::fromTimestamp(timePoint));
-  return *this;
-}
-
-Name&
-Name::appendSequenceNumber(uint64_t seqNo)
-{
-  m_nameBlock.push_back(Component::fromSequenceNumber(seqNo));
-  return *this;
-}
-
-Name&
-Name::appendImplicitSha256Digest(const ConstBufferPtr& digest)
-{
-  m_nameBlock.push_back(Component::fromImplicitSha256Digest(digest));
-  return *this;
-}
-
-Name&
-Name::appendImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
-{
-  m_nameBlock.push_back(Component::fromImplicitSha256Digest(digest, digestSize));
-  return *this;
+  return reinterpret_cast<const Component&>(m_wire.elements()[i]);
 }
 
 PartialName
@@ -277,11 +212,40 @@
   return result;
 }
 
+// ---- modifiers ----
+
+Name&
+Name::appendVersion()
+{
+  return appendVersion(time::toUnixTimestamp(time::system_clock::now()).count());
+}
+
+Name&
+Name::appendTimestamp()
+{
+  return appendTimestamp(time::system_clock::now());
+}
+
+Name&
+Name::append(const PartialName& name)
+{
+  if (&name == this)
+    // Copying from this name, so need to make a copy first.
+    return append(PartialName(name));
+
+  for (size_t i = 0; i < name.size(); ++i)
+    append(name.at(i));
+
+  return *this;
+}
+
+// ---- algorithms ----
+
 Name
 Name::getSuccessor() const
 {
   if (empty()) {
-    static uint8_t firstValue[] = { 0 };
+    static uint8_t firstValue[] {0};
     Name firstName;
     firstName.append(firstValue, 1);
     return firstName;
@@ -291,13 +255,15 @@
 }
 
 bool
-Name::equals(const Name& name) const
+Name::isPrefixOf(const Name& other) const
 {
-  if (size() != name.size())
+  // This name is longer than the name we are checking against.
+  if (size() > other.size())
     return false;
 
+  // Check if at least one of given components doesn't match.
   for (size_t i = 0; i < size(); ++i) {
-    if (at(i) != name.at(i))
+    if (get(i) != other.get(i))
       return false;
   }
 
@@ -305,15 +271,13 @@
 }
 
 bool
-Name::isPrefixOf(const Name& name) const
+Name::equals(const Name& other) const
 {
-  // This name is longer than the name we are checking against.
-  if (size() > name.size())
+  if (size() != other.size())
     return false;
 
-  // Check if at least one of given components doesn't match.
   for (size_t i = 0; i < size(); ++i) {
-    if (at(i) != name.at(i))
+    if (get(i) != other.get(i))
       return false;
   }
 
@@ -328,7 +292,7 @@
   size_t count = std::min(count1, count2);
 
   for (size_t i = 0; i < count; ++i) {
-    int comp = this->at(pos1 + i).compare(other.at(pos2 + i));
+    int comp = get(pos1 + i).compare(other.get(pos2 + i));
     if (comp != 0) { // i-th component differs
       return comp;
     }
@@ -337,20 +301,20 @@
   return count1 - count2;
 }
 
+// ---- stream operators ----
+
 std::ostream&
 operator<<(std::ostream& os, const Name& name)
 {
-  if (name.empty())
-    {
+  if (name.empty()) {
+    os << "/";
+  }
+  else {
+    for (const auto& component : name) {
       os << "/";
+      component.toUri(os);
     }
-  else
-    {
-      for (Name::const_iterator i = name.begin(); i != name.end(); i++) {
-        os << "/";
-        i->toUri(os);
-      }
-    }
+  }
   return os;
 }
 
@@ -367,6 +331,7 @@
 } // namespace ndn
 
 namespace std {
+
 size_t
 hash<ndn::Name>::operator()(const ndn::Name& name) const
 {
diff --git a/src/name.hpp b/src/name.hpp
index 880343c..1760def 100644
--- a/src/name.hpp
+++ b/src/name.hpp
@@ -26,29 +26,22 @@
 #ifndef NDN_NAME_HPP
 #define NDN_NAME_HPP
 
-#include "common.hpp"
 #include "name-component.hpp"
-
 #include <boost/iterator/reverse_iterator.hpp>
 
 namespace ndn {
 
 class Name;
 
-/**
- * @brief Partial name abstraction to represent an arbitrary sequence of name components
+/** @brief Represents an arbitrary sequence of name components
  */
-typedef Name PartialName;
+using PartialName = Name;
 
-/**
- * @brief Name abstraction to represent an absolute name
+/** @brief Represents an absolute name
  */
 class Name
 {
-public:
-  /**
-   * @brief Error that can be thrown from Name
-   */
+public: // nested types
   class Error : public name::Component::Error
   {
   public:
@@ -59,180 +52,156 @@
     }
   };
 
-  typedef name::Component Component;
+  using Component = name::Component;
+  using component_container = std::vector<Component>;
 
-  typedef std::vector<Component>  component_container;
+  // Name appears as a container of name components
+  using value_type             = Component;
+  using allocator_type         = void;
+  using reference              = Component&;
+  using const_reference        = const Component;
+  using pointer                = Component*;
+  using const_pointer          = const Component*;
+  using iterator               = Component*;
+  using const_iterator         = const Component*;
+  using reverse_iterator       = boost::reverse_iterator<iterator>;
+  using const_reverse_iterator = boost::reverse_iterator<const_iterator>;
+  using difference_type        = component_container::difference_type;
+  using size_type              = component_container::size_type;
 
-  typedef Component               value_type;
-  typedef void                    allocator_type;
-  typedef Component&              reference;
-  typedef const Component         const_reference;
-  typedef Component*              pointer;
-  typedef const Component*        const_pointer;
-  typedef Component*              iterator;
-  typedef const Component*        const_iterator;
-
-  typedef boost::reverse_iterator<iterator>       reverse_iterator;
-  typedef boost::reverse_iterator<const_iterator> const_reverse_iterator;
-
-  typedef component_container::difference_type difference_type;
-  typedef component_container::size_type       size_type;
-
-  /**
-   * @brief Create a new Name with no components.
+public: // constructors, encoding, decoding
+  /** @brief Create an empty name
+   *  @post empty() == true
    */
   Name();
 
-  /**
-   * @brief Create Name object from wire block
+  /** @brief Decode Name from wire encoding
+   *  @throw tlv::Error wire encoding is invalid
    *
-   * This is a more efficient equivalent for
-   * @code
+   *  This is a more efficient equivalent for
+   *  @code
    *    Name name;
    *    name.wireDecode(wire);
-   * @endcode
+   *  @endcode
    */
   explicit
   Name(const Block& wire);
 
-  /**
-   * @brief Create name from @p uri (NDN URI scheme)
-   * @param uri The null-terminated URI string
+  /** @brief Parse name from NDN URI
+   *  @param uri a null-terminated URI string
+   *  @sa https://named-data.net/doc/ndn-tlv/name.html#ndn-uri-scheme
    */
   Name(const char* uri);
 
-  /**
-   * @brief Create name from @p uri (NDN URI scheme)
-   * @param uri The URI string
+  /** @brief Create name from NDN URI
+   *  @param uri a URI string
+   *  @sa https://named-data.net/doc/ndn-tlv/name.html#ndn-uri-scheme
    */
   Name(std::string uri);
 
-  /**
-   * @brief Make a deep copy of the name, reallocating the underlying memory buffer
+  /** @brief Get URI representation of the name
+   *  @return URI representation; "ndn:" scheme identifier is not included
+   *  @sa https://named-data.net/doc/ndn-tlv/name.html#ndn-uri-scheme
+   *  @note To print URI representation into a stream, it is more efficient to use ``os << name``.
    */
-  Name
-  deepCopy() const;
+  std::string
+  toUri() const;
 
-  /**
-   * @brief Fast encoding or block size estimation
+  /** @brief Check if this Name instance already has wire encoding
+   */
+  bool
+  hasWire() const
+  {
+    return m_wire.hasWire();
+  }
+
+  /** @brief Fast encoding or block size estimation
    */
   template<encoding::Tag TAG>
   size_t
   wireEncode(EncodingImpl<TAG>& encoder) const;
 
+  /** @brief Perform wire encoding, or return existing wire encoding
+   *  @post hasWire() == true
+   */
   const Block&
   wireEncode() const;
 
+  /** @brief Decode name from wire encoding
+   *  @throw tlv::Error wire encoding is invalid
+   *  @post hasWire() == true
+   */
   void
   wireDecode(const Block& wire);
 
-  /**
-   * @brief Check if already has wire
+  /** @brief Make a deep copy of the name, reallocating the underlying memory buffer
+   */
+  Name
+  deepCopy() const;
+
+public: // access
+  /** @brief Check if name is empty
    */
   bool
-  hasWire() const;
-
-  /**
-   * @brief Append a new component, copying from value of length valueLength.
-   * @return This name so that you can chain calls to append.
-   */
-  Name&
-  append(const uint8_t* value, size_t valueLength)
+  empty() const
   {
-    m_nameBlock.push_back(Component(value, valueLength));
-    return *this;
+    return m_wire.elements().empty();
   }
 
-  /**
-   * @brief Append a new component, copying from value frome the range [@p first, @p last) of bytes
-   * @param first     Iterator pointing to the beginning of the buffer
-   * @param last      Iterator pointing to the ending of the buffer
-   * @tparam Iterator iterator type satisfying at least InputIterator concept.  Implementation
-   *                  is more optimal when the iterator type satisfies RandomAccessIterator concept.
-   *                  It is required that sizeof(std::iterator_traits<Iterator>::value_type) == 1.
-   * @return This name so that you can chain calls to append.
+  /** @brief Get number of components
    */
-  template<class Iterator>
-  Name&
-  append(Iterator first, Iterator last)
+  size_t
+  size() const
   {
-    m_nameBlock.push_back(Component(first, last));
-    return *this;
+    return m_wire.elements_size();
   }
 
-  /**
-   * @brief Append component @p value
+  /** @brief Get the component at the given index
+   *  @param i zero-based index; if negative, it starts at the end of this name
+   *  @warning Indexing out of bounds triggers undefined behavior.
    */
-  Name&
-  append(const Component& value)
+  const Component&
+  get(ssize_t i) const
   {
-    m_nameBlock.push_back(value);
-    return *this;
+    if (i < 0) {
+      i += size();
+    }
+    return reinterpret_cast<const Component&>(m_wire.elements()[i]);
   }
 
-  /**
-   * @brief Append name component that represented as a string
+  /** @brief Equivalent to get(i)
+   */
+  const Component&
+  operator[](ssize_t i) const
+  {
+    return get(i);
+  }
+
+  /** @brief Get the component at the given index
+   *  @param i zero-based index; if negative, size()+i is used instead
+   *  @throws Name::Error index is out of bounds
+   */
+  const Component&
+  at(ssize_t i) const;
+
+  /** @brief Extract some components as a sub-name (PartialName)
+   *  @param iStartComponent zero-based index of the first component;
+   *                         if negative, size()+iStartComponent is used instead
+   *  @param nComponents Number of components starting at iStartComponent.
+   *                     Use @p npos to get the PartialName until the end of this Name.
+   *  @return a new PartialName containing the extracted components
    *
-   * Note that this method is necessary to ensure correctness and unambiguity of
-   * ``append("string")`` operations (both Component and Name can be implicitly
-   * converted from string, each having different outcomes
-   */
-  Name&
-  append(const char* value)
-  {
-    m_nameBlock.push_back(Component(value));
-    return *this;
-  }
-
-  Name&
-  append(const Block& value)
-  {
-    if (value.type() == tlv::NameComponent)
-      m_nameBlock.push_back(value);
-    else
-      m_nameBlock.push_back(Block(tlv::NameComponent, value));
-
-    return *this;
-  }
-
-  /**
-   * @brief append a PartialName to this Name.
-   * @param name the components to append
-   * @return this name
-   */
-  Name&
-  append(const PartialName& name);
-
-  /**
-   * Clear all the components.
-   */
-  void
-  clear()
-  {
-    m_nameBlock = Block(tlv::Name);
-  }
-
-  /**
-   * @brief Extract a sub-name (PartialName) of @p nComponents components starting
-   *        from @p iStartComponent
-   * @param iStartComponent index of the first component;
-   *        if iStartComponent is negative, size()+iStartComponent is used instead
-   * @param nComponents The number of components starting at iStartComponent.
-   *                    Use npos to get the Partial Name until the end of this Name.
-   * @details If iStartComponent is out of bounds and is negative, returns the components
-   *          starting from the beginning of the Name.
-   *          If iStartComponent is out of bounds and is positive, returns the component "/".
-   *          If nComponents is out of bounds, returns the components until the end of
-   *          this Name
-   * @return A new partial name
+   *  If iStartComponent is positive and indexes out of bounds, returns an empty PartialName.
+   *  If iStartComponent is negative and indexes out of bounds, returns components starting from the
+   *  beginning of the Name. If nComponents is out of bounds, returns the components until the end
+   *  of this Name.
    */
   PartialName
   getSubName(ssize_t iStartComponent, size_t nComponents = npos) const;
 
-  /**
-   * @brief Extract a prefix (PartialName) of the name, containing first @p nComponents components
-   *
-   * @param nComponents The number of prefix components.  If nComponents is -N then return
+  /** @brief Extract a prefix of the name
+   *  @param nComponents Number of components; if negative, size()+nComponents is used instead
+   *  @return a new Name containing the prefix
    *                    the prefix up to name.size() - N. For example getPrefix(-1)
    *                    returns the name without the final component.
    * @return A new partial name
@@ -241,348 +210,31 @@
   getPrefix(ssize_t nComponents) const
   {
     if (nComponents < 0)
-      return getSubName(0, m_nameBlock.elements_size() + nComponents);
+      return getSubName(0, size() + nComponents);
     else
       return getSubName(0, nComponents);
   }
 
-  /**
-   * Encode this name as a URI.
-   * @return The encoded URI.
-   */
-  std::string
-  toUri() const;
-
-  /**
-   * @brief Append a component with the number encoded as nonNegativeInteger
-   *
-   * @see http://named-data.net/doc/ndn-tlv/tlv.html#non-negative-integer-encoding
-   *
-   * @param number The non-negative number
-   * @return This name so that you can chain calls to append.
-   */
-  Name&
-  appendNumber(uint64_t number);
-
-  /**
-   * @brief Create a component encoded as NameComponentWithMarker
-   *
-   * @see http://named-data.net/doc/tech-memos/naming-conventions.pdf
-   *
-   * @param marker 1-byte marker octet
-   * @param number The non-negative number
-   */
-  Name&
-  appendNumberWithMarker(uint8_t marker, uint64_t number);
-
-  /**
-   * @brief Append version using NDN naming conventions
-   *
-   * @see http://named-data.net/doc/tech-memos/naming-conventions.pdf
-   */
-  Name&
-  appendVersion(uint64_t version);
-
-  /**
-   * @brief Append version using NDN naming conventions based on current UNIX timestamp
-   *        in milliseconds
-   *
-   * @see http://named-data.net/doc/tech-memos/naming-conventions.pdf
-   */
-  Name&
-  appendVersion();
-
-  /**
-   * @brief Append segment number (sequential) using NDN naming conventions
-   *
-   * @see http://named-data.net/doc/tech-memos/naming-conventions.pdf
-   */
-  Name&
-  appendSegment(uint64_t segmentNo);
-
-  /**
-   * @brief Append segment byte offset using NDN naming conventions
-   *
-   * @see http://named-data.net/doc/tech-memos/naming-conventions.pdf
-   */
-  Name&
-  appendSegmentOffset(uint64_t offset);
-
-  /**
-   * @brief Append timestamp using NDN naming conventions
-   *
-   * @see http://named-data.net/doc/tech-memos/naming-conventions.pdf
-   */
-  Name&
-  appendTimestamp(const time::system_clock::TimePoint& timePoint = time::system_clock::now());
-
-  /**
-   * @brief Append sequence number using NDN naming conventions
-   *
-   * @see http://named-data.net/doc/tech-memos/naming-conventions.pdf
-   */
-  Name&
-  appendSequenceNumber(uint64_t seqNo);
-
-  /**
-   * @brief Append ImplicitSha256Digest
-   */
-  Name&
-  appendImplicitSha256Digest(const ConstBufferPtr& digest);
-
-  /**
-   * @brief Append ImplicitSha256Digest
-   */
-  Name&
-  appendImplicitSha256Digest(const uint8_t* digest, size_t digestSize);
-
-  /**
-   * @brief Get the successor of a name
-   *
-   * The successor of a name is defined as follows:
-   *
-   *     N represents the set of NDN Names, and X,Y ∈ N.
-   *     Operator < is defined by canonical order on N.
-   *     Y is the successor of X, if (a) X < Y, and (b) ∄ Z ∈ N s.t. X < Z < Y.
-   *
-   * In plain words, successor of a name is the same name, but with its last component
-   * advanced to a next possible value.
-   *
-   * Examples:
-   *
-   * - successor for / is /%00
-   * - successor for /%00%01/%01%02 is /%00%01/%01%03
-   * - successor for /%00%01/%01%FF is /%00%01/%02%00
-   * - successor for /%00%01/%FF%FF is /%00%01/%00%00%00
-   *
-   * @return a new name
-   */
-  Name
-  getSuccessor() const;
-
-  /**
-   * Check if this name has the same component count and components as the given name.
-   * @param name The Name to check.
-   * @return true if the names are equal, otherwise false.
-   */
-  bool
-  equals(const Name& name) const;
-
-  /**
-   * @brief Check if the N components of this name are the same as the first N components
-   *        of the given name.
-   *
-   * @param name The Name to check.
-   * @return true if this matches the given name, otherwise false.  This always returns
-   *              true if this name is empty.
-   */
-  bool
-  isPrefixOf(const Name& name) const;
-
-  //
-  // vector equivalent interface.
-  //
-
-  /**
-   * @brief Check if name is emtpy
-   */
-  bool
-  empty() const
-  {
-    return m_nameBlock.elements().empty();
-  }
-
-  /**
-   * Get the number of components.
-   * @return The number of components.
-   */
-  size_t
-  size() const
-  {
-    return m_nameBlock.elements_size();
-  }
-
-  /**
-   * Get the component at the given index.
-   * @param i The index of the component, starting from 0.
-   * @return The name component at the index.
-   */
-  const Component&
-  get(ssize_t i) const
-  {
-    if (i >= 0)
-      return reinterpret_cast<const Component&>(m_nameBlock.elements()[i]);
-    else
-      return reinterpret_cast<const Component&>(m_nameBlock.elements()[size() + i]);
-  }
-
-  const Component&
-  operator[](ssize_t i) const
-  {
-    return get(i);
-  }
-
-  /**
-   * @brief Get component at the specified index
-   *
-   * Unlike get() and operator[] methods, at() checks for out of bounds
-   * and will throw Name::Error when it happens
-   *
-   * @throws Name::Error if index out of bounds
-   */
-  const Component&
-  at(ssize_t i) const
-  {
-    if ((i >= 0 && static_cast<size_t>(i) >= size()) ||
-        (i < 0  && static_cast<size_t>(-i) > size()))
-      BOOST_THROW_EXCEPTION(Error("Requested component does not exist (out of bounds)"));
-
-    return get(i);
-  }
-
-  /**
-   * @brief Compare this to the other Name using NDN canonical ordering.
-   *
-   * If the first components of each name are not equal, this returns a negative value if
-   * the first comes before the second using the NDN canonical ordering for name
-   * components, or a positive value if it comes after.  If they are equal, this compares
-   * the second components of each name, etc. If both names are the same up to the size
-   * of the shorter name, this returns a negative value if the first name is shorter than
-   * the second or a positive value if it is longer.  For example, if you std::sort gives:
-   * /a/b/d /a/b/cc /c /c/a /bb .
-   * This is intuitive because all names with the prefix /a are next to each other.
-   * But it may be also be counter-intuitive because /c comes before /bb according
-   * to NDN canonical ordering since it is shorter.
-   *
-   * @param other The other Name to compare with.
-   *
-   * @retval negative this comes before other in canonical ordering
-   * @retval zero this equals other
-   * @retval positive this comes after other in canonical ordering
-   *
-   * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
-   */
-  int
-  compare(const Name& other) const
-  {
-    return this->compare(0, npos, other);
-  }
-
-  /** \brief compares [pos1, pos1+count1) components in this Name
-   *         to [pos2, pos2+count2) components in \p other
-   *
-   *  This is equivalent to this->getSubName(pos1, count1).compare(other.getSubName(pos2, count2));
-   */
-  int
-  compare(size_t pos1, size_t count1,
-          const Name& other, size_t pos2 = 0, size_t count2 = npos) const;
-
-  /**
-   * Append the component
-   * @param component The component of type T.
-   */
-  template<class T> void
-  push_back(const T& component)
-  {
-    append(component);
-  }
-
-  /**
-   * Check if this name has the same component count and components as the given name.
-   * @param name The Name to check.
-   * @return true if the names are equal, otherwise false.
-   */
-  bool
-  operator==(const Name& name) const
-  {
-    return equals(name);
-  }
-
-  /**
-   * Check if this name has the same component count and components as the given name.
-   * @param name The Name to check.
-   * @return true if the names are not equal, otherwise false.
-   */
-  bool
-  operator!=(const Name& name) const
-  {
-    return !equals(name);
-  }
-
-  /**
-   * Return true if this is less than or equal to the other Name in the NDN canonical ordering.
-   * @param other The other Name to compare with.
-   *
-   * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
-   */
-  bool
-  operator<=(const Name& other) const
-  {
-    return compare(other) <= 0;
-  }
-
-  /**
-   * Return true if this is less than the other Name in the NDN canonical ordering.
-   * @param other The other Name to compare with.
-   *
-   * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
-   */
-  bool
-  operator<(const Name& other) const
-  {
-    return compare(other) < 0;
-  }
-
-  /**
-   * Return true if this is less than or equal to the other Name in the NDN canonical ordering.
-   * @param other The other Name to compare with.
-   *
-   * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
-   */
-  bool
-  operator>=(const Name& other) const
-  {
-    return compare(other) >= 0;
-  }
-
-  /**
-   * Return true if this is greater than the other Name in the NDN canonical ordering.
-   * @param other The other Name to compare with.
-   *
-   * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
-   */
-  bool
-  operator>(const Name& other) const
-  {
-    return compare(other) > 0;
-  }
-
-  //
-  // Iterator interface to name components.
-  //
-
-  /**
-   * Begin iterator (const).
+public: // iterators
+  /** @brief Begin iterator
    */
   const_iterator
   begin() const
   {
-    return reinterpret_cast<const_iterator>(&*m_nameBlock.elements().begin());
+    // XXX This triggers undefined behavior if name is empty (#4181)
+    return reinterpret_cast<const_iterator>(&*m_wire.elements().begin());
   }
 
-  /**
-   * End iterator (const).
-   *
-   * @todo Check if this crash when there are no elements in the buffer
+  /** @brief End iterator
    */
   const_iterator
   end() const
   {
-    return reinterpret_cast<const_iterator>(&*m_nameBlock.elements().end());
+    // XXX This triggers undefined behavior if name is empty (#4181)
+    return reinterpret_cast<const_iterator>(&*m_wire.elements().end());
   }
 
-  /**
-   * Reverse begin iterator (const).
+  /** @brief Reverse begin iterator
    */
   const_reverse_iterator
   rbegin() const
@@ -590,8 +242,7 @@
     return const_reverse_iterator(end());
   }
 
-  /**
-   * Reverse end iterator (const).
+  /** @brief Reverse end iterator
    */
   const_reverse_iterator
   rend() const
@@ -599,30 +250,350 @@
     return const_reverse_iterator(begin());
   }
 
+public: // modifiers
+  /** @brief Append a component
+   *  @return a reference to this name, to allow chaining
+   */
+  Name&
+  append(const Component& component)
+  {
+    m_wire.push_back(component);
+    return *this;
+  }
+
+  /** @brief Append a component, copying from a zero-terminated byte string
+   *  @param value a zero-terminated string; it will be constructed as a component as is
+   *               and will not be interpreted as a URI
+   *  @note This overload is necessary to ensure unambiguity of ``append("string")``, because both
+   *        Component and PartialName are implicitly convertible from zero-terminated byte string.
+   *        This overload ensures the string is constructed as a Component.
+   */
+  Name&
+  append(const char* value)
+  {
+    return append(Component(value));
+  }
+
+  /** @brief Append a component, copying from [@p value, @p value + @p valueLength)
+   *  @return a reference to this name, to allow chaining
+   */
+  Name&
+  append(const uint8_t* value, size_t valueLength)
+  {
+    return append(Component(value, valueLength));
+  }
+
+  /** @brief Append a component, copying from [@p first, @p last)
+   *  @tparam Iterator an InputIterator dereferencing to a one-octet value type. More efficient
+   *                   implementation is available when @p Iterator additionally satisfies
+   *                   RandomAccessIterator concept.
+   *  @param first     begin position of the value
+   *  @param last      end position of the value
+   *  @return a reference to this name, to allow chaining
+   */
+  template<class Iterator>
+  Name&
+  append(Iterator first, Iterator last)
+  {
+    static_assert(sizeof(std::iterator_traits<Iterator>::value_type) == 1,
+                  "iterator does not dereference to one-octet value type");
+    return append(Component(first, last));
+  }
+
+  /** @brief Append a component, decoding from a Block
+   *  @param value a Block; if its TLV-TYPE is not NameComponent, it is nested into a NameComponent
+   *  @return a reference to this name, to allow chaining
+   */
+  Name&
+  append(const Block& value)
+  {
+    if (value.type() == tlv::NameComponent) {
+      m_wire.push_back(value);
+    }
+    else {
+      m_wire.push_back(Block(tlv::NameComponent, value));
+    }
+
+    return *this;
+  }
+
+  /** @brief Append a component with a nonNegativeInteger
+   *  @sa number the number
+   *  @return a reference to this name, to allow chaining
+   *  @sa https://named-data.net/doc/ndn-tlv/tlv.html#non-negative-integer-encoding
+   */
+  Name&
+  appendNumber(uint64_t number)
+  {
+    return append(Component::fromNumber(number));
+  }
+
+  /** @brief Append a component with a marked number
+   *  @param marker 1-octet marker
+   *  @param number the number
+   *
+   *  The component is encoded as a 1-octet marker, followed by a nonNegativeInteger.
+   *
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendNumberWithMarker(uint8_t marker, uint64_t number)
+  {
+    return append(Component::fromNumberWithMarker(marker, number));
+  }
+
+  /** @brief Append a version component
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendVersion(uint64_t version)
+  {
+    return append(Component::fromVersion(version));
+  }
+
+  /** @brief Append a version component based on current time
+   *
+   *  The version number is the current UNIX timestamp in milliseconds
+   *
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendVersion();
+
+  /** @brief Append a segment number (sequential) component
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendSegment(uint64_t segmentNo)
+  {
+    return append(Component::fromSegment(segmentNo));
+  }
+
+  /** @brief Append a segment byte offset component
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendSegmentOffset(uint64_t offset)
+  {
+    return append(Component::fromSegmentOffset(offset));
+  }
+
+  /** @brief Append a timestamp component
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendTimestamp(const time::system_clock::TimePoint& timePoint)
+  {
+    return append(Component::fromTimestamp(timePoint));
+  }
+
+  /** @brief Append a timestamp component based on current time
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendTimestamp();
+
+  /** @brief Append a sequence number component
+   *  @return a reference to this name, to allow chaining
+   *  @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   */
+  Name&
+  appendSequenceNumber(uint64_t seqNo)
+  {
+    return append(Component::fromSequenceNumber(seqNo));
+  }
+
+  /** @brief Append an ImplicitSha256Digest component
+   *  @return a reference to this name, to allow chaining
+   */
+  Name&
+  appendImplicitSha256Digest(const ConstBufferPtr& digest)
+  {
+    return append(Component::fromImplicitSha256Digest(digest));
+  }
+
+  /** @brief Append an ImplicitSha256Digest component
+   *  @return a reference to this name, to allow chaining
+   */
+  Name&
+  appendImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
+  {
+    return append(Component::fromImplicitSha256Digest(digest, digestSize));
+  }
+
+  /** @brief Append a PartialName
+   *  @param name the components to append
+   *  @return a reference to this name, to allow chaining
+   */
+  Name&
+  append(const PartialName& name);
+
+  /** @brief Append a component
+   *  @note This makes push_back an alias of append, giving Name a similar API as STL vector.
+   */
+  template<class T>
+  void
+  push_back(const T& component)
+  {
+    append(component);
+  }
+
+  /** @brief Remove all components
+   *  @post empty() == true
+   */
+  void
+  clear()
+  {
+    m_wire = Block(tlv::Name);
+  }
+
+public: // algorithms
+  /** @brief Get the successor of a name
+   *
+   *  The successor of a name is defined as follows:
+   *
+   *      N represents the set of NDN Names, and X,Y ∈ N.
+   *      Operator < is defined by canonical order on N.
+   *      Y is the successor of X, if (a) X < Y, and (b) ∄ Z ∈ N s.t. X < Z < Y.
+   *
+   *  In plain words, successor of a name is the same name, but with its last component
+   *  advanced to a next possible value.
+   *
+   *  Examples:
+   *
+   *  - successor for / is /%00
+   *  - successor for /%00%01/%01%02 is /%00%01/%01%03
+   *  - successor for /%00%01/%01%FF is /%00%01/%02%00
+   *  - successor for /%00%01/%FF%FF is /%00%01/%00%00%00
+   *
+   *  @return a new Name containing the successor
+   */
+  Name
+  getSuccessor() const;
+
+  /** @brief Check if this name is a prefix of another name
+   *
+   *  This name is a prefix of @p other if the N components of this name are same as the first N
+   *  components of @p other.
+   *
+   *  @retval true this name is a prefix of @p other
+   *  @retval false this name is not a prefix of @p other
+   */
+  bool
+  isPrefixOf(const Name& other) const;
+
+  /** @brief Check if this name equals another name
+   *
+   *  Two names are equal if they have the same number of components, and components at each index
+   *  are equal.
+   */
+  bool
+  equals(const Name& other) const;
+
+  /** @brief Compare this to the other Name using NDN canonical ordering.
+   *
+   *  If the first components of each name are not equal, this returns a negative value if
+   *  the first comes before the second using the NDN canonical ordering for name
+   *  components, or a positive value if it comes after.  If they are equal, this compares
+   *  the second components of each name, etc. If both names are the same up to the size
+   *  of the shorter name, this returns a negative value if the first name is shorter than
+   *  the second or a positive value if it is longer.  For example, if you std::sort gives:
+   *  /a/b/d /a/b/cc /c /c/a /bb .
+   *  This is intuitive because all names with the prefix /a are next to each other.
+   *  But it may be also be counter-intuitive because /c comes before /bb according
+   *  to NDN canonical ordering since it is shorter.
+   *
+   *  @param other The other Name to compare with.
+   *
+   *  @retval negative this comes before other in canonical ordering
+   *  @retval zero this equals other
+   *  @retval positive this comes after other in canonical ordering
+   *
+   *  @sa https://named-data.net/doc/ndn-tlv/name.html#canonical-order
+   */
+  int
+  compare(const Name& other) const
+  {
+    return this->compare(0, npos, other);
+  }
+
+  /** @brief compares [pos1, pos1+count1) components in this Name
+   *         to [pos2, pos2+count2) components in @p other
+   *
+   *  This is equivalent to this->getSubName(pos1, count1).compare(other.getSubName(pos2, count2));
+   */
+  int
+  compare(size_t pos1, size_t count1,
+          const Name& other, size_t pos2 = 0, size_t count2 = npos) const;
+
 public:
   /** \brief indicates "until the end" in getSubName and compare
    */
   static const size_t npos;
 
 private:
-  mutable Block m_nameBlock;
+  mutable Block m_wire;
 };
 
+inline bool
+operator==(const Name& lhs, const Name& rhs)
+{
+  return lhs.equals(rhs);
+}
+
+inline bool
+operator!=(const Name& lhs, const Name& rhs)
+{
+  return !lhs.equals(rhs);
+}
+
+inline bool
+operator<=(const Name& lhs, const Name& rhs)
+{
+  return lhs.compare(rhs) <= 0;
+}
+
+inline bool
+operator<(const Name& lhs, const Name& rhs)
+{
+  return lhs.compare(rhs) < 0;
+}
+
+inline bool
+operator>=(const Name& lhs, const Name& rhs)
+{
+  return lhs.compare(rhs) >= 0;
+}
+
+inline bool
+operator>(const Name& lhs, const Name& rhs)
+{
+  return lhs.compare(rhs) > 0;
+}
+
+/** @brief Print URI representation of a name
+ *  @sa https://named-data.net/doc/ndn-tlv/name.html#ndn-uri-scheme
+ */
 std::ostream&
 operator<<(std::ostream& os, const Name& name);
 
+/** @brief Parse URI from stream as Name
+ *  @sa https://named-data.net/doc/ndn-tlv/name.html#ndn-uri-scheme
+ */
 std::istream&
 operator>>(std::istream& is, Name& name);
 
-inline bool
-Name::hasWire() const
-{
-  return m_nameBlock.hasWire();
-}
-
 } // namespace ndn
 
 namespace std {
+
 template<>
 struct hash<ndn::Name>
 {