name-component.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2024 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  *
21  * @author Jeff Thompson <jefft0@remap.ucla.edu>
22  * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
23  * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
24  */
25 
27 #include "ndn-cxx/impl/name-component-types.hpp"
28 
29 #include <cstdlib>
30 #include <cstring>
31 #include <sstream>
32 
33 #include <boost/logic/tribool.hpp>
34 
35 namespace ndn::name {
36 
37 static Convention g_conventionEncoding = Convention::TYPED;
38 static Convention g_conventionDecoding = Convention::EITHER;
39 
42 {
43  return g_conventionEncoding;
44 }
45 
46 void
48 {
49  switch (convention) {
50  case Convention::MARKER:
51  case Convention::TYPED:
52  g_conventionEncoding = convention;
53  break;
54  default:
55  NDN_THROW(std::invalid_argument("Unknown naming convention"));
56  }
57 }
58 
61 {
62  return g_conventionDecoding;
63 }
64 
65 void
67 {
68  g_conventionDecoding = convention;
69 }
70 
71 static bool
72 canDecodeMarkerConvention() noexcept
73 {
74  return (to_underlying(g_conventionDecoding) & to_underlying(Convention::MARKER)) != 0;
75 }
76 
77 static bool
78 canDecodeTypedConvention() noexcept
79 {
80  return (to_underlying(g_conventionDecoding) & to_underlying(Convention::TYPED)) != 0;
81 }
82 
84 
85 Component::Component(uint32_t type)
86  : Block(type)
87 {
88  ensureValid();
89 }
90 
92  : Block(wire)
93 {
94  ensureValid();
95 }
96 
97 Component::Component(uint32_t type, ConstBufferPtr buffer)
98  : Block(type, std::move(buffer))
99 {
100  ensureValid();
101 }
102 
103 Component::Component(uint32_t type, span<const uint8_t> value)
104  : Block(makeBinaryBlock(type, value))
105 {
106  ensureValid();
107 }
108 
109 Component::Component(std::string_view str)
111 {
112 }
113 
114 void
115 Component::ensureValid() const
116 {
118  NDN_THROW(Error("TLV-TYPE " + std::to_string(type()) + " is not a valid NameComponent"));
119  }
120  getComponentTypeTable().get(type()).check(*this);
121 }
122 
123 static Component
124 parseUriEscapedValue(uint32_t type, std::string_view input)
125 {
126  std::ostringstream oss;
127  unescape(oss, input);
128  std::string value = oss.str();
129  if (value.find_first_not_of('.') == std::string::npos) { // all periods
130  if (value.size() < 3) {
131  NDN_THROW(Component::Error("Illegal URI (name component cannot be . or ..)"));
132  }
133  return Component(type, {reinterpret_cast<const uint8_t*>(value.data()), value.size() - 3});
134  }
135  return Component(type, {reinterpret_cast<const uint8_t*>(value.data()), value.size()});
136 }
137 
138 Component
139 Component::fromUri(std::string_view input)
140 {
141  size_t equalPos = input.find('=');
142  if (equalPos == std::string_view::npos) {
143  return parseUriEscapedValue(tlv::GenericNameComponent, input);
144  }
145 
146  auto typePrefix = input.substr(0, equalPos);
147  auto type = std::strtoul(typePrefix.data(), nullptr, 10);
149  std::to_string(type) == typePrefix) {
150  return parseUriEscapedValue(static_cast<uint32_t>(type), input.substr(equalPos + 1));
151  }
152 
153  auto ct = getComponentTypeTable().findByUriPrefix(typePrefix);
154  if (ct == nullptr) {
155  NDN_THROW(Error("Unknown TLV-TYPE '" + std::string(typePrefix) + "' in NameComponent URI"));
156  }
157  return ct->parseAltUriValue(input.substr(equalPos + 1));
158 }
159 
160 static bool
161 wantAltUri(UriFormat format)
162 {
163  static const auto wantAltEnv = []() -> boost::tribool {
164  const char* env = std::getenv("NDN_NAME_ALT_URI");
165  if (env == nullptr)
166  return boost::indeterminate;
167  else if (env[0] == '0')
168  return false;
169  else if (env[0] == '1')
170  return true;
171  else
172  return boost::indeterminate;
173  }();
174 
175  if (format == UriFormat::ENV_OR_CANONICAL) {
176  static const bool wantAlt = boost::indeterminate(wantAltEnv) ? false : bool(wantAltEnv);
177  return wantAlt;
178  }
179  else if (format == UriFormat::ENV_OR_ALTERNATE) {
180  static const bool wantAlt = boost::indeterminate(wantAltEnv) ? true : bool(wantAltEnv);
181  return wantAlt;
182  }
183  else {
184  return format == UriFormat::ALTERNATE;
185  }
186 }
187 
188 void
189 Component::toUri(std::ostream& os, UriFormat format) const
190 {
191  if (wantAltUri(format)) {
192  getComponentTypeTable().get(type()).writeUri(os, *this);
193  }
194  else {
195  ComponentType().writeUri(os, *this);
196  }
197 }
198 
199 std::string
201 {
202  std::ostringstream os;
203  toUri(os, format);
204  return os.str();
205 }
206 
208 
209 bool
210 Component::isNumber() const noexcept
211 {
212  return value_size() == 1 || value_size() == 2 ||
213  value_size() == 4 || value_size() == 8;
214 }
215 
216 bool
217 Component::isNumberWithMarker(uint8_t marker) const noexcept
218 {
219  return (value_size() == 2 || value_size() == 3 ||
220  value_size() == 5 || value_size() == 9) && value()[0] == marker;
221 }
222 
223 bool
224 Component::isSegment() const noexcept
225 {
226  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_MARKER)) ||
227  (canDecodeTypedConvention() && type() == tlv::SegmentNameComponent && isNumber());
228 }
229 
230 bool
231 Component::isByteOffset() const noexcept
232 {
233  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_OFFSET_MARKER)) ||
234  (canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent && isNumber());
235 }
236 
237 bool
238 Component::isVersion() const noexcept
239 {
240  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(VERSION_MARKER)) ||
241  (canDecodeTypedConvention() && type() == tlv::VersionNameComponent && isNumber());
242 }
243 
244 bool
245 Component::isTimestamp() const noexcept
246 {
247  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(TIMESTAMP_MARKER)) ||
248  (canDecodeTypedConvention() && type() == tlv::TimestampNameComponent && isNumber());
249 }
250 
251 bool
253 {
254  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEQUENCE_NUMBER_MARKER)) ||
255  (canDecodeTypedConvention() && type() == tlv::SequenceNumNameComponent && isNumber());
256 }
257 
259 
260 uint64_t
262 {
263  if (!isNumber())
264  NDN_THROW(Error("Name component does not have NonNegativeInteger value"));
265 
266  return readNonNegativeInteger(*this);
267 }
268 
269 uint64_t
270 Component::toNumberWithMarker(uint8_t marker) const
271 {
272  if (!isNumberWithMarker(marker))
273  NDN_THROW(Error("Name component does not have the requested marker "
274  "or the value is not a NonNegativeInteger"));
275 
276  auto valueBegin = value_begin() + 1;
277  return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
278 }
279 
280 uint64_t
282 {
283  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
285  }
286  if (canDecodeTypedConvention() && type() == tlv::SegmentNameComponent) {
287  return toNumber();
288  }
289  NDN_THROW(Error("Not a Segment component"));
290 }
291 
292 uint64_t
294 {
295  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
297  }
298  if (canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent) {
299  return toNumber();
300  }
301  NDN_THROW(Error("Not a ByteOffset component"));
302 }
303 
304 uint64_t
306 {
307  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
309  }
310  if (canDecodeTypedConvention() && type() == tlv::VersionNameComponent) {
311  return toNumber();
312  }
313  NDN_THROW(Error("Not a Version component"));
314 }
315 
318 {
319  uint64_t value = 0;
320  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
322  }
323  else if (canDecodeTypedConvention() && type() == tlv::TimestampNameComponent) {
324  value = toNumber();
325  }
326  else {
327  NDN_THROW(Error("Not a Timestamp component"));
328  }
330 }
331 
332 uint64_t
334 {
335  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
337  }
338  if (canDecodeTypedConvention() && type() == tlv::SequenceNumNameComponent) {
339  return toNumber();
340  }
341  NDN_THROW(Error("Not a SequenceNumber component"));
342 }
343 
345 
346 Component
347 Component::fromNumber(uint64_t number, uint32_t type)
348 {
349  return Component(makeNonNegativeIntegerBlock(type, number));
350 }
351 
352 Component
353 Component::fromNumberWithMarker(uint8_t marker, uint64_t number)
354 {
355  EncodingEstimator estimator;
356  size_t valueLength = estimator.prependNonNegativeInteger(number);
357  valueLength += estimator.prependBytes({marker});
358  size_t totalLength = valueLength;
359  totalLength += estimator.prependVarNumber(valueLength);
360  totalLength += estimator.prependVarNumber(tlv::GenericNameComponent);
361 
362  EncodingBuffer encoder(totalLength, 0);
363  encoder.prependNonNegativeInteger(number);
364  encoder.prependBytes({marker});
365  encoder.prependVarNumber(valueLength);
366  encoder.prependVarNumber(tlv::GenericNameComponent);
367 
368  return Component(encoder.block());
369 }
370 
371 Component
372 Component::fromSegment(uint64_t segmentNo)
373 {
374  return g_conventionEncoding == Convention::MARKER ?
377 }
378 
379 Component
381 {
382  return g_conventionEncoding == Convention::MARKER ?
385 }
386 
387 Component
388 Component::fromVersion(uint64_t version)
389 {
390  return g_conventionEncoding == Convention::MARKER ?
393 }
394 
395 Component
397 {
398  uint64_t value = time::duration_cast<time::microseconds>(timePoint - time::getUnixEpoch()).count();
399  return g_conventionEncoding == Convention::MARKER ?
402 }
403 
404 Component
406 {
407  return g_conventionEncoding == Convention::MARKER ?
410 }
411 
413 
414 bool
416 {
418 }
419 
420 bool
422 {
424 }
425 
427 
428 int
429 Component::compare(const Component& other) const
430 {
431  if (this->hasWire() && other.hasWire()) {
432  // In the common case where both components have wire encoding,
433  // it's more efficient to simply compare the wire encoding.
434  // This works because lexical order of TLV encoding happens to be
435  // the same as canonical order of the value.
436  return std::memcmp(data(), other.data(), std::min(size(), other.size()));
437  }
438 
439  int cmpType = type() - other.type();
440  if (cmpType != 0)
441  return cmpType;
442 
443  int cmpSize = value_size() - other.value_size();
444  if (cmpSize != 0)
445  return cmpSize;
446 
447  if (empty())
448  return 0;
449 
450  return std::memcmp(value(), other.value(), value_size());
451 }
452 
453 Component
455 {
456  auto [isOverflow, successor] = getComponentTypeTable().get(type()).getSuccessor(*this);
457  if (!isOverflow) {
458  return successor;
459  }
460 
461  uint32_t type = this->type() + 1;
462  auto value = getComponentTypeTable().get(type).getMinValue();
463  return {type, value};
464 }
465 
466 template<encoding::Tag TAG>
467 size_t
469 {
470  size_t totalLength = 0;
471  if (value_size() > 0) {
472  totalLength += encoder.prependBytes(value_bytes());
473  }
474  totalLength += encoder.prependVarNumber(value_size());
475  totalLength += encoder.prependVarNumber(type());
476  return totalLength;
477 }
478 
480 
481 const Block&
483 {
484  if (this->hasWire())
485  return *this;
486 
487  EncodingEstimator estimator;
488  size_t estimatedSize = wireEncode(estimator);
489 
490  EncodingBuffer buffer(estimatedSize, 0);
491  wireEncode(buffer);
492 
493  const_cast<Component*>(this)->wireDecode(buffer.block());
494  return *this;
495 }
496 
497 void
499 {
500  *this = Component(wire);
501  // validity check is done within Component(const Block& wire)
502 }
503 
504 } // namespace ndn::name
Represents a TLV element of the NDN packet format.
Definition: block.hpp:45
const uint8_t * data() const
Returns a raw pointer to the beginning of the encoded wire, i.e., the whole TLV.
Definition: block.cpp:256
const_iterator value_end() const noexcept
Get end iterator of TLV-VALUE.
Definition: block.hpp:338
element_const_iterator find(uint32_t type) const
Find the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:425
size_t size() const
Returns the size of the encoded wire, i.e., of the whole TLV.
Definition: block.cpp:265
bool hasWire() const noexcept
Check if the Block contains a fully encoded wire representation.
Definition: block.hpp:205
span< const uint8_t > value_bytes() const noexcept
Return a read-only view of TLV-VALUE as a contiguous range of bytes.
Definition: block.hpp:308
uint32_t type() const noexcept
Return the TLV-TYPE of the Block.
Definition: block.hpp:275
const_iterator value_begin() const noexcept
Get begin iterator of TLV-VALUE.
Definition: block.hpp:328
const Block & get(uint32_t type) const
Return the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:414
size_t value_size() const noexcept
Return the size of TLV-VALUE, i.e., the TLV-LENGTH.
Definition: block.hpp:299
const uint8_t * value() const noexcept
Return a raw pointer to the beginning of TLV-VALUE.
Definition: block.cpp:308
constexpr size_t prependNonNegativeInteger(uint64_t n) const noexcept
Prepend n in NonNegativeInteger encoding.
Definition: estimator.hpp:99
Represents a name component.
uint64_t toSegment() const
Interpret as segment number component using NDN naming conventions.
static Component fromSegment(uint64_t segmentNo)
Create a segment number component using NDN naming conventions.
bool isTimestamp() const noexcept
Check if the component is a timestamp per NDN naming conventions.
const Block & wireEncode() const
Encode to TLV wire format.
Component getSuccessor() const
Get the successor of this name component.
static Component fromNumber(uint64_t number, uint32_t type=tlv::GenericNameComponent)
Create a component encoded as NonNegativeInteger.
static Component fromTimestamp(const time::system_clock::time_point &timePoint)
Create a timestamp component using NDN naming conventions.
bool isImplicitSha256Digest() const noexcept
Check if the component is an ImplicitSha256DigestComponent.
static Component fromUri(std::string_view input)
Construct a NameComponent from its string representation in NDN URI format.
bool isSegment() const noexcept
Check if the component is a segment number per NDN naming conventions.
uint64_t toNumberWithMarker(uint8_t marker) const
Interpret this name component as a NameComponentWithMarker.
bool isParametersSha256Digest() const noexcept
Check if the component is a ParametersSha256DigestComponent.
static Component fromSequenceNumber(uint64_t seqNo)
Create a sequence number component using NDN naming conventions.
uint64_t toByteOffset() const
Interpret as byte offset component using NDN naming conventions.
static Component fromVersion(uint64_t version)
Create a version component using NDN naming conventions.
uint64_t toSequenceNumber() const
Interpret as sequence number component using NDN naming conventions.
bool isByteOffset() const noexcept
Check if the component is a byte offset per NDN naming conventions.
Component(uint32_t type=tlv::GenericNameComponent)
Construct a NameComponent of TLV-TYPE type and with empty TLV-VALUE.
static Component fromByteOffset(uint64_t offset)
Create a byte offset component using NDN naming conventions.
bool isNumberWithMarker(uint8_t marker) const noexcept
Check if the component is a NameComponentWithMarker per NDN naming conventions rev1.
static Component fromNumberWithMarker(uint8_t marker, uint64_t number)
Create a component encoded as NameComponentWithMarker.
void toUri(std::ostream &os, UriFormat format=UriFormat::DEFAULT) const
Write *this to the output stream, escaping characters according to the NDN URI format.
void wireDecode(const Block &wire)
Decode from TLV wire format.
bool empty() const noexcept
time::system_clock::time_point toTimestamp() const
Interpret as timestamp component using NDN naming conventions.
bool isVersion() const noexcept
Check if the component is a version per NDN naming conventions.
bool isSequenceNumber() const noexcept
Check if the component is a sequence number per NDN naming conventions.
bool isNumber() const noexcept
Check if the component is a NonNegativeInteger.
int compare(const Component &other) const
Compare this component to other using NDN canonical ordering.
uint64_t toNumber() const
Interpret this name component as a NonNegativeInteger.
uint64_t toVersion() const
Interpret as version component using NDN naming conventions.
::boost::chrono::time_point< system_clock > time_point
Definition: time.hpp:205
static constexpr size_t DIGEST_SIZE
Length in bytes of a SHA-256 digest.
Definition: sha256.hpp:54
#define NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(ClassName)
#define NDN_THROW(e)
Definition: exception.hpp:56
EncodingImpl< EstimatorTag > EncodingEstimator
Block makeStringBlock(uint32_t type, std::string_view value)
Create a TLV block containing a string.
uint64_t readNonNegativeInteger(const Block &block)
Read a non-negative integer from a TLV element.
Block makeNonNegativeIntegerBlock(uint32_t type, uint64_t value)
Create a TLV block containing a non-negative integer.
Block makeBinaryBlock(uint32_t type, span< const uint8_t > value)
Create a TLV block copying the TLV-VALUE from a byte range.
EncodingImpl< EncoderTag > EncodingBuffer
std::string to_string(const errinfo_stacktrace &x)
Definition: exception.cpp:30
Convention
Identify a style of NDN Naming Conventions.
@ MARKER
Component markers (revision 1)
@ TYPED
Typed name components (revision 3)
@ SEQUENCE_NUMBER_MARKER
void setConventionDecoding(Convention convention)
Set which Naming Conventions style(s) to accept while decoding.
Convention getConventionEncoding() noexcept
Return which Naming Conventions style to use while encoding.
void setConventionEncoding(Convention convention)
Set which Naming Conventions style to use while encoding.
UriFormat
Format used for the URI representation of a name.
@ ALTERNATE
Always prefer the alternate format when available.
@ ENV_OR_ALTERNATE
Same as UriFormat::ALTERNATE, unless NDN_NAME_ALT_URI environment variable is set to '0'.
@ ENV_OR_CANONICAL
Same as UriFormat::CANONICAL, unless NDN_NAME_ALT_URI environment variable is set to '1'.
Convention getConventionDecoding() noexcept
Return which Naming Conventions style(s) to accept while decoding.
const system_clock::time_point & getUnixEpoch()
Return a system_clock::time_point representing the UNIX time epoch, i.e., 00:00:00 UTC on 1 January 1...
Definition: time.cpp:105
::boost::chrono::microseconds microseconds
Definition: time.hpp:53
@ TimestampNameComponent
Definition: tlv.hpp:79
@ GenericNameComponent
Definition: tlv.hpp:72
@ ByteOffsetNameComponent
Definition: tlv.hpp:77
@ NameComponentMin
Definition: tlv.hpp:115
@ VersionNameComponent
Definition: tlv.hpp:78
@ SequenceNumNameComponent
Definition: tlv.hpp:80
@ NameComponentMax
Definition: tlv.hpp:116
@ ParametersSha256DigestComponent
Definition: tlv.hpp:74
@ ImplicitSha256DigestComponent
Definition: tlv.hpp:73
@ SegmentNameComponent
Definition: tlv.hpp:76
constexpr uint64_t readNonNegativeInteger(size_t size, Iterator &begin, Iterator end)
Read a NonNegativeInteger in NDN-TLV encoding.
Definition: tlv.hpp:414
std::string unescape(std::string_view str)
Decode a percent-encoded string.
std::shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:140
constexpr std::underlying_type_t< T > to_underlying(T val) noexcept
Definition: backports.hpp:44