cannam@226: /* cannam@226: Copyright 2011-2016 David Robillard cannam@226: cannam@226: Permission to use, copy, modify, and/or distribute this software for any cannam@226: purpose with or without fee is hereby granted, provided that the above cannam@226: copyright notice and this permission notice appear in all copies. cannam@226: cannam@226: THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES cannam@226: WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF cannam@226: MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR cannam@226: ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES cannam@226: WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN cannam@226: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF cannam@226: OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. cannam@226: */ cannam@226: cannam@226: #include "serd_internal.h" cannam@226: cannam@226: #include cannam@226: cannam@226: SERD_API cannam@226: const uint8_t* cannam@226: serd_strerror(SerdStatus st) cannam@226: { cannam@226: switch (st) { cannam@226: case SERD_SUCCESS: return (const uint8_t*)"Success"; cannam@226: case SERD_FAILURE: return (const uint8_t*)"Non-fatal failure"; cannam@226: case SERD_ERR_UNKNOWN: return (const uint8_t*)"Unknown error"; cannam@226: case SERD_ERR_BAD_SYNTAX: return (const uint8_t*)"Invalid syntax"; cannam@226: case SERD_ERR_BAD_ARG: return (const uint8_t*)"Invalid argument"; cannam@226: case SERD_ERR_NOT_FOUND: return (const uint8_t*)"Not found"; cannam@226: case SERD_ERR_ID_CLASH: return (const uint8_t*)"Blank node ID clash"; cannam@226: case SERD_ERR_BAD_CURIE: return (const uint8_t*)"Invalid CURIE"; cannam@226: case SERD_ERR_INTERNAL: return (const uint8_t*)"Internal error"; cannam@226: } cannam@226: return (const uint8_t*)"Unknown error"; // never reached cannam@226: } cannam@226: cannam@226: SERD_API cannam@226: size_t cannam@226: serd_strlen(const uint8_t* str, size_t* n_bytes, SerdNodeFlags* flags) cannam@226: { cannam@226: size_t n_chars = 0; cannam@226: size_t i = 0; cannam@226: SerdNodeFlags f = 0; cannam@226: for (; str[i]; ++i) { cannam@226: if ((str[i] & 0xC0) != 0x80) { cannam@226: // Does not start with `10', start of a new character cannam@226: ++n_chars; cannam@226: switch (str[i]) { cannam@226: case '\r': case '\n': cannam@226: f |= SERD_HAS_NEWLINE; cannam@226: break; cannam@226: case '"': cannam@226: f |= SERD_HAS_QUOTE; cannam@226: } cannam@226: } cannam@226: } cannam@226: if (n_bytes) { cannam@226: *n_bytes = i; cannam@226: } cannam@226: if (flags) { cannam@226: *flags = f; cannam@226: } cannam@226: return n_chars; cannam@226: } cannam@226: cannam@226: static inline double cannam@226: read_sign(const char** sptr) cannam@226: { cannam@226: double sign = 1.0; cannam@226: switch (**sptr) { cannam@226: case '-': sign = -1.0; cannam@226: case '+': ++(*sptr); cannam@226: default: return sign; cannam@226: } cannam@226: } cannam@226: cannam@226: SERD_API cannam@226: double cannam@226: serd_strtod(const char* str, char** endptr) cannam@226: { cannam@226: double result = 0.0; cannam@226: cannam@226: // Point s at the first non-whitespace character cannam@226: const char* s = str; cannam@226: while (is_space(*s)) { ++s; } cannam@226: cannam@226: // Read leading sign if necessary cannam@226: const double sign = read_sign(&s); cannam@226: cannam@226: // Parse integer part cannam@226: for (; is_digit(*s); ++s) { cannam@226: result = (result * 10.0) + (*s - '0'); cannam@226: } cannam@226: cannam@226: // Parse fractional part cannam@226: if (*s == '.') { cannam@226: double denom = 10.0; cannam@226: for (++s; is_digit(*s); ++s) { cannam@226: result += (*s - '0') / denom; cannam@226: denom *= 10.0; cannam@226: } cannam@226: } cannam@226: cannam@226: // Parse exponent cannam@226: if (*s == 'e' || *s == 'E') { cannam@226: ++s; cannam@226: double expt = 0.0; cannam@226: double expt_sign = read_sign(&s); cannam@226: for (; is_digit(*s); ++s) { cannam@226: expt = (expt * 10.0) + (*s - '0'); cannam@226: } cannam@226: result *= pow(10, expt * expt_sign); cannam@226: } cannam@226: cannam@226: if (endptr) { cannam@226: *endptr = (char*)s; cannam@226: } cannam@226: cannam@226: return result * sign; cannam@226: } cannam@226: cannam@226: /** cannam@226: Base64 decoding table. cannam@226: This is indexed by encoded characters and returns the numeric value used cannam@226: for decoding, shifted up by 47 to be in the range of printable ASCII. cannam@226: A '$' is a placeholder for characters not in the base64 alphabet. cannam@226: */ cannam@226: static const char b64_unmap[] = cannam@226: "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$" cannam@226: "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$" cannam@226: "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" cannam@226: "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"; cannam@226: cannam@226: static inline uint8_t unmap(const uint8_t in) { return b64_unmap[in] - 47; } cannam@226: cannam@226: /** cannam@226: Decode 4 base64 characters to 3 raw bytes. cannam@226: */ cannam@226: static inline size_t cannam@226: decode_chunk(const uint8_t in[4], uint8_t out[3]) cannam@226: { cannam@226: out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4); cannam@226: out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2); cannam@226: out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3])); cannam@226: return 1 + (in[2] != '=') + ((in[2] != '=') && (in[3] != '=')); cannam@226: } cannam@226: cannam@226: SERD_API cannam@226: void* cannam@226: serd_base64_decode(const uint8_t* str, size_t len, size_t* size) cannam@226: { cannam@226: void* buf = malloc((len * 3) / 4 + 2); cannam@226: *size = 0; cannam@226: for (size_t i = 0, j = 0; i < len; j += 3) { cannam@226: uint8_t in[] = "===="; cannam@226: size_t n_in = 0; cannam@226: for (; i < len && n_in < 4; ++n_in) { cannam@226: for (; i < len && !is_base64(str[i]); ++i) {} // Skip junk cannam@226: in[n_in] = str[i++]; cannam@226: } cannam@226: if (n_in > 1) { cannam@226: *size += decode_chunk(in, (uint8_t*)buf + j); cannam@226: } cannam@226: } cannam@226: return buf; cannam@226: }