blob: 728f29e2d869aa12bb12485736d032a4b3ac6ffa [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 Thompson47eecfc2013-07-07 22:56:46 -07005 * See COPYING for copyright and distribution information.
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07006 */
7
Jeff Thompson54909772013-07-07 22:38:57 -07008#include <stdexcept>
Jeff Thompsonb8f1b132013-08-13 11:07:43 -07009#include <algorithm>
Jeff Thompsond8e53e62013-10-29 16:59:49 -070010#include <string.h>
Jeff Thompson25b4e612013-10-10 16:03:24 -070011#include <ndn-cpp/name.hpp>
12#include "c/name.h"
Jeff Thompson9c41dfe2013-06-27 12:10:25 -070013
14using namespace std;
15
16namespace ndn {
17
Jeff Thompson26c63d62013-07-02 18:00:26 -070018static const char *WHITESPACE_CHARS = " \n\r\t";
19
20/**
21 * Modify str in place to erase whitespace on the left.
22 * @param str
23 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070024static inline void
25trimLeft(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070026{
27 size_t found = str.find_first_not_of(WHITESPACE_CHARS);
28 if (found != string::npos) {
29 if (found > 0)
30 str.erase(0, found);
31 }
32 else
33 // All whitespace
34 str.clear();
35}
36
37/**
38 * Modify str in place to erase whitespace on the right.
39 * @param str
40 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070041static inline void
42trimRight(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070043{
44 size_t found = str.find_last_not_of(WHITESPACE_CHARS);
45 if (found != string::npos) {
46 if (found + 1 < str.size())
47 str.erase(found + 1);
48 }
49 else
50 // All whitespace
51 str.clear();
52}
53
54/**
55 * Modify str in place to erase whitespace on the left and right.
56 * @param str
57 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070058static void
59trim(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070060{
61 trimLeft(str);
62 trimRight(str);
63}
Jeff Thompson443398d2013-07-02 19:45:46 -070064
Jeff Thompson26c63d62013-07-02 18:00:26 -070065/**
66 * Convert the hex character to an integer from 0 to 15, or -1 if not a hex character.
67 * @param c
68 * @return
69 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070070static int
Jeff Thompson10ad12a2013-09-24 16:19:11 -070071fromHexChar(uint8_t c)
Jeff Thompson26c63d62013-07-02 18:00:26 -070072{
73 if (c >= '0' && c <= '9')
74 return (int)c - (int)'0';
75 else if (c >= 'A' && c <= 'F')
76 return (int)c - (int)'A' + 10;
77 else if (c >= 'a' && c <= 'f')
78 return (int)c - (int)'a' + 10;
79 else
80 return -1;
81}
82
83/**
84 * Return a copy of str, converting each escaped "%XX" to the char value.
85 * @param str
86 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070087static string
88unescape(const string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070089{
90 ostringstream result;
91
Jeff Thompson97223af2013-09-24 17:01:27 -070092 for (size_t i = 0; i < str.size(); ++i) {
Jeff Thompson26c63d62013-07-02 18:00:26 -070093 if (str[i] == '%' && i + 2 < str.size()) {
94 int hi = fromHexChar(str[i + 1]);
95 int lo = fromHexChar(str[i + 2]);
96
97 if (hi < 0 || lo < 0)
98 // Invalid hex characters, so just keep the escaped string.
99 result << str[i] << str[i + 1] << str[i + 2];
100 else
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700101 result << (uint8_t)(16 * hi + lo);
Jeff Thompson26c63d62013-07-02 18:00:26 -0700102
103 // Skip ahead past the escaped value.
104 i += 2;
105 }
106 else
107 // Just copy through.
108 result << str[i];
109 }
110
111 return result.str();
112}
113
Jeff Thompson27cae532013-10-08 12:52:41 -0700114uint64_t Name::Component::toNumberWithMarker(uint8_t marker) const
115{
116 struct ndn_NameComponent componentStruct;
117 get(componentStruct);
118 uint64_t result;
119
120 ndn_Error error;
121 if ((error = ndn_NameComponent_toNumberWithMarker(&componentStruct, marker, &result)))
Jeff Thompson4affbf52013-10-18 14:36:46 -0700122 throw runtime_error(ndn_getErrorString(error));
Jeff Thompson27cae532013-10-08 12:52:41 -0700123
124 return result;
125}
126
Jeff Thompsond129ac12013-10-11 14:30:12 -0700127Name::Component
Jeff Thompsond129ac12013-10-11 14:30:12 -0700128Name::Component::fromNumber(uint64_t number)
Jeff Thompson8aac1992013-08-12 17:26:02 -0700129{
Alexander Afanasyevb1d67462013-11-18 20:25:00 -0800130 ptr_lib::shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
Jeff Thompson8aac1992013-08-12 17:26:02 -0700131
Jeff Thompsond129ac12013-10-11 14:30:12 -0700132 // First encode in little endian.
133 while (number != 0) {
134 value->push_back(number & 0xff);
135 number >>= 8;
136 }
137
138 // Make it big endian.
139 reverse(value->begin(), value->end());
140 return Blob(value);
141}
142
143Name::Component
144Name::Component::fromNumberWithMarker(uint64_t number, uint8_t marker)
145{
Alexander Afanasyevb1d67462013-11-18 20:25:00 -0800146 ptr_lib::shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
Jeff Thompsond129ac12013-10-11 14:30:12 -0700147
148 // Add the leading marker.
149 value->push_back(marker);
Jeff Thompsonb8f1b132013-08-13 11:07:43 -0700150
Jeff Thompson8aac1992013-08-12 17:26:02 -0700151 // First encode in little endian.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700152 while (number != 0) {
153 value->push_back(number & 0xff);
154 number >>= 8;
Jeff Thompson8aac1992013-08-12 17:26:02 -0700155 }
156
Jeff Thompson8aac1992013-08-12 17:26:02 -0700157 // Make it big endian.
Jeff Thompson995aba52013-09-12 12:04:52 -0700158 reverse(value->begin() + 1, value->end());
Jeff Thompson46411c92013-09-13 19:31:25 -0700159 return Blob(value);
Jeff Thompson8aac1992013-08-12 17:26:02 -0700160}
161
Jeff Thompson0050abe2013-09-17 12:50:25 -0700162void
Jeff Thompson25b4e612013-10-10 16:03:24 -0700163Name::Component::get(struct ndn_NameComponent& componentStruct) const
164{
165 value_.get(componentStruct.value);
166}
167
168uint64_t
169Name::Component::toNumber() const
170{
171 struct ndn_NameComponent componentStruct;
172 get(componentStruct);
173 return ndn_NameComponent_toNumber(&componentStruct);
174}
175
176void
Jeff Thompson0050abe2013-09-17 12:50:25 -0700177Name::set(const char *uri_cstr)
Jeff Thompson443398d2013-07-02 19:45:46 -0700178{
Jeff Thompson67515bd2013-08-15 17:43:22 -0700179 components_.clear();
180
Jeff Thompson443398d2013-07-02 19:45:46 -0700181 string uri = uri_cstr;
182 trim(uri);
183 if (uri.size() == 0)
184 return;
185
186 size_t iColon = uri.find(':');
187 if (iColon != string::npos) {
188 // Make sure the colon came before a '/'.
189 size_t iFirstSlash = uri.find('/');
190 if (iFirstSlash == string::npos || iColon < iFirstSlash) {
191 // Omit the leading protocol such as ndn:
192 uri.erase(0, iColon + 1);
193 trim(uri);
194 }
195 }
196
197 // Trim the leading slash and possibly the authority.
198 if (uri[0] == '/') {
199 if (uri.size() >= 2 && uri[1] == '/') {
200 // Strip the authority following "//".
201 size_t iAfterAuthority = uri.find('/', 2);
202 if (iAfterAuthority == string::npos)
203 // Unusual case: there was only an authority.
204 return;
205 else {
206 uri.erase(0, iAfterAuthority + 1);
207 trim(uri);
208 }
209 }
210 else {
211 uri.erase(0, 1);
212 trim(uri);
213 }
214 }
215
216 size_t iComponentStart = 0;
217
218 // Unescape the components.
219 while (iComponentStart < uri.size()) {
220 size_t iComponentEnd = uri.find("/", iComponentStart);
221 if (iComponentEnd == string::npos)
222 iComponentEnd = uri.size();
223
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700224 Component component(fromEscapedString(&uri[0], iComponentStart, iComponentEnd));
Jeff Thompson46411c92013-09-13 19:31:25 -0700225 // Ignore illegal components. This also gets rid of a trailing '/'.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700226 if (component.getValue())
Jeff Thompson46411c92013-09-13 19:31:25 -0700227 components_.push_back(Component(component));
Jeff Thompson443398d2013-07-02 19:45:46 -0700228
229 iComponentStart = iComponentEnd + 1;
230 }
231}
232
Jeff Thompson0050abe2013-09-17 12:50:25 -0700233void
234Name::get(struct ndn_Name& nameStruct) const
Jeff Thompson48815112013-06-28 18:22:48 -0700235{
Jeff Thompson016ed642013-07-02 14:39:06 -0700236 if (nameStruct.maxComponents < components_.size())
237 throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()");
238
239 nameStruct.nComponents = components_.size();
Jeff Thompson97223af2013-09-24 17:01:27 -0700240 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson016ed642013-07-02 14:39:06 -0700241 components_[i].get(nameStruct.components[i]);
Jeff Thompson48815112013-06-28 18:22:48 -0700242}
243
Jeff Thompson0050abe2013-09-17 12:50:25 -0700244void
245Name::set(const struct ndn_Name& nameStruct)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700246{
247 clear();
Jeff Thompson97223af2013-09-24 17:01:27 -0700248 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson3a715632013-10-31 11:36:35 -0700249 append(nameStruct.components[i].value.value, nameStruct.components[i].value.length);
Jeff Thompsonb468c312013-07-01 17:50:14 -0700250}
251
Jeff Thompson26b0d792013-09-23 16:19:01 -0700252Name&
253Name::append(const Name& name)
254{
255 if (&name == this)
256 // Copying from this name, so need to make a copy first.
257 return append(Name(name));
258
259 for (size_t i = 0; i < name.components_.size(); ++i)
260 components_.push_back(name.components_[i]);
261
262 return *this;
263}
264
Jeff Thompson4affbf52013-10-18 14:36:46 -0700265string
Jeff Thompson0050abe2013-09-17 12:50:25 -0700266Name::toUri() const
Jeff Thompsone6063512013-07-01 15:11:28 -0700267{
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700268 if (components_.size() == 0)
269 return "/";
270
271 ostringstream result;
Jeff Thompson97223af2013-09-24 17:01:27 -0700272 for (size_t i = 0; i < components_.size(); ++i) {
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700273 result << "/";
Jeff Thompson9bdb3b22013-09-12 12:42:13 -0700274 toEscapedString(*components_[i].getValue(), result);
Jeff Thompsone6063512013-07-01 15:11:28 -0700275 }
276
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700277 return result.str();
Jeff Thompsone6063512013-07-01 15:11:28 -0700278}
279
Jeff Thompsond0159d72013-09-23 13:34:15 -0700280Name
281Name::getSubName(size_t iStartComponent, size_t nComponents) const
282{
283 Name result;
284
Jeff Thompson97223af2013-09-24 17:01:27 -0700285 size_t iEnd = iStartComponent + nComponents;
286 for (size_t i = iStartComponent; i < iEnd && i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700287 result.components_.push_back(components_[i]);
288
289 return result;
290}
291
292Name
293Name::getSubName(size_t iStartComponent) const
294{
295 Name result;
296
Jeff Thompson97223af2013-09-24 17:01:27 -0700297 for (size_t i = iStartComponent; i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700298 result.components_.push_back(components_[i]);
299
300 return result;
301}
302
Jeff Thompson0050abe2013-09-17 12:50:25 -0700303bool
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700304Name::equals(const Name& name) const
305{
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700306 if (components_.size() != name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700307 return false;
308
309 for (size_t i = 0; i < components_.size(); ++i) {
310 if (*components_[i].getValue() != *name.components_[i].getValue())
311 return false;
312 }
313
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700314 return true;
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700315}
316
317bool
Jeff Thompson0050abe2013-09-17 12:50:25 -0700318Name::match(const Name& name) const
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700319{
320 // Imitate ndn_Name_match.
321
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700322 // This name is longer than the name we are checking it against.
323 if (components_.size() > name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700324 return false;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700325
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700326 // Check if at least one of given components doesn't match.
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700327 for (size_t i = 0; i < components_.size(); ++i) {
328 if (*components_[i].getValue() != *name.components_[i].getValue())
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700329 return false;
330 }
331
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700332 return true;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700333}
334
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700335Blob
336Name::fromEscapedString(const char *escapedString, size_t beginOffset, size_t endOffset)
337{
338 string trimmedString(escapedString + beginOffset, escapedString + endOffset);
339 trim(trimmedString);
340 string value = unescape(trimmedString);
341
342 if (value.find_first_not_of(".") == string::npos) {
343 // Special case for component of only periods.
344 if (value.size() <= 2)
345 // Zero, one or two periods is illegal. Ignore this component.
346 return Blob();
347 else
348 // Remove 3 periods.
349 return Blob((const uint8_t *)&value[3], value.size() - 3);
350 }
351 else
352 return Blob((const uint8_t *)&value[0], value.size());
353}
354
355Blob
356Name::fromEscapedString(const char *escapedString)
357{
358 return fromEscapedString(escapedString, 0, ::strlen(escapedString));
359}
360
Jeff Thompson0050abe2013-09-17 12:50:25 -0700361void
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700362Name::toEscapedString(const vector<uint8_t>& value, ostringstream& result)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700363{
364 bool gotNonDot = false;
365 for (unsigned i = 0; i < value.size(); ++i) {
366 if (value[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 << "...";
Jeff Thompson97223af2013-09-24 17:01:27 -0700374 for (size_t i = 0; i < value.size(); ++i)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700375 result << '.';
376 }
377 else {
378 // In case we need to escape, set to upper case hex and save the previous flags.
379 ios::fmtflags saveFlags = result.flags(ios::hex | ios::uppercase);
380
Jeff Thompson97223af2013-09-24 17:01:27 -0700381 for (size_t i = 0; i < value.size(); ++i) {
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700382 uint8_t x = value[i];
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700383 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
Alexander Afanasyevb1d67462013-11-18 20:25:00 -0800384 if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
385 (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
386 x == 0x2e || x == 0x5f)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700387 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
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700401string
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700402Name::toEscapedString(const vector<uint8_t>& value)
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700403{
404 ostringstream result;
405 toEscapedString(value, result);
406 return result.str();
407}
408
Jeff Thompson9c41dfe2013-06-27 12:10:25 -0700409}