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-2022 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 {
36 namespace name {
37 
38 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Component>));
39 BOOST_CONCEPT_ASSERT((WireEncodable<Component>));
40 BOOST_CONCEPT_ASSERT((WireEncodableWithEncodingBuffer<Component>));
41 BOOST_CONCEPT_ASSERT((WireDecodable<Component>));
42 static_assert(std::is_base_of<tlv::Error, Component::Error>::value,
43  "name::Component::Error must inherit from tlv::Error");
44 
45 static Convention g_conventionEncoding = Convention::TYPED;
46 static Convention g_conventionDecoding = Convention::EITHER;
47 
50 {
51  return g_conventionEncoding;
52 }
53 
54 void
56 {
57  switch (convention) {
58  case Convention::MARKER:
59  case Convention::TYPED:
60  g_conventionEncoding = convention;
61  break;
62  default:
63  NDN_THROW(std::invalid_argument("Unknown naming convention"));
64  }
65 }
66 
69 {
70  return g_conventionDecoding;
71 }
72 
73 void
75 {
76  g_conventionDecoding = convention;
77 }
78 
79 static bool
80 canDecodeMarkerConvention()
81 {
82  return (to_underlying(g_conventionDecoding) & to_underlying(Convention::MARKER)) != 0;
83 }
84 
85 static bool
86 canDecodeTypedConvention()
87 {
88  return (to_underlying(g_conventionDecoding) & to_underlying(Convention::TYPED)) != 0;
89 }
90 
91 static bool
92 wantAltUri(UriFormat format)
93 {
94  static const auto wantAltEnv = []() -> boost::tribool {
95  const char* env = std::getenv("NDN_NAME_ALT_URI");
96  if (env == nullptr)
97  return boost::indeterminate;
98  else if (env[0] == '0')
99  return false;
100  else if (env[0] == '1')
101  return true;
102  else
103  return boost::indeterminate;
104  }();
105 
106  if (format == UriFormat::ENV_OR_CANONICAL) {
107  static const bool wantAlt = boost::indeterminate(wantAltEnv) ? false : bool(wantAltEnv);
108  return wantAlt;
109  }
110  else if (format == UriFormat::ENV_OR_ALTERNATE) {
111  static const bool wantAlt = boost::indeterminate(wantAltEnv) ? true : bool(wantAltEnv);
112  return wantAlt;
113  }
114  else {
115  return format == UriFormat::ALTERNATE;
116  }
117 }
118 
119 void
120 Component::ensureValid() const
121 {
123  NDN_THROW(Error("TLV-TYPE " + to_string(type()) + " is not a valid NameComponent"));
124  }
125  detail::getComponentTypeTable().get(type()).check(*this);
126 }
127 
128 Component::Component(uint32_t type)
129  : Block(type)
130 {
131  ensureValid();
132 }
133 
135  : Block(wire)
136 {
137  ensureValid();
138 }
139 
140 Component::Component(uint32_t type, ConstBufferPtr buffer)
141  : Block(type, std::move(buffer))
142 {
143  ensureValid();
144 }
145 
146 Component::Component(uint32_t type, span<const uint8_t> value)
147  : Block(makeBinaryBlock(type, value))
148 {
149  ensureValid();
150 }
151 
152 Component::Component(const char* str)
153  : Block(makeBinaryBlock(tlv::GenericNameComponent, str, std::char_traits<char>::length(str)))
154 {
155 }
156 
157 Component::Component(const std::string& str)
159 {
160 }
161 
162 static Component
163 parseUriEscapedValue(uint32_t type, const char* input, size_t len)
164 {
165  std::ostringstream oss;
166  unescape(oss, input, len);
167  std::string value = oss.str();
168  if (value.find_first_not_of('.') == std::string::npos) { // all periods
169  if (value.size() < 3) {
170  NDN_THROW(Component::Error("Illegal URI (name component cannot be . or ..)"));
171  }
172  return Component(type, reinterpret_cast<const uint8_t*>(value.data()), value.size() - 3);
173  }
174  return Component(type, reinterpret_cast<const uint8_t*>(value.data()), value.size());
175 }
176 
177 Component
178 Component::fromEscapedString(const std::string& input)
179 {
180  size_t equalPos = input.find('=');
181  if (equalPos == std::string::npos) {
182  return parseUriEscapedValue(tlv::GenericNameComponent, input.data(), input.size());
183  }
184 
185  auto typePrefix = input.substr(0, equalPos);
186  auto type = std::strtoul(typePrefix.data(), nullptr, 10);
188  to_string(type) == typePrefix) {
189  size_t valuePos = equalPos + 1;
190  return parseUriEscapedValue(static_cast<uint32_t>(type),
191  input.data() + valuePos, input.size() - valuePos);
192  }
193 
194  auto ct = detail::getComponentTypeTable().findByUriPrefix(typePrefix);
195  if (ct == nullptr) {
196  NDN_THROW(Error("Unknown TLV-TYPE '" + typePrefix + "' in NameComponent URI"));
197  }
198  return ct->parseAltUriValue(input.substr(equalPos + 1));
199 }
200 
201 void
202 Component::toUri(std::ostream& os, UriFormat format) const
203 {
204  if (wantAltUri(format)) {
205  detail::getComponentTypeTable().get(type()).writeUri(os, *this);
206  }
207  else {
208  detail::ComponentType().writeUri(os, *this);
209  }
210 }
211 
212 std::string
214 {
215  std::ostringstream os;
216  toUri(os, format);
217  return os.str();
218 }
219 
221 
222 bool
224 {
225  return value_size() == 1 || value_size() == 2 ||
226  value_size() == 4 || value_size() == 8;
227 }
228 
229 bool
230 Component::isNumberWithMarker(uint8_t marker) const
231 {
232  return (value_size() == 2 || value_size() == 3 ||
233  value_size() == 5 || value_size() == 9) && value()[0] == marker;
234 }
235 
236 bool
238 {
239  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(VERSION_MARKER)) ||
240  (canDecodeTypedConvention() && type() == tlv::VersionNameComponent && isNumber());
241 }
242 
243 bool
245 {
246  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_MARKER)) ||
247  (canDecodeTypedConvention() && type() == tlv::SegmentNameComponent && isNumber());
248 }
249 
250 bool
252 {
253  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_OFFSET_MARKER)) ||
254  (canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent && isNumber());
255 }
256 
257 bool
259 {
260  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(TIMESTAMP_MARKER)) ||
261  (canDecodeTypedConvention() && type() == tlv::TimestampNameComponent && isNumber());
262 }
263 
264 bool
266 {
267  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEQUENCE_NUMBER_MARKER)) ||
268  (canDecodeTypedConvention() && type() == tlv::SequenceNumNameComponent && isNumber());
269 }
270 
272 
273 uint64_t
275 {
276  if (!isNumber())
277  NDN_THROW(Error("Name component does not have NonNegativeInteger value"));
278 
279  return readNonNegativeInteger(*this);
280 }
281 
282 uint64_t
283 Component::toNumberWithMarker(uint8_t marker) const
284 {
285  if (!isNumberWithMarker(marker))
286  NDN_THROW(Error("Name component does not have the requested marker "
287  "or the value is not a NonNegativeInteger"));
288 
289  auto valueBegin = value_begin() + 1;
290  return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
291 }
292 
293 uint64_t
295 {
296  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
298  }
299  if (canDecodeTypedConvention() && type() == tlv::VersionNameComponent) {
300  return toNumber();
301  }
302  NDN_THROW(Error("Not a Version component"));
303 }
304 
305 uint64_t
307 {
308  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
310  }
311  if (canDecodeTypedConvention() && type() == tlv::SegmentNameComponent) {
312  return toNumber();
313  }
314  NDN_THROW(Error("Not a Segment component"));
315 }
316 
317 uint64_t
319 {
320  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
322  }
323  if (canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent) {
324  return toNumber();
325  }
326  NDN_THROW(Error("Not a ByteOffset component"));
327 }
328 
331 {
332  uint64_t value = 0;
333  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
335  }
336  else if (canDecodeTypedConvention() && type() == tlv::TimestampNameComponent) {
337  value = toNumber();
338  }
339  else {
340  NDN_THROW(Error("Not a Timestamp component"));
341  }
343 }
344 
345 uint64_t
347 {
348  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
350  }
351  if (canDecodeTypedConvention() && type() == tlv::SequenceNumNameComponent) {
352  return toNumber();
353  }
354  NDN_THROW(Error("Not a SequenceNumber component"));
355 }
356 
358 
359 Component
360 Component::fromNumber(uint64_t number, uint32_t type)
361 {
362  return makeNonNegativeIntegerBlock(type, number);
363 }
364 
365 Component
366 Component::fromNumberWithMarker(uint8_t marker, uint64_t number)
367 {
368  EncodingEstimator estimator;
369  size_t valueLength = estimator.prependNonNegativeInteger(number);
370  valueLength += estimator.prependBytes({marker});
371  size_t totalLength = valueLength;
372  totalLength += estimator.prependVarNumber(valueLength);
373  totalLength += estimator.prependVarNumber(tlv::GenericNameComponent);
374 
375  EncodingBuffer encoder(totalLength, 0);
376  encoder.prependNonNegativeInteger(number);
377  encoder.prependBytes({marker});
378  encoder.prependVarNumber(valueLength);
379  encoder.prependVarNumber(tlv::GenericNameComponent);
380 
381  return encoder.block();
382 }
383 
384 Component
385 Component::fromVersion(uint64_t version)
386 {
387  return g_conventionEncoding == Convention::MARKER ?
390 }
391 
392 Component
393 Component::fromSegment(uint64_t segmentNo)
394 {
395  return g_conventionEncoding == Convention::MARKER ?
398 }
399 
400 Component
402 {
403  return g_conventionEncoding == Convention::MARKER ?
406 }
407 
408 Component
410 {
411  uint64_t value = time::duration_cast<time::microseconds>(timePoint - time::getUnixEpoch()).count();
412  return g_conventionEncoding == Convention::MARKER ?
415 }
416 
417 Component
419 {
420  return g_conventionEncoding == Convention::MARKER ?
423 }
424 
426 
427 bool
429 {
430  return type() == tlv::GenericNameComponent;
431 }
432 
433 bool
435 {
436  return detail::getComponentType1().match(*this);
437 }
438 
439 Component
441 {
442  return detail::getComponentType1().create(std::move(digest));
443 }
444 
445 Component
446 Component::fromImplicitSha256Digest(span<const uint8_t> digest)
447 {
448  return detail::getComponentType1().create(digest);
449 }
450 
451 bool
453 {
454  return detail::getComponentType2().match(*this);
455 }
456 
457 Component
459 {
460  return detail::getComponentType2().create(std::move(digest));
461 }
462 
463 Component
464 Component::fromParametersSha256Digest(span<const uint8_t> digest)
465 {
466  return detail::getComponentType2().create(digest);
467 }
468 
470 
471 bool
472 Component::equals(const Component& other) const
473 {
474  return type() == other.type() &&
475  value_size() == other.value_size() &&
476  std::equal(value_begin(), value_end(), other.value_begin());
477 }
478 
479 int
480 Component::compare(const Component& other) const
481 {
482  if (this->hasWire() && other.hasWire()) {
483  // In the common case where both components have wire encoding,
484  // it's more efficient to simply compare the wire encoding.
485  // This works because lexical order of TLV encoding happens to be
486  // the same as canonical order of the value.
487  return std::memcmp(wire(), other.wire(), std::min(size(), other.size()));
488  }
489 
490  int cmpType = type() - other.type();
491  if (cmpType != 0)
492  return cmpType;
493 
494  int cmpSize = value_size() - other.value_size();
495  if (cmpSize != 0)
496  return cmpSize;
497 
498  if (empty())
499  return 0;
500 
501  return std::memcmp(value(), other.value(), value_size());
502 }
503 
504 Component
506 {
507  bool isOverflow = false;
508  Component successor;
509  std::tie(isOverflow, successor) = detail::getComponentTypeTable().get(type()).getSuccessor(*this);
510  if (!isOverflow) {
511  return successor;
512  }
513 
514  uint32_t type = this->type() + 1;
515  auto value = detail::getComponentTypeTable().get(type).getMinValue();
516  return Component(type, value.data(), value.size());
517 }
518 
519 template<encoding::Tag TAG>
520 size_t
522 {
523  size_t totalLength = 0;
524  if (value_size() > 0)
525  totalLength += encoder.prependBytes({value(), value_size()});
526  totalLength += encoder.prependVarNumber(value_size());
527  totalLength += encoder.prependVarNumber(type());
528  return totalLength;
529 }
530 
532 
533 const Block&
535 {
536  if (this->hasWire())
537  return *this;
538 
539  EncodingEstimator estimator;
540  size_t estimatedSize = wireEncode(estimator);
541 
542  EncodingBuffer buffer(estimatedSize, 0);
543  wireEncode(buffer);
544 
545  const_cast<Component&>(*this) = buffer.block();
546  return *this;
547 }
548 
549 void
551 {
552  *this = wire;
553  // validity check is done within Component(const Block& wire)
554 }
555 
556 } // namespace name
557 } // namespace ndn
Represents a TLV element of the NDN packet format.
Definition: block.hpp:45
const uint8_t * wire() const
Return a raw pointer to the beginning of the encoded wire.
Definition: block.cpp:296
element_const_iterator find(uint32_t type) const
Find the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:441
uint32_t type() const
Return the TLV-TYPE of the Block.
Definition: block.hpp:285
size_t size() const
Return the size of the encoded wire, i.e.
Definition: block.cpp:305
bool hasWire() const noexcept
Check if the Block contains a fully encoded wire representation.
Definition: block.hpp:241
Buffer::const_iterator value_end() const
Get end iterator of TLV-VALUE.
Definition: block.hpp:316
Buffer::const_iterator value_begin() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:307
const Block & get(uint32_t type) const
Return the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:429
size_t value_size() const noexcept
Return the size of TLV-VALUE, aka TLV-LENGTH.
Definition: block.cpp:323
const uint8_t * value() const noexcept
Return a raw pointer to the beginning of TLV-VALUE.
Definition: block.cpp:317
a concept check for TLV abstraction with .wireDecode method and constructible from Block
Definition: concepts.hpp:81
a concept check for TLV abstraction with .wireEncode method
Definition: concepts.hpp:61
a concept check for TLV abstraction with .wireEncode method
Definition: concepts.hpp:45
constexpr size_t prependNonNegativeInteger(uint64_t n) const noexcept
Prepend n in NonNegativeInteger encoding.
Definition: estimator.hpp:144
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.
const Block & wireEncode() const
Encode to a wire format.
Component getSuccessor() const
Get the successor of this name component.
bool isGeneric() const
Check if the component is GenericNameComponent.
static Component fromEscapedString(const char *input, size_t beginOffset, size_t endOffset)
Decode NameComponent from a URI 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 isByteOffset() const
Check if the component is a byte offset per NDN naming conventions.
bool isSegment() const
Check if the component is a segment number per NDN naming conventions.
static Component fromParametersSha256Digest(ConstBufferPtr digest)
Create ParametersSha256DigestComponent component.
uint64_t toNumberWithMarker(uint8_t marker) const
Interpret this name component as NameComponentWithMarker.
bool isTimestamp() const
Check if the component is a timestamp per NDN naming conventions.
bool isParametersSha256Digest() const
Check if the component is 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 isSequenceNumber() const
Check if the component is a sequence number 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 isVersion() const
Check if the component is a version per NDN naming conventions.
bool isNumberWithMarker(uint8_t marker) const
Check if the component is a NameComponentWithMarker per NDN naming conventions rev1.
bool isNumber() const
Check if the component is a NonNegativeInteger.
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 the wire format.
time::system_clock::time_point toTimestamp() const
Interpret as timestamp component using NDN naming conventions.
static Component fromImplicitSha256Digest(ConstBufferPtr digest)
Create ImplicitSha256DigestComponent component.
bool equals(const Component &other) const
Check if this is the same component as other.
int compare(const Component &other) const
Compare this to the other Component using NDN canonical ordering.
uint64_t toNumber() const
Interpret this name component as NonNegativeInteger.
bool isImplicitSha256Digest() const
Check if the component is ImplicitSha256DigestComponent.
uint64_t toVersion() const
Interpret as version component using NDN naming conventions.
boost::chrono::time_point< system_clock > time_point
Definition: time.hpp:200
#define NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(ClassName)
#define NDN_THROW(e)
Definition: exception.hpp:61
EncodingImpl< EstimatorTag > EncodingEstimator
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
Block makeStringBlock(uint32_t type, const std::string &value)
Create a TLV block containing a string.
std::string to_string(const errinfo_stacktrace &x)
Definition: exception.cpp:31
Convention
Identify a style of NDN Naming Conventions.
@ MARKER
Component markers (revision 1)
@ TYPED
Typed name components (revision 3)
void setConventionDecoding(Convention convention)
Set which Naming Conventions style(s) to accept while decoding.
Convention getConventionEncoding()
Return which Naming Conventions style to use while encoding.
@ SEQUENCE_NUMBER_MARKER
void setConventionEncoding(Convention convention)
Set which Naming Conventions style to use while encoding.
Convention getConventionDecoding()
Return which Naming Conventions style(s) to accept while decoding.
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'.
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:106
boost::chrono::microseconds microseconds
Definition: time.hpp:49
@ GenericNameComponent
Definition: tlv.hpp:68
@ NameComponentMin
Definition: tlv.hpp:96
@ NameComponentMax
Definition: tlv.hpp:97
@ TimestampNameComponent
Definition: tlv.hpp:124
@ ByteOffsetNameComponent
Definition: tlv.hpp:122
@ VersionNameComponent
Definition: tlv.hpp:123
@ SequenceNumNameComponent
Definition: tlv.hpp:125
@ SegmentNameComponent
Definition: tlv.hpp:121
uint64_t readNonNegativeInteger(size_t size, Iterator &begin, Iterator end)
Read a NonNegativeInteger in NDN-TLV encoding.
Definition: tlv.hpp:490
Definition: data.cpp:25
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:139
std::string unescape(const std::string &str)
Decode a percent-encoded string.
constexpr std::underlying_type_t< T > to_underlying(T val) noexcept
Definition: backports.hpp:136