blob: 5012ca27651829019cbbdff4209046504cfac20b [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;
Jeff Thompsond4144fe2013-09-18 15:59:57 -070017using namespace ndn::ptr_lib;
Jeff Thompson9c41dfe2013-06-27 12:10:25 -070018
19namespace ndn {
20
Jeff Thompson26c63d62013-07-02 18:00:26 -070021static const char *WHITESPACE_CHARS = " \n\r\t";
22
23/**
24 * Modify str in place to erase whitespace on the left.
25 * @param str
26 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070027static inline void
28trimLeft(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070029{
30 size_t found = str.find_first_not_of(WHITESPACE_CHARS);
31 if (found != string::npos) {
32 if (found > 0)
33 str.erase(0, found);
34 }
35 else
36 // All whitespace
37 str.clear();
38}
39
40/**
41 * Modify str in place to erase whitespace on the right.
42 * @param str
43 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070044static inline void
45trimRight(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070046{
47 size_t found = str.find_last_not_of(WHITESPACE_CHARS);
48 if (found != string::npos) {
49 if (found + 1 < str.size())
50 str.erase(found + 1);
51 }
52 else
53 // All whitespace
54 str.clear();
55}
56
57/**
58 * Modify str in place to erase whitespace on the left and right.
59 * @param str
60 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070061static void
62trim(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070063{
64 trimLeft(str);
65 trimRight(str);
66}
Jeff Thompson443398d2013-07-02 19:45:46 -070067
Jeff Thompson26c63d62013-07-02 18:00:26 -070068/**
69 * Convert the hex character to an integer from 0 to 15, or -1 if not a hex character.
70 * @param c
71 * @return
72 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070073static int
Jeff Thompson10ad12a2013-09-24 16:19:11 -070074fromHexChar(uint8_t c)
Jeff Thompson26c63d62013-07-02 18:00:26 -070075{
76 if (c >= '0' && c <= '9')
77 return (int)c - (int)'0';
78 else if (c >= 'A' && c <= 'F')
79 return (int)c - (int)'A' + 10;
80 else if (c >= 'a' && c <= 'f')
81 return (int)c - (int)'a' + 10;
82 else
83 return -1;
84}
85
86/**
87 * Return a copy of str, converting each escaped "%XX" to the char value.
88 * @param str
89 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070090static string
91unescape(const string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070092{
93 ostringstream result;
94
Jeff Thompson97223af2013-09-24 17:01:27 -070095 for (size_t i = 0; i < str.size(); ++i) {
Jeff Thompson26c63d62013-07-02 18:00:26 -070096 if (str[i] == '%' && i + 2 < str.size()) {
97 int hi = fromHexChar(str[i + 1]);
98 int lo = fromHexChar(str[i + 2]);
99
100 if (hi < 0 || lo < 0)
101 // Invalid hex characters, so just keep the escaped string.
102 result << str[i] << str[i + 1] << str[i + 2];
103 else
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700104 result << (uint8_t)(16 * hi + lo);
Jeff Thompson26c63d62013-07-02 18:00:26 -0700105
106 // Skip ahead past the escaped value.
107 i += 2;
108 }
109 else
110 // Just copy through.
111 result << str[i];
112 }
113
114 return result.str();
115}
116
Jeff Thompson27cae532013-10-08 12:52:41 -0700117uint64_t Name::Component::toNumberWithMarker(uint8_t marker) const
118{
119 struct ndn_NameComponent componentStruct;
120 get(componentStruct);
121 uint64_t result;
122
123 ndn_Error error;
124 if ((error = ndn_NameComponent_toNumberWithMarker(&componentStruct, marker, &result)))
Jeff Thompson4affbf52013-10-18 14:36:46 -0700125 throw runtime_error(ndn_getErrorString(error));
Jeff Thompson27cae532013-10-08 12:52:41 -0700126
127 return result;
128}
129
Jeff Thompsond129ac12013-10-11 14:30:12 -0700130Name::Component
Jeff Thompsond129ac12013-10-11 14:30:12 -0700131Name::Component::fromNumber(uint64_t number)
Jeff Thompson8aac1992013-08-12 17:26:02 -0700132{
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700133 shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
Jeff Thompson8aac1992013-08-12 17:26:02 -0700134
Jeff Thompsond129ac12013-10-11 14:30:12 -0700135 // First encode in little endian.
136 while (number != 0) {
137 value->push_back(number & 0xff);
138 number >>= 8;
139 }
140
141 // Make it big endian.
142 reverse(value->begin(), value->end());
143 return Blob(value);
144}
145
146Name::Component
147Name::Component::fromNumberWithMarker(uint64_t number, uint8_t marker)
148{
149 shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
150
151 // Add the leading marker.
152 value->push_back(marker);
Jeff Thompsonb8f1b132013-08-13 11:07:43 -0700153
Jeff Thompson8aac1992013-08-12 17:26:02 -0700154 // First encode in little endian.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700155 while (number != 0) {
156 value->push_back(number & 0xff);
157 number >>= 8;
Jeff Thompson8aac1992013-08-12 17:26:02 -0700158 }
159
Jeff Thompson8aac1992013-08-12 17:26:02 -0700160 // Make it big endian.
Jeff Thompson995aba52013-09-12 12:04:52 -0700161 reverse(value->begin() + 1, value->end());
Jeff Thompson46411c92013-09-13 19:31:25 -0700162 return Blob(value);
Jeff Thompson8aac1992013-08-12 17:26:02 -0700163}
164
Jeff Thompson0050abe2013-09-17 12:50:25 -0700165void
Jeff Thompson25b4e612013-10-10 16:03:24 -0700166Name::Component::get(struct ndn_NameComponent& componentStruct) const
167{
168 value_.get(componentStruct.value);
169}
170
171uint64_t
172Name::Component::toNumber() const
173{
174 struct ndn_NameComponent componentStruct;
175 get(componentStruct);
176 return ndn_NameComponent_toNumber(&componentStruct);
177}
178
Jeff Thompsona98000c2013-12-16 14:40:09 -0800179int
180Name::Component::compare(const Name::Component& other) const
181{
182 // Imitate ndn_Exclude_compareComponents.
183 if (value_.size() < other.value_.size())
184 return -1;
185 if (value_.size() > other.value_.size())
186 return 1;
187
188 // The components are equal length. Just do a byte compare.
189 return ndn_memcmp((uint8_t*)value_.buf(), (uint8_t*)other.value_.buf(), value_.size());
190}
191
Jeff Thompson25b4e612013-10-10 16:03:24 -0700192void
Jeff Thompson0050abe2013-09-17 12:50:25 -0700193Name::set(const char *uri_cstr)
Jeff Thompson443398d2013-07-02 19:45:46 -0700194{
Jeff Thompson67515bd2013-08-15 17:43:22 -0700195 components_.clear();
196
Jeff Thompson443398d2013-07-02 19:45:46 -0700197 string uri = uri_cstr;
198 trim(uri);
199 if (uri.size() == 0)
200 return;
201
202 size_t iColon = uri.find(':');
203 if (iColon != string::npos) {
204 // Make sure the colon came before a '/'.
205 size_t iFirstSlash = uri.find('/');
206 if (iFirstSlash == string::npos || iColon < iFirstSlash) {
207 // Omit the leading protocol such as ndn:
208 uri.erase(0, iColon + 1);
209 trim(uri);
210 }
211 }
212
213 // Trim the leading slash and possibly the authority.
214 if (uri[0] == '/') {
215 if (uri.size() >= 2 && uri[1] == '/') {
216 // Strip the authority following "//".
217 size_t iAfterAuthority = uri.find('/', 2);
218 if (iAfterAuthority == string::npos)
219 // Unusual case: there was only an authority.
220 return;
221 else {
222 uri.erase(0, iAfterAuthority + 1);
223 trim(uri);
224 }
225 }
226 else {
227 uri.erase(0, 1);
228 trim(uri);
229 }
230 }
231
232 size_t iComponentStart = 0;
233
234 // Unescape the components.
235 while (iComponentStart < uri.size()) {
236 size_t iComponentEnd = uri.find("/", iComponentStart);
237 if (iComponentEnd == string::npos)
238 iComponentEnd = uri.size();
239
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700240 Component component(fromEscapedString(&uri[0], iComponentStart, iComponentEnd));
Jeff Thompson46411c92013-09-13 19:31:25 -0700241 // Ignore illegal components. This also gets rid of a trailing '/'.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700242 if (component.getValue())
Jeff Thompson46411c92013-09-13 19:31:25 -0700243 components_.push_back(Component(component));
Jeff Thompson443398d2013-07-02 19:45:46 -0700244
245 iComponentStart = iComponentEnd + 1;
246 }
247}
248
Jeff Thompson0050abe2013-09-17 12:50:25 -0700249void
250Name::get(struct ndn_Name& nameStruct) const
Jeff Thompson48815112013-06-28 18:22:48 -0700251{
Jeff Thompson016ed642013-07-02 14:39:06 -0700252 if (nameStruct.maxComponents < components_.size())
253 throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()");
254
255 nameStruct.nComponents = components_.size();
Jeff Thompson97223af2013-09-24 17:01:27 -0700256 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson016ed642013-07-02 14:39:06 -0700257 components_[i].get(nameStruct.components[i]);
Jeff Thompson48815112013-06-28 18:22:48 -0700258}
259
Jeff Thompson0050abe2013-09-17 12:50:25 -0700260void
261Name::set(const struct ndn_Name& nameStruct)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700262{
263 clear();
Jeff Thompson97223af2013-09-24 17:01:27 -0700264 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson3a715632013-10-31 11:36:35 -0700265 append(nameStruct.components[i].value.value, nameStruct.components[i].value.length);
Jeff Thompsonb468c312013-07-01 17:50:14 -0700266}
267
Jeff Thompson26b0d792013-09-23 16:19:01 -0700268Name&
269Name::append(const Name& name)
270{
271 if (&name == this)
272 // Copying from this name, so need to make a copy first.
273 return append(Name(name));
274
275 for (size_t i = 0; i < name.components_.size(); ++i)
276 components_.push_back(name.components_[i]);
277
278 return *this;
279}
280
Jeff Thompson4affbf52013-10-18 14:36:46 -0700281string
Jeff Thompson0050abe2013-09-17 12:50:25 -0700282Name::toUri() const
Jeff Thompsone6063512013-07-01 15:11:28 -0700283{
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700284 if (components_.size() == 0)
285 return "/";
286
287 ostringstream result;
Jeff Thompson97223af2013-09-24 17:01:27 -0700288 for (size_t i = 0; i < components_.size(); ++i) {
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700289 result << "/";
Jeff Thompson9bdb3b22013-09-12 12:42:13 -0700290 toEscapedString(*components_[i].getValue(), result);
Jeff Thompsone6063512013-07-01 15:11:28 -0700291 }
292
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700293 return result.str();
Jeff Thompsone6063512013-07-01 15:11:28 -0700294}
295
Jeff Thompsond0159d72013-09-23 13:34:15 -0700296Name
297Name::getSubName(size_t iStartComponent, size_t nComponents) const
298{
299 Name result;
300
Jeff Thompson97223af2013-09-24 17:01:27 -0700301 size_t iEnd = iStartComponent + nComponents;
302 for (size_t i = iStartComponent; i < iEnd && i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700303 result.components_.push_back(components_[i]);
304
305 return result;
306}
307
308Name
309Name::getSubName(size_t iStartComponent) const
310{
311 Name result;
312
Jeff Thompson97223af2013-09-24 17:01:27 -0700313 for (size_t i = iStartComponent; i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700314 result.components_.push_back(components_[i]);
315
316 return result;
317}
318
Jeff Thompson0050abe2013-09-17 12:50:25 -0700319bool
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700320Name::equals(const Name& name) const
321{
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700322 if (components_.size() != name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700323 return false;
324
325 for (size_t i = 0; i < components_.size(); ++i) {
326 if (*components_[i].getValue() != *name.components_[i].getValue())
327 return false;
328 }
329
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700330 return true;
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700331}
332
333bool
Jeff Thompson0050abe2013-09-17 12:50:25 -0700334Name::match(const Name& name) const
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700335{
336 // Imitate ndn_Name_match.
337
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700338 // This name is longer than the name we are checking it against.
339 if (components_.size() > name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700340 return false;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700341
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700342 // Check if at least one of given components doesn't match.
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700343 for (size_t i = 0; i < components_.size(); ++i) {
344 if (*components_[i].getValue() != *name.components_[i].getValue())
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700345 return false;
346 }
347
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700348 return true;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700349}
350
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700351Blob
352Name::fromEscapedString(const char *escapedString, size_t beginOffset, size_t endOffset)
353{
354 string trimmedString(escapedString + beginOffset, escapedString + endOffset);
355 trim(trimmedString);
356 string value = unescape(trimmedString);
357
358 if (value.find_first_not_of(".") == string::npos) {
359 // Special case for component of only periods.
360 if (value.size() <= 2)
361 // Zero, one or two periods is illegal. Ignore this component.
362 return Blob();
363 else
364 // Remove 3 periods.
365 return Blob((const uint8_t *)&value[3], value.size() - 3);
366 }
367 else
368 return Blob((const uint8_t *)&value[0], value.size());
369}
370
371Blob
372Name::fromEscapedString(const char *escapedString)
373{
374 return fromEscapedString(escapedString, 0, ::strlen(escapedString));
375}
376
Jeff Thompson0050abe2013-09-17 12:50:25 -0700377void
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700378Name::toEscapedString(const vector<uint8_t>& value, ostringstream& result)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700379{
380 bool gotNonDot = false;
381 for (unsigned i = 0; i < value.size(); ++i) {
382 if (value[i] != 0x2e) {
383 gotNonDot = true;
384 break;
385 }
386 }
387 if (!gotNonDot) {
388 // Special case for component of zero or more periods. Add 3 periods.
389 result << "...";
Jeff Thompson97223af2013-09-24 17:01:27 -0700390 for (size_t i = 0; i < value.size(); ++i)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700391 result << '.';
392 }
393 else {
394 // In case we need to escape, set to upper case hex and save the previous flags.
395 ios::fmtflags saveFlags = result.flags(ios::hex | ios::uppercase);
396
Jeff Thompson97223af2013-09-24 17:01:27 -0700397 for (size_t i = 0; i < value.size(); ++i) {
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700398 uint8_t x = value[i];
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700399 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
Jeff Thompsonf68a74a2013-11-20 15:16:11 -0800400 if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
401 (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
402 x == 0x2e || x == 0x5f)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700403 result << x;
404 else {
405 result << '%';
406 if (x < 16)
407 result << '0';
408 result << (unsigned int)x;
409 }
410 }
411
412 // Restore.
413 result.flags(saveFlags);
414 }
415}
416
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700417string
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700418Name::toEscapedString(const vector<uint8_t>& value)
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700419{
420 ostringstream result;
421 toEscapedString(value, result);
422 return result.str();
423}
424
Jeff Thompson9c41dfe2013-06-27 12:10:25 -0700425}