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>
 {
diff --git a/tests/unit-tests/name-component.t.cpp b/tests/unit-tests/name-component.t.cpp
index 8a99b2f..651b160 100644
--- a/tests/unit-tests/name-component.t.cpp
+++ b/tests/unit-tests/name-component.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -20,6 +20,7 @@
  */
 
 #include "name-component.hpp"
+#include "name.hpp"
 
 #include "boost-test.hpp"
 #include <boost/mpl/vector.hpp>
@@ -205,6 +206,149 @@
 
 BOOST_AUTO_TEST_SUITE_END() // CreateFromIterators
 
+BOOST_AUTO_TEST_SUITE(NamingConvention)
+
+template<typename ArgType>
+struct ConventionTest
+{
+  function<name::Component(ArgType)> makeComponent;
+  function<ArgType(const name::Component&)> getValue;
+  function<Name&(Name&, ArgType)> append;
+  Name expected;
+  ArgType value;
+  function<bool(const name::Component&)> isComponent;
+};
+
+class NumberWithMarker
+{
+public:
+  ConventionTest<uint64_t>
+  operator()() const
+  {
+    return {bind(&name::Component::fromNumberWithMarker, 0xAA, _1),
+            bind(&name::Component::toNumberWithMarker, _1, 0xAA),
+            bind(&Name::appendNumberWithMarker, _1, 0xAA, _2),
+            Name("/%AA%03%E8"),
+            1000,
+            bind(&name::Component::isNumberWithMarker, _1, 0xAA)};
+  }
+};
+
+class Segment
+{
+public:
+  ConventionTest<uint64_t>
+  operator()() const
+  {
+    return {&name::Component::fromSegment,
+            bind(&name::Component::toSegment, _1),
+            bind(&Name::appendSegment, _1, _2),
+            Name("/%00%27%10"),
+            10000,
+            bind(&name::Component::isSegment, _1)};
+  }
+};
+
+class SegmentOffset
+{
+public:
+  ConventionTest<uint64_t>
+  operator()() const
+  {
+    return {&name::Component::fromSegmentOffset,
+            bind(&name::Component::toSegmentOffset, _1),
+            bind(&Name::appendSegmentOffset, _1, _2),
+            Name("/%FB%00%01%86%A0"),
+            100000,
+            bind(&name::Component::isSegmentOffset, _1)};
+  }
+};
+
+class Version
+{
+public:
+  ConventionTest<uint64_t>
+  operator()() const
+  {
+    return {&name::Component::fromVersion,
+            bind(&name::Component::toVersion, _1),
+            [] (Name& name, uint64_t version) -> Name& { return name.appendVersion(version); },
+            Name("/%FD%00%0FB%40"),
+            1000000,
+            bind(&name::Component::isVersion, _1)};
+  }
+};
+
+class Timestamp
+{
+public:
+  ConventionTest<time::system_clock::TimePoint>
+  operator()() const
+  {
+    return {&name::Component::fromTimestamp,
+            bind(&name::Component::toTimestamp, _1),
+            [] (Name& name, time::system_clock::TimePoint t) -> Name& { return name.appendTimestamp(t); },
+            Name("/%FC%00%04%7BE%E3%1B%00%00"),
+            time::getUnixEpoch() + time::days(14600), // 40 years
+            bind(&name::Component::isTimestamp, _1)};
+  }
+};
+
+class SequenceNumber
+{
+public:
+  ConventionTest<uint64_t>
+  operator()() const
+  {
+    return {&name::Component::fromSequenceNumber,
+            bind(&name::Component::toSequenceNumber, _1),
+            bind(&Name::appendSequenceNumber, _1, _2),
+            Name("/%FE%00%98%96%80"),
+            10000000,
+            bind(&name::Component::isSequenceNumber, _1)};
+  }
+};
+
+using ConventionTests = boost::mpl::vector<
+  NumberWithMarker,
+  Segment,
+  SegmentOffset,
+  Version,
+  Timestamp,
+  SequenceNumber
+>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Convention, T, ConventionTests)
+{
+  name::Component invalidComponent1;
+  name::Component invalidComponent2("1234567890");
+
+  auto test = T()();
+
+  const Name& expected = test.expected;
+  BOOST_TEST_MESSAGE("Check " << expected[0].toUri());
+
+  BOOST_CHECK_EQUAL(expected[0].isGeneric(), true);
+
+  name::Component actualComponent = test.makeComponent(test.value);
+  BOOST_CHECK_EQUAL(actualComponent, expected[0]);
+
+  Name actualName;
+  test.append(actualName, test.value);
+  BOOST_CHECK_EQUAL(actualName, expected);
+
+  BOOST_CHECK_EQUAL(test.isComponent(expected[0]), true);
+  BOOST_CHECK_EQUAL(test.getValue(expected[0]), test.value);
+
+  BOOST_CHECK_EQUAL(test.isComponent(invalidComponent1), false);
+  BOOST_CHECK_EQUAL(test.isComponent(invalidComponent2), false);
+
+  BOOST_CHECK_THROW(test.getValue(invalidComponent1), name::Component::Error);
+  BOOST_CHECK_THROW(test.getValue(invalidComponent2), name::Component::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // NamingConvention
+
 BOOST_AUTO_TEST_SUITE_END() // TestNameComponent
 
 } // namespace tests
diff --git a/tests/unit-tests/name.t.cpp b/tests/unit-tests/name.t.cpp
index 9840196..dff17e4 100644
--- a/tests/unit-tests/name.t.cpp
+++ b/tests/unit-tests/name.t.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).
@@ -22,8 +22,6 @@
 #include "name.hpp"
 
 #include "boost-test.hpp"
