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 <math.h>
|
cannam@226
|
20
|
cannam@226
|
21 SERD_API
|
cannam@226
|
22 const uint8_t*
|
cannam@226
|
23 serd_strerror(SerdStatus st)
|
cannam@226
|
24 {
|
cannam@226
|
25 switch (st) {
|
cannam@226
|
26 case SERD_SUCCESS: return (const uint8_t*)"Success";
|
cannam@226
|
27 case SERD_FAILURE: return (const uint8_t*)"Non-fatal failure";
|
cannam@226
|
28 case SERD_ERR_UNKNOWN: return (const uint8_t*)"Unknown error";
|
cannam@226
|
29 case SERD_ERR_BAD_SYNTAX: return (const uint8_t*)"Invalid syntax";
|
cannam@226
|
30 case SERD_ERR_BAD_ARG: return (const uint8_t*)"Invalid argument";
|
cannam@226
|
31 case SERD_ERR_NOT_FOUND: return (const uint8_t*)"Not found";
|
cannam@226
|
32 case SERD_ERR_ID_CLASH: return (const uint8_t*)"Blank node ID clash";
|
cannam@226
|
33 case SERD_ERR_BAD_CURIE: return (const uint8_t*)"Invalid CURIE";
|
cannam@226
|
34 case SERD_ERR_INTERNAL: return (const uint8_t*)"Internal error";
|
cannam@226
|
35 }
|
cannam@226
|
36 return (const uint8_t*)"Unknown error"; // never reached
|
cannam@226
|
37 }
|
cannam@226
|
38
|
cannam@226
|
39 SERD_API
|
cannam@226
|
40 size_t
|
cannam@226
|
41 serd_strlen(const uint8_t* str, size_t* n_bytes, SerdNodeFlags* flags)
|
cannam@226
|
42 {
|
cannam@226
|
43 size_t n_chars = 0;
|
cannam@226
|
44 size_t i = 0;
|
cannam@226
|
45 SerdNodeFlags f = 0;
|
cannam@226
|
46 for (; str[i]; ++i) {
|
cannam@226
|
47 if ((str[i] & 0xC0) != 0x80) {
|
cannam@226
|
48 // Does not start with `10', start of a new character
|
cannam@226
|
49 ++n_chars;
|
cannam@226
|
50 switch (str[i]) {
|
cannam@226
|
51 case '\r': case '\n':
|
cannam@226
|
52 f |= SERD_HAS_NEWLINE;
|
cannam@226
|
53 break;
|
cannam@226
|
54 case '"':
|
cannam@226
|
55 f |= SERD_HAS_QUOTE;
|
cannam@226
|
56 }
|
cannam@226
|
57 }
|
cannam@226
|
58 }
|
cannam@226
|
59 if (n_bytes) {
|
cannam@226
|
60 *n_bytes = i;
|
cannam@226
|
61 }
|
cannam@226
|
62 if (flags) {
|
cannam@226
|
63 *flags = f;
|
cannam@226
|
64 }
|
cannam@226
|
65 return n_chars;
|
cannam@226
|
66 }
|
cannam@226
|
67
|
cannam@226
|
68 static inline double
|
cannam@226
|
69 read_sign(const char** sptr)
|
cannam@226
|
70 {
|
cannam@226
|
71 double sign = 1.0;
|
cannam@226
|
72 switch (**sptr) {
|
cannam@226
|
73 case '-': sign = -1.0;
|
cannam@226
|
74 case '+': ++(*sptr);
|
cannam@226
|
75 default: return sign;
|
cannam@226
|
76 }
|
cannam@226
|
77 }
|
cannam@226
|
78
|
cannam@226
|
79 SERD_API
|
cannam@226
|
80 double
|
cannam@226
|
81 serd_strtod(const char* str, char** endptr)
|
cannam@226
|
82 {
|
cannam@226
|
83 double result = 0.0;
|
cannam@226
|
84
|
cannam@226
|
85 // Point s at the first non-whitespace character
|
cannam@226
|
86 const char* s = str;
|
cannam@226
|
87 while (is_space(*s)) { ++s; }
|
cannam@226
|
88
|
cannam@226
|
89 // Read leading sign if necessary
|
cannam@226
|
90 const double sign = read_sign(&s);
|
cannam@226
|
91
|
cannam@226
|
92 // Parse integer part
|
cannam@226
|
93 for (; is_digit(*s); ++s) {
|
cannam@226
|
94 result = (result * 10.0) + (*s - '0');
|
cannam@226
|
95 }
|
cannam@226
|
96
|
cannam@226
|
97 // Parse fractional part
|
cannam@226
|
98 if (*s == '.') {
|
cannam@226
|
99 double denom = 10.0;
|
cannam@226
|
100 for (++s; is_digit(*s); ++s) {
|
cannam@226
|
101 result += (*s - '0') / denom;
|
cannam@226
|
102 denom *= 10.0;
|
cannam@226
|
103 }
|
cannam@226
|
104 }
|
cannam@226
|
105
|
cannam@226
|
106 // Parse exponent
|
cannam@226
|
107 if (*s == 'e' || *s == 'E') {
|
cannam@226
|
108 ++s;
|
cannam@226
|
109 double expt = 0.0;
|
cannam@226
|
110 double expt_sign = read_sign(&s);
|
cannam@226
|
111 for (; is_digit(*s); ++s) {
|
cannam@226
|
112 expt = (expt * 10.0) + (*s - '0');
|
cannam@226
|
113 }
|
cannam@226
|
114 result *= pow(10, expt * expt_sign);
|
cannam@226
|
115 }
|
cannam@226
|
116
|
cannam@226
|
117 if (endptr) {
|
cannam@226
|
118 *endptr = (char*)s;
|
cannam@226
|
119 }
|
cannam@226
|
120
|
cannam@226
|
121 return result * sign;
|
cannam@226
|
122 }
|
cannam@226
|
123
|
cannam@226
|
124 /**
|
cannam@226
|
125 Base64 decoding table.
|
cannam@226
|
126 This is indexed by encoded characters and returns the numeric value used
|
cannam@226
|
127 for decoding, shifted up by 47 to be in the range of printable ASCII.
|
cannam@226
|
128 A '$' is a placeholder for characters not in the base64 alphabet.
|
cannam@226
|
129 */
|
cannam@226
|
130 static const char b64_unmap[] =
|
cannam@226
|
131 "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$"
|
cannam@226
|
132 "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$"
|
cannam@226
|
133 "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
|
cannam@226
|
134 "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$";
|
cannam@226
|
135
|
cannam@226
|
136 static inline uint8_t unmap(const uint8_t in) { return b64_unmap[in] - 47; }
|
cannam@226
|
137
|
cannam@226
|
138 /**
|
cannam@226
|
139 Decode 4 base64 characters to 3 raw bytes.
|
cannam@226
|
140 */
|
cannam@226
|
141 static inline size_t
|
cannam@226
|
142 decode_chunk(const uint8_t in[4], uint8_t out[3])
|
cannam@226
|
143 {
|
cannam@226
|
144 out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4);
|
cannam@226
|
145 out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2);
|
cannam@226
|
146 out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3]));
|
cannam@226
|
147 return 1 + (in[2] != '=') + ((in[2] != '=') && (in[3] != '='));
|
cannam@226
|
148 }
|
cannam@226
|
149
|
cannam@226
|
150 SERD_API
|
cannam@226
|
151 void*
|
cannam@226
|
152 serd_base64_decode(const uint8_t* str, size_t len, size_t* size)
|
cannam@226
|
153 {
|
cannam@226
|
154 void* buf = malloc((len * 3) / 4 + 2);
|
cannam@226
|
155 *size = 0;
|
cannam@226
|
156 for (size_t i = 0, j = 0; i < len; j += 3) {
|
cannam@226
|
157 uint8_t in[] = "====";
|
cannam@226
|
158 size_t n_in = 0;
|
cannam@226
|
159 for (; i < len && n_in < 4; ++n_in) {
|
cannam@226
|
160 for (; i < len && !is_base64(str[i]); ++i) {} // Skip junk
|
cannam@226
|
161 in[n_in] = str[i++];
|
cannam@226
|
162 }
|
cannam@226
|
163 if (n_in > 1) {
|
cannam@226
|
164 *size += decode_chunk(in, (uint8_t*)buf + j);
|
cannam@226
|
165 }
|
cannam@226
|
166 }
|
cannam@226
|
167 return buf;
|
cannam@226
|
168 }
|