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 #ifndef SERD_INTERNAL_H
|
cannam@226
|
18 #define SERD_INTERNAL_H
|
cannam@226
|
19
|
cannam@226
|
20 #define _POSIX_C_SOURCE 200809L /* for posix_memalign and posix_fadvise */
|
cannam@226
|
21
|
cannam@226
|
22 #include <assert.h>
|
cannam@226
|
23 #include <errno.h>
|
cannam@226
|
24 #include <stdio.h>
|
cannam@226
|
25 #include <stdlib.h>
|
cannam@226
|
26 #include <string.h>
|
cannam@226
|
27
|
cannam@226
|
28 #include "serd/serd.h"
|
cannam@226
|
29 #include "serd_config.h"
|
cannam@226
|
30
|
cannam@226
|
31 #if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO)
|
cannam@226
|
32 # include <fcntl.h>
|
cannam@226
|
33 #endif
|
cannam@226
|
34
|
cannam@226
|
35 #define SERD_PAGE_SIZE 4096
|
cannam@226
|
36
|
cannam@226
|
37 #ifndef MIN
|
cannam@226
|
38 # define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
cannam@226
|
39 #endif
|
cannam@226
|
40
|
cannam@226
|
41 /* File and Buffer Utilities */
|
cannam@226
|
42
|
cannam@226
|
43 static inline FILE*
|
cannam@226
|
44 serd_fopen(const char* path, const char* mode)
|
cannam@226
|
45 {
|
cannam@226
|
46 FILE* fd = fopen((const char*)path, mode);
|
cannam@226
|
47 if (!fd) {
|
cannam@226
|
48 fprintf(stderr, "Error opening file %s (%s)\n", path, strerror(errno));
|
cannam@226
|
49 return NULL;
|
cannam@226
|
50 }
|
cannam@226
|
51 #if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO)
|
cannam@226
|
52 posix_fadvise(fileno(fd), 0, 0, POSIX_FADV_SEQUENTIAL);
|
cannam@226
|
53 #endif
|
cannam@226
|
54 return fd;
|
cannam@226
|
55 }
|
cannam@226
|
56
|
cannam@226
|
57 static inline void*
|
cannam@226
|
58 serd_bufalloc(size_t size)
|
cannam@226
|
59 {
|
cannam@226
|
60 #ifdef HAVE_POSIX_MEMALIGN
|
cannam@226
|
61 void* ptr;
|
cannam@226
|
62 const int ret = posix_memalign(&ptr, SERD_PAGE_SIZE, size);
|
cannam@226
|
63 return ret ? NULL : ptr;
|
cannam@226
|
64 #else
|
cannam@226
|
65 return malloc(size);
|
cannam@226
|
66 #endif
|
cannam@226
|
67 }
|
cannam@226
|
68
|
cannam@226
|
69 /* Byte source */
|
cannam@226
|
70
|
cannam@226
|
71 typedef struct {
|
cannam@226
|
72 SerdSource read_func; ///< Read function (e.g. fread)
|
cannam@226
|
73 SerdStreamErrorFunc error_func; ///< Error function (e.g. ferror)
|
cannam@226
|
74 void* stream; ///< Stream (e.g. FILE)
|
cannam@226
|
75 size_t page_size; ///< Number of bytes to read at a time
|
cannam@226
|
76 uint8_t* file_buf; ///< Buffer iff reading pages from a file
|
cannam@226
|
77 const uint8_t* read_buf; ///< Pointer to file_buf or read_byte
|
cannam@226
|
78 size_t read_head; ///< Offset into read_buf
|
cannam@226
|
79 uint8_t read_byte; ///< 1-byte 'buffer' used when not paging
|
cannam@226
|
80 bool from_stream; ///< True iff reading from `stream`
|
cannam@226
|
81 bool prepared; ///< True iff prepared for reading
|
cannam@226
|
82 } SerdByteSource;
|
cannam@226
|
83
|
cannam@226
|
84 SerdStatus
|
cannam@226
|
85 serd_byte_source_open_file(SerdByteSource* source,
|
cannam@226
|
86 FILE* file,
|
cannam@226
|
87 bool bulk);
|
cannam@226
|
88
|
cannam@226
|
89 SerdStatus
|
cannam@226
|
90 serd_byte_source_open_string(SerdByteSource* source, const uint8_t* utf8);
|
cannam@226
|
91
|
cannam@226
|
92 SerdStatus
|
cannam@226
|
93 serd_byte_source_open_source(SerdByteSource* source,
|
cannam@226
|
94 SerdSource read_func,
|
cannam@226
|
95 SerdStreamErrorFunc error_func,
|
cannam@226
|
96 void* stream,
|
cannam@226
|
97 size_t page_size);
|
cannam@226
|
98
|
cannam@226
|
99 SerdStatus
|
cannam@226
|
100 serd_byte_source_close(SerdByteSource* source);
|
cannam@226
|
101
|
cannam@226
|
102 SerdStatus
|
cannam@226
|
103 serd_byte_source_prepare(SerdByteSource* source);
|
cannam@226
|
104
|
cannam@226
|
105 static inline uint8_t
|
cannam@226
|
106 serd_byte_source_peek(SerdByteSource* source)
|
cannam@226
|
107 {
|
cannam@226
|
108 assert(source->prepared);
|
cannam@226
|
109 return source->read_buf[source->read_head];
|
cannam@226
|
110 }
|
cannam@226
|
111
|
cannam@226
|
112 SerdStatus
|
cannam@226
|
113 serd_byte_source_advance(SerdByteSource* source);
|
cannam@226
|
114
|
cannam@226
|
115 /* Stack */
|
cannam@226
|
116
|
cannam@226
|
117 /** A dynamic stack in memory. */
|
cannam@226
|
118 typedef struct {
|
cannam@226
|
119 uint8_t* buf; ///< Stack memory
|
cannam@226
|
120 size_t buf_size; ///< Allocated size of buf (>= size)
|
cannam@226
|
121 size_t size; ///< Conceptual size of stack in buf
|
cannam@226
|
122 } SerdStack;
|
cannam@226
|
123
|
cannam@226
|
124 /** An offset to start the stack at. Note 0 is reserved for NULL. */
|
cannam@226
|
125 #define SERD_STACK_BOTTOM sizeof(void*)
|
cannam@226
|
126
|
cannam@226
|
127 static inline SerdStack
|
cannam@226
|
128 serd_stack_new(size_t size)
|
cannam@226
|
129 {
|
cannam@226
|
130 SerdStack stack;
|
cannam@226
|
131 stack.buf = (uint8_t*)malloc(size);
|
cannam@226
|
132 stack.buf_size = size;
|
cannam@226
|
133 stack.size = SERD_STACK_BOTTOM;
|
cannam@226
|
134 return stack;
|
cannam@226
|
135 }
|
cannam@226
|
136
|
cannam@226
|
137 static inline bool
|
cannam@226
|
138 serd_stack_is_empty(SerdStack* stack)
|
cannam@226
|
139 {
|
cannam@226
|
140 return stack->size <= SERD_STACK_BOTTOM;
|
cannam@226
|
141 }
|
cannam@226
|
142
|
cannam@226
|
143 static inline void
|
cannam@226
|
144 serd_stack_free(SerdStack* stack)
|
cannam@226
|
145 {
|
cannam@226
|
146 free(stack->buf);
|
cannam@226
|
147 stack->buf = NULL;
|
cannam@226
|
148 stack->buf_size = 0;
|
cannam@226
|
149 stack->size = 0;
|
cannam@226
|
150 }
|
cannam@226
|
151
|
cannam@226
|
152 static inline uint8_t*
|
cannam@226
|
153 serd_stack_push(SerdStack* stack, size_t n_bytes)
|
cannam@226
|
154 {
|
cannam@226
|
155 const size_t new_size = stack->size + n_bytes;
|
cannam@226
|
156 if (stack->buf_size < new_size) {
|
cannam@226
|
157 stack->buf_size *= 2;
|
cannam@226
|
158 stack->buf = (uint8_t*)realloc(stack->buf, stack->buf_size);
|
cannam@226
|
159 }
|
cannam@226
|
160 uint8_t* const ret = (stack->buf + stack->size);
|
cannam@226
|
161 stack->size = new_size;
|
cannam@226
|
162 return ret;
|
cannam@226
|
163 }
|
cannam@226
|
164
|
cannam@226
|
165 static inline void
|
cannam@226
|
166 serd_stack_pop(SerdStack* stack, size_t n_bytes)
|
cannam@226
|
167 {
|
cannam@226
|
168 assert(stack->size >= n_bytes);
|
cannam@226
|
169 stack->size -= n_bytes;
|
cannam@226
|
170 }
|
cannam@226
|
171
|
cannam@226
|
172 static inline void*
|
cannam@226
|
173 serd_stack_push_aligned(SerdStack* stack, size_t n_bytes, size_t align)
|
cannam@226
|
174 {
|
cannam@226
|
175 // Push one byte to ensure space for a pad count
|
cannam@226
|
176 serd_stack_push(stack, 1);
|
cannam@226
|
177
|
cannam@226
|
178 // Push padding if necessary
|
cannam@226
|
179 const uint8_t pad = align - stack->size % align;
|
cannam@226
|
180 if (pad > 0) {
|
cannam@226
|
181 serd_stack_push(stack, pad);
|
cannam@226
|
182 }
|
cannam@226
|
183
|
cannam@226
|
184 // Set top of stack to pad count so we can properly pop later
|
cannam@226
|
185 stack->buf[stack->size - 1] = pad;
|
cannam@226
|
186
|
cannam@226
|
187 // Push requested space at aligned location
|
cannam@226
|
188 return serd_stack_push(stack, n_bytes);
|
cannam@226
|
189 }
|
cannam@226
|
190
|
cannam@226
|
191 static inline void
|
cannam@226
|
192 serd_stack_pop_aligned(SerdStack* stack, size_t n_bytes)
|
cannam@226
|
193 {
|
cannam@226
|
194 // Pop requested space down to aligned location
|
cannam@226
|
195 serd_stack_pop(stack, n_bytes);
|
cannam@226
|
196
|
cannam@226
|
197 // Get amount of padding from top of stack
|
cannam@226
|
198 const uint8_t pad = stack->buf[stack->size - 1];
|
cannam@226
|
199
|
cannam@226
|
200 // Pop padding and pad count
|
cannam@226
|
201 serd_stack_pop(stack, pad + 1);
|
cannam@226
|
202 }
|
cannam@226
|
203
|
cannam@226
|
204 /* Byte Sink */
|
cannam@226
|
205
|
cannam@226
|
206 typedef struct SerdByteSinkImpl {
|
cannam@226
|
207 SerdSink sink;
|
cannam@226
|
208 void* stream;
|
cannam@226
|
209 uint8_t* buf;
|
cannam@226
|
210 size_t size;
|
cannam@226
|
211 size_t block_size;
|
cannam@226
|
212 } SerdByteSink;
|
cannam@226
|
213
|
cannam@226
|
214 static inline SerdByteSink
|
cannam@226
|
215 serd_byte_sink_new(SerdSink sink, void* stream, size_t block_size)
|
cannam@226
|
216 {
|
cannam@226
|
217 SerdByteSink bsink;
|
cannam@226
|
218 bsink.sink = sink;
|
cannam@226
|
219 bsink.stream = stream;
|
cannam@226
|
220 bsink.size = 0;
|
cannam@226
|
221 bsink.block_size = block_size;
|
cannam@226
|
222 bsink.buf = ((block_size > 1)
|
cannam@226
|
223 ? (uint8_t*)serd_bufalloc(block_size)
|
cannam@226
|
224 : NULL);
|
cannam@226
|
225 return bsink;
|
cannam@226
|
226 }
|
cannam@226
|
227
|
cannam@226
|
228 static inline void
|
cannam@226
|
229 serd_byte_sink_flush(SerdByteSink* bsink)
|
cannam@226
|
230 {
|
cannam@226
|
231 if (bsink->block_size > 1 && bsink->size > 0) {
|
cannam@226
|
232 bsink->sink(bsink->buf, bsink->size, bsink->stream);
|
cannam@226
|
233 bsink->size = 0;
|
cannam@226
|
234 }
|
cannam@226
|
235 }
|
cannam@226
|
236
|
cannam@226
|
237 static inline void
|
cannam@226
|
238 serd_byte_sink_free(SerdByteSink* bsink)
|
cannam@226
|
239 {
|
cannam@226
|
240 serd_byte_sink_flush(bsink);
|
cannam@226
|
241 free(bsink->buf);
|
cannam@226
|
242 bsink->buf = NULL;
|
cannam@226
|
243 }
|
cannam@226
|
244
|
cannam@226
|
245 static inline size_t
|
cannam@226
|
246 serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* bsink)
|
cannam@226
|
247 {
|
cannam@226
|
248 if (len == 0) {
|
cannam@226
|
249 return 0;
|
cannam@226
|
250 } else if (bsink->block_size == 1) {
|
cannam@226
|
251 return bsink->sink(buf, len, bsink->stream);
|
cannam@226
|
252 }
|
cannam@226
|
253
|
cannam@226
|
254 const size_t orig_len = len;
|
cannam@226
|
255 while (len) {
|
cannam@226
|
256 const size_t space = bsink->block_size - bsink->size;
|
cannam@226
|
257 const size_t n = MIN(space, len);
|
cannam@226
|
258
|
cannam@226
|
259 // Write as much as possible into the remaining buffer space
|
cannam@226
|
260 memcpy(bsink->buf + bsink->size, buf, n);
|
cannam@226
|
261 bsink->size += n;
|
cannam@226
|
262 buf = (const uint8_t*)buf + n;
|
cannam@226
|
263 len -= n;
|
cannam@226
|
264
|
cannam@226
|
265 // Flush page if buffer is full
|
cannam@226
|
266 if (bsink->size == bsink->block_size) {
|
cannam@226
|
267 bsink->sink(bsink->buf, bsink->block_size, bsink->stream);
|
cannam@226
|
268 bsink->size = 0;
|
cannam@226
|
269 }
|
cannam@226
|
270 }
|
cannam@226
|
271 return orig_len;
|
cannam@226
|
272 }
|
cannam@226
|
273
|
cannam@226
|
274 /* Character utilities */
|
cannam@226
|
275
|
cannam@226
|
276 /** Return true if `c` lies within [`min`...`max`] (inclusive) */
|
cannam@226
|
277 static inline bool
|
cannam@226
|
278 in_range(const uint8_t c, const uint8_t min, const uint8_t max)
|
cannam@226
|
279 {
|
cannam@226
|
280 return (c >= min && c <= max);
|
cannam@226
|
281 }
|
cannam@226
|
282
|
cannam@226
|
283 /** RFC2234: ALPHA := %x41-5A / %x61-7A ; A-Z / a-z */
|
cannam@226
|
284 static inline bool
|
cannam@226
|
285 is_alpha(const uint8_t c)
|
cannam@226
|
286 {
|
cannam@226
|
287 return in_range(c, 'A', 'Z') || in_range(c, 'a', 'z');
|
cannam@226
|
288 }
|
cannam@226
|
289
|
cannam@226
|
290 /** RFC2234: DIGIT ::= %x30-39 ; 0-9 */
|
cannam@226
|
291 static inline bool
|
cannam@226
|
292 is_digit(const uint8_t c)
|
cannam@226
|
293 {
|
cannam@226
|
294 return in_range(c, '0', '9');
|
cannam@226
|
295 }
|
cannam@226
|
296
|
cannam@226
|
297 static inline bool
|
cannam@226
|
298 is_space(const char c)
|
cannam@226
|
299 {
|
cannam@226
|
300 switch (c) {
|
cannam@226
|
301 case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
|
cannam@226
|
302 return true;
|
cannam@226
|
303 default:
|
cannam@226
|
304 return false;
|
cannam@226
|
305 }
|
cannam@226
|
306 }
|
cannam@226
|
307
|
cannam@226
|
308 static inline bool
|
cannam@226
|
309 is_base64(const uint8_t c)
|
cannam@226
|
310 {
|
cannam@226
|
311 return is_alpha(c) || is_digit(c) || c == '+' || c == '/' || c == '=';
|
cannam@226
|
312 }
|
cannam@226
|
313
|
cannam@226
|
314 static inline bool
|
cannam@226
|
315 is_windows_path(const uint8_t* path)
|
cannam@226
|
316 {
|
cannam@226
|
317 return is_alpha(path[0]) && (path[1] == ':' || path[1] == '|')
|
cannam@226
|
318 && (path[2] == '/' || path[2] == '\\');
|
cannam@226
|
319 }
|
cannam@226
|
320
|
cannam@226
|
321 /* URI utilities */
|
cannam@226
|
322
|
cannam@226
|
323 static inline bool
|
cannam@226
|
324 chunk_equals(const SerdChunk* a, const SerdChunk* b)
|
cannam@226
|
325 {
|
cannam@226
|
326 return a->len == b->len
|
cannam@226
|
327 && !strncmp((const char*)a->buf, (const char*)b->buf, a->len);
|
cannam@226
|
328 }
|
cannam@226
|
329
|
cannam@226
|
330 static inline size_t
|
cannam@226
|
331 uri_path_len(const SerdURI* uri)
|
cannam@226
|
332 {
|
cannam@226
|
333 return uri->path_base.len + uri->path.len;
|
cannam@226
|
334 }
|
cannam@226
|
335
|
cannam@226
|
336 static inline uint8_t
|
cannam@226
|
337 uri_path_at(const SerdURI* uri, size_t i)
|
cannam@226
|
338 {
|
cannam@226
|
339 if (i < uri->path_base.len) {
|
cannam@226
|
340 return uri->path_base.buf[i];
|
cannam@226
|
341 } else {
|
cannam@226
|
342 return uri->path.buf[i - uri->path_base.len];
|
cannam@226
|
343 }
|
cannam@226
|
344 }
|
cannam@226
|
345
|
cannam@226
|
346 /** Return true iff `uri` is within the base of `root` */
|
cannam@226
|
347 static inline bool
|
cannam@226
|
348 uri_is_under(const SerdURI* uri, const SerdURI* root)
|
cannam@226
|
349 {
|
cannam@226
|
350 if (!root || !root->scheme.len ||
|
cannam@226
|
351 !chunk_equals(&root->scheme, &uri->scheme) ||
|
cannam@226
|
352 !chunk_equals(&root->authority, &uri->authority)) {
|
cannam@226
|
353 return false;
|
cannam@226
|
354 }
|
cannam@226
|
355
|
cannam@226
|
356 bool differ = false;
|
cannam@226
|
357 const size_t path_len = uri_path_len(uri);
|
cannam@226
|
358 const size_t root_len = uri_path_len(root);
|
cannam@226
|
359 for (size_t i = 0; i < path_len && i < root_len; ++i) {
|
cannam@226
|
360 if (uri_path_at(uri, i) != uri_path_at(root, i)) {
|
cannam@226
|
361 differ = true;
|
cannam@226
|
362 }
|
cannam@226
|
363 if (differ && uri_path_at(root, i) == '/') {
|
cannam@226
|
364 return false;
|
cannam@226
|
365 }
|
cannam@226
|
366 }
|
cannam@226
|
367
|
cannam@226
|
368 return true;
|
cannam@226
|
369 }
|
cannam@226
|
370
|
cannam@226
|
371 /* Error reporting */
|
cannam@226
|
372
|
cannam@226
|
373 static inline void
|
cannam@226
|
374 serd_error(SerdErrorSink error_sink, void* handle, const SerdError* e)
|
cannam@226
|
375 {
|
cannam@226
|
376 if (error_sink) {
|
cannam@226
|
377 error_sink(handle, e);
|
cannam@226
|
378 } else {
|
cannam@226
|
379 fprintf(stderr, "error: %s:%u:%u: ", e->filename, e->line, e->col);
|
cannam@226
|
380 vfprintf(stderr, e->fmt, *e->args);
|
cannam@226
|
381 }
|
cannam@226
|
382 }
|
cannam@226
|
383
|
cannam@226
|
384 #endif // SERD_INTERNAL_H
|