aboutsummaryrefslogtreecommitdiffstats
path: root/src/reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reader.c')
-rw-r--r--src/reader.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/reader.c b/src/reader.c
new file mode 100644
index 00000000..f37eac14
--- /dev/null
+++ b/src/reader.c
@@ -0,0 +1,399 @@
+/*
+ Copyright 2011-2017 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "serd_internal.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+r_err(SerdReader* reader, SerdStatus st, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ const Cursor* const cur = &reader->source.cur;
+ const SerdError e = { st, cur->filename, cur->line, cur->col, fmt, &args };
+ serd_error(reader->error_sink, reader->error_handle, &e);
+ va_end(args);
+ return 0;
+}
+
+void
+set_blank_id(SerdReader* reader, Ref ref, size_t buf_size)
+{
+ SerdNode* node = deref(reader, ref);
+ const char* prefix = reader->bprefix ? (const char*)reader->bprefix : "";
+ node->n_bytes = node->n_chars = snprintf(
+ (char*)node->buf, buf_size, "%sb%u", prefix, reader->next_id++);
+}
+
+size_t
+genid_size(SerdReader* reader)
+{
+ return reader->bprefix_len + 1 + 10 + 1; // + "b" + UINT32_MAX + \0
+}
+
+Ref
+blank_id(SerdReader* reader)
+{
+ Ref ref = push_node_padded(reader, genid_size(reader), SERD_BLANK, "", 0);
+ set_blank_id(reader, ref, genid_size(reader));
+ return ref;
+}
+
+/** fread-like wrapper for getc (which is faster). */
+static size_t
+serd_file_read_byte(void* buf, size_t size, size_t nmemb, void* stream)
+{
+ const int c = getc((FILE*)stream);
+ if (c == EOF) {
+ *((uint8_t*)buf) = 0;
+ return 0;
+ }
+ *((uint8_t*)buf) = (uint8_t)c;
+ return 1;
+}
+
+Ref
+push_node_padded(SerdReader* reader, size_t maxlen,
+ SerdType type, const char* str, size_t n_bytes)
+{
+ void* mem = serd_stack_push_aligned(
+ &reader->stack, sizeof(SerdNode) + maxlen + 1, sizeof(SerdNode));
+
+ SerdNode* const node = (SerdNode*)mem;
+ node->n_bytes = node->n_chars = n_bytes;
+ node->flags = 0;
+ node->type = type;
+ node->buf = NULL;
+
+ uint8_t* buf = (uint8_t*)(node + 1);
+ memcpy(buf, str, n_bytes + 1);
+
+#ifdef SERD_STACK_CHECK
+ reader->allocs = realloc(
+ reader->allocs, sizeof(reader->allocs) * (++reader->n_allocs));
+ reader->allocs[reader->n_allocs - 1] = ((uint8_t*)mem - reader->stack.buf);
+#endif
+ return (uint8_t*)node - reader->stack.buf;
+}
+
+Ref
+push_node(SerdReader* reader, SerdType type, const char* str, size_t n_bytes)
+{
+ return push_node_padded(reader, n_bytes, type, str, n_bytes);
+}
+
+SerdNode*
+deref(SerdReader* reader, const Ref ref)
+{
+ if (ref) {
+ SerdNode* node = (SerdNode*)(reader->stack.buf + ref);
+ node->buf = (uint8_t*)node + sizeof(SerdNode);
+ return node;
+ }
+ return NULL;
+}
+
+Ref
+pop_node(SerdReader* reader, Ref ref)
+{
+ if (ref && ref != reader->rdf_first && ref != reader->rdf_rest
+ && ref != reader->rdf_nil) {
+#ifdef SERD_STACK_CHECK
+ SERD_STACK_ASSERT_TOP(reader, ref);
+ --reader->n_allocs;
+#endif
+ SerdNode* const node = deref(reader, ref);
+ uint8_t* const top = reader->stack.buf + reader->stack.size;
+ serd_stack_pop_aligned(&reader->stack, top - (uint8_t*)node);
+ }
+ return 0;
+}
+
+bool
+emit_statement(SerdReader* reader, ReadContext ctx, Ref o, Ref d, Ref l)
+{
+ SerdNode* graph = deref(reader, ctx.graph);
+ if (!graph && reader->default_graph.buf) {
+ graph = &reader->default_graph;
+ }
+ bool ret = !reader->statement_sink ||
+ !reader->statement_sink(
+ reader->handle, *ctx.flags, graph,
+ deref(reader, ctx.subject), deref(reader, ctx.predicate),
+ deref(reader, o), deref(reader, d), deref(reader, l));
+ *ctx.flags &= SERD_ANON_CONT|SERD_LIST_CONT; // Preserve only cont flags
+ return ret;
+}
+
+static bool
+read_statement(SerdReader* reader)
+{
+ switch (reader->syntax) {
+ default: return read_n3_statement(reader);
+ }
+}
+
+static bool
+read_doc(SerdReader* reader)
+{
+ switch (reader->syntax) {
+ case SERD_NQUADS: return read_nquadsDoc(reader);
+ default: return read_turtleTrigDoc(reader);
+ }
+}
+
+SerdReader*
+serd_reader_new(SerdSyntax syntax,
+ void* handle,
+ void (*free_handle)(void*),
+ SerdBaseSink base_sink,
+ SerdPrefixSink prefix_sink,
+ SerdStatementSink statement_sink,
+ SerdEndSink end_sink)
+{
+ SerdReader* me = (SerdReader*)calloc(1, sizeof(SerdReader));
+ me->handle = handle;
+ me->free_handle = free_handle;
+ me->base_sink = base_sink;
+ me->prefix_sink = prefix_sink;
+ me->statement_sink = statement_sink;
+ me->end_sink = end_sink;
+ me->default_graph = SERD_NODE_NULL;
+ me->stack = serd_stack_new(SERD_PAGE_SIZE);
+ me->syntax = syntax;
+ me->next_id = 1;
+ me->strict = true;
+
+ me->rdf_first = push_node(me, SERD_URI, NS_RDF "first", 48);
+ me->rdf_rest = push_node(me, SERD_URI, NS_RDF "rest", 47);
+ me->rdf_nil = push_node(me, SERD_URI, NS_RDF "nil", 46);
+
+ return me;
+}
+
+void
+serd_reader_set_strict(SerdReader* reader, bool strict)
+{
+ reader->strict = strict;
+}
+
+void
+serd_reader_set_error_sink(SerdReader* reader,
+ SerdErrorSink error_sink,
+ void* error_handle)
+{
+ reader->error_sink = error_sink;
+ reader->error_handle = error_handle;
+}
+
+void
+serd_reader_free(SerdReader* reader)
+{
+ pop_node(reader, reader->rdf_nil);
+ pop_node(reader, reader->rdf_rest);
+ pop_node(reader, reader->rdf_first);
+ serd_node_free(&reader->default_graph);
+
+#ifdef SERD_STACK_CHECK
+ free(reader->allocs);
+#endif
+ free(reader->stack.buf);
+ free(reader->bprefix);
+ if (reader->free_handle) {
+ reader->free_handle(reader->handle);
+ }
+ free(reader);
+}
+
+void*
+serd_reader_get_handle(const SerdReader* reader)
+{
+ return reader->handle;
+}
+
+void
+serd_reader_add_blank_prefix(SerdReader* reader,
+ const uint8_t* prefix)
+{
+ free(reader->bprefix);
+ reader->bprefix_len = 0;
+ reader->bprefix = NULL;
+ if (prefix) {
+ reader->bprefix_len = strlen((const char*)prefix);
+ reader->bprefix = (uint8_t*)malloc(reader->bprefix_len + 1);
+ memcpy(reader->bprefix, prefix, reader->bprefix_len + 1);
+ }
+}
+
+void
+serd_reader_set_default_graph(SerdReader* reader,
+ const SerdNode* graph)
+{
+ serd_node_free(&reader->default_graph);
+ reader->default_graph = serd_node_copy(graph);
+}
+
+SerdStatus
+serd_reader_read_file(SerdReader* reader,
+ const uint8_t* uri)
+{
+ uint8_t* const path = serd_file_uri_parse(uri, NULL);
+ if (!path) {
+ return SERD_ERR_BAD_ARG;
+ }
+
+ FILE* fd = serd_fopen((const char*)path, "rb");
+ if (!fd) {
+ serd_free(path);
+ return SERD_ERR_UNKNOWN;
+ }
+
+ SerdStatus ret = serd_reader_read_file_handle(reader, fd, path);
+ fclose(fd);
+ free(path);
+ return ret;
+}
+
+static SerdStatus
+skip_bom(SerdReader* me)
+{
+ if (serd_byte_source_peek(&me->source) == 0xEF) {
+ serd_byte_source_advance(&me->source);
+ if (serd_byte_source_peek(&me->source) != 0xBB ||
+ serd_byte_source_advance(&me->source) ||
+ serd_byte_source_peek(&me->source) != 0xBF ||
+ serd_byte_source_advance(&me->source)) {
+ r_err(me, SERD_ERR_BAD_SYNTAX, "corrupt byte order mark\n");
+ return SERD_ERR_BAD_SYNTAX;
+ }
+ }
+
+ return SERD_SUCCESS;
+}
+
+SerdStatus
+serd_reader_start_stream(SerdReader* reader,
+ FILE* file,
+ const uint8_t* name,
+ bool bulk)
+{
+ return serd_reader_start_source_stream(
+ reader,
+ bulk ? (SerdSource)fread : serd_file_read_byte,
+ (SerdStreamErrorFunc)ferror,
+ file,
+ name,
+ bulk ? SERD_PAGE_SIZE : 1);
+}
+
+SerdStatus
+serd_reader_start_source_stream(SerdReader* reader,
+ SerdSource read_func,
+ SerdStreamErrorFunc error_func,
+ void* stream,
+ const uint8_t* name,
+ size_t page_size)
+{
+ return serd_byte_source_open_source(
+ &reader->source, read_func, error_func, stream, name, page_size);
+}
+
+static SerdStatus
+serd_reader_prepare(SerdReader* reader)
+{
+ reader->status = serd_byte_source_prepare(&reader->source);
+ if (reader->status == SERD_SUCCESS) {
+ reader->status = skip_bom(reader);
+ } else if (reader->status == SERD_FAILURE) {
+ reader->source.eof = true;
+ } else {
+ r_err(reader, reader->status, "read error: %s\n", strerror(errno));
+ }
+ return reader->status;
+}
+
+SerdStatus
+serd_reader_read_chunk(SerdReader* reader)
+{
+ SerdStatus st = SERD_SUCCESS;
+ if (!reader->source.prepared) {
+ st = serd_reader_prepare(reader);
+ } else if (reader->source.eof) {
+ st = serd_byte_source_advance(&reader->source);
+ }
+
+ return st ? st : read_statement(reader) ? SERD_SUCCESS : SERD_FAILURE;
+}
+
+SerdStatus
+serd_reader_end_stream(SerdReader* reader)
+{
+ return serd_byte_source_close(&reader->source);
+}
+
+SerdStatus
+serd_reader_read_file_handle(SerdReader* reader,
+ FILE* file,
+ const uint8_t* name)
+{
+ return serd_reader_read_source(
+ reader, (SerdSource)fread, (SerdStreamErrorFunc)ferror,
+ file, name, SERD_PAGE_SIZE);
+}
+
+SerdStatus
+serd_reader_read_source(SerdReader* reader,
+ SerdSource source,
+ SerdStreamErrorFunc error,
+ void* stream,
+ const uint8_t* name,
+ size_t page_size)
+{
+ SerdStatus st = serd_reader_start_source_stream(
+ reader, source, error, stream, name, page_size);
+
+ if (st || (st = serd_reader_prepare(reader))) {
+ serd_reader_end_stream(reader);
+ return st;
+ } else if (!read_doc(reader)) {
+ serd_reader_end_stream(reader);
+ return SERD_ERR_UNKNOWN;
+ }
+
+ return serd_reader_end_stream(reader);
+}
+
+SerdStatus
+serd_reader_read_string(SerdReader* reader, const uint8_t* utf8)
+{
+ serd_byte_source_open_string(&reader->source, utf8);
+
+ SerdStatus st = serd_reader_prepare(reader);
+ if (!st) {
+ st = read_doc(reader) ? SERD_SUCCESS : SERD_ERR_UNKNOWN;
+ }
+
+ serd_byte_source_close(&reader->source);
+
+ return st;
+}