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() noexcept
81 {
82  return (to_underlying(g_conventionDecoding) & to_underlying(Convention::MARKER)) != 0;
83 }
84 
85 static bool
86 canDecodeTypedConvention() noexcept
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  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 = 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  getComponentTypeTable().get(type()).writeUri(os, *this);
206  }
207  else {
208  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
223 Component::isNumber() const noexcept
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 noexcept
231 {
232  return (value_size() == 2 || value_size() == 3 ||
233  value_size() == 5 || value_size() == 9) && value()[0] == marker;
234 }
235 
236 bool
237 Component::isSegment() const noexcept
238 {
239  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_MARKER)) ||
240  (canDecodeTypedConvention() && type() == tlv::SegmentNameComponent && isNumber());
241 }
242 
243 bool
244 Component::isByteOffset() const noexcept
245 {
246  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(SEGMENT_OFFSET_MARKER)) ||
247  (canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent && isNumber());
248 }
249 
250 bool
251 Component::isVersion() const noexcept
252 {
253  return (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent && isNumberWithMarker(VERSION_MARKER)) ||
254  (canDecodeTypedConvention() && type() == tlv::VersionNameComponent && isNumber());
255 }
256 
257 bool
258 Component::isTimestamp() const noexcept
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::SegmentNameComponent) {
300  return toNumber();
301  }
302  NDN_THROW(Error("Not a Segment component"));
303 }
304 
305 uint64_t
307 {
308  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
310  }
311  if (canDecodeTypedConvention() && type() == tlv::ByteOffsetNameComponent) {
312  return toNumber();
313  }
314  NDN_THROW(Error("Not a ByteOffset component"));
315 }
316 
317 uint64_t
319 {
320  if (canDecodeMarkerConvention() && type() == tlv::GenericNameComponent) {
322  }
323  if (canDecodeTypedConvention() && type() == tlv::VersionNameComponent) {
324  return toNumber();
325  }
326  NDN_THROW(Error("Not a Version 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 Component(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 Component(encoder.block());
382 }
383 
384 Component
385 Component::fromSegment(uint64_t segmentNo)
386 {
387  return g_conventionEncoding == Convention::MARKER ?
390 }
391 
392 Component
394 {
395  return g_conventionEncoding == Convention::MARKER ?
398 }
399 
400 Component
401 Component::fromVersion(uint64_t version)
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 {
431 }
432 
433 Component
435 {
436  return {tlv::ImplicitSha256DigestComponent, std::move(digest)};
437 }
438 
439 Component
440 Component::fromImplicitSha256Digest(span<const uint8_t> digest)
441 {
442  return {tlv::ImplicitSha256DigestComponent, digest};
443 }
444 
445 bool
447 {
449 }
450 
451 Component
453 {
454  return {tlv::ParametersSha256DigestComponent, std::move(digest)};
455 }
456 
457 Component
458 Component::fromParametersSha256Digest(span<const uint8_t> digest)
459 {
460  return {tlv::ParametersSha256DigestComponent, digest};
461 }
462 
464 
465 bool
466 Component::equals(const Component& other) const noexcept
467 {
468  return type() == other.type() &&
469  value_size() == other.value_size() &&
470  std::equal(value_begin(), value_end(), other.value_begin());
471 }
472 
473 int
474 Component::compare(const Component& other) const
475 {
476  if (this->hasWire() && other.hasWire()) {
477  // In the common case where both components have wire encoding,
478  // it's more efficient to simply compare the wire encoding.
479  // This works because lexical order of TLV encoding happens to be
480  // the same as canonical order of the value.
481  return std::memcmp(data(), other.data(), std::min(size(), other.size()));
482  }
483 
484  int cmpType = type() - other.type();
485  if (cmpType != 0)
486  return cmpType;
487 
488  int cmpSize = value_size() - other.value_size();
489  if (cmpSize != 0)
490  return cmpSize;
491 
492  if (empty())
493  return 0;
494 
495  return std::memcmp(value(), other.value(), value_size());
496 }
497 
498 Component
500 {
501  bool isOverflow = false;
502  Component successor;
503  std::tie(isOverflow, successor) = getComponentTypeTable().get(type()).getSuccessor(*this);
504  if (!isOverflow) {
505  return successor;
506  }
507 
508  uint32_t type = this->type() + 1;
509  auto value = getComponentTypeTable().get(type).getMinValue();
510  return {type, value};
511 }
512 
513 template<encoding::Tag TAG>
514 size_t
516 {
517  size_t totalLength = 0;
518  if (value_size() > 0) {
519  totalLength += encoder.prependBytes(value_bytes());
520  }
521  totalLength += encoder.prependVarNumber(value_size());
522  totalLength += encoder.prependVarNumber(type());
523  return totalLength;
524 }
525 
527 
528 const Block&
530 {
531  if (this->hasWire())
532  return *this;
533 
534  EncodingEstimator estimator;
535  size_t estimatedSize = wireEncode(estimator);
536 
537  EncodingBuffer buffer(estimatedSize, 0);
538  wireEncode(buffer);
539 
540  const_cast<Component*>(this)->wireDecode(buffer.block());
541  return *this;
542 }
543 
544 void
546 {
547  *this = Component(wire);
548  // validity check is done within Component(const Block& wire)
549 }
550 
551 } // namespace name
552 } // namespace ndn
Represents a TLV element of the NDN packet format.
Definition: block.hpp:45
const uint8_t * data() const
Return a raw pointer to the beginning of the encoded wire.
Definition: block.cpp:285
const_iterator value_end() const noexcept
Get end iterator of TLV-VALUE.
Definition: block.hpp:311
element_const_iterator find(uint32_t type) const
Find the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:424
size_t size() const
Return the size of the encoded wire, i.e., of the whole TLV.
Definition: block.cpp:294
bool hasWire() const noexcept
Check if the Block contains a fully encoded wire representation.
Definition: block.hpp:221
const uint8_t * wire() const
Definition: block.hpp:250
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:330
uint32_t type() const noexcept
Return the TLV-TYPE of the Block.
Definition: block.hpp:277
const_iterator value_begin() const noexcept
Get begin iterator of TLV-VALUE.
Definition: block.hpp:301
const Block & get(uint32_t type) const
Return the first sub-element of the specified TLV-TYPE.
Definition: block.cpp:412
size_t value_size() const noexcept
Return the size of TLV-VALUE, i.e., the TLV-LENGTH.
Definition: block.hpp:321
const uint8_t * value() const noexcept
Return a raw pointer to the beginning of TLV-VALUE.
Definition: block.cpp:306
A concept check for TLV abstraction with a wireDecode(Block) method and constructible from Block.
Definition: concepts.hpp:81
A concept check for TLV abstraction with a wireEncode(EncodingBuffer) method.
Definition: concepts.hpp:61
A concept check for TLV abstraction with a wireEncode() method.
Definition: concepts.hpp:45
constexpr size_t prependNonNegativeInteger(uint64_t n) const noexcept
Prepend n in NonNegativeInteger encoding.
Definition: estimator.hpp:100
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 a wire format.
Component getSuccessor() const
Get the successor of this name component.
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 isImplicitSha256Digest() const noexcept
Check if the component is an ImplicitSha256DigestComponent.
bool isSegment() const noexcept
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 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.
bool equals(const Component &other) const noexcept
Check if this is the same component as other.
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 the 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.
static Component fromImplicitSha256Digest(ConstBufferPtr digest)
Create ImplicitSha256DigestComponent component.
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 to the other Component 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:200
static const size_t DIGEST_SIZE
Length in bytes of a SHA-256 digest.
Definition: sha256.hpp:56
#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)
@ 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:106
boost::chrono::microseconds microseconds
Definition: time.hpp:49
@ 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
uint64_t readNonNegativeInteger(size_t size, Iterator &begin, Iterator end)
Read a NonNegativeInteger in NDN-TLV encoding.
Definition: tlv.hpp:474
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:125