blob: 893369fc7795014553f28361128c1bbe7f51c37f [file] [log] [blame]
Alexander Afanasyev15f67312014-07-22 15:11:09 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2014 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
28#include "encoding/block-helpers.hpp"
29#include "encoding/encoding-buffer.hpp"
30#include "util/string-helper.hpp"
Junxiao Shic2b8d242014-11-04 08:35:29 -070031#include "util/concepts.hpp"
Alexander Afanasyev15f67312014-07-22 15:11:09 -070032
33namespace ndn {
34namespace name {
35
Junxiao Shic2b8d242014-11-04 08:35:29 -070036BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Component>));
37BOOST_CONCEPT_ASSERT((WireEncodable<Component>));
38BOOST_CONCEPT_ASSERT((WireDecodable<Component>));
39static_assert(std::is_base_of<tlv::Error, Component::Error>::value,
40 "name::Component::Error must inherit from tlv::Error");
41
Alexander Afanasyev15f67312014-07-22 15:11:09 -070042Component::Component()
43 : Block(tlv::NameComponent)
44{
45}
46
47Component::Component(const Block& wire)
48 : Block(wire)
49{
50 if (type() != tlv::NameComponent)
51 throw Error("Cannot construct name::Component from not a NameComponent TLV wire block");
52}
53
54Component::Component(const ConstBufferPtr& buffer)
55 : Block(tlv::NameComponent, buffer)
56{
57}
58
59Component::Component(const Buffer& value)
60 : Block(dataBlock(tlv::NameComponent, value.buf(), value.size()))
61{
62}
63
64Component::Component(const uint8_t* value, size_t valueLen)
65 : Block(dataBlock(tlv::NameComponent, value, valueLen))
66{
67}
68
69Component::Component(const char* str)
Davide Pesaventodfe9c6b2014-08-25 21:17:10 +020070 : Block(dataBlock(tlv::NameComponent, str, std::char_traits<char>::length(str)))
Alexander Afanasyev15f67312014-07-22 15:11:09 -070071{
72}
73
74Component::Component(const std::string& str)
75 : Block(dataBlock(tlv::NameComponent, str.c_str(), str.size()))
76{
77}
78
79
80Component
81Component::fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset)
82{
83 std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
84 trim(trimmedString);
85 std::string value = unescape(trimmedString);
86
87 if (value.find_first_not_of(".") == std::string::npos) {
88 // Special case for component of only periods.
89 if (value.size() <= 2)
90 // Zero, one or two periods is illegal. Ignore this component.
91 throw Error("Illegal URI (name component cannot be . or ..)");
92 else
93 // Remove 3 periods.
94 return Component(reinterpret_cast<const uint8_t*>(&value[3]), value.size() - 3);
95 }
96 else
97 return Component(reinterpret_cast<const uint8_t*>(&value[0]), value.size());
98}
99
100
101void
102Component::toUri(std::ostream& result) const
103{
104 const uint8_t* value = this->value();
105 size_t valueSize = value_size();
106
107 bool gotNonDot = false;
108 for (size_t i = 0; i < valueSize; ++i) {
109 if (value[i] != 0x2e) {
110 gotNonDot = true;
111 break;
112 }
113 }
114 if (!gotNonDot) {
115 // Special case for component of zero or more periods. Add 3 periods.
116 result << "...";
117 for (size_t i = 0; i < valueSize; ++i)
118 result << '.';
119 }
120 else {
121 // In case we need to escape, set to upper case hex and save the previous flags.
122 std::ios::fmtflags saveFlags = result.flags(std::ios::hex | std::ios::uppercase);
123
124 for (size_t i = 0; i < valueSize; ++i) {
125 uint8_t x = value[i];
126 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
127 if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
128 (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
129 x == 0x2e || x == 0x5f)
130 result << x;
131 else {
132 result << '%';
133 if (x < 16)
134 result << '0';
135 result << static_cast<uint32_t>(x);
136 }
137 }
138
139 // Restore.
140 result.flags(saveFlags);
141 }
142}
143
144std::string
145Component::toUri() const
146{
147 std::ostringstream os;
148 toUri(os);
149 return os.str();
150}
151
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700152
153bool
154Component::isNumber() const
155{
156 return (value_size() == 1 || value_size() == 2 ||
157 value_size() == 4 || value_size() == 8);
158}
159
160bool
161Component::isNumberWithMarker(uint8_t marker) const
162{
163 return (!empty() && value()[0] == marker &&
164 (value_size() == 2 || value_size() == 3 ||
165 value_size() == 5 || value_size() == 9));
166}
167
168bool
169Component::isVersion() const
170{
171 return isNumberWithMarker(VERSION_MARKER);
172}
173
174bool
175Component::isSegment() const
176{
177 return isNumberWithMarker(SEGMENT_MARKER);
178}
179
180bool
181Component::isSegmentOffset() const
182{
183 return isNumberWithMarker(SEGMENT_OFFSET_MARKER);
184}
185
186bool
187Component::isTimestamp() const
188{
189 return isNumberWithMarker(TIMESTAMP_MARKER);
190}
191
192bool
193Component::isSequenceNumber() const
194{
195 return isNumberWithMarker(SEQUENCE_NUMBER_MARKER);
196}
197
198
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700199uint64_t
200Component::toNumber() const
201{
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700202 if (!isNumber())
203 throw Error("Name component does not have nonNegativeInteger value");
204
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700205 return readNonNegativeInteger(*this);
206}
207
208uint64_t
209Component::toNumberWithMarker(uint8_t marker) const
210{
Alexander Afanasyev0f232c52014-10-23 13:07:31 -0700211 if (!isNumberWithMarker(marker))
212 throw Error("Name component does not have the requested marker "
213 "or the value is not a nonNegativeInteger");
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700214
215 Buffer::const_iterator valueBegin = value_begin() + 1;
216 return tlv::readNonNegativeInteger(value_size() - 1, valueBegin, value_end());
217}
218
219uint64_t
220Component::toVersion() const
221{
222 return toNumberWithMarker(VERSION_MARKER);
223}
224
225uint64_t
226Component::toSegment() const
227{
228 return toNumberWithMarker(SEGMENT_MARKER);
229}
230
231uint64_t
232Component::toSegmentOffset() const
233{
234 return toNumberWithMarker(SEGMENT_OFFSET_MARKER);
235}
236
237time::system_clock::TimePoint
238Component::toTimestamp() const
239{
240 uint64_t value = toNumberWithMarker(TIMESTAMP_MARKER);
241 return time::getUnixEpoch() + time::microseconds(value);
242}
243
244uint64_t
245Component::toSequenceNumber() const
246{
247 return toNumberWithMarker(SEQUENCE_NUMBER_MARKER);
248}
249
250
251Component
252Component::fromNumber(uint64_t number)
253{
254 /// \todo Change to tlv::NumberComponent
255 return nonNegativeIntegerBlock(tlv::NameComponent, number);
256}
257
258Component
259Component::fromNumberWithMarker(uint8_t marker, uint64_t number)
260{
261 EncodingEstimator estimator;
262
263 size_t valueLength = estimator.prependNonNegativeInteger(number);
264 valueLength += estimator.prependByteArray(&marker, 1);
265 size_t totalLength = valueLength;
266 totalLength += estimator.prependVarNumber(valueLength);
267 totalLength += estimator.prependVarNumber(tlv::NameComponent);
268
269 EncodingBuffer encoder(totalLength, 0);
270 encoder.prependNonNegativeInteger(number);
271 encoder.prependByteArray(&marker, 1);
272 encoder.prependVarNumber(valueLength);
273 encoder.prependVarNumber(tlv::NameComponent);
274
275 return encoder.block();
276}
277
278Component
279Component::fromVersion(uint64_t version)
280{
281 return fromNumberWithMarker(VERSION_MARKER, version);
282}
283
284Component
285Component::fromSegment(uint64_t segmentNo)
286{
287 return fromNumberWithMarker(SEGMENT_MARKER, segmentNo);
288}
289
290Component
291Component::fromSegmentOffset(uint64_t offset)
292{
293 return fromNumberWithMarker(SEGMENT_OFFSET_MARKER, offset);
294}
295
296Component
297Component::fromTimestamp(const time::system_clock::TimePoint& timePoint)
298{
299 using namespace time;
300 uint64_t value = duration_cast<microseconds>(timePoint - getUnixEpoch()).count();
301 return fromNumberWithMarker(TIMESTAMP_MARKER, value);
302}
303
304Component
305Component::fromSequenceNumber(uint64_t seqNo)
306{
307 return fromNumberWithMarker(SEQUENCE_NUMBER_MARKER, seqNo);
308}
309
310int
311Component::compare(const Component& other) const
312{
313 // Imitate ndn_Exclude_compareComponents.
314 if (value_size() < other.value_size())
315 return -1;
316 if (value_size() > other.value_size())
317 return 1;
318
319 if (value_size() == 0)
320 return 0;
321
322 // The components are equal length. Just do a byte compare.
323 return std::memcmp(value(), other.value(), value_size());
324}
325
326Component
327Component::getSuccessor() const
328{
329 size_t totalLength = 0;
330 EncodingBuffer encoder(size() + 1, 1); // + 1 in case there is an overflow
331 // in unlikely case TLV length increases,
332 // EncodingBuffer will take care of that
333
334 bool isOverflow = true;
335 size_t i = value_size();
336 for (; isOverflow && i > 0; i--) {
337 uint8_t newValue = static_cast<uint8_t>((value()[i - 1] + 1) & 0xFF);
338 totalLength += encoder.prependByte(newValue);
339 isOverflow = (newValue == 0);
340 }
341 totalLength += encoder.prependByteArray(value(), i);
342
343 if (isOverflow) {
344 // new name components has to be extended
345 totalLength += encoder.appendByte(0);
346 }
347
348 totalLength += encoder.prependVarNumber(totalLength);
349 totalLength += encoder.prependVarNumber(tlv::NameComponent);
350
351 return encoder.block();
352}
353
354
355template<bool T>
356size_t
357Component::wireEncode(EncodingImpl<T>& block) const
358{
359 size_t totalLength = 0;
360 if (value_size() > 0)
361 totalLength += block.prependByteArray(value(), value_size());
362 totalLength += block.prependVarNumber(value_size());
363 totalLength += block.prependVarNumber(tlv::NameComponent);
364 return totalLength;
365}
366
367template size_t
368Component::wireEncode<true>(EncodingImpl<true>& block) const;
369
370template size_t
371Component::wireEncode<false>(EncodingImpl<false>& block) const;
372
373const Block&
374Component::wireEncode() const
375{
376 if (this->hasWire())
377 return *this;
378
379 EncodingEstimator estimator;
380 size_t estimatedSize = wireEncode(estimator);
381
382 EncodingBuffer buffer(estimatedSize, 0);
383 wireEncode(buffer);
384
385 const_cast<Component&>(*this) = buffer.block();
386 return *this;
387}
388
389void
390Component::wireDecode(const Block& wire)
391{
392 if (wire.type() != tlv::NameComponent)
393 throw Error("name::Component::wireDecode called on not a NameComponent TLV wire block");
394
395 *this = wire;
396}
397
398} // namespace name
399} // namespace ndn