diff options
-rw-r--r-- | serd/serd.h | 63 | ||||
-rw-r--r-- | src/reader.c | 102 | ||||
-rw-r--r-- | src/serdi.c | 89 | ||||
-rw-r--r-- | wscript | 7 |
4 files changed, 183 insertions, 78 deletions
diff --git a/serd/serd.h b/serd/serd.h index 803ddea7..a56ab971 100644 --- a/serd/serd.h +++ b/serd/serd.h @@ -63,7 +63,7 @@ extern "C" { */ /** - Namespace prefixes. + Environment (namespace prefixes). A SerdEnv represents a set of namespace prefixes, and is used to resolve CURIEs to full URIs. @@ -79,6 +79,16 @@ typedef struct SerdEnvImpl* SerdEnv; typedef struct SerdReaderImpl* SerdReader; /** + Read state. + + This represents state (context) necessary for fully resolving URIs during a + read (i.e. the base URI and namespace prefixes). It is implemented + separately from SerdReader so the reader can avoid the overhead in cases + where this information is unnecessary (e.g. streaming reserialisation). +*/ +typedef struct SerdReadStateImpl* SerdReadState; + +/** RDF writer. A SerdWriter provides a number of functions to allow writing RDF syntax out @@ -330,7 +340,7 @@ serd_node_free(SerdNode* node); */ /** - Sink for base URI changes. + Sink (callback) for base URI changes. Called whenever the base URI of the serialisation changes. */ @@ -338,7 +348,7 @@ typedef bool (*SerdBaseSink)(void* handle, const SerdNode* uri); /** - Sink for namespace definitions. + Sink (callback) for namespace definitions. Called whenever a prefix is defined in the serialisation. */ @@ -347,7 +357,7 @@ typedef bool (*SerdPrefixSink)(void* handle, const SerdNode* uri); /** - Sink for statements. + Sink (callback) for statements. Called for every RDF statement in the serialisation. */ @@ -360,7 +370,7 @@ typedef bool (*SerdStatementSink)(void* handle, const SerdNode* object_lang); /** - Sink for anonymous node end markers. + Sink (callback) for anonymous node end markers. This is called to indicate that the anonymous node with the given @a value will no longer be referred to by any future statements @@ -483,6 +493,49 @@ void serd_reader_free(SerdReader reader); /** + Create a new read state with the given initial base URI and environment. + + A reference to @a env will be kept, and @a env will be modified as the + state is modified. +*/ +SERD_API +SerdReadState +serd_read_state_new(SerdEnv env, + const uint8_t* base_uri_str); + +/** + Free @a state. +*/ +SERD_API +void +serd_read_state_free(SerdReadState state); + +/** + Get the current base URI. +*/ +SERD_API +SerdNode +serd_read_state_get_base_uri(SerdReadState state, + SerdURI* out); + +/** + Set the current base URI. +*/ +SERD_API +bool +serd_read_state_set_base_uri(SerdReadState state, + const SerdNode* uri_node); + +/** + Set a namespace prefix. +*/ +SERD_API +bool +serd_read_state_set_prefix(SerdReadState state, + const SerdNode* name, + const SerdNode* uri_node); + +/** @} @name SerdWriter @{ diff --git a/src/reader.c b/src/reader.c index e0790e39..b19ffcdc 100644 --- a/src/reader.c +++ b/src/reader.c @@ -41,9 +41,6 @@ #define STACK_PAGE_SIZE 4096 #define READ_BUF_LEN 4096 -#ifndef NDEBUG -#define STACK_DEBUG 1 -#endif typedef struct { const uint8_t* filename; @@ -98,12 +95,18 @@ struct SerdReaderImpl { int32_t read_head; ///< Offset into read_buf bool from_file; ///< True iff reading from @ref fd bool eof; -#ifdef STACK_DEBUG +#ifdef SUIL_STACK_CHECK Ref* alloc_stack; ///< Stack of push offsets size_t n_allocs; ///< Number of stack pushes #endif }; +struct SerdReadStateImpl { + SerdEnv env; + SerdNode base_uri_node; + SerdURI base_uri; +}; + typedef enum { SERD_SUCCESS = 0, ///< Completed successfully SERD_FAILURE = 1, ///< Non-fatal failure @@ -202,7 +205,7 @@ eat_string(SerdReader reader, const char* str, unsigned n) } } -#ifdef STACK_DEBUG +#ifdef SUIL_STACK_CHECK static inline bool stack_is_top_string(SerdReader reader, Ref ref) { @@ -229,7 +232,7 @@ push_string(SerdReader reader, const char* c_str, size_t n_bytes) str->n_bytes = n_bytes; str->n_chars = n_bytes - 1; memcpy(str->buf, c_str, n_bytes); -#ifdef STACK_DEBUG +#ifdef SUIL_STACK_CHECK reader->alloc_stack = realloc(reader->alloc_stack, sizeof(uint8_t*) * (++reader->n_allocs)); reader->alloc_stack[reader->n_allocs - 1] = (mem - reader->stack.buf); @@ -249,7 +252,7 @@ deref(SerdReader reader, const Ref ref) static inline void push_byte(SerdReader reader, Ref ref, const uint8_t c) { - #ifdef STACK_DEBUG + #ifdef SUIL_STACK_CHECK assert(stack_is_top_string(reader, ref)); #endif serd_stack_push(&reader->stack, 1); @@ -273,7 +276,7 @@ pop_string(SerdReader reader, Ref ref) || ref == reader->rdf_rest.value) { return; } - #ifdef STACK_DEBUG + #ifdef SUIL_STACK_CHECK if (!stack_is_top_string(reader, ref)) { fprintf(stderr, "attempt to pop non-top string %s\n", deref(reader, ref)->buf); @@ -1377,7 +1380,7 @@ serd_reader_new(SerdSyntax syntax, me->read_buf = 0; me->read_head = 0; me->eof = false; -#ifdef STACK_DEBUG +#ifdef SERD_STACK_CHECK me->alloc_stack = 0; me->n_allocs = 0; #endif @@ -1401,7 +1404,7 @@ serd_reader_free(SerdReader reader) pop_string(me, me->rdf_rest.value); pop_string(me, me->rdf_first.value); -#ifdef STACK_DEBUG +#ifdef SERD_STACK_CHECK free(me->alloc_stack); #endif free(me->stack.buf); @@ -1458,3 +1461,82 @@ serd_reader_read_string(SerdReader me, const uint8_t* utf8) me->read_buf = NULL; return ret; } + +SERD_API +SerdReadState +serd_read_state_new(SerdEnv env, + const uint8_t* base_uri_str) +{ + SerdReadState state = malloc(sizeof(struct SerdReadStateImpl)); + SerdURI base_base_uri = SERD_URI_NULL; + state->env = env; + state->base_uri_node = serd_node_new_uri_from_string( + base_uri_str, &base_base_uri, &state->base_uri); + return state; +} + +SERD_API +void +serd_read_state_free(SerdReadState state) +{ + serd_node_free(&state->base_uri_node); + free(state); +} + +SERD_API +SerdNode +serd_read_state_get_base_uri(SerdReadState state, + SerdURI* out) +{ + *out = state->base_uri; + return state->base_uri_node; +} + +SERD_API +bool +serd_read_state_set_base_uri(SerdReadState state, + const SerdNode* uri_node) +{ + // Resolve base URI and create a new node and URI for it + SerdURI base_uri; + SerdNode base_uri_node = serd_node_new_uri_from_node( + uri_node, &state->base_uri, &base_uri); + + if (base_uri_node.buf) { + // Replace the current base URI + serd_node_free(&state->base_uri_node); + state->base_uri_node = base_uri_node; + state->base_uri = base_uri; + return true; + } + return false; +} + +SERD_API +bool +serd_read_state_set_prefix(SerdReadState state, + const SerdNode* name, + const SerdNode* uri_node) +{ + if (serd_uri_string_has_scheme(uri_node->buf)) { + // Set prefix to absolute URI + serd_env_add(state->env, name, uri_node); + return true; + } else { + // Resolve relative URI and create a new node and URI for it + SerdURI abs_uri; + SerdNode abs_uri_node = serd_node_new_uri_from_node( + uri_node, &state->base_uri, &abs_uri); + + if (!abs_uri_node.buf) { + return false; + } + + // Set prefix to resolved (absolute) URI + serd_env_add(state->env, name, &abs_uri_node); + serd_node_free(&abs_uri_node); + return true; + } + return false; +} + diff --git a/src/serdi.c b/src/serdi.c index d97cb9f9..7eede1e6 100644 --- a/src/serdi.c +++ b/src/serdi.c @@ -31,38 +31,20 @@ #include "serd-config.h" typedef struct { - SerdWriter writer; - SerdEnv env; - SerdNode base_uri_node; - SerdURI base_uri; + SerdEnv env; + SerdReadState read_state; + SerdWriter writer; } State; -static uint8_t* -copy_string(const uint8_t* str, size_t* n_bytes) -{ - const size_t len = strlen((const char*)str); - uint8_t* const ret = malloc(len + 1); - memcpy(ret, str, len + 1); - *n_bytes = len + 1; - return ret; -} - static bool event_base(void* handle, const SerdNode* uri_node) { State* const state = (State*)handle; - // Resolve base URI and create a new node and URI for it - SerdURI base_uri; - SerdNode base_uri_node = serd_node_new_uri_from_node( - uri_node, &state->base_uri, &base_uri); - - if (base_uri_node.buf) { - // Replace the current base URI - serd_node_free(&state->base_uri_node); - state->base_uri_node = base_uri_node; - state->base_uri = base_uri; + if (serd_read_state_set_base_uri(state->read_state, uri_node)) { + SerdURI base_uri; + serd_read_state_get_base_uri(state->read_state, &base_uri); serd_writer_set_base_uri(state->writer, &base_uri); return true; } @@ -75,23 +57,8 @@ event_prefix(void* handle, const SerdNode* uri_node) { State* const state = (State*)handle; - if (serd_uri_string_has_scheme(uri_node->buf)) { - // Set prefix to absolute URI - serd_env_add(state->env, name, uri_node); - } else { - // Resolve relative URI and create a new node and URI for it - SerdURI abs_uri; - SerdNode abs_uri_node = serd_node_new_uri_from_node( - uri_node, &state->base_uri, &abs_uri); - - if (!abs_uri_node.buf) { - return false; - } - - // Set prefix to resolved (absolute) URI - serd_env_add(state->env, name, &abs_uri_node); - serd_node_free(&abs_uri_node); - } + + serd_read_state_set_prefix(state->read_state, name, uri_node); serd_writer_set_prefix(state->writer, name, uri_node); return true; } @@ -218,20 +185,19 @@ main(int argc, char** argv) } } - uint8_t* base_uri_str = NULL; - size_t base_uri_n_bytes = 0; - SerdURI base_uri; + const uint8_t* base_uri_str = NULL; + SerdURI base_uri; if (a < argc) { // Base URI given on command line const uint8_t* const in_base_uri = (const uint8_t*)argv[a++]; if (!serd_uri_parse((const uint8_t*)in_base_uri, &base_uri)) { fprintf(stderr, "invalid base URI `%s'\n", argv[2]); return 1; } - base_uri_str = copy_string(in_base_uri, &base_uri_n_bytes); + base_uri_str = in_base_uri; } else if (from_file) { // Use input file URI - base_uri_str = copy_string(input, &base_uri_n_bytes); + base_uri_str = input; } else { - base_uri_str = copy_string((const uint8_t*)"", &base_uri_n_bytes); + base_uri_str = (const uint8_t*)""; } if (!serd_uri_parse(base_uri_str, &base_uri)) { @@ -241,22 +207,21 @@ main(int argc, char** argv) FILE* out_fd = stdout; SerdEnv env = serd_env_new(); - SerdStyle output_style = (output_syntax == SERD_NTRIPLES) - ? SERD_STYLE_ASCII - : SERD_STYLE_ABBREVIATED; + SerdStyle output_style = SERD_STYLE_RESOLVED; + if (output_syntax == SERD_NTRIPLES) { + output_style |= SERD_STYLE_ASCII; + } else { + output_style |= SERD_STYLE_ABBREVIATED; + } - output_style |= SERD_STYLE_RESOLVED; + SerdReadState read_state = serd_read_state_new(env, base_uri_str); - const SerdNode base_uri_node = { SERD_URI, - base_uri_n_bytes, - base_uri_n_bytes - 1, - base_uri_str }; + serd_read_state_get_base_uri(read_state, &base_uri); - State state = { - serd_writer_new(output_syntax, output_style, - env, &base_uri, file_sink, out_fd), - env, base_uri_node, base_uri - }; + SerdWriter writer = serd_writer_new( + output_syntax, output_style, env, &base_uri, file_sink, out_fd); + + State state = { env, read_state, writer }; SerdReader reader = serd_reader_new( SERD_TURTLE, &state, @@ -274,11 +239,9 @@ main(int argc, char** argv) serd_writer_finish(state.writer); serd_writer_free(state.writer); - + serd_read_state_free(state.read_state); serd_env_free(state.env); - serd_node_free(&state.base_uri_node); - if (success) { return 0; } @@ -32,6 +32,8 @@ def options(opt): help="Do not build command line utilities") opt.add_option('--test', action='store_true', default=False, dest='build_tests', help="Build unit tests") + opt.add_option('--stack-check', action='store_true', default=False, dest='stack_check', + help="Include runtime stack sanity checks") def configure(conf): conf.line_just = max(conf.line_just, 59) @@ -44,6 +46,11 @@ def configure(conf): conf.env['BUILD_TESTS'] = Options.options.build_tests conf.env['BUILD_UTILS'] = not Options.options.no_utils + if Options.options.stack_check: + autowaf.define(conf, 'SERD_STACK_CHECK', SERD_VERSION) + + conf.env['BUILD_TESTS'] = Options.options.build_tests + autowaf.define(conf, 'SERD_VERSION', SERD_VERSION) conf.write_config_header('serd-config.h', remove=False) |