| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2013-2019 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 "ndn-cxx/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 |
| using Error = name::Component::Error; |
| |
| 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-packet-spec/current/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-packet-spec/current/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-packet-spec/current/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 Checks if the name is empty, i.e. has no components. |
| */ |
| NDN_CXX_NODISCARD bool |
| empty() const |
| { |
| return m_wire.elements().empty(); |
| } |
| |
| /** @brief Returns the number of components. |
| */ |
| size_t |
| size() const |
| { |
| 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 |
| { |
| if (i < 0) { |
| i += static_cast<ssize_t>(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 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 @c 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 |
| { |
| 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 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 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. |
| * @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 @p count bytes at @p value as |
| * TLV-VALUE. |
| * @return a reference to this name, to allow chaining. |
| */ |
| Name& |
| append(uint32_t type, const uint8_t* value, size_t count) |
| { |
| return append(Component(type, value, count)); |
| } |
| |
| /** @brief Append a GenericNameComponent, copying @p count bytes at @p value as TLV-VALUE. |
| * @return a reference to this name, to allow chaining. |
| */ |
| Name& |
| append(const uint8_t* value, size_t count) |
| { |
| return append(Component(value, count)); |
| } |
| |
| /** @brief Append a NameComponent of TLV-TYPE @p type, copying 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 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(first, last)); |
| } |
| |
| /** @brief Append a GenericNameComponent, copying 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 URI component. |
| * @return a reference to this name, to allow chaining. |
| */ |
| Name& |
| append(const char* str) |
| { |
| return append(Component(str)); |
| } |
| |
| /** @brief Append a GenericNameComponent from a TLV element. |
| * @param value a TLV element. If its TLV-TYPE is tlv::GenericNameComponent, it is |
| * appended as is. Otherwise, it is nested into a GenericNameComponent. |
| * @return a reference to this name, to allow chaining. |
| */ |
| Name& |
| append(Block value) |
| { |
| if (value.type() == tlv::GenericNameComponent) { |
| m_wire.push_back(std::move(value)); |
| } |
| else { |
| m_wire.push_back(Block(tlv::GenericNameComponent, std::move(value))); |
| } |
| return *this; |
| } |
| |
| /** @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 |
| * @sa number the number |
| * @return a reference to this name, to allow chaining |
| * @sa https://named-data.net/doc/NDN-packet-spec/current/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 |
| * @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/doc/tech-memos/naming-conventions.pdf |
| */ |
| Name& |
| appendVersion(optional<uint64_t> version = nullopt); |
| |
| /** @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 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& |
| appendByteOffset(uint64_t offset) |
| { |
| return append(Component::fromByteOffset(offset)); |
| } |
| |
| /// @deprecated use appendByteOffset |
| Name& |
| appendSegmentOffset(uint64_t offset) |
| { |
| return appendByteOffset(offset); |
| } |
| |
| /** @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/doc/tech-memos/naming-conventions.pdf |
| */ |
| Name& |
| appendTimestamp(optional<time::system_clock::TimePoint> timestamp = nullopt); |
| |
| /** @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(ConstBufferPtr digest) |
| { |
| return append(Component::fromImplicitSha256Digest(std::move(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 ParametersSha256Digest component. |
| * @return a reference to this name, to allow chaining |
| */ |
| Name& |
| appendParametersSha256Digest(ConstBufferPtr digest) |
| { |
| return append(Component::fromParametersSha256Digest(std::move(digest))); |
| } |
| |
| /** @brief Append a ParametersSha256Digest component. |
| * @return a reference to this name, to allow chaining |
| */ |
| Name& |
| appendParametersSha256Digest(const uint8_t* digest, size_t digestSize) |
| { |
| return append(Component::fromParametersSha256Digest(digest, digestSize)); |
| } |
| |
| /** @brief Append a placeholder for a ParametersSha256Digest component. |
| * @return a reference to this name, to allow chaining |
| */ |
| Name& |
| appendParametersSha256DigestPlaceholder(); |
| |
| /** @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 Erase 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 |
| * @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; |
| |
| /** @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-packet-spec/current/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. |
| |
| friend bool |
| operator==(const Name& lhs, const Name& rhs) |
| { |
| return lhs.equals(rhs); |
| } |
| |
| friend bool |
| operator!=(const Name& lhs, const Name& rhs) |
| { |
| return !lhs.equals(rhs); |
| } |
| |
| friend bool |
| operator<(const Name& lhs, const Name& rhs) |
| { |
| return lhs.compare(rhs) < 0; |
| } |
| |
| friend bool |
| operator<=(const Name& lhs, const Name& rhs) |
| { |
| return lhs.compare(rhs) <= 0; |
| } |
| |
| friend bool |
| operator>(const Name& lhs, const Name& rhs) |
| { |
| return lhs.compare(rhs) > 0; |
| } |
| |
| friend bool |
| operator>=(const Name& lhs, const Name& rhs) |
| { |
| return lhs.compare(rhs) >= 0; |
| } |
| |
| 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); |
| |
| /** @brief Print URI representation of a name |
| * @sa https://named-data.net/doc/NDN-packet-spec/current/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-packet-spec/current/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 |