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>
{