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