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-2017 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 
26 #include "name-component.hpp"
27 
30 #include "util/sha256.hpp"
31 #include "util/string-helper.hpp"
32 
33 #include <boost/algorithm/string/trim.hpp>
34 #include <sstream>
35 
36 namespace ndn {
37 namespace name {
38 
39 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Component>));
43 static_assert(std::is_base_of<tlv::Error, Component::Error>::value,
44  "name::Component::Error must inherit from tlv::Error");
45 
46 static const std::string&
48 {
49  static const std::string prefix{"sha256digest="};
50  return prefix;
51 }
52 
54  : Block(tlv::NameComponent)
55 {
56 }
57 
59  : Block(wire)
60 {
61  if (!isGeneric() && !isImplicitSha256Digest())
62  BOOST_THROW_EXCEPTION(Error("Cannot construct name::Component from not a NameComponent "
63  "or ImplicitSha256DigestComponent TLV wire block"));
64 }
65 
67  : Block(tlv::NameComponent, buffer)
68 {
69 }
70 
72  : Block(makeBinaryBlock(tlv::NameComponent, value.data(), value.size()))
73 {
74 }
75 
76 Component::Component(const uint8_t* value, size_t valueLen)
77  : Block(makeBinaryBlock(tlv::NameComponent, value, valueLen))
78 {
79 }
80 
81 Component::Component(const char* str)
82  : Block(makeBinaryBlock(tlv::NameComponent, str, std::char_traits<char>::length(str)))
83 {
84 }
85 
86 Component::Component(const std::string& str)
87  : Block(makeStringBlock(tlv::NameComponent, str))
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  boost::algorithm::trim(trimmedString);
96 
97  if (trimmedString.compare(0, getSha256DigestUriPrefix().size(),
98  getSha256DigestUriPrefix()) == 0) {
99  if (trimmedString.size() != getSha256DigestUriPrefix().size() + util::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 (const StringHelperError&) {
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  auto savedFlags = 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(savedFlags);
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() != util::Sha256::DIGEST_SIZE)
367  BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
368  to_string(util::Sha256::DIGEST_SIZE) + " octets)"));
369 
371 }
372 
373 Component
374 Component::fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
375 {
376  if (digestSize != util::Sha256::DIGEST_SIZE)
377  BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
378  to_string(util::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 template<encoding::Tag TAG>
448 size_t
449 Component::wireEncode(EncodingImpl<TAG>& encoder) const
450 {
451  size_t totalLength = 0;
452  if (value_size() > 0)
453  totalLength += encoder.prependByteArray(value(), value_size());
454  totalLength += encoder.prependVarNumber(value_size());
455  totalLength += encoder.prependVarNumber(type());
456  return totalLength;
457 }
458 
460 
461 const Block&
463 {
464  if (this->hasWire())
465  return *this;
466 
467  EncodingEstimator estimator;
468  size_t estimatedSize = wireEncode(estimator);
469 
470  EncodingBuffer buffer(estimatedSize, 0);
471  wireEncode(buffer);
472 
473  const_cast<Component&>(*this) = buffer.block();
474  return *this;
475 }
476 
477 void
479 {
480  *this = wire;
481  // validity check is done within Component(const Block& wire)
482 }
483 
484 } // namespace name
485 } // 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-2017 Regents of the University of California.
Definition: common.hpp:66
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.
BOOST_CONCEPT_ASSERT((boost::EqualityComparable< Component >))
static const size_t DIGEST_SIZE
Length in bytes of a SHA-256 digest.
Definition: sha256.hpp:60
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 Component fromTimestamp(const time::system_clock::TimePoint &timePoint)
Create sequence number component using NDN naming conventions.
static const std::string & getSha256DigestUriPrefix()
STL namespace.
Represents a TLV element of NDN packet format.
Definition: block.hpp:42
Block makeNonNegativeIntegerBlock(uint32_t type, uint64_t value)
Create a TLV block containing a non-negative integer.
Buffer::const_iterator value_begin() const
Get begin iterator of TLV-VALUE.
Definition: block.hpp:255
Buffer::const_iterator value_end() const
Get end iterator of TLV-VALUE.
Definition: block.hpp:264
uint64_t readNonNegativeInteger(const Block &block)
Read a non-negative integer from a TLV element.
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
Get size of encoded wire, including Type-Length-Value.
Definition: block.cpp:299
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:60
bool isSegment() const
Check if the component is segment number per NDN naming conventions.
uint64_t readNonNegativeInteger(size_t size, Iterator &begin, const Iterator &end)
Read nonNegativeInteger in NDN-TLV encoding.
bool isVersion() const
Check if the component is version per NDN naming conventions.
shared_ptr< Buffer > fromHex(const std::string &hexString)
Convert the hex string to buffer.
uint64_t toVersion() const
Interpret as version component using NDN naming conventions.
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 copying TLV-VALUE from raw buffer.
Component()
Create a new name::Component with an empty value.
Block makeStringBlock(uint32_t type, const std::string &value)
Create a TLV block containing a string.
#define NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(ClassName)
std::string unescape(const std::string &str)
Decode a percent-encoded string.
size_t value_size() const
Get size of TLV-VALUE aka TLV-LENGTH.
Definition: block.cpp:317
uint32_t type() const
Get TLV-TYPE.
Definition: block.hpp:235
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
Get pointer to encoded wire.
Definition: block.cpp:290
bool hasWire() const
Check if the Block has fully encoded wire.
Definition: block.cpp:250
const uint8_t * value() const
Get pointer to TLV-VALUE.
Definition: block.cpp:311
static Component fromVersion(uint64_t version)
Create version component using NDN naming conventions.
void printHex(std::ostream &os, uint64_t num, bool wantUpperCase)
Output the hex representation of num to the output stream os.
std::string to_string(const V &v)
Definition: backports.hpp:84
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.
a concept check for TLV abstraction with .wireEncode method
Definition: concepts.hpp:44
Block()
Create an empty Block.
Definition: block.cpp:49
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:80
General-purpose automatically managed/resized buffer.
Definition: buffer.hpp:40
size_t prependNonNegativeInteger(uint64_t integer)
Prepend non-negative integer integer of NDN TLV encoding.
Definition: estimator.cpp:81
EncodingImpl< EncoderTag > EncodingBuffer
const system_clock::TimePoint & getUnixEpoch()
Get system_clock::TimePoint representing UNIX time epoch (00:00:00 on Jan 1, 1970) ...
Definition: time.cpp:112
EncodingImpl< EstimatorTag > EncodingEstimator
bool isImplicitSha256Digest() const
Check if the component is ImplicitSha256DigestComponent.
Error that can be thrown from name::Component.
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:89