name-component.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "name-component.hpp"
27 
30 #include "util/string-helper.hpp"
31 #include "util/crypto.hpp"
32 
33 #include <boost/lexical_cast.hpp>
34 
35 namespace ndn {
36 namespace name {
37 
38 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Component>));
39 BOOST_CONCEPT_ASSERT((WireEncodable<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 const std::string&
47 {
48  static const std::string prefix{"sha256digest="};
49  return prefix;
50 }
51 
53  : Block(tlv::NameComponent)
54 {
55 }
56 
58  : Block(wire)
59 {
60  if (!isGeneric() && !isImplicitSha256Digest())
61  BOOST_THROW_EXCEPTION(Error("Cannot construct name::Component from not a NameComponent "
62  "or ImplicitSha256DigestComponent TLV wire block"));
63 }
64 
66  : Block(tlv::NameComponent, buffer)
67 {
68 }
69 
71  : Block(makeBinaryBlock(tlv::NameComponent, value.buf(), value.size()))
72 {
73 }
74 
75 Component::Component(const uint8_t* value, size_t valueLen)
76  : Block(makeBinaryBlock(tlv::NameComponent, value, valueLen))
77 {
78 }
79 
80 Component::Component(const char* str)
81  : Block(makeBinaryBlock(tlv::NameComponent, str, std::char_traits<char>::length(str)))
82 {
83 }
84 
85 Component::Component(const std::string& str)
86  : Block(makeStringBlock(tlv::NameComponent, str))
87 {
88 }
89 
90 
92 Component::fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset)
93 {
94  std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
95  trim(trimmedString);
96 
97  if (trimmedString.compare(0, getSha256DigestUriPrefix().size(),
98  getSha256DigestUriPrefix()) == 0) {
99  if (trimmedString.size() != getSha256DigestUriPrefix().size() + crypto::SHA256_DIGEST_SIZE * 2)
100  BOOST_THROW_EXCEPTION(Error("Cannot convert to ImplicitSha256DigestComponent"
101  "(expected sha256 in hex encoding)"));
102 
103  try {
104  trimmedString.erase(0, getSha256DigestUriPrefix().size());
105  return fromImplicitSha256Digest(fromHex(trimmedString));
106  }
107  catch (StringHelperError& e) {
108  BOOST_THROW_EXCEPTION(Error("Cannot convert to a ImplicitSha256DigestComponent (invalid hex "
109  "encoding)"));
110  }
111  }
112  else {
113  std::string value = unescape(trimmedString);
114 
115  if (value.find_first_not_of(".") == std::string::npos) {
116  // Special case for component of only periods.
117  if (value.size() <= 2)
118  // Zero, one or two periods is illegal. Ignore this component.
119  BOOST_THROW_EXCEPTION(Error("Illegal URI (name component cannot be . or ..)"));
120  else
121  // Remove 3 periods.
122  return Component(reinterpret_cast<const uint8_t*>(&value[3]), value.size() - 3);
123  }
124  else
125  return Component(reinterpret_cast<const uint8_t*>(&value[0]), value.size());
126  }
127 }
128 
129 
130 void
131 Component::toUri(std::ostream& result) const
132 {
134  result << getSha256DigestUriPrefix();
135 
136  printHex(result, value(), value_size(), false);
137  }
138  else {
139  const uint8_t* value = this->value();
140  size_t valueSize = value_size();
141 
142  bool gotNonDot = false;
143  for (size_t i = 0; i < valueSize; ++i) {
144  if (value[i] != 0x2e) {
145  gotNonDot = true;
146  break;
147  }
148  }
149  if (!gotNonDot) {
150  // Special case for component of zero or more periods. Add 3 periods.
151  result << "...";
152  for (size_t i = 0; i < valueSize; ++i)
153  result << '.';
154  }
155  else {
156  // In case we need to escape, set to upper case hex and save the previous flags.
157  std::ios::fmtflags saveFlags = result.flags(std::ios::hex | std::ios::uppercase);
158 
159  for (size_t i = 0; i < valueSize; ++i) {
160  uint8_t x = value[i];
161  // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
162  if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
163  (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
164  x == 0x2e || x == 0x5f)
165  result << x;
166  else {
167  result << '%';
168  if (x < 16)
169  result << '0';
170  result << static_cast<uint32_t>(x);
171  }
172  }
173 
174  // Restore.
175  result.flags(saveFlags);
176  }
177  }
178 }
179 
180 std::string
182 {
183  std::ostringstream os;
184  toUri(os);
185  return os.str();
186 }
187 
189 
190 bool
192 {
193  return (value_size() == 1 || value_size() == 2 ||
194  value_size() == 4 || value_size() == 8);
195 }
196 
197 bool
198 Component::isNumberWithMarker(uint8_t marker) const
199 {
200  return (!empty() && value()[0] == marker &&
201  (value_size() == 2 || value_size() == 3 ||
202  value_size() == 5 || value_size() == 9));
203 }
204 
205 bool
207 {
209 }
210 
211 bool
213 {
215 }
216 
217 bool
219 {
221 }
222 
223 bool
225 {
227 }
228 
229 bool
231 {
233 }
234 
236 
237 uint64_t
239 {
240  if (!isNumber())
241  BOOST_THROW_EXCEPTION(Error("Name component does not have nonNegativeInteger value"));
242 
243  return readNonNegativeInteger(*this);
244 }
245 
246 uint64_t
247 Component::toNumberWithMarker(uint8_t marker) const
248 {
249  if (!isNumberWithMarker(marker))
250  BOOST_THROW_EXCEPTION(Error("Name component does not have the requested marker "
251  "or the value is not a nonNegativeInteger"));
252 
253  Buffer::const_iterator valueBegin = value_begin() + 1;
254  return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
255 }
256 
257 uint64_t
259 {
261 }
262 
263 uint64_t
265 {
267 }
268 
269 uint64_t
271 {
273 }
274 
277 {
279  return time::getUnixEpoch() + time::microseconds(value);
280 }
281 
282 uint64_t
284 {
286 }
287 
289 
290 Component
291 Component::fromNumber(uint64_t number)
292 {
294 }
295 
296 Component
297 Component::fromNumberWithMarker(uint8_t marker, uint64_t number)
298 {
299  EncodingEstimator estimator;
300 
301  size_t valueLength = estimator.prependNonNegativeInteger(number);
302  valueLength += estimator.prependByteArray(&marker, 1);
303  size_t totalLength = valueLength;
304  totalLength += estimator.prependVarNumber(valueLength);
305  totalLength += estimator.prependVarNumber(tlv::NameComponent);
306 
307  EncodingBuffer encoder(totalLength, 0);
308  encoder.prependNonNegativeInteger(number);
309  encoder.prependByteArray(&marker, 1);
310  encoder.prependVarNumber(valueLength);
311  encoder.prependVarNumber(tlv::NameComponent);
312 
313  return encoder.block();
314 }
315 
316 Component
317 Component::fromVersion(uint64_t version)
318 {
319  return fromNumberWithMarker(VERSION_MARKER, version);
320 }
321 
322 Component
323 Component::fromSegment(uint64_t segmentNo)
324 {
325  return fromNumberWithMarker(SEGMENT_MARKER, segmentNo);
326 }
327 
328 Component
330 {
332 }
333 
334 Component
336 {
337  using namespace time;
338  uint64_t value = duration_cast<microseconds>(timePoint - getUnixEpoch()).count();
339  return fromNumberWithMarker(TIMESTAMP_MARKER, value);
340 }
341 
342 Component
344 {
346 }
347 
349 
350 bool
352 {
353  return (type() == tlv::NameComponent);
354 }
355 
356 bool
358 {
361 }
362 
363 Component
365 {
366  if (digest->size() != crypto::SHA256_DIGEST_SIZE)
367  BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
368  to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
369 
371 }
372 
373 Component
374 Component::fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
375 {
376  if (digestSize != crypto::SHA256_DIGEST_SIZE)
377  BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
378  to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
379 
380  return makeBinaryBlock(tlv::ImplicitSha256DigestComponent, digest, digestSize);
381 }
382 
384 
385 bool
386 Component::equals(const Component& other) const
387 {
388  return type() == other.type() &&
389  value_size() == other.value_size() &&
390  (empty() || // needed on OSX 10.9 due to STL bug
391  std::equal(value_begin(), value_end(), other.value_begin()));
392 }
393 
394 int
395 Component::compare(const Component& other) const
396 {
397  if (this->hasWire() && other.hasWire()) {
398  // In the common case where both components have wire encoding,
399  // it's more efficient to simply compare the wire encoding.
400  // This works because lexical order of TLV encoding happens to be
401  // the same as canonical order of the value.
402  return std::memcmp(wire(), other.wire(), std::min(size(), other.size()));
403  }
404 
405  int cmpType = type() - other.type();
406  if (cmpType != 0)
407  return cmpType;
408 
409  int cmpSize = value_size() - other.value_size();
410  if (cmpSize != 0)
411  return cmpSize;
412 
413  if (empty()) // needed on OSX 10.9 due to STL bug
414  return 0;
415 
416  return std::memcmp(value(), other.value(), value_size());
417 }
418 
419 Component
421 {
422  size_t totalLength = 0;
423  EncodingBuffer encoder(size() + 1, 1); // + 1 in case there is an overflow
424  // in unlikely case TLV length increases,
425  // EncodingBuffer will take care of that
426 
427  bool isOverflow = true;
428  size_t i = value_size();
429  for (; isOverflow && i > 0; i--) {
430  uint8_t newValue = static_cast<uint8_t>((value()[i - 1] + 1) & 0xFF);
431  totalLength += encoder.prependByte(newValue);
432  isOverflow = (newValue == 0);
433  }
434  totalLength += encoder.prependByteArray(value(), i);
435 
436  if (isOverflow) {
437  // new name components has to be extended
438  totalLength += encoder.appendByte(0);
439  }
440 
441  encoder.prependVarNumber(totalLength);
442  encoder.prependVarNumber(type());
443 
444  return encoder.block();
445 }
446 
447 
448 template<encoding::Tag TAG>
449 size_t
450 Component::wireEncode(EncodingImpl<TAG>& encoder) const
451 {
452  size_t totalLength = 0;
453  if (value_size() > 0)
454  totalLength += encoder.prependByteArray(value(), value_size());
455  totalLength += encoder.prependVarNumber(value_size());
456  totalLength += encoder.prependVarNumber(type());
457  return totalLength;
458 }
459 
460 template size_t
461 Component::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& encoder) const;
462 
463 template size_t
464 Component::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& encoder) const;
465 
466 const Block&
468 {
469  if (this->hasWire())
470  return *this;
471 
472  EncodingEstimator estimator;
473  size_t estimatedSize = wireEncode(estimator);
474 
475  EncodingBuffer buffer(estimatedSize, 0);
476  wireEncode(buffer);
477 
478  const_cast<Component&>(*this) = buffer.block();
479  return *this;
480 }
481 
482 void
484 {
485  *this = wire;
486  // validity check is done within Component(const Block& wire)
487 }
488 
489 } // namespace name
490 } // namespace ndn
static Component fromNumber(uint64_t number)
Create a component encoded as nonNegativeInteger.
static Component fromSequenceNumber(uint64_t seqNo)
Create sequence number component using NDN naming conventions.
bool isGeneric() const
Check if the component is GenericComponent.
Copyright (c) 2013-2016 Regents of the University of California.
Definition: common.hpp:74
uint64_t toSegmentOffset() const
Interpret as segment offset component using NDN naming conventions.
bool isTimestamp() const
Check if the component is timestamp per NDN naming conventions.
static Component fromNumberWithMarker(uint8_t marker, uint64_t number)
Create a component encoded as NameComponentWithMarker.
int compare(const Component &other) const
Compare this to the other Component using NDN canonical ordering.
static Component fromEscapedString(const char *escapedString, size_t beginOffset, size_t endOffset)
Create name::Component by decoding the escapedString between beginOffset and endOffset according to t...
const Block & wireEncode() const
Encode to a wire format.
std::string toUri() const
Convert *this by escaping characters according to the NDN URI Scheme.
EncodingImpl< EstimatorTag > EncodingEstimator
time::system_clock::TimePoint toTimestamp() const
Interpret as timestamp component using NDN naming conventions.
bool isNumberWithMarker(uint8_t marker) const
Check if the component is NameComponentWithMarker per NDN naming conventions.
bool isSequenceNumber() const
Check if the component is sequence number per NDN naming conventions.
static const size_t SHA256_DIGEST_SIZE
number of octets in a SHA256 digest
Definition: crypto.hpp:32
static Component fromTimestamp(const time::system_clock::TimePoint &timePoint)
Create sequence number component using NDN naming conventions.
static const std::string & getSha256DigestUriPrefix()
STL namespace.
Class representing a wire element of NDN-TLV packet format.
Definition: block.hpp:43
void printHex(std::ostream &os, const uint8_t *buffer, size_t length, bool isUpperCase)
Output the hex representation of the bytes in array to the output stream os.
Block makeNonNegativeIntegerBlock(uint32_t type, uint64_t value)
Create a TLV block type type containing non-negative integer value.
Buffer::const_iterator value_begin() const
Definition: block.hpp:330
Buffer::const_iterator value_end() const
Definition: block.hpp:336
uint64_t readNonNegativeInteger(const Block &block)
Helper to read a non-negative integer from a block.
static Component fromSegmentOffset(uint64_t offset)
Create segment offset component using NDN naming conventions.
static const uint8_t SEGMENT_OFFSET_MARKER
Segment offset marker for NDN naming conventions.
uint64_t toNumberWithMarker(uint8_t marker) const
Interpret this name component as NameComponentWithMarker.
size_t size() const
Definition: block.cpp:504
static Component fromSegment(uint64_t segmentNo)
Create segment number component using NDN naming conventions.
a concept check for TLV abstraction with .wireEncode method
Definition: concepts.hpp:56
bool isSegment() const
Check if the component is segment number per NDN naming conventions.
bool isVersion() const
Check if the component is version per NDN naming conventions.
uint64_t toVersion() const
Interpret as version component using NDN naming conventions.
EncodingImpl< EncoderTag > EncodingBuffer
uint64_t toSegment() const
Interpret as segment number component using NDN naming conventions.
Block makeBinaryBlock(uint32_t type, const uint8_t *value, size_t length)
Create a TLV block type type with value from a buffer value of size length.
Component()
Create a new name::Component with an empty value.
Block makeStringBlock(uint32_t type, const std::string &value)
Create a TLV block type type with value from a string value.
std::string unescape(const std::string &str)
Decode a percent-encoded string.
uint64_t readNonNegativeInteger(size_t size, InputIterator &begin, const InputIterator &end)
Read nonNegativeInteger in NDN-TLV encoding.
size_t value_size() const
Definition: block.cpp:529
uint32_t type() const
Definition: block.hpp:324
Component getSuccessor() const
static const uint8_t VERSION_MARKER
Version marker for NDN naming conventions.
time_point TimePoint
Definition: time.hpp:90
Component holds a read-only name component value.
static Component fromImplicitSha256Digest(const ConstBufferPtr &digest)
Create ImplicitSha256DigestComponent component.
void wireDecode(const Block &wire)
Decode from the wire format.
static const uint8_t SEGMENT_MARKER
Segment marker for NDN naming conventions.
bool isNumber() const
Check if the component is nonNegativeInteger.
const uint8_t * wire() const
Definition: block.cpp:495
bool hasWire() const
Check if the Block has fully encoded wire.
Definition: block.cpp:471
void trim(std::string &str)
Modify str in place to erase whitespace on the left and right.
const uint8_t * value() const
Definition: block.cpp:520
static Component fromVersion(uint64_t version)
Create version component using NDN naming conventions.
std::string to_string(const V &v)
Definition: backports.hpp:51
bool isSegmentOffset() const
Check if the component is segment offset per NDN naming conventions.
static const uint8_t SEQUENCE_NUMBER_MARKER
Sequence number marker for NDN naming conventions.
uint64_t toSequenceNumber() const
Interpret as sequence number component using NDN naming conventions.
bool equals(const Component &other) const
Check if this is the same component as other.
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:33
shared_ptr< const Buffer > fromHex(const std::string &hexString)
Convert the hex string to buffer.
a concept check for TLV abstraction with .wireEncode method
Definition: concepts.hpp:40
Block()
Create an empty Block.
Definition: block.cpp:48
uint64_t toNumber() const
Interpret this name component as nonNegativeInteger.
static const uint8_t TIMESTAMP_MARKER
Timestamp marker for NDN naming conventions.
a concept check for TLV abstraction with .wireDecode method and constructible from Block ...
Definition: concepts.hpp:76
Class representing a general-use automatically managed/resized buffer.
Definition: buffer.hpp:44
size_t prependNonNegativeInteger(uint64_t integer)
Prepend non-negative integer integer of NDN TLV encoding.
Definition: estimator.cpp:81
const system_clock::TimePoint & getUnixEpoch()
Get system_clock::TimePoint representing UNIX time epoch (00:00:00 on Jan 1, 1970) ...
Definition: time.cpp:111
bool isImplicitSha256Digest() const
Check if the component is ImplicitSha256DigestComponent.
Error that can be thrown from name::Component.