cannam@226
|
1 /*
|
cannam@226
|
2 Copyright 2011-2016 David Robillard <http://drobilla.net>
|
cannam@226
|
3
|
cannam@226
|
4 Permission to use, copy, modify, and/or distribute this software for any
|
cannam@226
|
5 purpose with or without fee is hereby granted, provided that the above
|
cannam@226
|
6 copyright notice and this permission notice appear in all copies.
|
cannam@226
|
7
|
cannam@226
|
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
cannam@226
|
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
cannam@226
|
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
cannam@226
|
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
cannam@226
|
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
cannam@226
|
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
cannam@226
|
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
cannam@226
|
15 */
|
cannam@226
|
16
|
cannam@226
|
17 #include "serd_internal.h"
|
cannam@226
|
18
|
cannam@226
|
19 #include <stdlib.h>
|
cannam@226
|
20 #include <string.h>
|
cannam@226
|
21
|
cannam@226
|
22 #include <math.h>
|
cannam@226
|
23 #include <float.h>
|
cannam@226
|
24
|
cannam@226
|
25 #ifdef _WIN32
|
cannam@226
|
26 # ifndef isnan
|
cannam@226
|
27 # define isnan(x) _isnan(x)
|
cannam@226
|
28 # endif
|
cannam@226
|
29 # ifndef isinf
|
cannam@226
|
30 # define isinf(x) (!_finite(x))
|
cannam@226
|
31 # endif
|
cannam@226
|
32 #endif
|
cannam@226
|
33
|
cannam@226
|
34 SERD_API
|
cannam@226
|
35 SerdNode
|
cannam@226
|
36 serd_node_from_string(SerdType type, const uint8_t* buf)
|
cannam@226
|
37 {
|
cannam@226
|
38 if (!buf) {
|
cannam@226
|
39 return SERD_NODE_NULL;
|
cannam@226
|
40 }
|
cannam@226
|
41
|
cannam@226
|
42 uint32_t flags = 0;
|
cannam@226
|
43 size_t buf_n_bytes = 0;
|
cannam@226
|
44 const size_t buf_n_chars = serd_strlen(buf, &buf_n_bytes, &flags);
|
cannam@226
|
45 SerdNode ret = { buf, buf_n_bytes, buf_n_chars, flags, type };
|
cannam@226
|
46 return ret;
|
cannam@226
|
47 }
|
cannam@226
|
48
|
cannam@226
|
49 SERD_API
|
cannam@226
|
50 SerdNode
|
cannam@226
|
51 serd_node_copy(const SerdNode* node)
|
cannam@226
|
52 {
|
cannam@226
|
53 if (!node || !node->buf) {
|
cannam@226
|
54 return SERD_NODE_NULL;
|
cannam@226
|
55 }
|
cannam@226
|
56
|
cannam@226
|
57 SerdNode copy = *node;
|
cannam@226
|
58 uint8_t* buf = (uint8_t*)malloc(copy.n_bytes + 1);
|
cannam@226
|
59 memcpy(buf, node->buf, copy.n_bytes + 1);
|
cannam@226
|
60 copy.buf = buf;
|
cannam@226
|
61 return copy;
|
cannam@226
|
62 }
|
cannam@226
|
63
|
cannam@226
|
64 SERD_API
|
cannam@226
|
65 bool
|
cannam@226
|
66 serd_node_equals(const SerdNode* a, const SerdNode* b)
|
cannam@226
|
67 {
|
cannam@226
|
68 return (a == b)
|
cannam@226
|
69 || (a->type == b->type
|
cannam@226
|
70 && a->n_bytes == b->n_bytes
|
cannam@226
|
71 && a->n_chars == b->n_chars
|
cannam@226
|
72 && ((a->buf == b->buf) || !memcmp((const char*)a->buf,
|
cannam@226
|
73 (const char*)b->buf,
|
cannam@226
|
74 a->n_bytes + 1)));
|
cannam@226
|
75 }
|
cannam@226
|
76
|
cannam@226
|
77 static size_t
|
cannam@226
|
78 serd_uri_string_length(const SerdURI* uri)
|
cannam@226
|
79 {
|
cannam@226
|
80 size_t len = uri->path_base.len;
|
cannam@226
|
81
|
cannam@226
|
82 #define ADD_LEN(field, n_delims) \
|
cannam@226
|
83 if ((field).len) { len += (field).len + (n_delims); }
|
cannam@226
|
84
|
cannam@226
|
85 ADD_LEN(uri->path, 1); // + possible leading `/'
|
cannam@226
|
86 ADD_LEN(uri->scheme, 1); // + trailing `:'
|
cannam@226
|
87 ADD_LEN(uri->authority, 2); // + leading `//'
|
cannam@226
|
88 ADD_LEN(uri->query, 1); // + leading `?'
|
cannam@226
|
89 ADD_LEN(uri->fragment, 1); // + leading `#'
|
cannam@226
|
90
|
cannam@226
|
91 return len + 2; // + 2 for authority `//'
|
cannam@226
|
92 }
|
cannam@226
|
93
|
cannam@226
|
94 static size_t
|
cannam@226
|
95 string_sink(const void* buf, size_t len, void* stream)
|
cannam@226
|
96 {
|
cannam@226
|
97 uint8_t** ptr = (uint8_t**)stream;
|
cannam@226
|
98 memcpy(*ptr, buf, len);
|
cannam@226
|
99 *ptr += len;
|
cannam@226
|
100 return len;
|
cannam@226
|
101 }
|
cannam@226
|
102
|
cannam@226
|
103 SERD_API
|
cannam@226
|
104 SerdNode
|
cannam@226
|
105 serd_node_new_uri_from_node(const SerdNode* uri_node,
|
cannam@226
|
106 const SerdURI* base,
|
cannam@226
|
107 SerdURI* out)
|
cannam@226
|
108 {
|
cannam@226
|
109 return (uri_node->type == SERD_URI && uri_node->buf)
|
cannam@226
|
110 ? serd_node_new_uri_from_string(uri_node->buf, base, out)
|
cannam@226
|
111 : SERD_NODE_NULL;
|
cannam@226
|
112 }
|
cannam@226
|
113
|
cannam@226
|
114 SERD_API
|
cannam@226
|
115 SerdNode
|
cannam@226
|
116 serd_node_new_uri_from_string(const uint8_t* str,
|
cannam@226
|
117 const SerdURI* base,
|
cannam@226
|
118 SerdURI* out)
|
cannam@226
|
119 {
|
cannam@226
|
120 if (!str || str[0] == '\0') {
|
cannam@226
|
121 // Empty URI => Base URI, or nothing if no base is given
|
cannam@226
|
122 return base ? serd_node_new_uri(base, NULL, out) : SERD_NODE_NULL;
|
cannam@226
|
123 }
|
cannam@226
|
124
|
cannam@226
|
125 SerdURI uri;
|
cannam@226
|
126 serd_uri_parse(str, &uri);
|
cannam@226
|
127 return serd_node_new_uri(&uri, base, out); // Resolve/Serialise
|
cannam@226
|
128 }
|
cannam@226
|
129
|
cannam@226
|
130 static inline bool
|
cannam@226
|
131 is_uri_path_char(const uint8_t c)
|
cannam@226
|
132 {
|
cannam@226
|
133 if (is_alpha(c) || is_digit(c)) {
|
cannam@226
|
134 return true;
|
cannam@226
|
135 }
|
cannam@226
|
136 switch (c) {
|
cannam@226
|
137 case '-': case '.': case '_': case '~': // unreserved
|
cannam@226
|
138 case ':': case '@': // pchar
|
cannam@226
|
139 case '/': // separator
|
cannam@226
|
140 // sub-delims
|
cannam@226
|
141 case '!': case '$': case '&': case '\'': case '(': case ')':
|
cannam@226
|
142 case '*': case '+': case ',': case ';': case '=':
|
cannam@226
|
143 return true;
|
cannam@226
|
144 default:
|
cannam@226
|
145 return false;
|
cannam@226
|
146 }
|
cannam@226
|
147 }
|
cannam@226
|
148
|
cannam@226
|
149 SERD_API
|
cannam@226
|
150 SerdNode
|
cannam@226
|
151 serd_node_new_file_uri(const uint8_t* path,
|
cannam@226
|
152 const uint8_t* hostname,
|
cannam@226
|
153 SerdURI* out,
|
cannam@226
|
154 bool escape)
|
cannam@226
|
155 {
|
cannam@226
|
156 const size_t path_len = strlen((const char*)path);
|
cannam@226
|
157 const size_t hostname_len = hostname ? strlen((const char*)hostname) : 0;
|
cannam@226
|
158 const bool evil = is_windows_path(path);
|
cannam@226
|
159 size_t uri_len = 0;
|
cannam@226
|
160 uint8_t* uri = NULL;
|
cannam@226
|
161
|
cannam@226
|
162 if (path[0] == '/' || is_windows_path(path)) {
|
cannam@226
|
163 uri_len = strlen("file://") + hostname_len + evil;
|
cannam@226
|
164 uri = (uint8_t*)malloc(uri_len + 1);
|
cannam@226
|
165 snprintf((char*)uri, uri_len + 1, "file://%s%s",
|
cannam@226
|
166 hostname ? (const char*)hostname : "",
|
cannam@226
|
167 evil ? "/" : "");
|
cannam@226
|
168 }
|
cannam@226
|
169
|
cannam@226
|
170 SerdChunk chunk = { uri, uri_len };
|
cannam@226
|
171 for (size_t i = 0; i < path_len; ++i) {
|
cannam@226
|
172 if (evil && path[i] == '\\') {
|
cannam@226
|
173 serd_chunk_sink("/", 1, &chunk);
|
cannam@226
|
174 } else if (path[i] == '%') {
|
cannam@226
|
175 serd_chunk_sink("%%", 2, &chunk);
|
cannam@226
|
176 } else if (!escape || is_uri_path_char(path[i])) {
|
cannam@226
|
177 serd_chunk_sink(path + i, 1, &chunk);
|
cannam@226
|
178 } else {
|
cannam@226
|
179 char escape_str[4] = { '%', 0, 0, 0 };
|
cannam@226
|
180 snprintf(escape_str + 1, sizeof(escape_str) - 1, "%X", path[i]);
|
cannam@226
|
181 serd_chunk_sink(escape_str, 3, &chunk);
|
cannam@226
|
182 }
|
cannam@226
|
183 }
|
cannam@226
|
184 serd_chunk_sink_finish(&chunk);
|
cannam@226
|
185
|
cannam@226
|
186 if (out) {
|
cannam@226
|
187 serd_uri_parse(chunk.buf, out);
|
cannam@226
|
188 }
|
cannam@226
|
189
|
cannam@226
|
190 return serd_node_from_string(SERD_URI, chunk.buf);
|
cannam@226
|
191 }
|
cannam@226
|
192
|
cannam@226
|
193 SERD_API
|
cannam@226
|
194 SerdNode
|
cannam@226
|
195 serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out)
|
cannam@226
|
196 {
|
cannam@226
|
197 SerdURI abs_uri = *uri;
|
cannam@226
|
198 if (base) {
|
cannam@226
|
199 serd_uri_resolve(uri, base, &abs_uri);
|
cannam@226
|
200 }
|
cannam@226
|
201
|
cannam@226
|
202 const size_t len = serd_uri_string_length(&abs_uri);
|
cannam@226
|
203 uint8_t* buf = (uint8_t*)malloc(len + 1);
|
cannam@226
|
204 SerdNode node = { buf, 0, 0, 0, SERD_URI };
|
cannam@226
|
205 uint8_t* ptr = buf;
|
cannam@226
|
206 const size_t actual_len = serd_uri_serialise(&abs_uri, string_sink, &ptr);
|
cannam@226
|
207
|
cannam@226
|
208 buf[actual_len] = '\0';
|
cannam@226
|
209 node.n_bytes = actual_len;
|
cannam@226
|
210 node.n_chars = serd_strlen(buf, NULL, NULL);
|
cannam@226
|
211
|
cannam@226
|
212 if (out) {
|
cannam@226
|
213 serd_uri_parse(buf, out); // TODO: cleverly avoid double parse
|
cannam@226
|
214 }
|
cannam@226
|
215
|
cannam@226
|
216 return node;
|
cannam@226
|
217 }
|
cannam@226
|
218
|
cannam@226
|
219 SERD_API
|
cannam@226
|
220 SerdNode
|
cannam@226
|
221 serd_node_new_relative_uri(const SerdURI* uri,
|
cannam@226
|
222 const SerdURI* base,
|
cannam@226
|
223 const SerdURI* root,
|
cannam@226
|
224 SerdURI* out)
|
cannam@226
|
225 {
|
cannam@226
|
226 const size_t uri_len = serd_uri_string_length(uri);
|
cannam@226
|
227 const size_t base_len = serd_uri_string_length(base);
|
cannam@226
|
228 uint8_t* buf = (uint8_t*)malloc(uri_len + base_len + 1);
|
cannam@226
|
229 SerdNode node = { buf, 0, 0, 0, SERD_URI };
|
cannam@226
|
230 uint8_t* ptr = buf;
|
cannam@226
|
231 const size_t actual_len = serd_uri_serialise_relative(
|
cannam@226
|
232 uri, base, root, string_sink, &ptr);
|
cannam@226
|
233
|
cannam@226
|
234 buf[actual_len] = '\0';
|
cannam@226
|
235 node.n_bytes = actual_len;
|
cannam@226
|
236 node.n_chars = serd_strlen(buf, NULL, NULL);
|
cannam@226
|
237
|
cannam@226
|
238 if (out) {
|
cannam@226
|
239 serd_uri_parse(buf, out); // TODO: cleverly avoid double parse
|
cannam@226
|
240 }
|
cannam@226
|
241
|
cannam@226
|
242 return node;
|
cannam@226
|
243 }
|
cannam@226
|
244
|
cannam@226
|
245 static inline unsigned
|
cannam@226
|
246 serd_digits(double abs)
|
cannam@226
|
247 {
|
cannam@226
|
248 const double lg = ceil(log10(floor(abs) + 1.0));
|
cannam@226
|
249 return lg < 1.0 ? 1U : (unsigned)lg;
|
cannam@226
|
250 }
|
cannam@226
|
251
|
cannam@226
|
252 SERD_API
|
cannam@226
|
253 SerdNode
|
cannam@226
|
254 serd_node_new_decimal(double d, unsigned frac_digits)
|
cannam@226
|
255 {
|
cannam@226
|
256 if (isnan(d) || isinf(d)) {
|
cannam@226
|
257 return SERD_NODE_NULL;
|
cannam@226
|
258 }
|
cannam@226
|
259
|
cannam@226
|
260 const double abs_d = fabs(d);
|
cannam@226
|
261 const unsigned int_digits = serd_digits(abs_d);
|
cannam@226
|
262 char* buf = (char*)calloc(int_digits + frac_digits + 3, 1);
|
cannam@226
|
263 SerdNode node = { (const uint8_t*)buf, 0, 0, 0, SERD_LITERAL };
|
cannam@226
|
264 const double int_part = floor(abs_d);
|
cannam@226
|
265
|
cannam@226
|
266 // Point s to decimal point location
|
cannam@226
|
267 char* s = buf + int_digits;
|
cannam@226
|
268 if (d < 0.0) {
|
cannam@226
|
269 *buf = '-';
|
cannam@226
|
270 ++s;
|
cannam@226
|
271 }
|
cannam@226
|
272
|
cannam@226
|
273 // Write integer part (right to left)
|
cannam@226
|
274 char* t = s - 1;
|
cannam@226
|
275 uint64_t dec = (uint64_t)int_part;
|
cannam@226
|
276 do {
|
cannam@226
|
277 *t-- = '0' + (dec % 10);
|
cannam@226
|
278 } while ((dec /= 10) > 0);
|
cannam@226
|
279
|
cannam@226
|
280 *s++ = '.';
|
cannam@226
|
281
|
cannam@226
|
282 // Write fractional part (right to left)
|
cannam@226
|
283 double frac_part = fabs(d - int_part);
|
cannam@226
|
284 if (frac_part < DBL_EPSILON) {
|
cannam@226
|
285 *s++ = '0';
|
cannam@226
|
286 node.n_bytes = node.n_chars = (s - buf);
|
cannam@226
|
287 } else {
|
cannam@226
|
288 uint64_t frac = frac_part * pow(10.0, (int)frac_digits) + 0.5;
|
cannam@226
|
289 s += frac_digits - 1;
|
cannam@226
|
290 unsigned i = 0;
|
cannam@226
|
291
|
cannam@226
|
292 // Skip trailing zeros
|
cannam@226
|
293 for (; i < frac_digits - 1 && !(frac % 10); ++i, --s, frac /= 10) {}
|
cannam@226
|
294
|
cannam@226
|
295 node.n_bytes = node.n_chars = (s - buf) + 1;
|
cannam@226
|
296
|
cannam@226
|
297 // Write digits from last trailing zero to decimal point
|
cannam@226
|
298 for (; i < frac_digits; ++i) {
|
cannam@226
|
299 *s-- = '0' + (frac % 10);
|
cannam@226
|
300 frac /= 10;
|
cannam@226
|
301 }
|
cannam@226
|
302 }
|
cannam@226
|
303
|
cannam@226
|
304 return node;
|
cannam@226
|
305 }
|
cannam@226
|
306
|
cannam@226
|
307 SERD_API
|
cannam@226
|
308 SerdNode
|
cannam@226
|
309 serd_node_new_integer(int64_t i)
|
cannam@226
|
310 {
|
cannam@226
|
311 int64_t abs_i = (i < 0) ? -i : i;
|
cannam@226
|
312 const unsigned digits = serd_digits(abs_i);
|
cannam@226
|
313 char* buf = (char*)calloc(digits + 2, 1);
|
cannam@226
|
314 SerdNode node = { (const uint8_t*)buf, 0, 0, 0, SERD_LITERAL };
|
cannam@226
|
315
|
cannam@226
|
316 // Point s to the end
|
cannam@226
|
317 char* s = buf + digits - 1;
|
cannam@226
|
318 if (i < 0) {
|
cannam@226
|
319 *buf = '-';
|
cannam@226
|
320 ++s;
|
cannam@226
|
321 }
|
cannam@226
|
322
|
cannam@226
|
323 node.n_bytes = node.n_chars = (s - buf) + 1;
|
cannam@226
|
324
|
cannam@226
|
325 // Write integer part (right to left)
|
cannam@226
|
326 do {
|
cannam@226
|
327 *s-- = '0' + (abs_i % 10);
|
cannam@226
|
328 } while ((abs_i /= 10) > 0);
|
cannam@226
|
329
|
cannam@226
|
330 return node;
|
cannam@226
|
331 }
|
cannam@226
|
332
|
cannam@226
|
333 /**
|
cannam@226
|
334 Base64 encoding table.
|
cannam@226
|
335 @see <a href="http://tools.ietf.org/html/rfc3548#section-3">RFC3986 S3</a>.
|
cannam@226
|
336 */
|
cannam@226
|
337 static const uint8_t b64_map[] =
|
cannam@226
|
338 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
cannam@226
|
339
|
cannam@226
|
340 /**
|
cannam@226
|
341 Encode 3 raw bytes to 4 base64 characters.
|
cannam@226
|
342 */
|
cannam@226
|
343 static inline void
|
cannam@226
|
344 encode_chunk(uint8_t out[4], const uint8_t in[3], size_t n_in)
|
cannam@226
|
345 {
|
cannam@226
|
346 out[0] = b64_map[in[0] >> 2];
|
cannam@226
|
347 out[1] = b64_map[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)];
|
cannam@226
|
348 out[2] = ((n_in > 1)
|
cannam@226
|
349 ? (b64_map[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)])
|
cannam@226
|
350 : (uint8_t)'=');
|
cannam@226
|
351 out[3] = ((n_in > 2) ? b64_map[in[2] & 0x3F] : (uint8_t)'=');
|
cannam@226
|
352 }
|
cannam@226
|
353
|
cannam@226
|
354 SERD_API
|
cannam@226
|
355 SerdNode
|
cannam@226
|
356 serd_node_new_blob(const void* buf, size_t size, bool wrap_lines)
|
cannam@226
|
357 {
|
cannam@226
|
358 const size_t len = ((size + 2) / 3) * 4 + (wrap_lines ? (size / 57) : 0);
|
cannam@226
|
359 uint8_t* str = (uint8_t*)calloc(1, len + 2);
|
cannam@226
|
360 SerdNode node = { str, len, len, 0, SERD_LITERAL };
|
cannam@226
|
361 for (size_t i = 0, j = 0; i < size; i += 3, j += 4) {
|
cannam@226
|
362 uint8_t in[4] = { 0, 0, 0, 0 };
|
cannam@226
|
363 size_t n_in = MIN(3, size - i);
|
cannam@226
|
364 memcpy(in, (const uint8_t*)buf + i, n_in);
|
cannam@226
|
365
|
cannam@226
|
366 if (wrap_lines && i > 0 && (i % 57) == 0) {
|
cannam@226
|
367 str[j++] = '\n';
|
cannam@226
|
368 node.flags |= SERD_HAS_NEWLINE;
|
cannam@226
|
369 }
|
cannam@226
|
370
|
cannam@226
|
371 encode_chunk(str + j, in, n_in);
|
cannam@226
|
372 }
|
cannam@226
|
373 return node;
|
cannam@226
|
374 }
|
cannam@226
|
375
|
cannam@226
|
376 SERD_API
|
cannam@226
|
377 void
|
cannam@226
|
378 serd_node_free(SerdNode* node)
|
cannam@226
|
379 {
|
cannam@226
|
380 if (node && node->buf) {
|
cannam@226
|
381 free((uint8_t*)node->buf);
|
cannam@226
|
382 node->buf = NULL;
|
cannam@226
|
383 }
|
cannam@226
|
384 }
|