blob: 3d4519ac732c3fc5afe7b3c0261b0eb58ff04ca8 [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;
Jeff Thompsond4144fe2013-09-18 15:59:57 -070015using namespace ndn::ptr_lib;
Jeff Thompson9c41dfe2013-06-27 12:10:25 -070016
17namespace ndn {
18
Jeff Thompson26c63d62013-07-02 18:00:26 -070019static const char *WHITESPACE_CHARS = " \n\r\t";
20
21/**
22 * Modify str in place to erase whitespace on the left.
23 * @param str
24 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070025static inline void
26trimLeft(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070027{
28 size_t found = str.find_first_not_of(WHITESPACE_CHARS);
29 if (found != string::npos) {
30 if (found > 0)
31 str.erase(0, found);
32 }
33 else
34 // All whitespace
35 str.clear();
36}
37
38/**
39 * Modify str in place to erase whitespace on the right.
40 * @param str
41 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070042static inline void
43trimRight(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070044{
45 size_t found = str.find_last_not_of(WHITESPACE_CHARS);
46 if (found != string::npos) {
47 if (found + 1 < str.size())
48 str.erase(found + 1);
49 }
50 else
51 // All whitespace
52 str.clear();
53}
54
55/**
56 * Modify str in place to erase whitespace on the left and right.
57 * @param str
58 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070059static void
60trim(string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070061{
62 trimLeft(str);
63 trimRight(str);
64}
Jeff Thompson443398d2013-07-02 19:45:46 -070065
Jeff Thompson26c63d62013-07-02 18:00:26 -070066/**
67 * Convert the hex character to an integer from 0 to 15, or -1 if not a hex character.
68 * @param c
69 * @return
70 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070071static int
Jeff Thompson10ad12a2013-09-24 16:19:11 -070072fromHexChar(uint8_t c)
Jeff Thompson26c63d62013-07-02 18:00:26 -070073{
74 if (c >= '0' && c <= '9')
75 return (int)c - (int)'0';
76 else if (c >= 'A' && c <= 'F')
77 return (int)c - (int)'A' + 10;
78 else if (c >= 'a' && c <= 'f')
79 return (int)c - (int)'a' + 10;
80 else
81 return -1;
82}
83
84/**
85 * Return a copy of str, converting each escaped "%XX" to the char value.
86 * @param str
87 */
Jeff Thompson0050abe2013-09-17 12:50:25 -070088static string
89unescape(const string& str)
Jeff Thompson26c63d62013-07-02 18:00:26 -070090{
91 ostringstream result;
92
Jeff Thompson97223af2013-09-24 17:01:27 -070093 for (size_t i = 0; i < str.size(); ++i) {
Jeff Thompson26c63d62013-07-02 18:00:26 -070094 if (str[i] == '%' && i + 2 < str.size()) {
95 int hi = fromHexChar(str[i + 1]);
96 int lo = fromHexChar(str[i + 2]);
97
98 if (hi < 0 || lo < 0)
99 // Invalid hex characters, so just keep the escaped string.
100 result << str[i] << str[i + 1] << str[i + 2];
101 else
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700102 result << (uint8_t)(16 * hi + lo);
Jeff Thompson26c63d62013-07-02 18:00:26 -0700103
104 // Skip ahead past the escaped value.
105 i += 2;
106 }
107 else
108 // Just copy through.
109 result << str[i];
110 }
111
112 return result.str();
113}
114
Jeff Thompson27cae532013-10-08 12:52:41 -0700115uint64_t Name::Component::toNumberWithMarker(uint8_t marker) const
116{
117 struct ndn_NameComponent componentStruct;
118 get(componentStruct);
119 uint64_t result;
120
121 ndn_Error error;
122 if ((error = ndn_NameComponent_toNumberWithMarker(&componentStruct, marker, &result)))
Jeff Thompson4affbf52013-10-18 14:36:46 -0700123 throw runtime_error(ndn_getErrorString(error));
Jeff Thompson27cae532013-10-08 12:52:41 -0700124
125 return result;
126}
127
Jeff Thompsond129ac12013-10-11 14:30:12 -0700128Name::Component
Jeff Thompsond129ac12013-10-11 14:30:12 -0700129Name::Component::fromNumber(uint64_t number)
Jeff Thompson8aac1992013-08-12 17:26:02 -0700130{
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700131 shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
Jeff Thompson8aac1992013-08-12 17:26:02 -0700132
Jeff Thompsond129ac12013-10-11 14:30:12 -0700133 // First encode in little endian.
134 while (number != 0) {
135 value->push_back(number & 0xff);
136 number >>= 8;
137 }
138
139 // Make it big endian.
140 reverse(value->begin(), value->end());
141 return Blob(value);
142}
143
144Name::Component
145Name::Component::fromNumberWithMarker(uint64_t number, uint8_t marker)
146{
147 shared_ptr<vector<uint8_t> > value(new vector<uint8_t>());
148
149 // Add the leading marker.
150 value->push_back(marker);
Jeff Thompsonb8f1b132013-08-13 11:07:43 -0700151
Jeff Thompson8aac1992013-08-12 17:26:02 -0700152 // First encode in little endian.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700153 while (number != 0) {
154 value->push_back(number & 0xff);
155 number >>= 8;
Jeff Thompson8aac1992013-08-12 17:26:02 -0700156 }
157
Jeff Thompson8aac1992013-08-12 17:26:02 -0700158 // Make it big endian.
Jeff Thompson995aba52013-09-12 12:04:52 -0700159 reverse(value->begin() + 1, value->end());
Jeff Thompson46411c92013-09-13 19:31:25 -0700160 return Blob(value);
Jeff Thompson8aac1992013-08-12 17:26:02 -0700161}
162
Jeff Thompson0050abe2013-09-17 12:50:25 -0700163void
Jeff Thompson25b4e612013-10-10 16:03:24 -0700164Name::Component::get(struct ndn_NameComponent& componentStruct) const
165{
166 value_.get(componentStruct.value);
167}
168
169uint64_t
170Name::Component::toNumber() const
171{
172 struct ndn_NameComponent componentStruct;
173 get(componentStruct);
174 return ndn_NameComponent_toNumber(&componentStruct);
175}
176
177void
Jeff Thompson0050abe2013-09-17 12:50:25 -0700178Name::set(const char *uri_cstr)
Jeff Thompson443398d2013-07-02 19:45:46 -0700179{
Jeff Thompson67515bd2013-08-15 17:43:22 -0700180 components_.clear();
181
Jeff Thompson443398d2013-07-02 19:45:46 -0700182 string uri = uri_cstr;
183 trim(uri);
184 if (uri.size() == 0)
185 return;
186
187 size_t iColon = uri.find(':');
188 if (iColon != string::npos) {
189 // Make sure the colon came before a '/'.
190 size_t iFirstSlash = uri.find('/');
191 if (iFirstSlash == string::npos || iColon < iFirstSlash) {
192 // Omit the leading protocol such as ndn:
193 uri.erase(0, iColon + 1);
194 trim(uri);
195 }
196 }
197
198 // Trim the leading slash and possibly the authority.
199 if (uri[0] == '/') {
200 if (uri.size() >= 2 && uri[1] == '/') {
201 // Strip the authority following "//".
202 size_t iAfterAuthority = uri.find('/', 2);
203 if (iAfterAuthority == string::npos)
204 // Unusual case: there was only an authority.
205 return;
206 else {
207 uri.erase(0, iAfterAuthority + 1);
208 trim(uri);
209 }
210 }
211 else {
212 uri.erase(0, 1);
213 trim(uri);
214 }
215 }
216
217 size_t iComponentStart = 0;
218
219 // Unescape the components.
220 while (iComponentStart < uri.size()) {
221 size_t iComponentEnd = uri.find("/", iComponentStart);
222 if (iComponentEnd == string::npos)
223 iComponentEnd = uri.size();
224
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700225 Component component(fromEscapedString(&uri[0], iComponentStart, iComponentEnd));
Jeff Thompson46411c92013-09-13 19:31:25 -0700226 // Ignore illegal components. This also gets rid of a trailing '/'.
Jeff Thompsond129ac12013-10-11 14:30:12 -0700227 if (component.getValue())
Jeff Thompson46411c92013-09-13 19:31:25 -0700228 components_.push_back(Component(component));
Jeff Thompson443398d2013-07-02 19:45:46 -0700229
230 iComponentStart = iComponentEnd + 1;
231 }
232}
233
Jeff Thompson0050abe2013-09-17 12:50:25 -0700234void
235Name::get(struct ndn_Name& nameStruct) const
Jeff Thompson48815112013-06-28 18:22:48 -0700236{
Jeff Thompson016ed642013-07-02 14:39:06 -0700237 if (nameStruct.maxComponents < components_.size())
238 throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()");
239
240 nameStruct.nComponents = components_.size();
Jeff Thompson97223af2013-09-24 17:01:27 -0700241 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson016ed642013-07-02 14:39:06 -0700242 components_[i].get(nameStruct.components[i]);
Jeff Thompson48815112013-06-28 18:22:48 -0700243}
244
Jeff Thompson0050abe2013-09-17 12:50:25 -0700245void
246Name::set(const struct ndn_Name& nameStruct)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700247{
248 clear();
Jeff Thompson97223af2013-09-24 17:01:27 -0700249 for (size_t i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompson3a715632013-10-31 11:36:35 -0700250 append(nameStruct.components[i].value.value, nameStruct.components[i].value.length);
Jeff Thompsonb468c312013-07-01 17:50:14 -0700251}
252
Jeff Thompson26b0d792013-09-23 16:19:01 -0700253Name&
254Name::append(const Name& name)
255{
256 if (&name == this)
257 // Copying from this name, so need to make a copy first.
258 return append(Name(name));
259
260 for (size_t i = 0; i < name.components_.size(); ++i)
261 components_.push_back(name.components_[i]);
262
263 return *this;
264}
265
Jeff Thompson4affbf52013-10-18 14:36:46 -0700266string
Jeff Thompson0050abe2013-09-17 12:50:25 -0700267Name::toUri() const
Jeff Thompsone6063512013-07-01 15:11:28 -0700268{
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700269 if (components_.size() == 0)
270 return "/";
271
272 ostringstream result;
Jeff Thompson97223af2013-09-24 17:01:27 -0700273 for (size_t i = 0; i < components_.size(); ++i) {
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700274 result << "/";
Jeff Thompson9bdb3b22013-09-12 12:42:13 -0700275 toEscapedString(*components_[i].getValue(), result);
Jeff Thompsone6063512013-07-01 15:11:28 -0700276 }
277
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700278 return result.str();
Jeff Thompsone6063512013-07-01 15:11:28 -0700279}
280
Jeff Thompsond0159d72013-09-23 13:34:15 -0700281Name
282Name::getSubName(size_t iStartComponent, size_t nComponents) const
283{
284 Name result;
285
Jeff Thompson97223af2013-09-24 17:01:27 -0700286 size_t iEnd = iStartComponent + nComponents;
287 for (size_t i = iStartComponent; i < iEnd && i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700288 result.components_.push_back(components_[i]);
289
290 return result;
291}
292
293Name
294Name::getSubName(size_t iStartComponent) const
295{
296 Name result;
297
Jeff Thompson97223af2013-09-24 17:01:27 -0700298 for (size_t i = iStartComponent; i < components_.size(); ++i)
Jeff Thompsond0159d72013-09-23 13:34:15 -0700299 result.components_.push_back(components_[i]);
300
301 return result;
302}
303
Jeff Thompson0050abe2013-09-17 12:50:25 -0700304bool
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700305Name::equals(const Name& name) const
306{
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700307 if (components_.size() != name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700308 return false;
309
310 for (size_t i = 0; i < components_.size(); ++i) {
311 if (*components_[i].getValue() != *name.components_[i].getValue())
312 return false;
313 }
314
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700315 return true;
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700316}
317
318bool
Jeff Thompson0050abe2013-09-17 12:50:25 -0700319Name::match(const Name& name) const
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700320{
321 // Imitate ndn_Name_match.
322
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700323 // This name is longer than the name we are checking it against.
324 if (components_.size() > name.components_.size())
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700325 return false;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700326
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700327 // Check if at least one of given components doesn't match.
Jeff Thompson3c2ab012013-10-02 14:18:16 -0700328 for (size_t i = 0; i < components_.size(); ++i) {
329 if (*components_[i].getValue() != *name.components_[i].getValue())
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700330 return false;
331 }
332
Jeff Thompsone589c3f2013-10-12 17:30:50 -0700333 return true;
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700334}
335
Jeff Thompsond8e53e62013-10-29 16:59:49 -0700336Blob
337Name::fromEscapedString(const char *escapedString, size_t beginOffset, size_t endOffset)
338{
339 string trimmedString(escapedString + beginOffset, escapedString + endOffset);
340 trim(trimmedString);
341 string value = unescape(trimmedString);
342
343 if (value.find_first_not_of(".") == string::npos) {
344 // Special case for component of only periods.
345 if (value.size() <= 2)
346 // Zero, one or two periods is illegal. Ignore this component.
347 return Blob();
348 else
349 // Remove 3 periods.
350 return Blob((const uint8_t *)&value[3], value.size() - 3);
351 }
352 else
353 return Blob((const uint8_t *)&value[0], value.size());
354}
355
356Blob
357Name::fromEscapedString(const char *escapedString)
358{
359 return fromEscapedString(escapedString, 0, ::strlen(escapedString));
360}
361
Jeff Thompson0050abe2013-09-17 12:50:25 -0700362void
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700363Name::toEscapedString(const vector<uint8_t>& value, ostringstream& result)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700364{
365 bool gotNonDot = false;
366 for (unsigned i = 0; i < value.size(); ++i) {
367 if (value[i] != 0x2e) {
368 gotNonDot = true;
369 break;
370 }
371 }
372 if (!gotNonDot) {
373 // Special case for component of zero or more periods. Add 3 periods.
374 result << "...";
Jeff Thompson97223af2013-09-24 17:01:27 -0700375 for (size_t i = 0; i < value.size(); ++i)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700376 result << '.';
377 }
378 else {
379 // In case we need to escape, set to upper case hex and save the previous flags.
380 ios::fmtflags saveFlags = result.flags(ios::hex | ios::uppercase);
381
Jeff Thompson97223af2013-09-24 17:01:27 -0700382 for (size_t i = 0; i < value.size(); ++i) {
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700383 uint8_t x = value[i];
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700384 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
Jeff Thompsonf68a74a2013-11-20 15:16:11 -0800385 if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
386 (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
387 x == 0x2e || x == 0x5f)
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700388 result << x;
389 else {
390 result << '%';
391 if (x < 16)
392 result << '0';
393 result << (unsigned int)x;
394 }
395 }
396
397 // Restore.
398 result.flags(saveFlags);
399 }
400}
401
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700402string
Jeff Thompson10ad12a2013-09-24 16:19:11 -0700403Name::toEscapedString(const vector<uint8_t>& value)
Jeff Thompson6653b0b2013-09-23 12:32:39 -0700404{
405 ostringstream result;
406 toEscapedString(value, result);
407 return result.str();
408}
409
Jeff Thompson9c41dfe2013-06-27 12:10:25 -0700410}