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-2018 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 <cstring>
35 #include <sstream>
36 
37 namespace ndn {
38 namespace name {
39 
40 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Component>));
41 BOOST_CONCEPT_ASSERT((WireEncodable<Component>));
42 BOOST_CONCEPT_ASSERT((WireEncodableWithEncodingBuffer<Component>));
43 BOOST_CONCEPT_ASSERT((WireDecodable<Component>));
44 static_assert(std::is_base_of<tlv::Error, Component::Error>::value,
45  "name::Component::Error must inherit from tlv::Error");
46 
47 static const std::string&
49 {
50  static const std::string prefix{"sha256digest="};
51  return prefix;
52 }
53 
55  : Block(tlv::NameComponent)
56 {
57 }
58 
60  : Block(wire)
61 {
62  if (!isGeneric() && !isImplicitSha256Digest())
63  BOOST_THROW_EXCEPTION(Error("Cannot construct name::Component from not a NameComponent "
64  "or ImplicitSha256DigestComponent TLV wire block"));
65 }
66 
68  : Block(tlv::NameComponent, buffer)
69 {
70 }
71 
73  : Block(makeBinaryBlock(tlv::NameComponent, value.data(), value.size()))
74 {
75 }
76 
77 Component::Component(const uint8_t* value, size_t valueLen)
78  : Block(makeBinaryBlock(tlv::NameComponent, value, valueLen))
79 {
80 }
81 
82 Component::Component(const char* str)
83  : Block(makeBinaryBlock(tlv::NameComponent, str, std::char_traits<char>::length(str)))
84 {
85 }
86 
87 Component::Component(const std::string& str)
88  : Block(makeStringBlock(tlv::NameComponent, str))
89 {
90 }
91 
93 Component::fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset)
94 {
95  std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
96  boost::algorithm::trim(trimmedString);
97 
98  if (trimmedString.compare(0, getSha256DigestUriPrefix().size(),
99  getSha256DigestUriPrefix()) == 0) {
100  if (trimmedString.size() != getSha256DigestUriPrefix().size() + util::Sha256::DIGEST_SIZE * 2)
101  BOOST_THROW_EXCEPTION(Error("Cannot convert to ImplicitSha256DigestComponent"
102  "(expected sha256 in hex encoding)"));
103 
104  try {
105  trimmedString.erase(0, getSha256DigestUriPrefix().size());
106  return fromImplicitSha256Digest(fromHex(trimmedString));
107  }
108  catch (const StringHelperError&) {
109  BOOST_THROW_EXCEPTION(Error("Cannot convert to a ImplicitSha256DigestComponent (invalid hex "
110  "encoding)"));
111  }
112  }
113  else {
114  std::string value = unescape(trimmedString);
115 
116  if (value.find_first_not_of(".") == std::string::npos) {
117  // Special case for component of only periods.
118  if (value.size() <= 2)
119  // Zero, one or two periods is illegal. Ignore this component.
120  BOOST_THROW_EXCEPTION(Error("Illegal URI (name component cannot be . or ..)"));
121  else
122  // Remove 3 periods.
123  return Component(reinterpret_cast<const uint8_t*>(&value[3]), value.size() - 3);
124  }
125  else
126  return Component(reinterpret_cast<const uint8_t*>(&value[0]), value.size());
127  }
128 }
129 
130 void
131 Component::toUri(std::ostream& result) const
132 {
134  result << getSha256DigestUriPrefix();
135  printHex(result, value(), value_size(), false);
136  }
137  else {
138  bool hasNonDot = std::any_of(value_begin(), value_end(),
139  [] (uint8_t x) { return x != '.'; });
140  if (!hasNonDot) {
141  // Special case for component of zero or more periods. Add 3 periods.
142  result << "...";
143  for (size_t i = 0; i < value_size(); ++i)
144  result << '.';
145  }
146  else {
147  escape(result, reinterpret_cast<const char*>(value()), value_size());
148  }
149  }
150 }
151 
152 std::string
154 {
155  std::ostringstream os;
156  toUri(os);
157  return os.str();
158 }
159 
161 
162 bool
164 {
165  return (value_size() == 1 || value_size() == 2 ||
166  value_size() == 4 || value_size() == 8);
167 }
168 
169 bool
170 Component::isNumberWithMarker(uint8_t marker) const
171 {
172  return (!empty() && value()[0] == marker &&
173  (value_size() == 2 || value_size() == 3 ||
174  value_size() == 5 || value_size() == 9));
175 }
176 
177 bool
179 {
181 }
182 
183 bool
185 {
187 }
188 
189 bool
191 {
193 }
194 
195 bool
197 {
199 }
200 
201 bool
203 {
205 }
206 
208 
209 uint64_t
211 {
212  if (!isNumber())
213  BOOST_THROW_EXCEPTION(Error("Name component does not have nonNegativeInteger value"));
214 
215  return readNonNegativeInteger(*this);
216 }
217 
218 uint64_t
219 Component::toNumberWithMarker(uint8_t marker) const
220 {
221  if (!isNumberWithMarker(marker))
222  BOOST_THROW_EXCEPTION(Error("Name component does not have the requested marker "
223  "or the value is not a nonNegativeInteger"));
224 
225  Buffer::const_iterator valueBegin = value_begin() + 1;
226  return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
227 }
228 
229 uint64_t
231 {
233 }
234 
235 uint64_t
237 {
239 }
240 
241 uint64_t
243 {
245 }
246 
249 {
251  return time::getUnixEpoch() + time::microseconds(value);
252 }
253 
254 uint64_t
256 {
258 }
259 
261 
262 Component
263 Component::fromNumber(uint64_t number)
264 {
266 }
267 
268 Component
269 Component::fromNumberWithMarker(uint8_t marker, uint64_t number)
270 {
271  EncodingEstimator estimator;
272 
273  size_t valueLength = estimator.prependNonNegativeInteger(number);
274  valueLength += estimator.prependByteArray(&marker, 1);
275  size_t totalLength = valueLength;
276  totalLength += estimator.prependVarNumber(valueLength);
277  totalLength += estimator.prependVarNumber(tlv::NameComponent);
278 
279  EncodingBuffer encoder(totalLength, 0);
280  encoder.prependNonNegativeInteger(number);
281  encoder.prependByteArray(&marker, 1);
282  encoder.prependVarNumber(valueLength);
283  encoder.prependVarNumber(tlv::NameComponent);
284 
285  return encoder.block();
286 }
287 
288 Component
289 Component::fromVersion(uint64_t version)
290 {
291  return fromNumberWithMarker(VERSION_MARKER, version);
292 }
293 
294 Component
295 Component::fromSegment(uint64_t segmentNo)
296 {
297  return fromNumberWithMarker(SEGMENT_MARKER, segmentNo);
298 }
299 
300 Component
302 {
304 }
305 
306 Component
308 {
309  using namespace time;
310  uint64_t value = duration_cast<microseconds>(timePoint - getUnixEpoch()).count();
311  return fromNumberWithMarker(TIMESTAMP_MARKER, value);
312 }
313 
314 Component
316 {
318 }
319 
321 
322 bool
324 {
325  return (type() == tlv::NameComponent);
326 }
327 
328 bool
330 {
333 }
334 
335 Component
337 {
338  if (digest->size() != util::Sha256::DIGEST_SIZE)
339  BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
340  to_string(util::Sha256::DIGEST_SIZE) + " octets)"));
341 
343 }
344 
345 Component
346 Component::fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
347 {
348  if (digestSize != util::Sha256::DIGEST_SIZE)
349  BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
350  to_string(util::Sha256::DIGEST_SIZE) + " octets)"));
351 
352  return makeBinaryBlock(tlv::ImplicitSha256DigestComponent, digest, digestSize);
353 }
354 
356 
357 bool
358 Component::equals(const Component& other) const
359 {
360  return type() == other.type() &&
361  value_size() == other.value_size() &&
362  (empty() || // needed with Apple clang < 9.0.0 due to libc++ bug
363  std::equal(value_begin(), value_end(), other.value_begin()));
364 }
365 
366 int
367 Component::compare(const Component& other) const
368 {
369  if (this->hasWire() && other.hasWire()) {
370  // In the common case where both components have wire encoding,
371  // it's more efficient to simply compare the wire encoding.
372  // This works because lexical order of TLV encoding happens to be
373  // the same as canonical order of the value.
374  return std::memcmp(wire(), other.wire(), std::min(size(), other.size()));
375  }
376 
377  int cmpType = type() - other.type();
378  if (cmpType != 0)
379  return cmpType;
380 
381  int cmpSize = value_size() - other.value_size();
382  if (cmpSize != 0)
383  return cmpSize;
384 
385  if (empty())
386  return 0;
387 
388  return std::memcmp(value(), other.value(), value_size());
389 }
390 
391 Component
393 {
394  size_t totalLength = 0;
395  EncodingBuffer encoder(size() + 1, 1); // + 1 in case there is an overflow
396  // in unlikely case TLV length increases,
397  // EncodingBuffer will take care of that
398 
399  bool isOverflow = true;
400  size_t i = value_size();
401  for (; isOverflow && i > 0; i--) {
402  uint8_t newValue = static_cast<uint8_t>((value()[i - 1] + 1) & 0xFF);
403  totalLength += encoder.prependByte(newValue);
404  isOverflow = (newValue == 0);
405  }
406  totalLength += encoder.prependByteArray(value(), i);
407 
408  if (isOverflow) {
409  // new name components has to be extended
410  totalLength += encoder.appendByte(0);
411  }
412 
413  encoder.prependVarNumber(totalLength);
414  encoder.prependVarNumber(type());
415 
416  return encoder.block();
417 }
418 
419 template<encoding::Tag TAG>
420 size_t
421 Component::wireEncode(EncodingImpl<TAG>& encoder) const
422 {
423  size_t totalLength = 0;
424  if (value_size() > 0)
425  totalLength += encoder.prependByteArray(value(), value_size());
426  totalLength += encoder.prependVarNumber(value_size());
427  totalLength += encoder.prependVarNumber(type());
428  return totalLength;
429 }
430 
432 
433 const Block&
435 {
436  if (this->hasWire())
437  return *this;
438 
439  EncodingEstimator estimator;
440  size_t estimatedSize = wireEncode(estimator);
441 
442  EncodingBuffer buffer(estimatedSize, 0);
443  wireEncode(buffer);
444 
445  const_cast<Component&>(*this) = buffer.block();
446  return *this;
447 }
448 
449 void
451 {
452  *this = wire;
453  // validity check is done within Component(const Block& wire)
454 }
455 
456 } // namespace name
457 } // 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.
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:300
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.
std::string escape(const std::string &str)
Percent-encode a string.
size_t value_size() const
Get size of TLV-VALUE aka TLV-LENGTH.
Definition: block.cpp:318
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:196
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:291
bool hasWire() const
Check if the Block has fully encoded wire.
Definition: block.cpp:251
const uint8_t * value() const
Get pointer to TLV-VALUE.
Definition: block.cpp:312
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:50
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:106
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