annotate ext/serd/src/serd_internal.h @ 296:50a0b4fea7f1 tip master

Merge pull request #8 from michel-slm/gcc15 Include headers needed to compile with GCC 15's -std=gnu23 default
author Chris Cannam <cannam@all-day-breakfast.com>
date Mon, 27 Jan 2025 08:53:58 +0000
parents c5cdc9e6a4bf
children
rev   line source
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