blob: 13005dd1350d3d01ad1b1ec07aca12c03490de95 [file] [log] [blame]
Alexander Afanasyev15f67312014-07-22 15:11:09 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Davide Pesaventodd461432017-01-28 21:47:26 -05003 * Copyright (c) 2013-2017 Regents of the University of California.
Alexander Afanasyev15f67312014-07-22 15:11:09 -07004 *
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
28#include "encoding/block-helpers.hpp"
29#include "encoding/encoding-buffer.hpp"
30#include "util/string-helper.hpp"
Alexander Afanasyev6486d522014-10-23 14:14:11 -070031#include "util/crypto.hpp"
Alexander Afanasyev15f67312014-07-22 15:11:09 -070032
Davide Pesaventodd461432017-01-28 21:47:26 -050033#include <boost/algorithm/string/trim.hpp>
Alexander Afanasyev6486d522014-10-23 14:14:11 -070034
Alexander Afanasyev15f67312014-07-22 15:11:09 -070035namespace ndn {
36namespace name {
37
Junxiao Shic2b8d242014-11-04 08:35:29 -070038BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Component>));
39BOOST_CONCEPT_ASSERT((WireEncodable<Component>));
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -070040BOOST_CONCEPT_ASSERT((WireEncodableWithEncodingBuffer<Component>));
Junxiao Shic2b8d242014-11-04 08:35:29 -070041BOOST_CONCEPT_ASSERT((WireDecodable<Component>));
42static_assert(std::is_base_of<tlv::Error, Component::Error>::value,
43 "name::Component::Error must inherit from tlv::Error");
44
Alexander Afanasyev6486d522014-10-23 14:14:11 -070045static const std::string&
46getSha256DigestUriPrefix()
47{
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -070048 static const std::string prefix{"sha256digest="};
Alexander Afanasyev6486d522014-10-23 14:14:11 -070049 return prefix;
50}
51
Alexander Afanasyev15f67312014-07-22 15:11:09 -070052Component::Component()
53 : Block(tlv::NameComponent)
54{
55}
56
57Component::Component(const Block& wire)
58 : Block(wire)
59{
Qiuhan Ding2c3cbe42014-11-25 18:10:23 -080060 if (!isGeneric() && !isImplicitSha256Digest())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070061 BOOST_THROW_EXCEPTION(Error("Cannot construct name::Component from not a NameComponent "
62 "or ImplicitSha256DigestComponent TLV wire block"));
Alexander Afanasyev15f67312014-07-22 15:11:09 -070063}
64
65Component::Component(const ConstBufferPtr& buffer)
66 : Block(tlv::NameComponent, buffer)
67{
68}
69
70Component::Component(const Buffer& value)
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -070071 : Block(makeBinaryBlock(tlv::NameComponent, value.buf(), value.size()))
Alexander Afanasyev15f67312014-07-22 15:11:09 -070072{
73}
74
75Component::Component(const uint8_t* value, size_t valueLen)
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -070076 : Block(makeBinaryBlock(tlv::NameComponent, value, valueLen))
Alexander Afanasyev15f67312014-07-22 15:11:09 -070077{
78}
79
80Component::Component(const char* str)
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -070081 : Block(makeBinaryBlock(tlv::NameComponent, str, std::char_traits<char>::length(str)))
Alexander Afanasyev15f67312014-07-22 15:11:09 -070082{
83}
84
85Component::Component(const std::string& str)
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -070086 : Block(makeStringBlock(tlv::NameComponent, str))
Alexander Afanasyev15f67312014-07-22 15:11:09 -070087{
88}
89
90
91Component
92Component::fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset)
93{
94 std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
Davide Pesaventodd461432017-01-28 21:47:26 -050095 boost::algorithm::trim(trimmedString);
Alexander Afanasyev15f67312014-07-22 15:11:09 -070096
Alexander Afanasyev6486d522014-10-23 14:14:11 -070097 if (trimmedString.compare(0, getSha256DigestUriPrefix().size(),
98 getSha256DigestUriPrefix()) == 0) {
99 if (trimmedString.size() != getSha256DigestUriPrefix().size() + crypto::SHA256_DIGEST_SIZE * 2)
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700100 BOOST_THROW_EXCEPTION(Error("Cannot convert to ImplicitSha256DigestComponent"
101 "(expected sha256 in hex encoding)"));
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700102
103 try {
Alexander Afanasyev8828ca62015-07-02 13:40:09 -0700104 trimmedString.erase(0, getSha256DigestUriPrefix().size());
105 return fromImplicitSha256Digest(fromHex(trimmedString));
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700106 }
Alexander Afanasyev8828ca62015-07-02 13:40:09 -0700107 catch (StringHelperError& e) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700108 BOOST_THROW_EXCEPTION(Error("Cannot convert to a ImplicitSha256DigestComponent (invalid hex "
109 "encoding)"));
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700110 }
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700111 }
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700112 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.
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700119 BOOST_THROW_EXCEPTION(Error("Illegal URI (name component cannot be . or ..)"));
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700120 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 }
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700127}
128
129
130void
131Component::toUri(std::ostream& result) const
132{
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700133 if (type() == tlv::ImplicitSha256DigestComponent) {
134 result << getSha256DigestUriPrefix();
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700135
Alexander Afanasyev8828ca62015-07-02 13:40:09 -0700136 printHex(result, value(), value_size(), false);
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700137 }
138 else {
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700139 const uint8_t* value = this->value();
140 size_t valueSize = value_size();
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700141
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700142 bool gotNonDot = false;
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700143 for (size_t i = 0; i < valueSize; ++i) {
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700144 if (value[i] != 0x2e) {
145 gotNonDot = true;
146 break;
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700147 }
148 }
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700149 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);
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700158
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700159 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 }
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700177 }
178}
179
180std::string
181Component::toUri() const
182{
183 std::ostringstream os;
184 toUri(os);
185 return os.str();
186}
187
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700188////////////////////////////////////////////////////////////////////////////////
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700189
190bool
191Component::isNumber() const
192{
193 return (value_size() == 1 || value_size() == 2 ||
194 value_size() == 4 || value_size() == 8);
195}
196
197bool
198Component::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
205bool
206Component::isVersion() const
207{
208 return isNumberWithMarker(VERSION_MARKER);
209}
210
211bool
212Component::isSegment() const
213{
214 return isNumberWithMarker(SEGMENT_MARKER);
215}
216
217bool
218Component::isSegmentOffset() const
219{
220 return isNumberWithMarker(SEGMENT_OFFSET_MARKER);
221}
222
223bool
224Component::isTimestamp() const
225{
226 return isNumberWithMarker(TIMESTAMP_MARKER);
227}
228
229bool
230Component::isSequenceNumber() const
231{
232 return isNumberWithMarker(SEQUENCE_NUMBER_MARKER);
233}
234
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700235////////////////////////////////////////////////////////////////////////////////
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700236
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700237uint64_t
238Component::toNumber() const
239{
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700240 if (!isNumber())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700241 BOOST_THROW_EXCEPTION(Error("Name component does not have nonNegativeInteger value"));
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700242
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700243 return readNonNegativeInteger(*this);
244}
245
246uint64_t
247Component::toNumberWithMarker(uint8_t marker) const
248{
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700249 if (!isNumberWithMarker(marker))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700250 BOOST_THROW_EXCEPTION(Error("Name component does not have the requested marker "
251 "or the value is not a nonNegativeInteger"));
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700252
253 Buffer::const_iterator valueBegin = value_begin() + 1;
254 return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
255}
256
257uint64_t
258Component::toVersion() const
259{
260 return toNumberWithMarker(VERSION_MARKER);
261}
262
263uint64_t
264Component::toSegment() const
265{
266 return toNumberWithMarker(SEGMENT_MARKER);
267}
268
269uint64_t
270Component::toSegmentOffset() const
271{
272 return toNumberWithMarker(SEGMENT_OFFSET_MARKER);
273}
274
275time::system_clock::TimePoint
276Component::toTimestamp() const
277{
278 uint64_t value = toNumberWithMarker(TIMESTAMP_MARKER);
279 return time::getUnixEpoch() + time::microseconds(value);
280}
281
282uint64_t
283Component::toSequenceNumber() const
284{
285 return toNumberWithMarker(SEQUENCE_NUMBER_MARKER);
286}
287
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700288////////////////////////////////////////////////////////////////////////////////
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700289
290Component
291Component::fromNumber(uint64_t number)
292{
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -0700293 return makeNonNegativeIntegerBlock(tlv::NameComponent, number);
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700294}
295
296Component
297Component::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
316Component
317Component::fromVersion(uint64_t version)
318{
319 return fromNumberWithMarker(VERSION_MARKER, version);
320}
321
322Component
323Component::fromSegment(uint64_t segmentNo)
324{
325 return fromNumberWithMarker(SEGMENT_MARKER, segmentNo);
326}
327
328Component
329Component::fromSegmentOffset(uint64_t offset)
330{
331 return fromNumberWithMarker(SEGMENT_OFFSET_MARKER, offset);
332}
333
334Component
335Component::fromTimestamp(const time::system_clock::TimePoint& timePoint)
336{
337 using namespace time;
338 uint64_t value = duration_cast<microseconds>(timePoint - getUnixEpoch()).count();
339 return fromNumberWithMarker(TIMESTAMP_MARKER, value);
340}
341
342Component
343Component::fromSequenceNumber(uint64_t seqNo)
344{
345 return fromNumberWithMarker(SEQUENCE_NUMBER_MARKER, seqNo);
346}
347
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700348////////////////////////////////////////////////////////////////////////////////
349
350bool
351Component::isGeneric() const
352{
353 return (type() == tlv::NameComponent);
354}
355
356bool
357Component::isImplicitSha256Digest() const
358{
359 return (type() == tlv::ImplicitSha256DigestComponent &&
360 value_size() == crypto::SHA256_DIGEST_SIZE);
361}
362
363Component
364Component::fromImplicitSha256Digest(const ConstBufferPtr& digest)
365{
366 if (digest->size() != crypto::SHA256_DIGEST_SIZE)
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700367 BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
Davide Pesavento96b96af2015-09-19 23:00:40 +0200368 to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700369
370 return Block(tlv::ImplicitSha256DigestComponent, digest);
371}
372
373Component
374Component::fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
375{
376 if (digestSize != crypto::SHA256_DIGEST_SIZE)
Davide Pesavento96b96af2015-09-19 23:00:40 +0200377 BOOST_THROW_EXCEPTION(Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
378 to_string(crypto::SHA256_DIGEST_SIZE) + " octets)"));
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700379
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -0700380 return makeBinaryBlock(tlv::ImplicitSha256DigestComponent, digest, digestSize);
Alexander Afanasyev6486d522014-10-23 14:14:11 -0700381}
382
383////////////////////////////////////////////////////////////////////////////////
384
Junxiao Shidf4b24e2016-07-14 21:41:43 +0000385bool
386Component::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
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700394int
395Component::compare(const Component& other) const
396{
Junxiao Shi010f0862016-10-11 21:08:32 +0000397 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
Junxiao Shidf4b24e2016-07-14 21:41:43 +0000405 int cmpType = type() - other.type();
406 if (cmpType != 0)
407 return cmpType;
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700408
Junxiao Shidf4b24e2016-07-14 21:41:43 +0000409 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
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700414 return 0;
415
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700416 return std::memcmp(value(), other.value(), value_size());
417}
418
419Component
420Component::getSuccessor() const
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
Davide Pesavento9bd4d982015-05-13 14:31:19 +0200441 encoder.prependVarNumber(totalLength);
442 encoder.prependVarNumber(type());
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700443
444 return encoder.block();
445}
446
447
Alexander Afanasyev74633892015-02-08 18:08:46 -0800448template<encoding::Tag TAG>
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700449size_t
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -0700450Component::wireEncode(EncodingImpl<TAG>& encoder) const
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700451{
452 size_t totalLength = 0;
453 if (value_size() > 0)
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -0700454 totalLength += encoder.prependByteArray(value(), value_size());
455 totalLength += encoder.prependVarNumber(value_size());
456 totalLength += encoder.prependVarNumber(type());
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700457 return totalLength;
458}
459
460template size_t
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -0700461Component::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& encoder) const;
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700462
463template size_t
Alexander Afanasyevd5c48e02015-06-24 11:58:14 -0700464Component::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& encoder) const;
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700465
466const Block&
467Component::wireEncode() const
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
482void
483Component::wireDecode(const Block& wire)
484{
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700485 *this = wire;
Qiuhan Ding2c3cbe42014-11-25 18:10:23 -0800486 // validity check is done within Component(const Block& wire)
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700487}
488
489} // namespace name
490} // namespace ndn