blob: b17a163a25f4d9b6d9c14813d5d0178ed50fd4b3 [file] [log] [blame]
Jeff Thompson25b4e612013-10-10 16:03:24 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
Jeff Thompson47eecfc2013-07-07 22:56:46 -07002/**
Jeff Thompson7687dc02013-09-13 11:54:07 -07003 * Copyright (C) 2013 Regents of the University of California.
4 * @author: Jeff Thompson <jefft0@remap.ucla.edu>
Jeff Thompsona98000c2013-12-16 14:40:09 -08005 * @author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
Jeff Thompson47eecfc2013-07-07 22:56:46 -07006 * See COPYING for copyright and distribution information.
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07007 */
8
Jeff Thompson54909772013-07-07 22:38:57 -07009#include <stdexcept>
Jeff Thompsonb8f1b132013-08-13 11:07:43 -070010#include <algorithm>
Jeff Thompsond8e53e62013-10-29 16:59:49 -070011#include <string.h>
Jeff Thompson25b4e612013-10-10 16:03:24 -070012#include <ndn-cpp/name.hpp>
13#include "c/name.h"
Jeff Thompsona98000c2013-12-16 14:40:09 -080014#include "c/util/ndn_memory.h"
Jeff Thompson9c41dfe2013-06-27 12:10:25 -070015
16using namespace std;
17
18namespace ndn {
19
Jeff Thompson26c63d62013-07-02 18:00:26 -070020static const char *WHITESPACE_CHARS = " \n\r\t";
21
22/**
23 * Modify str in place to erase whitespace on the left.
24 * @param str
25 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070026static inline void
27trimLeft(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070028{
29 size_t found = str.find_first_not_of(WHITESPACE_CHARS);
30 if (found != string::npos) {
31 if (found > 0)
32 str.erase(0, found);
33 }
34 else
35 // All whitespace
36 str.clear();
37}
38
39/**
40 * Modify str in place to erase whitespace on the right.
41 * @param str
42 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070043static inline void
44trimRight(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070045{
46 size_t found = str.find_last_not_of(WHITESPACE_CHARS);
47 if (found != string::npos) {
48 if (found + 1 < str.size())
49 str.erase(found + 1);
50 }
51 else
52 // All whitespace
53 str.clear();
54}
55
56/**
57 * Modify str in place to erase whitespace on the left and right.
58 * @param str
59 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070060static void
61trim(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070062{
63 trimLeft(str);
64 trimRight(str);
65}
Jeff Thompson443398d2013-07-02 19:45:46 -070066
Jeff Thompson26c63d62013-07-02 18:00:26 -070067/**
68 * Convert the hex character to an integer from 0 to 15, or -1 if not a hex character.
69 * @param c
70 * @return
71 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070072static int
Jeff Thompson10ad12a2013-09-24 16:19:11 -070073fromHexChar(uint8_t c)
Jeff Thompson26c63d62013-07-02 18:00:26 -070074{
75 if (c >= '0' && c <= '9')
76 return (int)c - (int)'0';
77 else if (c >= 'A' && c <= 'F')
78 return (int)c - (int)'A' + 10;
79 else if (c >= 'a' && c <= 'f')
80 return (int)c - (int)'a' + 10;
81 else
82 return -1;
83}
84
85/**
86 * Return a copy of str, converting each escaped "%XX" to the char value.
87 * @param str
88 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070089static string
90unescape(const string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070091{
92 ostringstream result;
93
Jeff Thompson97223af2013-09-24 17:01:27 -070094 for (size_t i = 0; i < str.size(); ++i) {
Jeff Thompson26c63d62013-07-02 18:00:26 -070095 if (str[i] == '%' && i + 2 < str.size()) {
96 int hi = fromHexChar(str[i + 1]);
97 int lo = fromHexChar(str[i + 2]);
98
99 if (hi < 0 || lo < 0)
100 // Invalid hex characters, so just keep the escaped string.
101 result << str[i] << str[i + 1] << str[i + 2];
102 else
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700103 result << (uint8_t)(16 * hi + lo);
Jeff Thompson26c63d62013-07-02 18:00:26 -0700104
105 // Skip ahead past the escaped value.
106 i += 2;
107 }
108 else
109 // Just copy through.
110 result << str[i];
111 }
112
113 return result.str();
114}
115
Jeff Thompson27cae532013-10-08 12:52:41 -0700116uint64_t Name::Component::toNumberWithMarker(uint8_t marker) const
117{
118 struct ndn_NameComponent componentStruct;
119 get(componentStruct);
120 uint64_t result;
121
122 ndn_Error error;
123 if ((error = ndn_NameComponent_toNumberWithMarker(&componentStruct, marker, &result)))
Jeff Thompson4affbf52013-10-18 14:36:46 -0700124 throw runtime_error(ndn_getErrorString(error));
Jeff Thompson27cae532013-10-08 12:52:41 -0700125
126 return result;
127}
128
Jeff Thompsond129ac12013-10-11 14:30:12 -0700129Name::Component
Jeff Thompsond129ac12013-10-11 14:30:12 -0700130Name::Component::fromNumber(uint64_t number)
Jeff Thompson8aac1992013-08-12 17:26:02 -0700131{
Jeff Thompsonce115762013-12-18 14:59:56 -0800132 ptr_lib::shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
Jeff Thompson8aac1992013-08-12 17:26:02 -0700133
Jeff Thompsond129ac12013-10-11 14:30:12 -0700134 // First encode in little endian.
135 while (number != 0) {
136 value->push_back(number & 0xff);
137 number >>= 8;
138 }
139
140 // Make it big endian.
141 reverse(value->begin(), value->end());
142 return Blob(value);
143}
144
145Name::Component
146Name::Component::fromNumberWithMarker(uint64_t number, uint8_t marker)
147{
Jeff Thompsonce115762013-12-18 14:59:56 -0800148 ptr_lib::shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
Jeff Thompsond129ac12013-10-11 14:30:12 -0700149
150 // Add the leading marker.
151 value->push_back(marker);
Jeff Thompsonb8f1b132013-08-13 11:07:43 -0700152
Jeff Thompson8aac1992013-08-12 17:26:02 -0700153 // First encode in little endian.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700154 while (number != 0) {
155 value->push_back(number & 0xff);
156 number >>= 8;
Jeff Thompson8aac1992013-08-12 17:26:02 -0700157 }
158
Jeff Thompson8aac1992013-08-12 17:26:02 -0700159 // Make it big endian.
Jeff Thompson995aba52013-09-12 12:04:52 -0700160 reverse(value->begin() + 1, value->end());
Jeff Thompson46411c92013-09-13 19:31:25 -0700161 return Blob(value);
Jeff Thompson8aac1992013-08-12 17:26:02 -0700162}
163
Jeff Thompson0050abe2013-09-17 12:50:25 -0700164void
Jeff Thompson25b4e612013-10-10 16:03:24 -0700165Name::Component::get(struct ndn_NameComponent& componentStruct) const
166{
167 value_.get(componentStruct.value);
168}
169
170uint64_t
171Name::Component::toNumber() const
172{
173 struct ndn_NameComponent componentStruct;
174 get(componentStruct);
175 return ndn_NameComponent_toNumber(&componentStruct);
176}
177
Jeff Thompsona98000c2013-12-16 14:40:09 -0800178int
179Name::Component::compare(const Name::Component& other) const
180{
181 // Imitate ndn_Exclude_compareComponents.
182 if (value_.size() < other.value_.size())
183 return -1;
184 if (value_.size() > other.value_.size())
185 return 1;
186
187 // The components are equal length. Just do a byte compare.
188 return ndn_memcmp((uint8_t*)value_.buf(), (uint8_t*)other.value_.buf(), value_.size());
189}
190
Jeff Thompson25b4e612013-10-10 16:03:24 -0700191void
Jeff Thompson0050abe2013-09-17 12:50:25 -0700192Name::set(const char *uri_cstr)
Jeff Thompson443398d2013-07-02 19:45:46 -0700193{
Jeff Thompson67515bd2013-08-15 17:43:22 -0700194 components_.clear();
195
Jeff Thompson443398d2013-07-02 19:45:46 -0700196 string uri = uri_cstr;
197 trim(uri);
198 if (uri.size() == 0)
199 return;
200
201 size_t iColon = uri.find(':');
202 if (iColon != string::npos) {
203 // Make sure the colon came before a '/'.
204 size_t iFirstSlash = uri.find('/');
205 if (iFirstSlash == string::npos || iColon < iFirstSlash) {
206 // Omit the leading protocol such as ndn:
207 uri.erase(0, iColon + 1);
208 trim(uri);
209 }
210 }
211
212 // Trim the leading slash and possibly the authority.
213 if (uri[0] == '/') {
214 if (uri.size() >= 2 && uri[1] == '/') {
215 // Strip the authority following "//".
216 size_t iAfterAuthority = uri.find('/', 2);
217 if (iAfterAuthority == string::npos)
218 // Unusual case: there was only an authority.
219 return;
220 else {
221 uri.erase(0, iAfterAuthority + 1);
222 trim(uri);
223 }
224 }
225 else {
226 uri.erase(0, 1);
227 trim(uri);
228 }
229 }
230
231 size_t iComponentStart = 0;
232
233 // Unescape the components.
234 while (iComponentStart < uri.size()) {
235 size_t iComponentEnd = uri.find("/", iComponentStart);
236 if (iComponentEnd == string::npos)
237 iComponentEnd = uri.size();
238
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700239 Component component(fromEscapedString(&uri[0], iComponentStart, iComponentEnd));
Jeff Thompson46411c92013-09-13 19:31:25 -0700240 // Ignore illegal components. This also gets rid of a trailing '/'.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700241 if (component.getValue())
Jeff Thompson46411c92013-09-13 19:31:25 -0700242 components_.push_back(Component(component));
Jeff Thompson443398d2013-07-02 19:45:46 -0700243
244 iComponentStart = iComponentEnd + 1;
245 }
246}
247
Jeff Thompson0050abe2013-09-17 12:50:25 -0700248void
249Name::get(struct ndn_Name& nameStruct) const
Jeff Thompson48815112013-06-28 18:22:48 -0700250{
Jeff Thompson016ed642013-07-02 14:39:06 -0700251 if (nameStruct.maxComponents < components_.size())
252 throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()");
253
254 nameStruct.nComponents = components_.size();
Jeff Thompson97223af2013-09-24 17:01:27 -0700255 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson016ed642013-07-02 14:39:06 -0700256 components_[i].get(nameStruct.components[i]);
Jeff Thompson48815112013-06-28 18:22:48 -0700257}
258
Jeff Thompson0050abe2013-09-17 12:50:25 -0700259void
260Name::set(const struct ndn_Name& nameStruct)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700261{
262 clear();
Jeff Thompson97223af2013-09-24 17:01:27 -0700263 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson3a715632013-10-31 11:36:35 -0700264 append(nameStruct.components[i].value.value, nameStruct.components[i].value.length);
Jeff Thompsonb468c312013-07-01 17:50:14 -0700265}
266
Jeff Thompson26b0d792013-09-23 16:19:01 -0700267Name&
268Name::append(const Name& name)
269{
270 if (&name == this)
271 // Copying from this name, so need to make a copy first.
272 return append(Name(name));
273
274 for (size_t i = 0; i < name.components_.size(); ++i)
275 components_.push_back(name.components_[i]);
276
277 return *this;
278}
279
Jeff Thompson4affbf52013-10-18 14:36:46 -0700280string
Jeff Thompson0050abe2013-09-17 12:50:25 -0700281Name::toUri() const
Jeff Thompsone6063512013-07-01 15:11:28 -0700282{
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700283 if (components_.size() == 0)
284 return "/";
285
286 ostringstream result;
Jeff Thompson97223af2013-09-24 17:01:27 -0700287 for (size_t i = 0; i < components_.size(); ++i) {
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700288 result << "/";
Jeff Thompson9bdb3b22013-09-12 12:42:13 -0700289 toEscapedString(*components_[i].getValue(), result);
Jeff Thompsone6063512013-07-01 15:11:28 -0700290 }
291
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700292 return result.str();
Jeff Thompsone6063512013-07-01 15:11:28 -0700293}
294
Jeff Thompsond0159d72013-09-23 13:34:15 -0700295Name
296Name::getSubName(size_t iStartComponent, size_t nComponents) const
297{
298 Name result;
299
Jeff Thompson97223af2013-09-24 17:01:27 -0700300 size_t iEnd = iStartComponent + nComponents;
301 for (size_t i = iStartComponent; i < iEnd && i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700302 result.components_.push_back(components_[i]);
303
304 return result;
305}
306
307Name
308Name::getSubName(size_t iStartComponent) const
309{
310 Name result;
311
Jeff Thompson97223af2013-09-24 17:01:27 -0700312 for (size_t i = iStartComponent; i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700313 result.components_.push_back(components_[i]);
314
315 return result;
316}
317
Jeff Thompson0050abe2013-09-17 12:50:25 -0700318bool
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700319Name::equals(const Name& name) const
320{
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700321 if (components_.size() != name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700322 return false;
323
324 for (size_t i = 0; i < components_.size(); ++i) {
325 if (*components_[i].getValue() != *name.components_[i].getValue())
326 return false;
327 }
328
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700329 return true;
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700330}
331
332bool
Jeff Thompson0050abe2013-09-17 12:50:25 -0700333Name::match(const Name& name) const
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700334{
335 // Imitate ndn_Name_match.
336
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700337 // This name is longer than the name we are checking it against.
338 if (components_.size() > name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700339 return false;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700340
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700341 // Check if at least one of given components doesn't match.
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700342 for (size_t i = 0; i < components_.size(); ++i) {
343 if (*components_[i].getValue() != *name.components_[i].getValue())
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700344 return false;
345 }
346
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700347 return true;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700348}
349
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700350Blob
351Name::fromEscapedString(const char *escapedString, size_t beginOffset, size_t endOffset)
352{
353 string trimmedString(escapedString + beginOffset, escapedString + endOffset);
354 trim(trimmedString);
355 string value = unescape(trimmedString);
356
357 if (value.find_first_not_of(".") == string::npos) {
358 // Special case for component of only periods.
359 if (value.size() <= 2)
360 // Zero, one or two periods is illegal. Ignore this component.
361 return Blob();
362 else
363 // Remove 3 periods.
364 return Blob((const uint8_t *)&value[3], value.size() - 3);
365 }
366 else
367 return Blob((const uint8_t *)&value[0], value.size());
368}
369
370Blob
371Name::fromEscapedString(const char *escapedString)
372{
373 return fromEscapedString(escapedString, 0, ::strlen(escapedString));
374}
375
Jeff Thompson0050abe2013-09-17 12:50:25 -0700376void
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700377Name::toEscapedString(const vector<uint8_t>& value, ostringstream& result)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700378{
379 bool gotNonDot = false;
380 for (unsigned i = 0; i < value.size(); ++i) {
381 if (value[i] != 0x2e) {
382 gotNonDot = true;
383 break;
384 }
385 }
386 if (!gotNonDot) {
387 // Special case for component of zero or more periods. Add 3 periods.
388 result << "...";
Jeff Thompson97223af2013-09-24 17:01:27 -0700389 for (size_t i = 0; i < value.size(); ++i)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700390 result << '.';
391 }
392 else {
393 // In case we need to escape, set to upper case hex and save the previous flags.
394 ios::fmtflags saveFlags = result.flags(ios::hex | ios::uppercase);
395
Jeff Thompson97223af2013-09-24 17:01:27 -0700396 for (size_t i = 0; i < value.size(); ++i) {
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700397 uint8_t x = value[i];
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700398 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
Jeff Thompsonf68a74a2013-11-20 15:16:11 -0800399 if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
400 (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
401 x == 0x2e || x == 0x5f)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700402 result << x;
403 else {
404 result << '%';
405 if (x < 16)
406 result << '0';
407 result << (unsigned int)x;
408 }
409 }
410
411 // Restore.
412 result.flags(saveFlags);
413 }
414}
415
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700416string
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700417Name::toEscapedString(const vector<uint8_t>& value)
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700418{
419 ostringstream result;
420 toEscapedString(value, result);
421 return result.str();
422}
423
Jeff Thompson82568ad2013-12-17 15:17:40 -0800424bool
425Name::breadthFirstLess(const Name& name1, const Name& name2)
426{
427 for (size_t i = 0; i < name1.size() && i < name2.size(); ++i) {
428 if (name1[i] == name2[i])
429 // The components at this index are equal, so check the next components.
430 continue;
431
432 // Otherwise, the result is based on the components at this index.
433 return name1[i] < name2[i];
434 }
435
436 // The components up to min(name1.size(), name2.size()) are equal, so sort on the shorter name.
437 return name1.size() < name2.size();
438}
439
440
Jeff Thompson9c41dfe2013-06-27 12:10:25 -0700441}