comparison ext/serd/src/node.c @ 226:c5cdc9e6a4bf

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