name: Allow negative start index in getSubName method

refs #1962

Change-Id: I2f1b77d95d534d837d925022934f9a6ccaa340d2
diff --git a/src/name.cpp b/src/name.cpp
index c5adb7d..ff698d8 100644
--- a/src/name.cpp
+++ b/src/name.cpp
@@ -188,11 +188,11 @@
 }
 
 Name&
-Name::append(const Name& name)
+Name::append(const PartialName& name)
 {
   if (&name == this)
     // Copying from this name, so need to make a copy first.
-    return append(Name(name));
+    return append(PartialName(name));
 
   for (size_t i = 0; i < name.size(); ++i)
     append(name.at(i));
@@ -270,16 +270,20 @@
   return *this;
 }
 
-Name
-Name::getSubName(size_t iStartComponent, size_t nComponents) const
+PartialName
+Name::getSubName(ssize_t iStartComponent, size_t nComponents) const
 {
-  Name result;
+  PartialName result;
 
+  ssize_t iStart = iStartComponent < 0 ? this->size() + iStartComponent : iStartComponent;
   size_t iEnd = this->size();
-  if (nComponents != npos)
-    iEnd = std::min(this->size(), iStartComponent + nComponents);
 
-  for (size_t i = iStartComponent; i < iEnd; ++i)
+  iStart = std::max(iStart, static_cast<ssize_t>(0));
+
+  if (nComponents != npos)
+    iEnd = std::min(this->size(), iStart + nComponents);
+
+  for (size_t i = iStart; i < iEnd; ++i)
     result.append(at(i));
 
   return result;
diff --git a/src/name.hpp b/src/name.hpp
index 719cf7f2..2cbab7d 100644
--- a/src/name.hpp
+++ b/src/name.hpp
@@ -33,13 +33,22 @@
 
 namespace ndn {
 
+class Name;
+
 /**
- * @brief A Name holds an array of name::Component and represents an NDN name
+ * @brief Partial name abstraction to represent an arbitrary sequence of name components
+ */
+typedef Name PartialName;
+
+/**
+ * @brief Name abstraction to represent an absolute name
  */
 class Name : public enable_shared_from_this<Name>
 {
 public:
-  /// @brief Error that can be thrown from Name
+  /**
+   * @brief Error that can be thrown from Name
+   */
   class Error : public name::Component::Error
   {
   public:
@@ -195,12 +204,12 @@
   }
 
   /**
-   * Append the components of the given name to this name.
-   * @param name The Name with components to append.
-   * @return This name so that you can chain calls to append.
+   * @brief append a PartialName to this Name.
+   * @param name the components to append
+   * @return this name
    */
   Name&
-  append(const Name& name);
+  append(const PartialName& name);
 
   /**
    * Clear all the components.
@@ -212,24 +221,31 @@
   }
 
   /**
-   * Get a new name, constructed as a subset of components.
-   * @param iStartComponent The index if the first component to get.
+   * @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 sub Name until the end of this Name.
-   * @return A new name.
+   *                    Use npos to get the Partial Name until the end of this Name.
+   * @detail If iStartComponent is out of bounds and is negative, will return the components
+   *         starting in the beginning of the Name
+   *         If iStartComponent is out of bounds and is positive, will return the component "/"
+   *         If nComponents is out of bounds, will return the components until the end of
+   *         this Name
+   * @return A new partial name
    */
-  Name
-  getSubName(size_t iStartComponent, size_t nComponents = npos) const;
+  PartialName
+  getSubName(ssize_t iStartComponent, size_t nComponents = npos) const;
 
   /**
-   * @brief Return a new Name with the first nComponents components of this Name.
+   * @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
    *                    the prefix up to name.size() - N. For example getPrefix(-1)
    *                    returns the name without the final component.
-   * @return A new Name.
+   * @return A new partial name
    */
-  Name
+  PartialName
   getPrefix(ssize_t nComponents) const
   {
     if (nComponents < 0)
diff --git a/tests/unit-tests/name.t.cpp b/tests/unit-tests/name.t.cpp
index 7576b61..0531f59 100644
--- a/tests/unit-tests/name.t.cpp
+++ b/tests/unit-tests/name.t.cpp
@@ -502,6 +502,62 @@
   BOOST_CHECK_THROW(Name("/hello//world"), name::Component::Error);
 }
 
+BOOST_AUTO_TEST_CASE(Append)
+{
+  PartialName toAppend("/and");
+  PartialName toAppend1("/beyond");
+  {
+    Name name("/hello/world");
+    BOOST_CHECK_EQUAL("/hello/world/hello/world", name.append(name));
+    BOOST_CHECK_EQUAL("/hello/world/hello/world", name);
+  }
+  {
+    Name name("/hello/world");
+    BOOST_CHECK_EQUAL("/hello/world/and", name.append(toAppend));
+  }
+  {
+    Name name("/hello/world");
+    BOOST_CHECK_EQUAL("/hello/world/and/beyond", name.append(toAppend).append(toAppend1));
+  }
+}
+
+BOOST_AUTO_TEST_CASE(SubName)
+{
+  Name name("/hello/world");
+
+  BOOST_CHECK_EQUAL("/hello/world", name.getSubName(0));
+  BOOST_CHECK_EQUAL("/world", name.getSubName(1));
+  BOOST_CHECK_EQUAL("/hello/", name.getSubName(0, 1));
+}
+
+BOOST_AUTO_TEST_CASE(SubNameNegativeIndex)
+{
+  Name name("/first/second/third/last");
+
+  BOOST_CHECK_EQUAL("/last", name.getSubName(-1));
+  BOOST_CHECK_EQUAL("/third/last", name.getSubName(-2));
+  BOOST_CHECK_EQUAL("/second", name.getSubName(-3, 1));
+}
+
+BOOST_AUTO_TEST_CASE(SubNameOutOfRangeIndexes)
+{
+  Name name("/first/second/last");
+  // No length
+  BOOST_CHECK_EQUAL("/first/second/last", name.getSubName(-10));
+  BOOST_CHECK_EQUAL("/", name.getSubName(10));
+
+  // Starting after the max position
+  BOOST_CHECK_EQUAL("/", name.getSubName(10, 1));
+  BOOST_CHECK_EQUAL("/", name.getSubName(10, 10));
+
+  // Not enough components
+  BOOST_CHECK_EQUAL("/second/last", name.getSubName(1, 10));
+  BOOST_CHECK_EQUAL("/last", name.getSubName(-1, 10));
+
+  // Start before first
+  BOOST_CHECK_EQUAL("/first/second", name.getSubName(-10, 2));
+  BOOST_CHECK_EQUAL("/first/second/last", name.getSubName(-10, 10));
+}
 
 BOOST_AUTO_TEST_SUITE_END()