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