-#include <boost/tuple/tuple.hpp>
-#include <boost/mpl/vector.hpp>
 #include <unordered_map>
 
 namespace ndn {
@@ -51,6 +49,8 @@
                            0x8,  0x3, // NameComponent
                              0x6e,  0x64,  0x6e};
 
+// ---- encoding, decoding ----
+
 BOOST_AUTO_TEST_CASE(Basic)
 {
   Name name("/hello/world");
@@ -89,23 +89,6 @@
   BOOST_CHECK_EQUAL(name.toUri(), "/local/ndn/prefix");
 }
 
-BOOST_AUTO_TEST_CASE(AppendsAndMultiEncode)
-{
-  Name name("/local");
-
-  BOOST_CHECK_EQUAL_COLLECTIONS(name.wireEncode().begin(), name.wireEncode().end(),
-                                Name1, Name1 + sizeof(Name1));
-
-  name.append("ndn");
-
-  BOOST_CHECK_EQUAL_COLLECTIONS(name.wireEncode().begin(), name.wireEncode().end(),
-                                Name2, Name2 + sizeof(Name2));
-
-  name.append("prefix");
-  BOOST_CHECK_EQUAL_COLLECTIONS(name.wireEncode().begin(), name.wireEncode().end(),
-                                TestName, TestName+sizeof(TestName));
-}
-
 BOOST_AUTO_TEST_CASE(ZeroLengthComponent)
 {
   static const uint8_t compOctets[] {0x08, 0x00};
@@ -130,183 +113,6 @@
   BOOST_CHECK(name2Encoded == nameBlock);
 }
 
