blob: a2efe1b8685219ebb9d68d3b272ea406f747cd75 [file] [log] [blame]
/* -*- 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).
*
* ndn-cxx library is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received copies of the GNU General Public License and GNU Lesser
* General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
* <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
*
* @author Jeff Thompson <jefft0@remap.ucla.edu>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
*/
#ifndef NDN_NAME_HPP
#define NDN_NAME_HPP
#include "name-component.hpp"
#include <iterator>
namespace ndn {
class Name;
/** @brief Represents an arbitrary sequence of name components
*/
using PartialName = Name;
/** @brief Represents an absolute name
*/
class Name
{
public: // nested types
class Error : public name::Component::Error
{
public:
explicit
Error(const std::string& what)
: name::Component::Error(what)
{
}
};
using Component = name::Component;
using component_container = std::vector<Component>;
// 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 = const Component*; // disallow modifying via iterator
using const_iterator = const Component*;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using difference_type = component_container::difference_type;
using size_type = component_container::size_type;
public: // constructors, encoding, decoding
/** @brief Create an empty name
* @post empty() == true
*/
Name();
/** @brief Decode Name from wire encoding
* @throw tlv::Error wire encoding is invalid
*
* This is a more efficient equivalent for
* @code
* Name name;
* name.wireDecode(wire);
* @endcode
*/
explicit
Name(const Block& wire);
/** @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 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 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``.
*/
std::string
toUri() const;
/** @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 Make a deep copy of the name, reallocating the underlying memory buffer
*/
Name
deepCopy() const;
public: // access
/** @brief Check if name is empty
*/
bool
empty() const
{
return m_wire.elements().empty();
}
/** @brief Get number of components
*/
size_t
size() const
{
return m_wire.elements_size();
}
/** @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.
*/
const Component&
get(ssize_t i) const
{
if (i < 0) {
i += size();
}
return reinterpret_cast<const Component&>(m_wire.elements()[i]);
}
/** @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
*
* 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 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
*/
PartialName
getPrefix(ssize_t nComponents) const
{
if (nComponents < 0)
return getSubName(0, size() + nComponents);
else
return getSubName(0, nComponents);
}
public: // iterators
/** @brief Begin iterator
*/
const_iterator
begin() const
{
return reinterpret_cast<const_iterator>(m_wire.elements().data());
}
/** @brief End iterator
*/
const_iterator
end() const
{
return reinterpret_cast<const_iterator>(m_wire.elements().data() + m_wire.elements().size());
}
/** @brief Reverse begin iterator
*/
const_reverse_iterator
rbegin() const
{
return const_reverse_iterator(end());
}
/** @brief Reverse end iterator
*/
const_reverse_iterator
rend() const
{
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_wire;
};
NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(Name);
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);
} // namespace ndn
namespace std {
template<>
struct hash<ndn::Name>
{
size_t
operator()(const ndn::Name& name) const;
};
} // namespace std
#endif // NDN_NAME_HPP