cannam@226: /* cannam@226: Copyright 2011-2016 David Robillard cannam@226: cannam@226: Permission to use, copy, modify, and/or distribute this software for any cannam@226: purpose with or without fee is hereby granted, provided that the above cannam@226: copyright notice and this permission notice appear in all copies. cannam@226: cannam@226: THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES cannam@226: WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF cannam@226: MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR cannam@226: ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES cannam@226: WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN cannam@226: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF cannam@226: OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. cannam@226: */ cannam@226: cannam@226: #ifndef SERD_INTERNAL_H cannam@226: #define SERD_INTERNAL_H cannam@226: cannam@226: #define _POSIX_C_SOURCE 200809L /* for posix_memalign and posix_fadvise */ cannam@226: cannam@226: #include cannam@226: #include cannam@226: #include cannam@226: #include cannam@226: #include cannam@226: cannam@226: #include "serd/serd.h" cannam@226: #include "serd_config.h" cannam@226: cannam@226: #if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO) cannam@226: # include cannam@226: #endif cannam@226: cannam@226: #define SERD_PAGE_SIZE 4096 cannam@226: cannam@226: #ifndef MIN cannam@226: # define MIN(a, b) (((a) < (b)) ? (a) : (b)) cannam@226: #endif cannam@226: cannam@226: /* File and Buffer Utilities */ cannam@226: cannam@226: static inline FILE* cannam@226: serd_fopen(const char* path, const char* mode) cannam@226: { cannam@226: FILE* fd = fopen((const char*)path, mode); cannam@226: if (!fd) { cannam@226: fprintf(stderr, "Error opening file %s (%s)\n", path, strerror(errno)); cannam@226: return NULL; cannam@226: } cannam@226: #if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO) cannam@226: posix_fadvise(fileno(fd), 0, 0, POSIX_FADV_SEQUENTIAL); cannam@226: #endif cannam@226: return fd; cannam@226: } cannam@226: cannam@226: static inline void* cannam@226: serd_bufalloc(size_t size) cannam@226: { cannam@226: #ifdef HAVE_POSIX_MEMALIGN cannam@226: void* ptr; cannam@226: const int ret = posix_memalign(&ptr, SERD_PAGE_SIZE, size); cannam@226: return ret ? NULL : ptr; cannam@226: #else cannam@226: return malloc(size); cannam@226: #endif cannam@226: } cannam@226: cannam@226: /* Byte source */ cannam@226: cannam@226: typedef struct { cannam@226: SerdSource read_func; ///< Read function (e.g. fread) cannam@226: SerdStreamErrorFunc error_func; ///< Error function (e.g. ferror) cannam@226: void* stream; ///< Stream (e.g. FILE) cannam@226: size_t page_size; ///< Number of bytes to read at a time cannam@226: uint8_t* file_buf; ///< Buffer iff reading pages from a file cannam@226: const uint8_t* read_buf; ///< Pointer to file_buf or read_byte cannam@226: size_t read_head; ///< Offset into read_buf cannam@226: uint8_t read_byte; ///< 1-byte 'buffer' used when not paging cannam@226: bool from_stream; ///< True iff reading from `stream` cannam@226: bool prepared; ///< True iff prepared for reading cannam@226: } SerdByteSource; cannam@226: cannam@226: SerdStatus cannam@226: serd_byte_source_open_file(SerdByteSource* source, cannam@226: FILE* file, cannam@226: bool bulk); cannam@226: cannam@226: SerdStatus cannam@226: serd_byte_source_open_string(SerdByteSource* source, const uint8_t* utf8); cannam@226: cannam@226: SerdStatus cannam@226: serd_byte_source_open_source(SerdByteSource* source, cannam@226: SerdSource read_func, cannam@226: SerdStreamErrorFunc error_func, cannam@226: void* stream, cannam@226: size_t page_size); cannam@226: cannam@226: SerdStatus cannam@226: serd_byte_source_close(SerdByteSource* source); cannam@226: cannam@226: SerdStatus cannam@226: serd_byte_source_prepare(SerdByteSource* source); cannam@226: cannam@226: static inline uint8_t cannam@226: serd_byte_source_peek(SerdByteSource* source) cannam@226: { cannam@226: assert(source->prepared); cannam@226: return source->read_buf[source->read_head]; cannam@226: } cannam@226: cannam@226: SerdStatus cannam@226: serd_byte_source_advance(SerdByteSource* source); cannam@226: cannam@226: /* Stack */ cannam@226: cannam@226: /** A dynamic stack in memory. */ cannam@226: typedef struct { cannam@226: uint8_t* buf; ///< Stack memory cannam@226: size_t buf_size; ///< Allocated size of buf (>= size) cannam@226: size_t size; ///< Conceptual size of stack in buf cannam@226: } SerdStack; cannam@226: cannam@226: /** An offset to start the stack at. Note 0 is reserved for NULL. */ cannam@226: #define SERD_STACK_BOTTOM sizeof(void*) cannam@226: cannam@226: static inline SerdStack cannam@226: serd_stack_new(size_t size) cannam@226: { cannam@226: SerdStack stack; cannam@226: stack.buf = (uint8_t*)malloc(size); cannam@226: stack.buf_size = size; cannam@226: stack.size = SERD_STACK_BOTTOM; cannam@226: return stack; cannam@226: } cannam@226: cannam@226: static inline bool cannam@226: serd_stack_is_empty(SerdStack* stack) cannam@226: { cannam@226: return stack->size <= SERD_STACK_BOTTOM; cannam@226: } cannam@226: cannam@226: static inline void cannam@226: serd_stack_free(SerdStack* stack) cannam@226: { cannam@226: free(stack->buf); cannam@226: stack->buf = NULL; cannam@226: stack->buf_size = 0; cannam@226: stack->size = 0; cannam@226: } cannam@226: cannam@226: static inline uint8_t* cannam@226: serd_stack_push(SerdStack* stack, size_t n_bytes) cannam@226: { cannam@226: const size_t new_size = stack->size + n_bytes; cannam@226: if (stack->buf_size < new_size) { cannam@226: stack->buf_size *= 2; cannam@226: stack->buf = (uint8_t*)realloc(stack->buf, stack->buf_size); cannam@226: } cannam@226: uint8_t* const ret = (stack->buf + stack->size); cannam@226: stack->size = new_size; cannam@226: return ret; cannam@226: } cannam@226: cannam@226: static inline void cannam@226: serd_stack_pop(SerdStack* stack, size_t n_bytes) cannam@226: { cannam@226: assert(stack->size >= n_bytes); cannam@226: stack->size -= n_bytes; cannam@226: } cannam@226: cannam@226: static inline void* cannam@226: serd_stack_push_aligned(SerdStack* stack, size_t n_bytes, size_t align) cannam@226: { cannam@226: // Push one byte to ensure space for a pad count cannam@226: serd_stack_push(stack, 1); cannam@226: cannam@226: // Push padding if necessary cannam@226: const uint8_t pad = align - stack->size % align; cannam@226: if (pad > 0) { cannam@226: serd_stack_push(stack, pad); cannam@226: } cannam@226: cannam@226: // Set top of stack to pad count so we can properly pop later cannam@226: stack->buf[stack->size - 1] = pad; cannam@226: cannam@226: // Push requested space at aligned location cannam@226: return serd_stack_push(stack, n_bytes); cannam@226: } cannam@226: cannam@226: static inline void cannam@226: serd_stack_pop_aligned(SerdStack* stack, size_t n_bytes) cannam@226: { cannam@226: // Pop requested space down to aligned location cannam@226: serd_stack_pop(stack, n_bytes); cannam@226: cannam@226: // Get amount of padding from top of stack cannam@226: const uint8_t pad = stack->buf[stack->size - 1]; cannam@226: cannam@226: // Pop padding and pad count cannam@226: serd_stack_pop(stack, pad + 1); cannam@226: } cannam@226: cannam@226: /* Byte Sink */ cannam@226: cannam@226: typedef struct SerdByteSinkImpl { cannam@226: SerdSink sink; cannam@226: void* stream; cannam@226: uint8_t* buf; cannam@226: size_t size; cannam@226: size_t block_size; cannam@226: } SerdByteSink; cannam@226: cannam@226: static inline SerdByteSink cannam@226: serd_byte_sink_new(SerdSink sink, void* stream, size_t block_size) cannam@226: { cannam@226: SerdByteSink bsink; cannam@226: bsink.sink = sink; cannam@226: bsink.stream = stream; cannam@226: bsink.size = 0; cannam@226: bsink.block_size = block_size; cannam@226: bsink.buf = ((block_size > 1) cannam@226: ? (uint8_t*)serd_bufalloc(block_size) cannam@226: : NULL); cannam@226: return bsink; cannam@226: } cannam@226: cannam@226: static inline void cannam@226: serd_byte_sink_flush(SerdByteSink* bsink) cannam@226: { cannam@226: if (bsink->block_size > 1 && bsink->size > 0) { cannam@226: bsink->sink(bsink->buf, bsink->size, bsink->stream); cannam@226: bsink->size = 0; cannam@226: } cannam@226: } cannam@226: cannam@226: static inline void cannam@226: serd_byte_sink_free(SerdByteSink* bsink) cannam@226: { cannam@226: serd_byte_sink_flush(bsink); cannam@226: free(bsink->buf); cannam@226: bsink->buf = NULL; cannam@226: } cannam@226: cannam@226: static inline size_t cannam@226: serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* bsink) cannam@226: { cannam@226: if (len == 0) { cannam@226: return 0; cannam@226: } else if (bsink->block_size == 1) { cannam@226: return bsink->sink(buf, len, bsink->stream); cannam@226: } cannam@226: cannam@226: const size_t orig_len = len; cannam@226: while (len) { cannam@226: const size_t space = bsink->block_size - bsink->size; cannam@226: const size_t n = MIN(space, len); cannam@226: cannam@226: // Write as much as possible into the remaining buffer space cannam@226: memcpy(bsink->buf + bsink->size, buf, n); cannam@226: bsink->size += n; cannam@226: buf = (const uint8_t*)buf + n; cannam@226: len -= n; cannam@226: cannam@226: // Flush page if buffer is full cannam@226: if (bsink->size == bsink->block_size) { cannam@226: bsink->sink(bsink->buf, bsink->block_size, bsink->stream); cannam@226: bsink->size = 0; cannam@226: } cannam@226: } cannam@226: return orig_len; cannam@226: } cannam@226: cannam@226: /* Character utilities */ cannam@226: cannam@226: /** Return true if `c` lies within [`min`...`max`] (inclusive) */ cannam@226: static inline bool cannam@226: in_range(const uint8_t c, const uint8_t min, const uint8_t max) cannam@226: { cannam@226: return (c >= min && c <= max); cannam@226: } cannam@226: cannam@226: /** RFC2234: ALPHA := %x41-5A / %x61-7A ; A-Z / a-z */ cannam@226: static inline bool cannam@226: is_alpha(const uint8_t c) cannam@226: { cannam@226: return in_range(c, 'A', 'Z') || in_range(c, 'a', 'z'); cannam@226: } cannam@226: cannam@226: /** RFC2234: DIGIT ::= %x30-39 ; 0-9 */ cannam@226: static inline bool cannam@226: is_digit(const uint8_t c) cannam@226: { cannam@226: return in_range(c, '0', '9'); cannam@226: } cannam@226: cannam@226: static inline bool cannam@226: is_space(const char c) cannam@226: { cannam@226: switch (c) { cannam@226: case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': cannam@226: return true; cannam@226: default: cannam@226: return false; cannam@226: } cannam@226: } cannam@226: cannam@226: static inline bool cannam@226: is_base64(const uint8_t c) cannam@226: { cannam@226: return is_alpha(c) || is_digit(c) || c == '+' || c == '/' || c == '='; cannam@226: } cannam@226: cannam@226: static inline bool cannam@226: is_windows_path(const uint8_t* path) cannam@226: { cannam@226: return is_alpha(path[0]) && (path[1] == ':' || path[1] == '|') cannam@226: && (path[2] == '/' || path[2] == '\\'); cannam@226: } cannam@226: cannam@226: /* URI utilities */ cannam@226: cannam@226: static inline bool cannam@226: chunk_equals(const SerdChunk* a, const SerdChunk* b) cannam@226: { cannam@226: return a->len == b->len cannam@226: && !strncmp((const char*)a->buf, (const char*)b->buf, a->len); cannam@226: } cannam@226: cannam@226: static inline size_t cannam@226: uri_path_len(const SerdURI* uri) cannam@226: { cannam@226: return uri->path_base.len + uri->path.len; cannam@226: } cannam@226: cannam@226: static inline uint8_t cannam@226: uri_path_at(const SerdURI* uri, size_t i) cannam@226: { cannam@226: if (i < uri->path_base.len) { cannam@226: return uri->path_base.buf[i]; cannam@226: } else { cannam@226: return uri->path.buf[i - uri->path_base.len]; cannam@226: } cannam@226: } cannam@226: cannam@226: /** Return true iff `uri` is within the base of `root` */ cannam@226: static inline bool cannam@226: uri_is_under(const SerdURI* uri, const SerdURI* root) cannam@226: { cannam@226: if (!root || !root->scheme.len || cannam@226: !chunk_equals(&root->scheme, &uri->scheme) || cannam@226: !chunk_equals(&root->authority, &uri->authority)) { cannam@226: return false; cannam@226: } cannam@226: cannam@226: bool differ = false; cannam@226: const size_t path_len = uri_path_len(uri); cannam@226: const size_t root_len = uri_path_len(root); cannam@226: for (size_t i = 0; i < path_len && i < root_len; ++i) { cannam@226: if (uri_path_at(uri, i) != uri_path_at(root, i)) { cannam@226: differ = true; cannam@226: } cannam@226: if (differ && uri_path_at(root, i) == '/') { cannam@226: return false; cannam@226: } cannam@226: } cannam@226: cannam@226: return true; cannam@226: } cannam@226: cannam@226: /* Error reporting */ cannam@226: cannam@226: static inline void cannam@226: serd_error(SerdErrorSink error_sink, void* handle, const SerdError* e) cannam@226: { cannam@226: if (error_sink) { cannam@226: error_sink(handle, e); cannam@226: } else { cannam@226: fprintf(stderr, "error: %s:%u:%u: ", e->filename, e->line, e->col); cannam@226: vfprintf(stderr, e->fmt, *e->args); cannam@226: } cannam@226: } cannam@226: cannam@226: #endif // SERD_INTERNAL_H