blob: 9ea09c55a6a022767ae29abc405fea1070525301 [file] [log] [blame]
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/**
3 * Copyright (C) 2013 Regents of the University of California.
4 * @author: Jeff Thompson <jefft0@remap.ucla.edu>
5 * @author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
6 * @author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
7 * See COPYING for copyright and distribution information.
8 */
9
10#ifndef NDN_NAME_COMPONENT_HPP
11#define NDN_NAME_COMPONENT_HPP
12
13#include "common.hpp"
14#include "encoding/block.hpp"
15#include "encoding/encoding-buffer.hpp"
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070016#include "util/string-helper.hpp"
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080017
18namespace ndn {
19namespace name {
20
21/**
22 * A Name::Component holds a read-only name component value.
23 */
24class Component : public Block
25{
26public:
27 /// @brief Error that can be thrown from the block
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070028 struct Error : public std::runtime_error {
29 Error(const std::string& what) : std::runtime_error(what) {}
30 };
31
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080032 /**
33 * Create a new Name::Component with a null value.
34 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070035 Component();
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080036
Alexander Afanasyev52eb20d2014-02-06 18:25:54 -080037 /**
38 * @brief Directly create component from wire block
39 *
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070040 * @throws Error if wire.type() is not Tlv::Component
Alexander Afanasyev52eb20d2014-02-06 18:25:54 -080041 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070042 Component(const Block& wire);
43
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080044 /**
45 * Create a new Name::Component, taking another pointer to the Blob value.
46 * @param value A blob with a pointer to an immutable array. The pointer is copied.
47 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070048 Component(const ConstBufferPtr& buffer);
49
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080050 /**
51 * Create a new Name::Component, copying the given value.
52 * @param value The value byte array.
53 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070054 Component(const Buffer& value);
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080055
56 /**
57 * Create a new Name::Component, copying the given value.
58 * @param value Pointer to the value byte array.
59 * @param valueLen Length of value.
60 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070061 Component(const uint8_t *value, size_t valueLen);
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080062
63 template<class InputIterator>
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070064 Component(InputIterator begin, InputIterator end);
Alexander Afanasyev52eb20d2014-02-06 18:25:54 -080065
66 explicit
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070067 Component(const char *str);
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080068
Alexander Afanasyev52eb20d2014-02-06 18:25:54 -080069 explicit
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070070 Component(const std::string& str);
71
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080072 /**
73 * @brief Fast encoding or block size estimation
74 */
75 template<bool T>
76 size_t
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070077 wireEncode(EncodingImpl<T>& block) const;
78
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080079 /**
80 * Make a Blob value by decoding the escapedString between beginOffset and endOffset according to the NDN URI Scheme.
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070081 * If the escaped string is "", "." or ".." then return a Blob with a null pointer,
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080082 * which means the component should be skipped in a URI name.
83 * @param escapedString The escaped string. It does not need to be null-terminated because we only scan to endOffset.
84 * @param beginOffset The offset in escapedString of the beginning of the portion to decode.
85 * @param endOffset The offset in escapedString of the end of the portion to decode.
86 * @return The Blob value. If the escapedString is not a valid escaped component, then the Blob is a null pointer.
87 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070088 static Component
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080089 fromEscapedString(const char *escapedString, size_t beginOffset, size_t endOffset);
90
91 /**
92 * Make a Blob value by decoding the escapedString according to the NDN URI Scheme.
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070093 * If the escaped string is "", "." or ".." then return a Blob with a null pointer,
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080094 * which means the component should be skipped in a URI name.
95 * @param escapedString The null-terminated escaped string.
96 * @return The Blob value. If the escapedString is not a valid escaped component, then the Blob is a null pointer.
97 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070098 static Component
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -080099 fromEscapedString(const char *escapedString)
100 {
101 return fromEscapedString(escapedString, 0, ::strlen(escapedString));
102 }
103
104 /**
105 * Make a Blob value by decoding the escapedString according to the NDN URI Scheme.
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700106 * If the escaped string is "", "." or ".." then return a Blob with a null pointer,
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800107 * which means the component should be skipped in a URI name.
108 * @param escapedString The escaped string.
109 * @return The Blob value. If the escapedString is not a valid escaped component, then the Blob is a null pointer.
110 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700111 static Component
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800112 fromEscapedString(const std::string& escapedString)
113 {
114 return fromEscapedString(escapedString.c_str());
115 }
116
117 /**
118 * Write the value to result, escaping characters according to the NDN URI Scheme.
119 * This also adds "..." to a value with zero or more ".".
120 * @param value the buffer with the value to escape
121 * @param result the string stream to write to.
122 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700123 void
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800124 toEscapedString(std::ostream& result) const;
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700125
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800126 /**
127 * Convert the value by escaping characters according to the NDN URI Scheme.
128 * This also adds "..." to a value with zero or more ".".
129 * @param value the buffer with the value to escape
130 * @return The escaped string.
131 */
132 inline std::string
133 toEscapedString() const
134 {
135 std::ostringstream result;
136 toEscapedString(result);
137 return result.str();
138 }
Alexander Afanasyev52eb20d2014-02-06 18:25:54 -0800139
140 inline void
141 toUri(std::ostream& result) const
142 {
143 return toEscapedString(result);
144 }
145
146 inline std::string
147 toUri() const
148 {
149 return toEscapedString();
150 }
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700151
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800152 /**
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700153 * @brief Interpret this name component as nonNegativeInteger
154 *
155 * @see http://named-data.net/doc/ndn-tlv/tlv.html#non-negative-integer-encoding
156 *
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800157 * @return The integer number.
158 */
159 uint64_t
160 toNumber() const;
161
162 /**
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700163 * @brief An alias for toNumber()
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800164 */
165 uint64_t
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700166 toVersion() const;
167
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800168 /**
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700169 * @brief An alias for toNumber()
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800170 */
171 uint64_t
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700172 toSegment() const;
173
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800174 /**
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700175 * @brief Create a component encoded as nonNegativeInteger
176 *
177 * @see http://named-data.net/doc/ndn-tlv/tlv.html#non-negative-integer-encoding
178 *
179 * @param number The non-negative number
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800180 * @return The component value.
181 */
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700182 static Component
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800183 fromNumber(uint64_t number);
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800184
185 /**
186 * Check if this is the same component as other.
187 * @param other The other Component to compare with.
188 * @return true if the components are equal, otherwise false.
189 */
190 bool
191 equals(const Component& other) const
192 {
193 if (value_size() != other.value_size())
194 return false;
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800195 if (value_size() == 0 /* == other.value_size()*/)
196 return true;
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800197
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800198 // somehow, behavior is wrong on OSX 10.9 when component is empty
199 // (probably some bug in STL...)
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800200 return std::equal(value_begin(), value_end(), other.value_begin());
201 }
202
203 bool
204 empty() const
205 {
206 return !hasValue();
207 }
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700208
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800209 /**
210 * Check if this is the same component as other.
211 * @param other The other Component to compare with.
212 * @return true if the components are equal, otherwise false.
213 */
214 bool
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700215 operator==(const Component& other) const { return equals(other); }
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800216
217 /**
218 * Check if this is not the same component as other.
219 * @param other The other Component to compare with.
220 * @return true if the components are not equal, otherwise false.
221 */
222 bool
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700223 operator!=(const Component& other) const { return !equals(other); }
224
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800225 /**
226 * Compare this to the other Component using NDN canonical ordering.
227 * @param other The other Component to compare with.
228 * @return 0 If they compare equal, -1 if *this comes before other in the canonical ordering, or
229 * 1 if *this comes after other in the canonical ordering.
230 *
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700231 * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800232 */
233 int
234 compare(const Component& other) const;
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800235
236 /**
237 * Return true if this is less than or equal to the other Component in the NDN canonical ordering.
238 * @param other The other Component to compare with.
239 *
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700240 * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800241 */
242 bool
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700243 operator<=(const Component& other) const { return compare(other) <= 0; }
244
245 /**
246 * Return true if this is less than the other Component in the NDN canonical ordering.
247 * @param other The other Component to compare with.
248 *
249 * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
250 */
251 bool
252 operator<(const Component& other) const { return compare(other) < 0; }
253
254 /**
255 * Return true if this is less than or equal to the other Component in the NDN canonical ordering.
256 * @param other The other Component to compare with.
257 *
258 * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
259 */
260 bool
261 operator>=(const Component& other) const { return compare(other) >= 0; }
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800262
263 /**
264 * Return true if this is greater than the other Component in the NDN canonical ordering.
265 * @param other The other Component to compare with.
266 *
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700267 * @see http://named-data.net/doc/ndn-tlv/name.html#canonical-order
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800268 */
269 bool
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700270 operator>(const Component& other) const { return compare(other) > 0; }
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800271
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700272 //
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800273 // !!! MUST NOT INCLUDE ANY DATA HERE !!!
274 //
275 // This class is just a helper and is directly reinterpret_cast'ed from Block
276};
277
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700278inline std::ostream&
279operator << (std::ostream& os, const Component& component)
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800280{
281 component.toEscapedString(os);
282 return os;
283}
284
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700285inline
286Component::Component()
287 : Block(Tlv::NameComponent)
288{
289}
290
291inline
292Component::Component(const Block& wire)
293 : Block(wire)
294{
295 if (type() != Tlv::NameComponent)
296 throw Error("Constructing name component from non name component TLV wire block");
297}
298
299inline
300Component::Component(const ConstBufferPtr& buffer)
301 : Block (Tlv::NameComponent, buffer)
302{
303}
304
305inline
306Component::Component(const Buffer& value)
307 : Block (Tlv::NameComponent, ConstBufferPtr(new Buffer(value)))
308{
309}
310
311inline
312Component::Component(const uint8_t *value, size_t valueLen)
313 : Block (Tlv::NameComponent, ConstBufferPtr(new Buffer(value, valueLen)))
314{
315}
316
317template<class InputIterator>
318inline
319Component::Component(InputIterator begin, InputIterator end)
320 : Block (Tlv::NameComponent, ConstBufferPtr(new Buffer(begin, end)))
321{
322}
323
324inline
325Component::Component(const char *str)
326 : Block (Tlv::NameComponent, ConstBufferPtr(new Buffer(str, ::strlen(str))))
327{
328}
329
330inline
331Component::Component(const std::string& str)
332 : Block (Tlv::NameComponent, ConstBufferPtr(new Buffer(str.begin(), str.end())))
333{
334}
335
336
337inline Component
338Component::fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset)
339{
340 std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
341 trim(trimmedString);
342 std::string value = unescape(trimmedString);
343
344 if (value.find_first_not_of(".") == std::string::npos) {
345 // Special case for component of only periods.
346 if (value.size() <= 2)
347 // Zero, one or two periods is illegal. Ignore this component.
348 return Component();
349 else
350 // Remove 3 periods.
351 return Component((const uint8_t *)&value[3], value.size() - 3);
352 }
353 else
354 return Component((const uint8_t *)&value[0], value.size());
355}
356
357
358inline void
359Component::toEscapedString(std::ostream& result) const
360{
361 const uint8_t *valuePtr = value();
362 size_t valueSize = value_size();
363
364 bool gotNonDot = false;
365 for (unsigned i = 0; i < valueSize; ++i) {
366 if (valuePtr[i] != 0x2e) {
367 gotNonDot = true;
368 break;
369 }
370 }
371 if (!gotNonDot) {
372 // Special case for component of zero or more periods. Add 3 periods.
373 result << "...";
374 for (size_t i = 0; i < valueSize; ++i)
375 result << '.';
376 }
377 else {
378 // In case we need to escape, set to upper case hex and save the previous flags.
379 std::ios::fmtflags saveFlags = result.flags(std::ios::hex | std::ios::uppercase);
380
381 for (size_t i = 0; i < valueSize; ++i) {
382 uint8_t x = valuePtr[i];
383 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
384 if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
385 (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
386 x == 0x2e || x == 0x5f)
387 result << x;
388 else {
389 result << '%';
390 if (x < 16)
391 result << '0';
392 result << (unsigned int)x;
393 }
394 }
395
396 // Restore.
397 result.flags(saveFlags);
398 }
399}
400
Alexander Afanasyev4b98e8c2014-03-22 19:10:19 -0700401
402inline Component
403Component::fromNumber(uint64_t number)
404{
405 /// \todo Change to Tlv::NumberComponent
406 return nonNegativeIntegerBlock(Tlv::NameComponent, number);
407}
408
409
410inline uint64_t
411Component::toNumber() const
412{
413 /// \todo Check if Component is of Tlv::NumberComponent type
414 return readNonNegativeInteger(static_cast<const Block&>(*this));
415}
416
417
418inline uint64_t
419Component::toVersion() const
420{
421 return toNumber();
422}
423
424inline uint64_t
425Component::toSegment() const
426{
427 return toNumber();
428}
429
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700430inline int
431Component::compare(const Component& other) const
432{
433 // Imitate ndn_Exclude_compareComponents.
434 if (value_size() < other.value_size())
435 return -1;
436 if (value_size() > other.value_size())
437 return 1;
438
439 if (value_size() == 0)
440 return 0;
441
442 // The components are equal length. Just do a byte compare.
443 return std::memcmp(value(), other.value(), value_size());
444}
445
446
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800447template<bool T>
448inline size_t
449Component::wireEncode(EncodingImpl<T>& block) const
450{
451 size_t total_len = 0;
Alexander Afanasyev380420b2014-02-09 20:52:29 -0800452 if (value_size() > 0)
453 total_len += block.prependByteArray (value(), value_size());
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800454 total_len += block.prependVarNumber (value_size());
455 total_len += block.prependVarNumber (Tlv::NameComponent);
456 return total_len;
457}
458
Alexander Afanasyev95e8c2f2014-02-06 17:29:30 -0800459} // namespace name
460} // namespace ndn
461
462#endif // NDN_NAME_COMPONENT_HPP