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