-BOOST_AUTO_TEST_CASE(AppendNumber)
-{
-  Name name;
-  for (uint32_t i = 0; i < 10; i++)
-    {
-      name.appendNumber(i);
-    }
-
-  BOOST_CHECK_EQUAL(name.size(), 10);
-
-  for (uint32_t i = 0; i < 10; i++)
-    {
-      BOOST_CHECK_EQUAL(name[i].toNumber(), i);
-    }
-}
-
-class Numeric
-{
-public:
-  typedef std::list<boost::tuple<function<name::Component(uint64_t)>,
-                                 function<uint64_t(const name::Component&)>,
-                                 function<Name&(Name&, uint64_t)>,
-                                 Name/*expected*/,
-                                 uint64_t/*value*/,
-                                 function<bool(const name::Component&)> > > Dataset;
-
-  Numeric()
-  {
-    dataset.push_back(boost::make_tuple(bind(&name::Component::fromNumberWithMarker,
-                                             0xAA, _1),
-                                        bind(&name::Component::toNumberWithMarker, _1, 0xAA),
-                                        bind(&Name::appendNumberWithMarker, _1, 0xAA, _2),
-                                        Name("/%AA%03%E8"),
-                                        1000,
-                                        bind(&name::Component::isNumberWithMarker, _1, 0xAA)));
-    dataset.push_back(boost::make_tuple(&name::Component::fromSegment,
-                                        bind(&name::Component::toSegment, _1),
-                                        bind(&Name::appendSegment, _1, _2),
-                                        Name("/%00%27%10"),
-                                        10000,
-                                        bind(&name::Component::isSegment, _1)));
-    dataset.push_back(boost::make_tuple(&name::Component::fromSegmentOffset,
-                                        bind(&name::Component::toSegmentOffset, _1),
-                                        bind(&Name::appendSegmentOffset, _1, _2),
-                                        Name("/%FB%00%01%86%A0"),
-                                        100000,
-                                        bind(&name::Component::isSegmentOffset, _1)));
-    dataset.push_back(boost::make_tuple(&name::Component::fromVersion,
-                                        bind(&name::Component::toVersion, _1),
-                                        bind(static_cast<Name&(Name::*)(uint64_t)>(
-                                               &Name::appendVersion), _1, _2),
-                                        Name("/%FD%00%0FB%40"),
-                                        1000000,
-                                        bind(&name::Component::isVersion, _1)));
-    dataset.push_back(boost::make_tuple(&name::Component::fromSequenceNumber,
-                                        bind(&name::Component::toSequenceNumber, _1),
-                                        bind(&Name::appendSequenceNumber, _1, _2),
-                                        Name("/%FE%00%98%96%80"),
-                                        10000000,
-                                        bind(&name::Component::isSequenceNumber, _1)));
-  }
-
-  Dataset dataset;
-};
-
-class Timestamp
-{
-public:
-  typedef std::list<boost::tuple<function<name::Component(const time::system_clock::TimePoint&)>,
-                                 function<time::system_clock::TimePoint(const name::Component&)>,
-                                 function<Name&(Name&, const time::system_clock::TimePoint&)>,
-                                 Name/*expected*/,
-                                 time::system_clock::TimePoint/*value*/,
-                                 function<bool(const name::Component&)> > > Dataset;
-  Timestamp()
-  {
-    dataset.push_back(boost::make_tuple(&name::Component::fromTimestamp,
-                                        ndn::bind(&name::Component::toTimestamp, _1),
-                                        ndn::bind(&Name::appendTimestamp, _1, _2),
-                                        Name("/%FC%00%04%7BE%E3%1B%00%00"),
-                                        time::getUnixEpoch() + time::days(14600/*40 years*/),
-                                        bind(&name::Component::isTimestamp, _1)));
-  }
-
-  Dataset dataset;
-};
-
-typedef boost::mpl::vector<Numeric, Timestamp> ConventionsDatasets;
-
-BOOST_FIXTURE_TEST_CASE_TEMPLATE(NamingConventions, T, ConventionsDatasets, T)
-{
-  // // These octets are obtained by the snippet below.
-  // // This check is intended to detect unexpected encoding change in the future.
-  // for (typename T::Dataset::const_iterator it = this->dataset.begin();
-  //      it != this->dataset.end(); ++it) {
-  //   Name name;
-  //   name.append(it->template get<0>()(it->template get<4>()));
-  //   std::cout << name << std::endl;
-  // }
-
-  name::Component invalidComponent1;
-  name::Component invalidComponent2("1234567890");
-
-  for (typename T::Dataset::const_iterator it = this->dataset.begin();
-       it != this->dataset.end(); ++it) {
-    const Name& expected = it->template get<3>();
-    BOOST_TEST_MESSAGE("Check " << expected[0].toUri());
-
-    BOOST_CHECK_EQUAL(expected[0].isGeneric(), true);
-
-    name::Component actualComponent = it->template get<0>()(it->template get<4>());
-    BOOST_CHECK_EQUAL(actualComponent, expected[0]);
-
-    Name actualName;
-    it->template get<2>()(actualName, it->template get<4>());
-    BOOST_CHECK_EQUAL(actualName, expected);
-
-    BOOST_CHECK_EQUAL(it->template get<5>()(expected[0]), true);
-    BOOST_REQUIRE_NO_THROW(it->template get<1>()(expected[0]));
-    BOOST_CHECK_EQUAL(it->template get<1>()(expected[0]), it->template get<4>());
-
-    BOOST_CHECK_EQUAL(it->template get<5>()(invalidComponent1), false);
-    BOOST_CHECK_EQUAL(it->template get<5>()(invalidComponent2), false);
-
-    BOOST_REQUIRE_THROW(it->template get<1>()(invalidComponent1), name::Component::Error);
-    BOOST_REQUIRE_THROW(it->template get<1>()(invalidComponent2), name::Component::Error);
-  }
-}
-
-BOOST_AUTO_TEST_CASE(GetSuccessor)
-{
-  BOOST_CHECK_EQUAL(Name("ndn:/%00%01/%01%02").getSuccessor(), Name("ndn:/%00%01/%01%03"));
-  BOOST_CHECK_EQUAL(Name("ndn:/%00%01/%01%FF").getSuccessor(), Name("ndn:/%00%01/%02%00"));
-  BOOST_CHECK_EQUAL(Name("ndn:/%00%01/%FF%FF").getSuccessor(), Name("ndn:/%00%01/%00%00%00"));
-  BOOST_CHECK_EQUAL(Name().getSuccessor(), Name("ndn:/%00"));
-}
-
-BOOST_AUTO_TEST_CASE(Markers)
-{
-  Name name;
-  uint64_t number;
-
-  BOOST_REQUIRE_NO_THROW(number = name.appendSegment(30923).at(-1).toSegment());
-  BOOST_CHECK_EQUAL(number, 30923);
-
-  BOOST_REQUIRE_NO_THROW(number = name.appendSegmentOffset(589).at(-1).toSegmentOffset());
-  BOOST_CHECK_EQUAL(number, 589);
-
-  BOOST_REQUIRE_NO_THROW(number = name.appendVersion().at(-1).toVersion());
-
-  BOOST_REQUIRE_NO_THROW(number = name.appendVersion(25912).at(-1).toVersion());
-  BOOST_CHECK_EQUAL(number, 25912);
-
-  const time::system_clock::TimePoint tp = time::system_clock::now();
-  time::system_clock::TimePoint tp2;
-  BOOST_REQUIRE_NO_THROW(tp2 = name.appendTimestamp(tp).at(-1).toTimestamp());
-  BOOST_CHECK_LE(std::abs(time::duration_cast<time::microseconds>(tp2 - tp).count()), 1);
-
-  BOOST_REQUIRE_NO_THROW(number = name.appendSequenceNumber(11676).at(-1).toSequenceNumber());
-  BOOST_CHECK_EQUAL(number, 11676);
-}
-
-BOOST_AUTO_TEST_CASE(UnorderedMap)
-{
-  std::unordered_map<Name, int> map;
-  Name name1("/1");
-  Name name2("/2");
-  Name name3("/3");
-  map[name1] = 1;
-  map[name2] = 2;
-  map[name3] = 3;
-
-  BOOST_CHECK_EQUAL(map[name1], 1);
-  BOOST_CHECK_EQUAL(map[name2], 2);
-  BOOST_CHECK_EQUAL(map[name3], 3);
-}
-
 BOOST_AUTO_TEST_CASE(ImplicitSha256Digest)
 {
   Name n;
@@ -361,54 +167,6 @@
   BOOST_CHECK(n2.get(1).isGeneric());
 }
 
