annotate 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
rev   line source
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 }