Mercurial > hg > piper-cpp
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 |