blob: 2a07f1bff1bd7bc67a08b419616bedafcf3aafc6 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013-2024 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.
*/
#ifndef NDN_CXX_NAME_HPP
#define NDN_CXX_NAME_HPP
#include "ndn-cxx/name-component.hpp"
#include <iterator>
#include <limits>
#include <optional>
namespace ndn {
class Name;
/**
* @brief Represents an arbitrary sequence of name components.
*/
using PartialName = Name;
/**
* @brief Represents an absolute name.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html
*/
class Name : private boost::totally_ordered<Name>
{
public: // nested types
using Component = name::Component;
using Error = Component::Error;
// Name appears as an ordered sequence 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 = std::vector<Component>::difference_type;
using size_type = std::vector<Component>::size_type;
public: // constructors, encoding, decoding
/**
* @brief Create an empty name.
* @post empty() == true
*/
Name();
/**
* @brief Create Name from wire encoding.
* @param wire TLV element of type tlv::Name
*
* This is equivalent to:
* @code
* Name name;
* name.wireDecode(wire);
* @endcode
*
* @throw tlv::Error The wire encoding is invalid.
*/
explicit
Name(const Block& wire);
/**
* @brief Create name from NDN URI.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
*/
explicit
Name(std::string_view uri);
/**
* @brief Create name from NDN URI.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
* @note This constructor enables implicit conversion from a string literal.
*/
Name(const char* uri)
: Name(std::string_view(uri))
{
}
/**
* @brief Create name from NDN URI.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
* @note This constructor enables implicit conversion from `std::string`.
*/
Name(const std::string& uri)
: Name(std::string_view(uri))
{
}
/**
* @brief Write URI representation of the name to the output stream.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
*/
void
toUri(std::ostream& os, name::UriFormat format = name::UriFormat::DEFAULT) const;
/**
* @brief Get URI representation of the name.
* @return URI representation; the "ndn:" scheme identifier is not included.
* @note To print the URI representation to a stream, it is more efficient to use `os << name`.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
*/
std::string
toUri(name::UriFormat format = name::UriFormat::DEFAULT) const;
/**
* @brief Check if this instance already has wire encoding.
*/
bool
hasWire() const noexcept
{
return m_wire.hasWire();
}
/**
* @brief Prepend wire encoding to @p encoder.
*/
template<encoding::Tag TAG>
size_t
wireEncode(EncodingImpl<TAG>& encoder) const;
/**
* @brief Perform wire encoding, or return existing (cached) wire encoding.
* @post hasWire() == true
*/
const Block&
wireEncode() const;
/**
* @brief Decode name from wire encoding.
* @throw tlv::Error The 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 Checks if the name is empty, i.e., has no components.
*/
[[nodiscard]] bool
empty() const noexcept
{
return m_wire.elements().empty();
}
/**
* @brief Returns the number of components.
*/
size_t
size() const noexcept
{
return m_wire.elements_size();
}
/**
* @brief Returns an immutable reference to the component at the specified index.
* @param i zero-based index of the component to return;
* if negative, it is interpreted as offset from the end of the name
* @warning No bounds checking is performed, using an out-of-range index is undefined behavior.
*/
const Component&
get(ssize_t i) const noexcept
{
if (i < 0) {
i += static_cast<ssize_t>(size());
}
return static_cast<const Component&>(m_wire.elements()[static_cast<size_t>(i)]);
}
/**
* @brief Equivalent to get().
*/
const Component&
operator[](ssize_t i) const noexcept
{
return get(i);
}
/**
* @brief Returns an immutable reference to the component at the specified index,
* with bounds checking.
* @param i zero-based index of the component to return;
* if negative, it is interpreted as offset from the end of the name
* @throws Error The index is out of bounds.
*/
const Component&
at(ssize_t i) const;
/** @brief Extracts 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 desired components, starting at @p iStartComponent;
* use #npos to return all components until the end of the name
* @return a new PartialName containing the extracted components
*
* If @p iStartComponent is positive and indexes out of bounds, returns an empty PartialName.
* If @p iStartComponent is negative and indexes out of bounds, the sub-name will start from
* the beginning of the name instead. If @p nComponents is out of bounds, returns all components
* until the end of the name.
*/
PartialName
getSubName(ssize_t iStartComponent, size_t nComponents = npos) const;
/** @brief Returns a prefix of the name.
* @param nComponents number of components; if negative, size()+nComponents is used instead
*
* Returns a new PartialName containing a prefix of this name up to `size() - nComponents`.
* For example, `getPrefix(-1)` returns the name without the final component.
*/
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 noexcept
{
return reinterpret_cast<const_iterator>(m_wire.elements().data());
}
/** @brief End iterator.
*/
const_iterator
end() const noexcept
{
return reinterpret_cast<const_iterator>(m_wire.elements().data() + m_wire.elements().size());
}
/** @brief Reverse begin iterator.
*/
const_reverse_iterator
rbegin() const noexcept
{
return const_reverse_iterator(end());
}
/** @brief Reverse end iterator.
*/
const_reverse_iterator
rend() const noexcept
{
return const_reverse_iterator(begin());
}
public: // modifiers
/** @brief Replace the component at the specified index.
* @param i zero-based index of the component to replace;
* if negative, it is interpreted as offset from the end of the name
* @param component the new component to use as a replacement
* @return A reference to this Name, to allow chaining.
* @warning No bounds checking is performed, using an out-of-range index is undefined behavior.
*/
Name&
set(ssize_t i, const Component& component);
/** @brief Replace the component at the specified index.
* @param i zero-based index of the component to replace;
* if negative, it is interpreted as offset from the end of the name
* @param component the new component to use as a replacement
* @return A reference to this Name, to allow chaining.
* @warning No bounds checking is performed, using an out-of-range index is undefined behavior.
*/
Name&
set(ssize_t i, Component&& component);
/**
* @brief Append a name 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 name component.
* @return A reference to this Name, to allow chaining.
*/
Name&
append(Component&& component)
{
m_wire.push_back(std::move(component));
return *this;
}
/**
* @brief Append a `NameComponent` of TLV-TYPE @p type, copying the TLV-VALUE from @p value.
* @return A reference to this Name, to allow chaining.
*/
Name&
append(uint32_t type, span<const uint8_t> value)
{
return append(Component(type, value));
}
/**
* @brief Append a `GenericNameComponent`, copying the TLV-VALUE from @p value.
* @return A reference to this Name, to allow chaining.
*/
Name&
append(span<const uint8_t> value)
{
return append(Component(tlv::GenericNameComponent, value));
}
/**
* @brief Append a `NameComponent` of TLV-TYPE @p type, copying the TLV-VALUE from a range.
* @tparam Iterator an @c InputIterator dereferencing to a one-octet value type. More efficient
* implementation is available when it is a @c RandomAccessIterator.
* @param type the TLV-TYPE.
* @param first beginning of the range.
* @param last past-end of the range.
* @return A reference to this Name, to allow chaining.
*/
template<class Iterator>
Name&
append(uint32_t type, Iterator first, Iterator last)
{
return append(Component(type, first, last));
}
/**
* @brief Append a `GenericNameComponent`, copying the TLV-VALUE from a range.
* @tparam Iterator an @c InputIterator dereferencing to a one-octet value type. More efficient
* implementation is available when it is a @c RandomAccessIterator.
* @param first beginning of the range.
* @param last past-end of the range.
* @return A reference to this Name, to allow chaining.
*/
template<class Iterator>
Name&
append(Iterator first, Iterator last)
{
return append(Component(tlv::GenericNameComponent, first, last));
}
/**
* @brief Append a `GenericNameComponent`, copying the TLV-VALUE from a null-terminated string.
* @param str a null-terminated string. Bytes from the string are copied as is, and not
* interpreted as a URI component.
* @return A reference to this Name, to allow chaining.
*/
Name&
append(const char* str)
{
return append(Component(str));
}
/**
* @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 with a NonNegativeInteger.
* @return A reference to this Name, to allow chaining.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/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 revision 1 (obsolete)
* https://named-data.net/wp-content/uploads/2014/08/ndn-tr-22-ndn-memo-naming-conventions.pdf
*/
Name&
appendNumberWithMarker(uint8_t marker, uint64_t number)
{
return append(Component::fromNumberWithMarker(marker, number));
}
/**
* @brief Append a segment number (sequential) component.
* @return A reference to this Name, to allow chaining.
* @sa NDN Naming Conventions
* https://named-data.net/publications/techreports/ndn-tr-22-3-ndn-memo-naming-conventions/
*/
Name&
appendSegment(uint64_t segmentNo)
{
return append(Component::fromSegment(segmentNo));
}
/**
* @brief Append a byte offset component.
* @return A reference to this Name, to allow chaining.
* @sa NDN Naming Conventions
* https://named-data.net/publications/techreports/ndn-tr-22-3-ndn-memo-naming-conventions/
*/
Name&
appendByteOffset(uint64_t offset)
{
return append(Component::fromByteOffset(offset));
}
/**
* @brief Append a version component.
* @param version the version number to append; if nullopt, the current UNIX time
* in milliseconds is used
* @return A reference to this Name, to allow chaining.
* @sa NDN Naming Conventions
* https://named-data.net/publications/techreports/ndn-tr-22-3-ndn-memo-naming-conventions/
*/
Name&
appendVersion(const std::optional<uint64_t>& version = std::nullopt);
/**
* @brief Append a timestamp component.
* @param timestamp the timestamp to append; if nullopt, the current system time is used
* @return A reference to this Name, to allow chaining.
* @sa NDN Naming Conventions
* https://named-data.net/publications/techreports/ndn-tr-22-3-ndn-memo-naming-conventions/
*/
Name&
appendTimestamp(const std::optional<time::system_clock::time_point>& timestamp = std::nullopt);
/**
* @brief Append a sequence number component.
* @return A reference to this Name, to allow chaining.
* @sa NDN Naming Conventions
* https://named-data.net/publications/techreports/ndn-tr-22-3-ndn-memo-naming-conventions/
*/
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(ConstBufferPtr digest)
{
return append(Component(tlv::ImplicitSha256DigestComponent, std::move(digest)));
}
/**
* @brief Append an `ImplicitSha256Digest` component.
* @return A reference to this Name, to allow chaining.
*/
Name&
appendImplicitSha256Digest(span<const uint8_t> digestBytes)
{
return append(Component(tlv::ImplicitSha256DigestComponent, digestBytes));
}
/**
* @brief Append a `ParametersSha256Digest` component.
* @return A reference to this Name, to allow chaining.
*/
Name&
appendParametersSha256Digest(ConstBufferPtr digest)
{
return append(Component(tlv::ParametersSha256DigestComponent, std::move(digest)));
}
/**
* @brief Append a `ParametersSha256Digest` component.
* @return A reference to this Name, to allow chaining.
*/
Name&
appendParametersSha256Digest(span<const uint8_t> digestBytes)
{
return append(Component(tlv::ParametersSha256DigestComponent, digestBytes));
}
/**
* @brief Append a placeholder for a `ParametersSha256Digest` component.
* @return A reference to this Name, to allow chaining.
*/
Name&
appendParametersSha256DigestPlaceholder();
/**
* @brief Append a keyword component.
* @return A reference to this Name, to allow chaining.
*/
Name&
appendKeyword(span<const uint8_t> keyword)
{
return append(Component(tlv::KeywordNameComponent, keyword));
}
/**
* @brief Append a keyword component.
* @return A reference to this Name, to allow chaining.
*/
Name&
appendKeyword(std::string_view keyword)
{
return append(Component(tlv::KeywordNameComponent,
{reinterpret_cast<const uint8_t*>(keyword.data()), keyword.size()}));
}
/**
* @brief Erase the component at the specified index.
* @param i zero-based index of the component to erase;
* if negative, it is interpreted as offset from the end of the name
* @warning No bounds checking is performed, using an out-of-range index is undefined behavior.
*/
void
erase(ssize_t i);
/**
* @brief Remove all components.
* @post `empty() == true`
*/
void
clear();
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 of `/` is
* `/sha256digest=0000000000000000000000000000000000000000000000000000000000000000`.
* - successor of `/sha256digest=0000000000000000000000000000000000000000000000000000000000000000`
* is `/sha256digest=0000000000000000000000000000000000000000000000000000000000000001`.
* - successor of `/sha256digest=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
* is `/2=...`.
* - successor of `/P/A` is `/P/B`.
* - successor of `/Q/%FF` is `/Q/%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 noexcept;
/** @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 noexcept;
/** @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://docs.named-data.net/NDN-packet-spec/0.3/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.
*
* Equivalent to `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;
private: // non-member operators
// NOTE: the following "hidden friend" operators are available via
// argument-dependent lookup only and must be defined inline.
// boost::totally_ordered provides !=, <=, >=, and > operators.
friend bool
operator==(const Name& lhs, const Name& rhs) noexcept
{
return lhs.equals(rhs);
}
friend bool
operator<(const Name& lhs, const Name& rhs)
{
return lhs.compare(rhs) < 0;
}
/**
* @brief Print the URI representation of a name.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
*/
friend std::ostream&
operator<<(std::ostream& os, const Name& name)
{
name.toUri(os);
return os;
}
public:
/**
* @brief Indicates "until the end" in getSubName() and compare().
*/
static constexpr size_t npos = std::numeric_limits<size_t>::max();
private:
mutable Block m_wire{tlv::Name};
};
NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(Name);
/**
* @brief Parse URI from stream as Name.
* @sa https://docs.named-data.net/NDN-packet-spec/0.3/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_CXX_NAME_HPP