-BOOST_AUTO_TEST_CASE(Compare)
-{
-  BOOST_CHECK_EQUAL(Name("/A")  .compare(Name("/A")),   0);
-  BOOST_CHECK_EQUAL(Name("/A")  .compare(Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/A")  .compare(Name("/B")),   0);
-  BOOST_CHECK_GT   (Name("/B")  .compare(Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/A")  .compare(Name("/AA")),  0);
-  BOOST_CHECK_GT   (Name("/AA") .compare(Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/A")  .compare(Name("/A/C")), 0);
-  BOOST_CHECK_GT   (Name("/A/C").compare(Name("/A")),   0);
-
-  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/A")),   0);
-  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/B")),   0);
-  BOOST_CHECK_GT   (Name("/Z/B/Y")  .compare(1, 1, Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/AA")),  0);
-  BOOST_CHECK_GT   (Name("/Z/AA/Y") .compare(1, 1, Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/A/C")), 0);
-  BOOST_CHECK_GT   (Name("/Z/A/C/Y").compare(1, 2, Name("/A")),   0);
-
-  BOOST_CHECK_EQUAL(Name("/Z/A")  .compare(1, Name::npos, Name("/A")),   0);
-  BOOST_CHECK_EQUAL(Name("/Z/A")  .compare(1, Name::npos, Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/Z/A")  .compare(1, Name::npos, Name("/B")),   0);
-  BOOST_CHECK_GT   (Name("/Z/B")  .compare(1, Name::npos, Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/Z/A")  .compare(1, Name::npos, Name("/AA")),  0);
-  BOOST_CHECK_GT   (Name("/Z/AA") .compare(1, Name::npos, Name("/A")),   0);
-  BOOST_CHECK_LT   (Name("/Z/A")  .compare(1, Name::npos, Name("/A/C")), 0);
-  BOOST_CHECK_GT   (Name("/Z/A/C").compare(1, Name::npos, Name("/A")),   0);
-
-  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/X/A/W"),   1, 1), 0);
-  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/X/A/W"),   1, 1), 0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/B/W"),   1, 1), 0);
-  BOOST_CHECK_GT   (Name("/Z/B/Y")  .compare(1, 1, Name("/X/A/W"),   1, 1), 0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/AA/W"),  1, 1), 0);
-  BOOST_CHECK_GT   (Name("/Z/AA/Y") .compare(1, 1, Name("/X/A/W"),   1, 1), 0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/A/C/W"), 1, 2), 0);
-  BOOST_CHECK_GT   (Name("/Z/A/C/Y").compare(1, 2, Name("/X/A/W"),   1, 1), 0);
-
-  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/X/A"),   1), 0);
-  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/X/A"),   1), 0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/B"),   1), 0);
-  BOOST_CHECK_GT   (Name("/Z/B/Y")  .compare(1, 1, Name("/X/A"),   1), 0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/AA"),  1), 0);
-  BOOST_CHECK_GT   (Name("/Z/AA/Y") .compare(1, 1, Name("/X/A"),   1), 0);
-  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/A/C"), 1), 0);
-  BOOST_CHECK_GT   (Name("/Z/A/C/Y").compare(1, 2, Name("/X/A"),   1), 0);
-}
-
 BOOST_AUTO_TEST_CASE(NameWithSpaces)
 {
   Name name("/ hello\t/\tworld \r\n");
@@ -417,6 +175,62 @@
   BOOST_CHECK_THROW(Name("/hello//world"), name::Component::Error);
 }
 
+BOOST_AUTO_TEST_CASE(DeepCopy)
+{
+  Name n1("/hello/world");
+  Name n2 = n1.deepCopy();
+
+  BOOST_CHECK_EQUAL(n1, n2);
+  BOOST_CHECK_NE(&n1.wireEncode(), &n2.wireEncode());
+
+  EncodingBuffer buffer(1024, 0);
+  n1.wireEncode(buffer);
+  Name n3(buffer.block());
+
+  BOOST_CHECK_EQUAL(n1, n3);
+  BOOST_CHECK_EQUAL(n3.wireEncode().getBuffer()->size(), 1024);
+  n3 = n3.deepCopy();
+
+  BOOST_CHECK_LT(n3.wireEncode().size(), 1024);
+  BOOST_CHECK_EQUAL(n3.wireEncode().getBuffer()->size(), n3.wireEncode().size());
+}
+
+// ---- iterators ----
+
+BOOST_AUTO_TEST_CASE(ForwardIterator)
+{
+  name::Component comps[] {
+    name::Component("A"),
+    name::Component("B"),
+    name::Component("C"),
+    name::Component("D")
+  };
+
+  Name n0;
+  BOOST_CHECK_EQUAL_COLLECTIONS(n0.begin(), n0.end(), comps, comps + 0);
+
+  Name n4("/A/B/C/D");
+  BOOST_CHECK_EQUAL_COLLECTIONS(n4.begin(), n4.end(), comps, comps + 4);
+}
+
+BOOST_AUTO_TEST_CASE(ReverseIterator)
+{
+  name::Component comps[] {
+    name::Component("D"),
+    name::Component("C"),
+    name::Component("B"),
+    name::Component("A")
+  };
+
+  Name n0;
+  BOOST_CHECK_EQUAL_COLLECTIONS(n0.rbegin(), n0.rend(), comps, comps + 0);
+
+  Name n4("/A/B/C/D");
+  BOOST_CHECK_EQUAL_COLLECTIONS(n4.rbegin(), n4.rend(), comps, comps + 4);
+}
+
+// ---- modifiers ----
+
 BOOST_AUTO_TEST_CASE(Append)
 {
   PartialName toAppend("/and");
@@ -436,6 +250,126 @@
   }
 }
 
+BOOST_AUTO_TEST_CASE(AppendsAndMultiEncode)
+{
+  Name name("/local");
+  BOOST_CHECK_EQUAL_COLLECTIONS(name.wireEncode().begin(), name.wireEncode().end(),
+                                Name1, Name1 + sizeof(Name1));
+
+  name.append("ndn");
+  BOOST_CHECK_EQUAL_COLLECTIONS(name.wireEncode().begin(), name.wireEncode().end(),
+                                Name2, Name2 + sizeof(Name2));
+
+  name.append("prefix");
+  BOOST_CHECK_EQUAL_COLLECTIONS(name.wireEncode().begin(), name.wireEncode().end(),
+                                TestName, TestName+sizeof(TestName));
+}
+
+BOOST_AUTO_TEST_CASE(AppendNumber)
+{
+  Name name;
+  for (uint32_t i = 0; i < 10; i++) {
+    name.appendNumber(i);
+  }
+
+  BOOST_CHECK_EQUAL(name.size(), 10);
+
+  for (uint32_t i = 0; i < 10; i++) {
+    BOOST_CHECK_EQUAL(name[i].toNumber(), i);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(Markers)
+{
+  // TestNameComponent/NamingConvention provides additional coverage for these methods,
+  // including verifications of the wire format.
+
+  Name name;
+  uint64_t number;
+
+  BOOST_REQUIRE_NO_THROW(number = name.appendSegment(30923).at(-1).toSegment());
+  BOOST_CHECK_EQUAL(number, 30923);
+
+  BOOST_REQUIRE_NO_THROW(number = name.appendSegmentOffset(589).at(-1).toSegmentOffset());
+  BOOST_CHECK_EQUAL(number, 589);
+
+  BOOST_REQUIRE_NO_THROW(number = name.appendVersion().at(-1).toVersion());
+
+  BOOST_REQUIRE_NO_THROW(number = name.appendVersion(25912).at(-1).toVersion());
+  BOOST_CHECK_EQUAL(number, 25912);
+
+  const time::system_clock::TimePoint tp = time::system_clock::now();
+  time::system_clock::TimePoint tp2;
+  BOOST_REQUIRE_NO_THROW(tp2 = name.appendTimestamp(tp).at(-1).toTimestamp());
+  BOOST_CHECK_LE(std::abs(time::duration_cast<time::microseconds>(tp2 - tp).count()), 1);
+
+  BOOST_REQUIRE_NO_THROW(number = name.appendSequenceNumber(11676).at(-1).toSequenceNumber());
+  BOOST_CHECK_EQUAL(number, 11676);
+}
+
+BOOST_AUTO_TEST_CASE(Clear)
+{
+  Name n("/A");
+  BOOST_CHECK_EQUAL(n.empty(), false);
+
+  n.clear();
+  BOOST_CHECK_EQUAL(n.empty(), true);
+  BOOST_CHECK_EQUAL(n.size(), 0);
+}
+
+// ---- algorithms ----
+
+BOOST_AUTO_TEST_CASE(GetSuccessor)
+{
+  BOOST_CHECK_EQUAL(Name("ndn:/%00%01/%01%02").getSuccessor(), Name("ndn:/%00%01/%01%03"));
+  BOOST_CHECK_EQUAL(Name("ndn:/%00%01/%01%FF").getSuccessor(), Name("ndn:/%00%01/%02%00"));
+  BOOST_CHECK_EQUAL(Name("ndn:/%00%01/%FF%FF").getSuccessor(), Name("ndn:/%00%01/%00%00%00"));
+  BOOST_CHECK_EQUAL(Name().getSuccessor(), Name("ndn:/%00"));
+}
+
+BOOST_AUTO_TEST_CASE(Compare)
+{
+  BOOST_CHECK_EQUAL(Name("/A")  .compare(Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/A")  .compare(Name("/B")),   0);
+  BOOST_CHECK_GT   (Name("/B")  .compare(Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/A")  .compare(Name("/AA")),  0);
+  BOOST_CHECK_GT   (Name("/AA") .compare(Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/A")  .compare(Name("/A/C")), 0);
+  BOOST_CHECK_GT   (Name("/A/C").compare(Name("/A")),   0);
+
+  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/B")),   0);
+  BOOST_CHECK_GT   (Name("/Z/B/Y")  .compare(1, 1, Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/AA")),  0);
+  BOOST_CHECK_GT   (Name("/Z/AA/Y") .compare(1, 1, Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/A/C")), 0);
+  BOOST_CHECK_GT   (Name("/Z/A/C/Y").compare(1, 2, Name("/A")),   0);
+
+  BOOST_CHECK_EQUAL(Name("/Z/A")  .compare(1, Name::npos, Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/Z/A")  .compare(1, Name::npos, Name("/B")),   0);
+  BOOST_CHECK_GT   (Name("/Z/B")  .compare(1, Name::npos, Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/Z/A")  .compare(1, Name::npos, Name("/AA")),  0);
+  BOOST_CHECK_GT   (Name("/Z/AA") .compare(1, Name::npos, Name("/A")),   0);
+  BOOST_CHECK_LT   (Name("/Z/A")  .compare(1, Name::npos, Name("/A/C")), 0);
+  BOOST_CHECK_GT   (Name("/Z/A/C").compare(1, Name::npos, Name("/A")),   0);
+
+  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/X/A/W"),   1, 1), 0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/B/W"),   1, 1), 0);
+  BOOST_CHECK_GT   (Name("/Z/B/Y")  .compare(1, 1, Name("/X/A/W"),   1, 1), 0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/AA/W"),  1, 1), 0);
+  BOOST_CHECK_GT   (Name("/Z/AA/Y") .compare(1, 1, Name("/X/A/W"),   1, 1), 0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/A/C/W"), 1, 2), 0);
+  BOOST_CHECK_GT   (Name("/Z/A/C/Y").compare(1, 2, Name("/X/A/W"),   1, 1), 0);
+
+  BOOST_CHECK_EQUAL(Name("/Z/A/Y")  .compare(1, 1, Name("/X/A"),   1), 0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/B"),   1), 0);
+  BOOST_CHECK_GT   (Name("/Z/B/Y")  .compare(1, 1, Name("/X/A"),   1), 0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/AA"),  1), 0);
+  BOOST_CHECK_GT   (Name("/Z/AA/Y") .compare(1, 1, Name("/X/A"),   1), 0);
+  BOOST_CHECK_LT   (Name("/Z/A/Y")  .compare(1, 1, Name("/X/A/C"), 1), 0);
+  BOOST_CHECK_GT   (Name("/Z/A/C/Y").compare(1, 2, Name("/X/A"),   1), 0);
+}
+
 BOOST_AUTO_TEST_CASE(SubName)
 {
   Name name("/hello/world");
@@ -474,24 +408,19 @@
   BOOST_CHECK_EQUAL("/first/second/last", name.getSubName(-10, 10));
 }
 
-BOOST_AUTO_TEST_CASE(DeepCopy)
+BOOST_AUTO_TEST_CASE(UnorderedMap)
 {
-  Name n1("/hello/world");
-  Name n2 = n1.deepCopy();
+  std::unordered_map<Name, int> map;
+  Name name1("/1");
+  Name name2("/2");
+  Name name3("/3");
+  map[name1] = 1;
+  map[name2] = 2;
+  map[name3] = 3;
 
-  BOOST_CHECK_EQUAL(n1, n2);
-  BOOST_CHECK_NE(&n1.wireEncode(), &n2.wireEncode());
-
-  EncodingBuffer buffer(1024, 0);
-  n1.wireEncode(buffer);
-  Name n3(buffer.block());
-
-  BOOST_CHECK_EQUAL(n1, n3);
-  BOOST_CHECK_EQUAL(n3.wireEncode().getBuffer()->size(), 1024);
-  n3 = n3.deepCopy();
-
-  BOOST_CHECK_LT(n3.wireEncode().size(), 1024);
-  BOOST_CHECK_EQUAL(n3.wireEncode().getBuffer()->size(), n3.wireEncode().size());
+  BOOST_CHECK_EQUAL(map[name1], 1);
+  BOOST_CHECK_EQUAL(map[name2], 2);
+  BOOST_CHECK_EQUAL(map[name3], 3);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestName