blob: bb2b1440b34af6da3dde77df76aa94c1b86b6f0b [file] [log] [blame]
Jeff Thompson47eecfc2013-07-07 22:56:46 -07001/**
2 * @author: Jeff Thompson
3 * See COPYING for copyright and distribution information.
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07004 */
5
Jeff Thompson54909772013-07-07 22:38:57 -07006#include <stdexcept>
Jeff Thompsonb8f1b132013-08-13 11:07:43 -07007#include <algorithm>
Jeff Thompson53412192013-08-06 13:35:50 -07008#include "name.hpp"
Jeff Thompson9c41dfe2013-06-27 12:10:25 -07009
10using namespace std;
11
12namespace ndn {
13
Jeff Thompson26c63d62013-07-02 18:00:26 -070014static const char *WHITESPACE_CHARS = " \n\r\t";
15
16/**
17 * Modify str in place to erase whitespace on the left.
18 * @param str
19 */
20static inline void trimLeft(string &str)
21{
22 size_t found = str.find_first_not_of(WHITESPACE_CHARS);
23 if (found != string::npos) {
24 if (found > 0)
25 str.erase(0, found);
26 }
27 else
28 // All whitespace
29 str.clear();
30}
31
32/**
33 * Modify str in place to erase whitespace on the right.
34 * @param str
35 */
36static inline void trimRight(string &str)
37{
38 size_t found = str.find_last_not_of(WHITESPACE_CHARS);
39 if (found != string::npos) {
40 if (found + 1 < str.size())
41 str.erase(found + 1);
42 }
43 else
44 // All whitespace
45 str.clear();
46}
47
48/**
49 * Modify str in place to erase whitespace on the left and right.
50 * @param str
51 */
52static void trim(string &str)
53{
54 trimLeft(str);
55 trimRight(str);
56}
Jeff Thompson443398d2013-07-02 19:45:46 -070057
Jeff Thompson26c63d62013-07-02 18:00:26 -070058/**
59 * Convert the hex character to an integer from 0 to 15, or -1 if not a hex character.
60 * @param c
61 * @return
62 */
63static int fromHexChar(unsigned char c)
64{
65 if (c >= '0' && c <= '9')
66 return (int)c - (int)'0';
67 else if (c >= 'A' && c <= 'F')
68 return (int)c - (int)'A' + 10;
69 else if (c >= 'a' && c <= 'f')
70 return (int)c - (int)'a' + 10;
71 else
72 return -1;
73}
74
75/**
76 * Return a copy of str, converting each escaped "%XX" to the char value.
77 * @param str
78 */
79static string unescape(const string &str)
80{
81 ostringstream result;
82
83 for (unsigned int i = 0; i < str.size(); ++i) {
84 if (str[i] == '%' && i + 2 < str.size()) {
85 int hi = fromHexChar(str[i + 1]);
86 int lo = fromHexChar(str[i + 2]);
87
88 if (hi < 0 || lo < 0)
89 // Invalid hex characters, so just keep the escaped string.
90 result << str[i] << str[i + 1] << str[i + 2];
91 else
92 result << (unsigned char)(16 * hi + lo);
93
94 // Skip ahead past the escaped value.
95 i += 2;
96 }
97 else
98 // Just copy through.
99 result << str[i];
100 }
101
102 return result.str();
103}
104
Jeff Thompson5a6b5ab2013-08-05 15:43:47 -0700105bool Name::Component::setFromEscapedString(const char *first, const char *last)
Jeff Thompson443398d2013-07-02 19:45:46 -0700106{
107 string trimmedString(first, last);
108 trim(trimmedString);
109 string component = unescape(trimmedString);
110
111 if (component.find_first_not_of(".") == string::npos) {
112 // Special case for component of only periods.
113 if (component.size() <= 2)
114 // Zero, one or two periods is illegal. Ignore this component.
115 return false;
116 else {
117 // Remove 3 periods.
118 value_.clear();
119 value_.insert(value_.begin(), component.begin() + 3, component.end());
120 }
121 }
122 else {
123 value_.clear();
124 value_.insert(value_.begin(), component.begin(), component.end());
125 }
126
127 return true;
128}
129
Jeff Thompson8aac1992013-08-12 17:26:02 -0700130void Name::Component::setSegment(unsigned long segment)
131{
132 value_.clear();
133
Jeff Thompsonb8f1b132013-08-13 11:07:43 -0700134 // Add the leading zero.
135 value_.push_back(0);
136
Jeff Thompson8aac1992013-08-12 17:26:02 -0700137 // First encode in little endian.
138 while (segment != 0) {
139 value_.push_back(segment & 0xff);
140 segment >>= 8;
141 }
142
Jeff Thompson8aac1992013-08-12 17:26:02 -0700143 // Make it big endian.
Jeff Thompsonb8f1b132013-08-13 11:07:43 -0700144 reverse(value_.begin() + 1, value_.end());
Jeff Thompson8aac1992013-08-12 17:26:02 -0700145}
146
Jeff Thompson67515bd2013-08-15 17:43:22 -0700147void Name::set(const char *uri_cstr)
Jeff Thompson443398d2013-07-02 19:45:46 -0700148{
Jeff Thompson67515bd2013-08-15 17:43:22 -0700149 components_.clear();
150
Jeff Thompson443398d2013-07-02 19:45:46 -0700151 string uri = uri_cstr;
152 trim(uri);
153 if (uri.size() == 0)
154 return;
155
156 size_t iColon = uri.find(':');
157 if (iColon != string::npos) {
158 // Make sure the colon came before a '/'.
159 size_t iFirstSlash = uri.find('/');
160 if (iFirstSlash == string::npos || iColon < iFirstSlash) {
161 // Omit the leading protocol such as ndn:
162 uri.erase(0, iColon + 1);
163 trim(uri);
164 }
165 }
166
167 // Trim the leading slash and possibly the authority.
168 if (uri[0] == '/') {
169 if (uri.size() >= 2 && uri[1] == '/') {
170 // Strip the authority following "//".
171 size_t iAfterAuthority = uri.find('/', 2);
172 if (iAfterAuthority == string::npos)
173 // Unusual case: there was only an authority.
174 return;
175 else {
176 uri.erase(0, iAfterAuthority + 1);
177 trim(uri);
178 }
179 }
180 else {
181 uri.erase(0, 1);
182 trim(uri);
183 }
184 }
185
186 size_t iComponentStart = 0;
187
188 // Unescape the components.
189 while (iComponentStart < uri.size()) {
190 size_t iComponentEnd = uri.find("/", iComponentStart);
191 if (iComponentEnd == string::npos)
192 iComponentEnd = uri.size();
193
Jeff Thompson5a6b5ab2013-08-05 15:43:47 -0700194 components_.push_back(Component());
Jeff Thompson443398d2013-07-02 19:45:46 -0700195 if (!components_[components_.size() - 1].setFromEscapedString(&uri[iComponentStart], &uri[iComponentEnd]))
196 // Ignore the illegal component. This also gets rid of a trailing '/'.
197 components_.pop_back();
198
199 iComponentStart = iComponentEnd + 1;
200 }
201}
202
Jeff Thompsond345a5b2013-07-08 16:18:23 -0700203void Name::get(struct ndn_Name &nameStruct) const
Jeff Thompson48815112013-06-28 18:22:48 -0700204{
Jeff Thompson016ed642013-07-02 14:39:06 -0700205 if (nameStruct.maxComponents < components_.size())
206 throw runtime_error("nameStruct.maxComponents must be >= this name getNComponents()");
207
208 nameStruct.nComponents = components_.size();
209 for (unsigned int i = 0; i < nameStruct.nComponents; ++i)
210 components_[i].get(nameStruct.components[i]);
Jeff Thompson48815112013-06-28 18:22:48 -0700211}
212
Jeff Thompsondd3d2292013-07-10 11:59:44 -0700213void Name::set(const struct ndn_Name &nameStruct)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700214{
215 clear();
Jeff Thompsonccb13c12013-07-01 18:16:00 -0700216 for (unsigned int i = 0; i < nameStruct.nComponents; ++i)
Jeff Thompsonb468c312013-07-01 17:50:14 -0700217 addComponent(nameStruct.components[i].value, nameStruct.components[i].valueLength);
218}
219
Jeff Thompson21844fc2013-08-08 14:52:51 -0700220std::string Name::toUri() const
Jeff Thompsone6063512013-07-01 15:11:28 -0700221{
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700222 if (components_.size() == 0)
223 return "/";
224
225 ostringstream result;
Jeff Thompsonccb13c12013-07-01 18:16:00 -0700226 for (unsigned int i = 0; i < components_.size(); ++i) {
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700227 result << "/";
228 toEscapedString(components_[i].getValue(), result);
Jeff Thompsone6063512013-07-01 15:11:28 -0700229 }
230
Jeff Thompson4b2479a2013-07-02 15:37:39 -0700231 return result.str();
Jeff Thompsone6063512013-07-01 15:11:28 -0700232}
233
Jeff Thompson15dc9642013-08-27 17:19:35 -0700234bool Name::match(const Name &name) const
Jeff Thompsoncc35cd42013-08-20 12:23:14 -0700235{
236 // Imitate ndn_Name_match.
237
238 // This name is longer than the name we are checking it against.
239 if (components_.size() > name.components_.size())
240 return 0;
241
242 // Check if at least one of given components doesn't match.
243 unsigned int i;
244 for (i = 0; i < components_.size(); ++i) {
245 const Component &selfComponent = components_[i];
246 const Component &nameComponent = name.components_[i];
247
248 if (selfComponent.getValue() != nameComponent.getValue())
249 return false;
250 }
251
252 return true;
253}
254
Jeff Thompsonec7789a2013-08-21 11:08:36 -0700255void Name::toEscapedString(const vector<unsigned char> &value, ostringstream &result)
256{
257 bool gotNonDot = false;
258 for (unsigned i = 0; i < value.size(); ++i) {
259 if (value[i] != 0x2e) {
260 gotNonDot = true;
261 break;
262 }
263 }
264 if (!gotNonDot) {
265 // Special case for component of zero or more periods. Add 3 periods.
266 result << "...";
267 for (unsigned int i = 0; i < value.size(); ++i)
268 result << '.';
269 }
270 else {
271 // In case we need to escape, set to upper case hex and save the previous flags.
272 ios::fmtflags saveFlags = result.flags(ios::hex | ios::uppercase);
273
274 for (unsigned int i = 0; i < value.size(); ++i) {
275 unsigned char x = value[i];
276 // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
277 if (x >= 0x30 && x <= 0x39 || x >= 0x41 && x <= 0x5a ||
278 x >= 0x61 && x <= 0x7a || x == 0x2b || x == 0x2d ||
279 x == 0x2e || x == 0x5f)
280 result << x;
281 else {
282 result << '%';
283 if (x < 16)
284 result << '0';
285 result << (unsigned int)x;
286 }
287 }
288
289 // Restore.
290 result.flags(saveFlags);
291 }
292}
293
Jeff Thompson9c41dfe2013-06-27 12:10:25 -0700294}