diff options
-rw-r--r-- | include/serd/serd.h | 78 | ||||
-rw-r--r-- | src/env.c | 92 | ||||
-rw-r--r-- | src/env.h | 26 | ||||
-rw-r--r-- | src/node.c | 152 | ||||
-rw-r--r-- | src/serdi.c | 24 | ||||
-rw-r--r-- | src/writer.c | 104 | ||||
-rw-r--r-- | test/test_env.c | 46 | ||||
-rw-r--r-- | test/test_node.c | 71 | ||||
-rw-r--r-- | test/test_reader_writer.c | 50 | ||||
-rw-r--r-- | test/test_uri.c | 12 | ||||
-rw-r--r-- | test/test_writer.c | 39 |
11 files changed, 395 insertions, 299 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h index 03fc0665..cb65ba14 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -483,37 +483,48 @@ typedef enum { } SerdNodeType; /** - Create a new node from `str`. + Create a new "simple" node that is just a string. + + This can be used to create blank, CURIE, or URI nodes from an already + measured string or slice of a buffer, which avoids a strlen compared to the + friendly constructors. This may not be used for literals since those must + be measured to set the SERD_HAS_NEWLINE and SERD_HAS_QUOTE flags. */ SERD_API SerdNode* SERD_ALLOCATED -serd_new_string(SerdNodeType type, const char* SERD_NULLABLE str); +serd_new_simple_node(SerdNodeType type, SerdStringView string); -/** - Create a new node from a prefix of `str`. -*/ +/// Create a new plain literal string node from `str` SERD_API SerdNode* SERD_ALLOCATED -serd_new_substring(SerdNodeType type, - const char* SERD_NULLABLE str, - size_t len); +serd_new_string(SerdStringView string); /** Create a new literal node from `str`. - Either `datatype` or `lang` can be given, but not both, unless `datatype` is - rdf:langString in which case it is ignored. + Either `datatype_uri` or `lang` can be given, but not both, unless + `datatype_uri` is rdf:langString in which case it is ignored. */ SERD_API SerdNode* SERD_ALLOCATED -serd_new_literal(const char* SERD_NONNULL str, - const char* SERD_NULLABLE datatype, - const char* SERD_NULLABLE lang); +serd_new_literal(SerdStringView string, + SerdStringView datatype_uri, + SerdStringView lang); + +/// Create a new blank node +SERD_API +SerdNode* SERD_ALLOCATED +serd_new_blank(SerdStringView string); + +/// Create a new CURIE node +SERD_API +SerdNode* SERD_ALLOCATED +serd_new_curie(SerdStringView string); /// Create a new URI node SERD_API SerdNode* SERD_ALLOCATED -serd_new_uri(const char* SERD_NONNULL str); +serd_new_uri(SerdStringView string); /// Create a new URI from a URI view SERD_API @@ -527,13 +538,10 @@ serd_new_parsed_uri(SerdURIView uri); percent encoded as necessary. If `path` is relative, `hostname` is ignored. - If `out` is not NULL, it will be set to the parsed URI. */ SERD_API SerdNode* SERD_ALLOCATED -serd_new_file_uri(const char* SERD_NONNULL path, - const char* SERD_NULLABLE hostname, - SerdURIView* SERD_NULLABLE out); +serd_new_file_uri(SerdStringView path, SerdStringView hostname); /** Create a new node by serialising `d` into an xsd:decimal string. @@ -735,7 +743,7 @@ typedef struct SerdEnvImpl SerdEnv; /// Create a new environment SERD_API SerdEnv* SERD_ALLOCATED -serd_env_new(const SerdNode* SERD_NULLABLE base_uri); +serd_env_new(SerdStringView base_uri); /// Free `env` SERD_API @@ -743,16 +751,14 @@ void serd_env_free(SerdEnv* SERD_NULLABLE env); /// Get the current base URI -SERD_API +SERD_PURE_API const SerdNode* SERD_NULLABLE -serd_env_base_uri(const SerdEnv* SERD_NONNULL env, - SerdURIView* SERD_NULLABLE out); +serd_env_base_uri(const SerdEnv* SERD_NULLABLE env); /// Set the current base URI SERD_API SerdStatus -serd_env_set_base_uri(SerdEnv* SERD_NONNULL env, - const SerdNode* SERD_NULLABLE uri); +serd_env_set_base_uri(SerdEnv* SERD_NONNULL env, SerdStringView uri); /** Set a namespace prefix. @@ -763,16 +769,9 @@ serd_env_set_base_uri(SerdEnv* SERD_NONNULL env, */ SERD_API SerdStatus -serd_env_set_prefix(SerdEnv* SERD_NONNULL env, - const SerdNode* SERD_NONNULL name, - const SerdNode* SERD_NONNULL uri); - -/// Set a namespace prefix -SERD_API -SerdStatus -serd_env_set_prefix_from_strings(SerdEnv* SERD_NONNULL env, - const char* SERD_NONNULL name, - const char* SERD_NONNULL uri); +serd_env_set_prefix(SerdEnv* SERD_NONNULL env, + SerdStringView name, + SerdStringView uri); /// Qualify `uri` into a CURIE if possible SERD_API @@ -997,12 +996,11 @@ typedef uint32_t SerdWriterFlags; /// Create a new RDF writer SERD_API SerdWriter* SERD_ALLOCATED -serd_writer_new(SerdSyntax syntax, - SerdWriterFlags flags, - SerdEnv* SERD_NONNULL env, - const SerdURIView* SERD_NULLABLE base_uri, - SerdSink SERD_NONNULL ssink, - void* SERD_NULLABLE stream); +serd_writer_new(SerdSyntax syntax, + SerdWriterFlags flags, + SerdEnv* SERD_NONNULL env, + SerdSink SERD_NONNULL ssink, + void* SERD_NULLABLE stream); /// Free `writer` SERD_API @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "env.h" + #include "node.h" #include "serd/serd.h" @@ -36,10 +38,10 @@ struct SerdEnvImpl { }; SerdEnv* -serd_env_new(const SerdNode* const base_uri) +serd_env_new(const SerdStringView base_uri) { SerdEnv* env = (SerdEnv*)calloc(1, sizeof(struct SerdEnvImpl)); - if (env && base_uri) { + if (env && base_uri.len) { serd_env_set_base_uri(env, base_uri); } @@ -62,41 +64,39 @@ serd_env_free(SerdEnv* const env) free(env); } -const SerdNode* -serd_env_base_uri(const SerdEnv* const env, SerdURIView* const out) +SerdURIView +serd_env_base_uri_view(const SerdEnv* const env) { - if (out) { - *out = env->base_uri; - } + return env->base_uri; +} - return env->base_uri_node; +const SerdNode* +serd_env_base_uri(const SerdEnv* const env) +{ + return env ? env->base_uri_node : NULL; } SerdStatus -serd_env_set_base_uri(SerdEnv* const env, const SerdNode* const uri) +serd_env_set_base_uri(SerdEnv* const env, const SerdStringView uri) { - if (uri && uri->type != SERD_URI) { - return SERD_ERR_BAD_ARG; - } - - if (!uri) { + if (!uri.len) { serd_node_free(env->base_uri_node); env->base_uri_node = NULL; env->base_uri = SERD_URI_NULL; return SERD_SUCCESS; } + SerdNode* const old_base_uri = env->base_uri_node; + // Resolve the new base against the current base in case it is relative const SerdURIView new_base_uri = - serd_resolve_uri(serd_parse_uri(serd_node_string(uri)), env->base_uri); - - SerdNode* const new_base_node = serd_new_parsed_uri(new_base_uri); + serd_resolve_uri(serd_parse_uri(uri.buf), env->base_uri); // Replace the current base URI - serd_node_free(env->base_uri_node); - env->base_uri_node = new_base_node; + env->base_uri_node = serd_new_parsed_uri(new_base_uri); env->base_uri = serd_node_uri_view(env->base_uri_node); + serd_node_free(old_base_uri); return SERD_SUCCESS; } @@ -118,36 +118,30 @@ serd_env_find(const SerdEnv* const env, } static void -serd_env_add(SerdEnv* const env, - const SerdNode* const name, - const SerdNode* const uri) +serd_env_add(SerdEnv* const env, + const SerdStringView name, + const SerdStringView uri) { - const char* name_str = serd_node_string(name); - SerdPrefix* const prefix = serd_env_find(env, name_str, name->length); + SerdPrefix* const prefix = serd_env_find(env, name.buf, name.len); if (prefix) { - if (!serd_node_equals(prefix->uri, uri)) { - SerdNode* old_prefix_uri = prefix->uri; - prefix->uri = serd_node_copy(uri); - serd_node_free(old_prefix_uri); + if (strcmp(serd_node_string(prefix->uri), uri.buf)) { + serd_node_free(prefix->uri); + prefix->uri = serd_new_uri(uri); } } else { env->prefixes = (SerdPrefix*)realloc( env->prefixes, (++env->n_prefixes) * sizeof(SerdPrefix)); - env->prefixes[env->n_prefixes - 1].name = serd_node_copy(name); - env->prefixes[env->n_prefixes - 1].uri = serd_node_copy(uri); + env->prefixes[env->n_prefixes - 1].name = serd_new_string(name); + env->prefixes[env->n_prefixes - 1].uri = serd_new_uri(uri); } } SerdStatus -serd_env_set_prefix(SerdEnv* const env, - const SerdNode* const name, - const SerdNode* const uri) +serd_env_set_prefix(SerdEnv* const env, + const SerdStringView name, + const SerdStringView uri) { - if (!name || uri->type != SERD_URI) { - return SERD_ERR_BAD_ARG; - } - - if (serd_uri_string_has_scheme(serd_node_string(uri))) { + if (serd_uri_string_has_scheme(uri.buf)) { // Set prefix to absolute URI serd_env_add(env, name, uri); return SERD_SUCCESS; @@ -158,32 +152,14 @@ serd_env_set_prefix(SerdEnv* const env, } // Resolve relative URI and create a new node and URI for it - SerdNode* const abs_uri = - serd_new_resolved_uri(serd_node_string_view(uri), env->base_uri); + SerdNode* const abs_uri = serd_new_resolved_uri(uri, env->base_uri); // Set prefix to resolved (absolute) URI - serd_env_add(env, name, abs_uri); - + serd_env_add(env, name, serd_node_string_view(abs_uri)); serd_node_free(abs_uri); - return SERD_SUCCESS; } -SerdStatus -serd_env_set_prefix_from_strings(SerdEnv* const env, - const char* const name, - const char* const uri) -{ - SerdNode* name_node = serd_new_string(SERD_LITERAL, name); - SerdNode* uri_node = serd_new_string(SERD_URI, uri); - - const SerdStatus st = serd_env_set_prefix(env, name_node, uri_node); - - serd_node_free(name_node); - serd_node_free(uri_node); - return st; -} - bool serd_env_qualify(const SerdEnv* const env, const SerdNode* const uri, diff --git a/src/env.h b/src/env.h new file mode 100644 index 00000000..1c36b4ee --- /dev/null +++ b/src/env.h @@ -0,0 +1,26 @@ +/* + Copyright 2011-2020 David Robillard <d@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. +*/ + +#ifndef SERD_ENV_H +#define SERD_ENV_H + +#include "serd/serd.h" + +SERD_CONST_FUNC +SerdURIView +serd_env_base_uri_view(const SerdEnv* env); + +#endif // SERD_ENV_H @@ -44,6 +44,9 @@ static const size_t serd_node_align = 2 * sizeof(uint64_t); static const SerdNodeFlags meta_mask = (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE); +static SerdNode* +serd_new_from_uri(SerdURIView uri, SerdURIView base); + static size_t serd_uri_string_length(const SerdURIView* const uri) { @@ -136,68 +139,77 @@ serd_node_set(SerdNode** const dst, const SerdNode* const src) } SerdNode* -serd_new_string(SerdNodeType type, const char* str) +serd_new_simple_node(const SerdNodeType type, const SerdStringView str) { + if (type != SERD_BLANK && type != SERD_CURIE && type != SERD_URI) { + return NULL; + } + SerdNodeFlags flags = 0; - const size_t length = serd_strlen(str, &flags); + const size_t length = str.buf ? serd_strlen(str.buf, &flags) : 0; SerdNode* node = serd_node_malloc(length, flags, type); - memcpy(serd_node_buffer(node), str, length); - node->length = length; + if (node) { + if (str.buf) { + memcpy(serd_node_buffer(node), str.buf, length); + } + + node->length = length; + } return node; } SerdNode* -serd_new_substring(const SerdNodeType type, - const char* const str, - const size_t len) +serd_new_string(const SerdStringView str) { SerdNodeFlags flags = 0; - const size_t length = serd_substrlen(str, len, &flags); - SerdNode* node = serd_node_malloc(length, flags, type); - memcpy(serd_node_buffer(node), str, length); + const size_t length = serd_substrlen(str.buf, str.len, &flags); + SerdNode* node = serd_node_malloc(length, flags, SERD_LITERAL); + + memcpy(serd_node_buffer(node), str.buf, str.len); node->length = length; + return node; } SerdNode* -serd_new_literal(const char* const str, - const char* const datatype, - const char* const lang) +serd_new_literal(const SerdStringView str, + const SerdStringView datatype_uri, + const SerdStringView lang) { SerdNodeFlags flags = 0; - const size_t length = serd_strlen(str, &flags); + const size_t length = serd_substrlen(str.buf, str.len, &flags); const size_t len = serd_node_pad_size(length); SerdNode* node = NULL; - if (lang) { - flags |= SERD_HAS_LANGUAGE; - const size_t lang_len = strlen(lang); - const size_t total_len = len + sizeof(SerdNode) + lang_len; - node = serd_node_malloc(total_len, flags, SERD_LITERAL); - memcpy(serd_node_buffer(node), str, length); + if (lang.len) { + const size_t total_len = len + sizeof(SerdNode) + lang.len; + + node = serd_node_malloc(total_len, flags | SERD_HAS_LANGUAGE, SERD_LITERAL); node->length = length; + memcpy(serd_node_buffer(node), str.buf, length); SerdNode* lang_node = node + 1 + (len / sizeof(SerdNode)); lang_node->type = SERD_LITERAL; - lang_node->length = lang_len; - memcpy(serd_node_buffer(lang_node), lang, lang_len); - } else if (datatype) { - flags |= SERD_HAS_DATATYPE; - const size_t datatype_len = strlen(datatype); - const size_t total_len = len + sizeof(SerdNode) + datatype_len; - node = serd_node_malloc(total_len, flags, SERD_LITERAL); - memcpy(serd_node_buffer(node), str, length); + lang_node->length = lang.len; + memcpy(serd_node_buffer(lang_node), lang.buf, lang.len); + + } else if (datatype_uri.len) { + const size_t total_len = len + sizeof(SerdNode) + datatype_uri.len; + + node = serd_node_malloc(total_len, flags | SERD_HAS_DATATYPE, SERD_LITERAL); node->length = length; + memcpy(serd_node_buffer(node), str.buf, length); SerdNode* datatype_node = node + 1 + (len / sizeof(SerdNode)); datatype_node->type = SERD_URI; - datatype_node->length = datatype_len; - memcpy(serd_node_buffer(datatype_node), datatype, datatype_len); + datatype_node->length = datatype_uri.len; + memcpy(serd_node_buffer(datatype_node), datatype_uri.buf, datatype_uri.len); + } else { node = serd_node_malloc(length, flags, SERD_LITERAL); - memcpy(serd_node_buffer(node), str, length); + memcpy(serd_node_buffer(node), str.buf, length); node->length = length; } @@ -205,6 +217,18 @@ serd_new_literal(const char* const str, } SerdNode* +serd_new_blank(const SerdStringView str) +{ + return serd_new_simple_node(SERD_BLANK, str); +} + +SerdNode* +serd_new_curie(const SerdStringView str) +{ + return serd_new_simple_node(SERD_CURIE, str); +} + +SerdNode* serd_node_copy(const SerdNode* node) { if (!node) { @@ -247,13 +271,9 @@ serd_node_equals(const SerdNode* const a, const SerdNode* const b) } SerdNode* -serd_new_uri(const char* const str) +serd_new_uri(const SerdStringView str) { - const size_t length = strlen(str); - SerdNode* node = serd_node_malloc(length, 0, SERD_URI); - memcpy(serd_node_buffer(node), str, length); - node->length = length; - return node; + return serd_new_simple_node(SERD_URI, str); } SerdNode* @@ -336,52 +356,46 @@ is_uri_path_char(const char c) } SerdNode* -serd_new_file_uri(const char* const path, - const char* const hostname, - SerdURIView* const out) -{ - const size_t path_len = strlen(path); - const size_t hostname_len = hostname ? strlen(hostname) : 0; - const bool is_windows = is_windows_path(path); - size_t uri_len = 0; - char* uri = NULL; - - if (path[0] == '/' || is_windows) { - uri_len = strlen("file://") + hostname_len + is_windows; +serd_new_file_uri(const SerdStringView path, const SerdStringView hostname) +{ + const bool is_windows = is_windows_path(path.buf); + size_t uri_len = 0; + char* uri = NULL; + + if (path.buf[0] == '/' || is_windows) { + uri_len = strlen("file://") + hostname.len + is_windows; uri = (char*)calloc(uri_len + 1, 1); memcpy(uri, "file://", 7); - if (hostname) { - memcpy(uri + 7, hostname, hostname_len + 1); + if (hostname.len) { + memcpy(uri + 7, hostname.buf, hostname.len + 1); } if (is_windows) { - uri[7 + hostname_len] = '/'; + uri[7 + hostname.len] = '/'; } } SerdBuffer buffer = {uri, uri_len}; - for (size_t i = 0; i < path_len; ++i) { - if (is_windows && path[i] == '\\') { + for (size_t i = 0; i < path.len; ++i) { + if (is_windows && path.buf[i] == '\\') { serd_buffer_sink("/", 1, &buffer); - } else if (path[i] == '%') { + } else if (path.buf[i] == '%') { serd_buffer_sink("%%", 2, &buffer); - } else if (is_uri_path_char(path[i])) { - serd_buffer_sink(path + i, 1, &buffer); + } else if (is_uri_path_char(path.buf[i])) { + serd_buffer_sink(path.buf + i, 1, &buffer); } else { char escape_str[10] = {'%', 0, 0, 0, 0, 0, 0, 0, 0, 0}; - snprintf(escape_str + 1, sizeof(escape_str) - 1, "%X", (unsigned)path[i]); + snprintf( + escape_str + 1, sizeof(escape_str) - 1, "%X", (unsigned)path.buf[i]); serd_buffer_sink(escape_str, 3, &buffer); } } serd_buffer_sink_finish(&buffer); SerdNode* node = - serd_new_substring(SERD_URI, (const char*)buffer.buf, buffer.len); - if (out) { - *out = serd_parse_uri(serd_node_buffer(node)); - } + serd_new_uri(SERD_SUBSTRING((const char*)buffer.buf, buffer.len - 1)); free(buffer.buf); return node; @@ -527,6 +541,14 @@ serd_node_uri_view(const SerdNode* const node) : SERD_URI_NULL; } +SERD_PURE_FUNC +static const SerdNode* +serd_node_meta_node(const SerdNode* node) +{ + const size_t len = serd_node_pad_size(node->length); + return node + 1 + (len / sizeof(SerdNode)); +} + const SerdNode* serd_node_datatype(const SerdNode* const node) { @@ -534,8 +556,7 @@ serd_node_datatype(const SerdNode* const node) return NULL; } - const size_t len = serd_node_pad_size(node->length); - const SerdNode* const datatype = node + 1 + (len / sizeof(SerdNode)); + const SerdNode* const datatype = serd_node_meta_node(node); assert(datatype->type == SERD_URI || datatype->type == SERD_CURIE); return datatype; } @@ -547,8 +568,7 @@ serd_node_language(const SerdNode* const node) return NULL; } - const size_t len = serd_node_pad_size(node->length); - const SerdNode* const lang = node + 1 + (len / sizeof(SerdNode)); + const SerdNode* const lang = serd_node_meta_node(node); assert(lang->type == SERD_LITERAL); return lang; } diff --git a/src/serdi.c b/src/serdi.c index e86042bb..ed34b0fb 100644 --- a/src/serdi.c +++ b/src/serdi.c @@ -316,20 +316,19 @@ main(int argc, char** argv) const SerdWriterFlags writer_flags = choose_style(input_syntax, output_syntax, ascii, bulk_write, full_uris); - SerdURIView base_uri = SERD_URI_NULL; - SerdNode* base = NULL; + SerdNode* base = NULL; if (a < argc) { // Base URI given on command line - base_uri = serd_parse_uri(argv[a]); - base = serd_new_parsed_uri(base_uri); + base = serd_new_uri(SERD_STRING((const char*)argv[a])); } else if (from_file && in_fd != stdin) { // Use input file URI - base = serd_new_file_uri(input, NULL, &base_uri); + base = serd_new_file_uri(SERD_STRING(input), SERD_EMPTY_STRING()); } FILE* const out_fd = stdout; - SerdEnv* const env = serd_env_new(base); + SerdEnv* const env = + serd_env_new(base ? serd_node_string_view(base) : SERD_EMPTY_STRING()); - SerdWriter* const writer = serd_writer_new( - output_syntax, writer_flags, env, &base_uri, serd_file_sink, out_fd); + SerdWriter* const writer = + serd_writer_new(output_syntax, writer_flags, env, serd_file_sink, out_fd); SerdReader* const reader = serd_reader_new(input_syntax, @@ -346,11 +345,14 @@ main(int argc, char** argv) serd_writer_set_error_sink(writer, quiet_error_sink, NULL); } - SerdNode* root = root_uri ? serd_new_string(SERD_URI, root_uri) : NULL; - serd_writer_set_root_uri(writer, root); + if (root_uri) { + SerdNode* const root = serd_new_uri(SERD_STRING(root_uri)); + serd_writer_set_root_uri(writer, root); + serd_node_free(root); + } + serd_writer_chop_blank_prefix(writer, chop_prefix); serd_reader_add_blank_prefix(reader, add_prefix); - serd_node_free(root); SerdStatus st = SERD_SUCCESS; if (!from_file) { diff --git a/src/writer.c b/src/writer.c index b4917052..227d516c 100644 --- a/src/writer.c +++ b/src/writer.c @@ -15,6 +15,7 @@ */ #include "byte_sink.h" +#include "env.h" #include "node.h" #include "serd_internal.h" #include "stack.h" @@ -96,7 +97,6 @@ struct SerdWriterImpl { SerdEnv* env; SerdNode* root_node; SerdURIView root_uri; - SerdURIView base_uri; SerdStack anon_stack; SerdByteSink byte_sink; SerdErrorFunc error_func; @@ -602,7 +602,7 @@ write_uri_node(SerdWriter* const writer, } if (!has_scheme && !supports_uriref(writer) && - !serd_env_base_uri(writer->env, NULL)) { + !serd_env_base_uri(writer->env)) { w_err(writer, SERD_ERR_BAD_ARG, "syntax does not support URI reference <%s>\n", @@ -611,22 +611,18 @@ write_uri_node(SerdWriter* const writer, } write_sep(writer, SEP_URI_BEGIN); - if (writer->flags & SERD_WRITE_RESOLVED) { - const SerdURIView uri = serd_parse_uri(node_str); - - SerdURIView in_base_uri; - serd_env_base_uri(writer->env, &in_base_uri); - - const SerdURIView abs_uri = serd_resolve_uri(uri, in_base_uri); - const bool rooted = uri_is_under(&in_base_uri, &writer->root_uri); - const SerdURIView* root = rooted ? &writer->root_uri : &in_base_uri; + if ((writer->flags & SERD_WRITE_RESOLVED) && serd_env_base_uri(writer->env)) { + const SerdURIView base_uri = serd_env_base_uri_view(writer->env); + SerdURIView uri = serd_parse_uri(node_str); + SerdURIView abs_uri = serd_resolve_uri(uri, base_uri); + bool rooted = uri_is_under(&base_uri, &writer->root_uri); + const SerdURIView* root = rooted ? &writer->root_uri : &base_uri; if (writer->syntax == SERD_NTRIPLES || writer->syntax == SERD_NQUADS || - !uri_is_under(&abs_uri, root) || - !uri_is_related(&abs_uri, &in_base_uri)) { + !uri_is_under(&abs_uri, root) || !uri_is_related(&abs_uri, &base_uri)) { serd_write_uri(abs_uri, uri_sink, writer); } else { - serd_write_uri(serd_relative_uri(uri, in_base_uri), uri_sink, writer); + serd_write_uri(serd_relative_uri(uri, base_uri), uri_sink, writer); } } else { write_uri_from_node(writer, node); @@ -939,12 +935,11 @@ serd_writer_finish(SerdWriter* writer) } SerdWriter* -serd_writer_new(SerdSyntax syntax, - SerdWriterFlags flags, - SerdEnv* env, - const SerdURIView* base_uri, - SerdSink ssink, - void* stream) +serd_writer_new(SerdSyntax syntax, + SerdWriterFlags flags, + SerdEnv* env, + SerdSink ssink, + void* stream) { const WriteContext context = WRITE_CONTEXT_NULL; SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter)); @@ -954,7 +949,6 @@ serd_writer_new(SerdSyntax syntax, writer->env = env; writer->root_node = NULL; writer->root_uri = SERD_URI_NULL; - writer->base_uri = base_uri ? *base_uri : SERD_URI_NULL; writer->anon_stack = serd_stack_new(4 * sizeof(WriteContext)); writer->context = context; writer->list_subj = NULL; @@ -992,22 +986,24 @@ serd_writer_chop_blank_prefix(SerdWriter* writer, const char* prefix) SerdStatus serd_writer_set_base_uri(SerdWriter* writer, const SerdNode* uri) { - if (!serd_env_set_base_uri(writer->env, uri)) { - serd_env_base_uri(writer->env, &writer->base_uri); + if (uri->type != SERD_URI) { + return SERD_ERR_BAD_ARG; + } - if (writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG) { - if (ctx(writer, FIELD_GRAPH) || ctx(writer, FIELD_SUBJECT)) { - sink(" .\n\n", 4, writer); - reset_context(writer, true); - } - sink("@base <", 7, writer); - sink(serd_node_string(uri), uri->length, writer); - sink("> .\n", 4, writer); + serd_env_set_base_uri(writer->env, serd_node_string_view(uri)); + + if (writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG) { + if (ctx(writer, FIELD_GRAPH) || ctx(writer, FIELD_SUBJECT)) { + sink(" .\n\n", 4, writer); + reset_context(writer, true); } - writer->indent = 0; - return reset_context(writer, true); + sink("@base <", 7, writer); + sink(serd_node_string(uri), uri->length, writer); + sink("> .\n", 4, writer); } - return SERD_ERR_UNKNOWN; + + writer->indent = 0; + return reset_context(writer, true); } SerdStatus @@ -1019,7 +1015,7 @@ serd_writer_set_root_uri(SerdWriter* writer, const SerdNode* uri) if (uri) { writer->root_node = serd_node_copy(uri); - writer->root_uri = serd_parse_uri(serd_node_string(writer->root_node)); + writer->root_uri = serd_node_uri_view(writer->root_node); } return SERD_SUCCESS; @@ -1030,22 +1026,32 @@ serd_writer_set_prefix(SerdWriter* writer, const SerdNode* name, const SerdNode* uri) { - if (!serd_env_set_prefix(writer->env, name, uri)) { - if (writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG) { - if (ctx(writer, FIELD_GRAPH) || ctx(writer, FIELD_SUBJECT)) { - sink(" .\n\n", 4, writer); - reset_context(writer, true); - } - sink("@prefix ", 8, writer); - sink(serd_node_string(name), name->length, writer); - sink(": <", 3, writer); - write_uri_from_node(writer, uri); - sink("> .\n", 4, writer); + SerdStatus st = SERD_SUCCESS; + + if (name->type != SERD_LITERAL || uri->type != SERD_URI) { + return SERD_ERR_BAD_ARG; + } + + if ((st = serd_env_set_prefix(writer->env, + serd_node_string_view(name), + serd_node_string_view(uri)))) { + return st; + } + + if (writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG) { + if (ctx(writer, FIELD_GRAPH) || ctx(writer, FIELD_SUBJECT)) { + sink(" .\n\n", 4, writer); + reset_context(writer, true); } - writer->indent = 0; - return reset_context(writer, true); + sink("@prefix ", 8, writer); + sink(serd_node_string(name), name->length, writer); + sink(": <", 3, writer); + write_uri_from_node(writer, uri); + sink("> .\n", 4, writer); } - return SERD_ERR_UNKNOWN; + + writer->indent = 0; + return reset_context(writer, true); } void diff --git a/test/test_env.c b/test/test_env.c index d470971e..3753ab35 100644 --- a/test/test_env.c +++ b/test/test_env.c @@ -34,13 +34,12 @@ count_prefixes(void* handle, const SerdNode* name, const SerdNode* uri) static void test_env(void) { - SerdNode* u = serd_new_string(SERD_URI, "http://example.org/foo"); - SerdNode* b = serd_new_string(SERD_CURIE, "invalid"); - SerdNode* e = serd_new_string(SERD_URI, ""); - SerdNode* c = serd_new_string(SERD_CURIE, "eg.2:b"); - SerdNode* s = serd_new_string(SERD_LITERAL, "hello"); - SerdEnv* env = serd_env_new(NULL); - serd_env_set_prefix_from_strings(env, "eg.2", "http://example.org/"); + SerdNode* u = serd_new_uri(SERD_STRING("http://example.org/foo")); + SerdNode* b = serd_new_curie(SERD_STRING("invalid")); + SerdNode* e = serd_new_uri(SERD_EMPTY_STRING()); + SerdNode* c = serd_new_curie(SERD_STRING("eg.2:b")); + SerdNode* s = serd_new_string(SERD_STRING("hello")); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); const SerdNode* prefix_node = NULL; SerdStringView prefix = SERD_EMPTY_STRING(); @@ -50,7 +49,15 @@ test_env(void) assert(serd_env_expand(env, NULL, &prefix, &suffix) == SERD_ERR_BAD_CURIE); - assert(serd_env_set_prefix_from_strings(env, "eg.3", "rel") == + assert(!serd_env_base_uri(env)); + assert(!serd_env_set_base_uri(env, SERD_EMPTY_STRING())); + assert(!serd_env_base_uri(env)); + assert(!serd_env_base_uri(env)); + + serd_env_set_prefix( + env, SERD_STRING("eg.2"), SERD_STRING("http://example.org/")); + + assert(serd_env_set_prefix(env, SERD_STRING("eg.3"), SERD_STRING("rel")) == SERD_ERR_BAD_ARG); assert(!serd_env_expand_node(NULL, u)); @@ -58,13 +65,13 @@ test_env(void) assert(!serd_env_expand_node(env, s)); assert(!serd_env_expand_node(env, e)); - assert(!serd_env_set_base_uri(env, NULL)); + assert(!serd_env_set_base_uri(env, SERD_EMPTY_STRING())); SerdNode* xu = serd_env_expand_node(env, u); assert(!strcmp(serd_node_string(xu), "http://example.org/foo")); serd_node_free(xu); - SerdNode* badpre = serd_new_string(SERD_CURIE, "hm:what"); + SerdNode* badpre = serd_new_curie(SERD_STRING("hm:what")); SerdNode* xbadpre = serd_env_expand_node(env, badpre); assert(!xbadpre); @@ -72,34 +79,31 @@ test_env(void) assert(!strcmp(serd_node_string(xc), "http://example.org/b")); serd_node_free(xc); - SerdNode* lit = serd_new_string(SERD_LITERAL, "hello"); - assert(serd_env_set_prefix(env, b, lit)); - - SerdNode* blank = serd_new_string(SERD_BLANK, "b1"); + SerdNode* blank = serd_new_blank(SERD_STRING("b1")); assert(!serd_env_expand_node(env, blank)); serd_node_free(blank); int n_prefixes = 0; - serd_env_set_prefix_from_strings(env, "eg.2", "http://example.org/"); + serd_env_set_prefix( + env, SERD_STRING("eg.2"), SERD_STRING("http://example.org/")); serd_env_foreach(env, count_prefixes, &n_prefixes); assert(n_prefixes == 1); - SerdNode* shorter_uri = serd_new_string(SERD_URI, "urn:foo"); + SerdNode* shorter_uri = serd_new_uri(SERD_STRING("urn:foo")); assert(!serd_env_qualify(env, shorter_uri, &prefix_node, &suffix)); - assert(!serd_env_set_base_uri(env, u)); - assert(serd_node_equals(serd_env_base_uri(env, NULL), u)); + assert(!serd_env_set_base_uri(env, serd_node_string_view(u))); + assert(serd_node_equals(serd_env_base_uri(env), u)); SerdNode* xe = serd_env_expand_node(env, e); assert(xe); assert(!strcmp(serd_node_string(xe), "http://example.org/foo")); serd_node_free(xe); - assert(!serd_env_set_base_uri(env, NULL)); - assert(!serd_env_base_uri(env, NULL)); + assert(!serd_env_set_base_uri(env, SERD_EMPTY_STRING())); + assert(!serd_env_base_uri(env)); serd_node_free(shorter_uri); - serd_node_free(lit); serd_node_free(badpre); serd_node_free(s); serd_node_free(c); diff --git a/test/test_node.c b/test/test_node.c index a16aa889..3df678cc 100644 --- a/test/test_node.c +++ b/test/test_node.c @@ -152,19 +152,22 @@ test_blob_to_node(void) static void test_node_equals(void) { - const uint8_t replacement_char_str[] = {0xEF, 0xBF, 0xBD, 0}; - SerdNode* lhs = - serd_new_string(SERD_LITERAL, (const char*)replacement_char_str); - SerdNode* rhs = serd_new_string(SERD_LITERAL, "123"); + static const uint8_t replacement_char_str[] = {0xEF, 0xBF, 0xBD, 0}; + + SerdNode* const lhs = + serd_new_string(SERD_SUBSTRING((const char*)replacement_char_str, 3)); + + assert(serd_node_equals(lhs, lhs)); + + SerdNode* const rhs = serd_new_string(SERD_STRING("123")); assert(!serd_node_equals(lhs, rhs)); - SerdNode* qnode = serd_new_string(SERD_CURIE, "foo:bar"); + SerdNode* const qnode = serd_new_curie(SERD_STRING("foo:bar")); assert(!serd_node_equals(lhs, qnode)); - assert(serd_node_equals(lhs, lhs)); + serd_node_free(qnode); assert(!serd_node_copy(NULL)); - serd_node_free(qnode); serd_node_free(lhs); serd_node_free(rhs); } @@ -172,29 +175,31 @@ test_node_equals(void) static void test_node_from_string(void) { - SerdNode* hello = serd_new_string(SERD_LITERAL, "hello\""); - assert(serd_node_length(hello) == 6 && - serd_node_flags(hello) == SERD_HAS_QUOTE && - !strcmp(serd_node_string(hello), "hello\"")); - + SerdNode* const hello = serd_new_string(SERD_STRING("hello\"")); + assert(serd_node_length(hello) == 6); + assert(serd_node_flags(hello) == SERD_HAS_QUOTE); + assert(!strncmp(serd_node_string(hello), "hello\"", 6)); serd_node_free(hello); } static void test_node_from_substring(void) { - SerdNode* a_b = serd_new_substring(SERD_LITERAL, "a\"bc", 3); - assert(serd_node_length(a_b) == 3 && serd_node_flags(a_b) == SERD_HAS_QUOTE && - !strncmp(serd_node_string(a_b), "a\"b", 3)); - - serd_node_free(a_b); - a_b = serd_new_substring(SERD_LITERAL, "a\"bc", 10); - assert(serd_node_length(a_b) == 4 && serd_node_flags(a_b) == SERD_HAS_QUOTE && - !strncmp(serd_node_string(a_b), "a\"bc", 4)); + SerdNode* const a_b = serd_new_string(SERD_SUBSTRING("a\"bc", 3)); + assert(serd_node_length(a_b) == 3); + assert(serd_node_flags(a_b) == SERD_HAS_QUOTE); + assert(strlen(serd_node_string(a_b)) == 3); + assert(!strncmp(serd_node_string(a_b), "a\"b", 3)); serd_node_free(a_b); } static void +test_simple_node(void) +{ + assert(!serd_new_simple_node(SERD_LITERAL, SERD_STRING("Literal"))); +} + +static void check_copy_equals(const SerdNode* const node) { SerdNode* const copy = serd_node_copy(node); @@ -207,14 +212,18 @@ check_copy_equals(const SerdNode* const node) static void test_literal(void) { - SerdNode* hello2 = serd_new_literal("hello\"", NULL, NULL); + SerdNode* hello2 = serd_new_literal( + SERD_STRING("hello\""), SERD_EMPTY_STRING(), SERD_EMPTY_STRING()); + assert(serd_node_length(hello2) == 6 && serd_node_flags(hello2) == SERD_HAS_QUOTE && !strcmp(serd_node_string(hello2), "hello\"")); check_copy_equals(hello2); serd_node_free(hello2); - SerdNode* hello_l = serd_new_literal("hello_l\"", NULL, "en"); + SerdNode* hello_l = serd_new_literal( + SERD_STRING("hello_l\""), SERD_EMPTY_STRING(), SERD_STRING("en")); + assert(serd_node_length(hello_l) == 8); assert(!strcmp(serd_node_string(hello_l), "hello_l\"")); assert(serd_node_flags(hello_l) == (SERD_HAS_QUOTE | SERD_HAS_LANGUAGE)); @@ -225,8 +234,10 @@ test_literal(void) check_copy_equals(hello_l); serd_node_free(hello_l); - SerdNode* hello_dt = - serd_new_literal("hello_dt\"", "http://example.org/Thing", NULL); + SerdNode* hello_dt = serd_new_literal(SERD_STRING("hello_dt\""), + SERD_STRING("http://example.org/Thing"), + SERD_EMPTY_STRING()); + assert(serd_node_length(hello_dt) == 9); assert(!strcmp(serd_node_string(hello_dt), "hello_dt\"")); assert(serd_node_flags(hello_dt) == (SERD_HAS_QUOTE | SERD_HAS_DATATYPE)); @@ -239,6 +250,16 @@ test_literal(void) serd_node_free(hello_dt); } +static void +test_blank(void) +{ + SerdNode* blank = serd_new_blank(SERD_STRING("b0")); + assert(serd_node_length(blank) == 2); + assert(serd_node_flags(blank) == 0); + assert(!strcmp(serd_node_string(blank), "b0")); + serd_node_free(blank); +} + int main(void) { @@ -249,7 +270,9 @@ main(void) test_node_equals(); test_node_from_string(); test_node_from_substring(); + test_simple_node(); test_literal(); + test_blank(); printf("Success\n"); return 0; diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c index 258b3c91..3a78889c 100644 --- a/test/test_reader_writer.c +++ b/test/test_reader_writer.c @@ -177,27 +177,28 @@ static void test_writer(const char* const path) { FILE* fd = fopen(path, "wb"); - SerdEnv* env = serd_env_new(NULL); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); assert(fd); - SerdWriter* writer = - serd_writer_new(SERD_TURTLE, 0, env, NULL, serd_file_sink, fd); + SerdWriter* writer = serd_writer_new(SERD_TURTLE, 0, env, serd_file_sink, fd); assert(writer); serd_writer_chop_blank_prefix(writer, "tmp"); serd_writer_chop_blank_prefix(writer, NULL); - SerdNode* lit = serd_new_string(SERD_LITERAL, "hello"); + SerdNode* lit = serd_new_string(SERD_STRING("hello")); assert(serd_writer_set_base_uri(writer, lit)); assert(serd_writer_set_prefix(writer, lit, lit)); assert(serd_writer_end_anon(writer, NULL)); assert(serd_writer_env(writer) == env); - uint8_t buf[] = {0xEF, 0xBF, 0xBD, 0}; - SerdNode* s = serd_new_string(SERD_URI, ""); - SerdNode* p = serd_new_string(SERD_URI, "http://example.org/pred"); - SerdNode* o = serd_new_string(SERD_LITERAL, (char*)buf); + static const uint8_t buf[] = {0xEF, 0xBF, 0xBD, 0}; + const SerdStringView buf_view = {(const char*)buf, 3}; + + SerdNode* s = serd_new_uri(SERD_EMPTY_STRING()); + SerdNode* p = serd_new_uri(SERD_STRING("http://example.org/pred")); + SerdNode* o = serd_new_string(buf_view); // Write 3 invalid statements (should write nothing) const SerdNode* junk[][3] = {{s, o, o}, {o, p, o}, {s, o, p}}; @@ -206,26 +207,33 @@ test_writer(const char* const path) writer, 0, NULL, junk[i][0], junk[i][1], junk[i][2])); } - SerdNode* t = serd_new_literal((char*)buf, "urn:Type", NULL); - SerdNode* l = serd_new_literal((char*)buf, NULL, "en"); + static const SerdStringView empty = SERD_EMPTY_STRING(); + static const SerdStringView urn_Type = SERD_STRING("urn:Type"); + static const SerdStringView en = SERD_STRING("en"); + + SerdNode* const t = serd_new_literal(buf_view, urn_Type, empty); + SerdNode* const l = serd_new_literal(buf_view, empty, en); const SerdNode* good[][3] = {{s, p, o}, {s, p, t}, {s, p, l}}; + for (size_t i = 0; i < sizeof(good) / (sizeof(SerdNode*) * 3); ++i) { assert(!serd_writer_write_statement( writer, 0, NULL, good[i][0], good[i][1], good[i][2])); } // Write statements with bad UTF-8 (should be replaced) - const char bad_str[] = {(char)0xFF, (char)0x90, 'h', 'i', 0}; - SerdNode* bad_lit = serd_new_string(SERD_LITERAL, bad_str); - SerdNode* bad_uri = serd_new_string(SERD_URI, bad_str); + const char bad_str[] = {(char)0xFF, (char)0x90, 'h', 'i', 0}; + const SerdStringView bad_view = {(const char*)bad_str, 4}; + SerdNode* bad_lit = serd_new_string(bad_view); + SerdNode* bad_uri = serd_new_uri(bad_view); assert(!serd_writer_write_statement(writer, 0, NULL, s, p, bad_lit)); assert(!serd_writer_write_statement(writer, 0, NULL, s, p, bad_uri)); - serd_node_free(bad_lit); + serd_node_free(bad_uri); + serd_node_free(bad_lit); // Write 1 valid statement serd_node_free(o); - o = serd_new_string(SERD_LITERAL, "hello"); + o = serd_new_string(SERD_STRING("hello")); assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o)); serd_writer_free(writer); @@ -236,13 +244,13 @@ test_writer(const char* const path) // Test buffer sink SerdBuffer buffer = {NULL, 0}; - writer = - serd_writer_new(SERD_TURTLE, 0, env, NULL, serd_buffer_sink, &buffer); + writer = serd_writer_new(SERD_TURTLE, 0, env, serd_buffer_sink, &buffer); - o = serd_new_string(SERD_URI, "http://example.org/base"); - assert(!serd_writer_set_base_uri(writer, o)); + SerdNode* const base = serd_new_uri(SERD_STRING("http://example.org/base")); - serd_node_free(o); + serd_writer_set_base_uri(writer, base); + + serd_node_free(base); serd_writer_free(writer); char* out = serd_buffer_sink_finish(&buffer); @@ -265,7 +273,7 @@ test_reader(const char* path) assert(reader); assert(serd_reader_handle(reader) == rt); - SerdNode* g = serd_new_string(SERD_URI, "http://example.org/"); + SerdNode* g = serd_new_uri(SERD_STRING("http://example.org/")); serd_reader_set_default_graph(reader, g); serd_reader_add_blank_prefix(reader, "tmp"); diff --git a/test/test_uri.c b/test/test_uri.c index 4eb4cc29..5d204a71 100644 --- a/test/test_uri.c +++ b/test/test_uri.c @@ -54,7 +54,9 @@ test_file_uri(const char* const hostname, expected_path = path; } - SerdNode* node = serd_new_file_uri(path, hostname, 0); + SerdNode* node = + serd_new_file_uri(SERD_STRING(path), SERD_OPTIONAL_STRING(hostname)); + const char* node_str = serd_node_string(node); char* out_hostname = NULL; char* out_path = serd_parse_file_uri(node_str, &out_hostname); @@ -160,7 +162,7 @@ check_rel_uri(const char* uri_string, SerdNode* const rel = is_within ? serd_new_parsed_uri(serd_relative_uri(uri, base_uri)) - : serd_new_uri(uri_string); + : serd_new_uri(SERD_STRING(uri_string)); const int ret = strcmp(serd_node_string(rel), expected); serd_node_free(rel); @@ -170,8 +172,10 @@ check_rel_uri(const char* uri_string, static void test_relative_uri(void) { - SerdNode* const root = serd_new_uri("http://example.org/a/b/ignored"); - SerdNode* const base = serd_new_uri("http://example.org/a/b/c/"); + SerdNode* const root = + serd_new_uri(SERD_STRING("http://example.org/a/b/ignored")); + + SerdNode* const base = serd_new_uri(SERD_STRING("http://example.org/a/b/c/")); check_rel_uri("http://example.org/a/b/c/foo", base, NULL, "foo"); check_rel_uri("http://example.org/a/", base, NULL, "../../"); diff --git a/test/test_writer.c b/test/test_writer.c index 531e229d..756535ad 100644 --- a/test/test_writer.c +++ b/test/test_writer.c @@ -22,18 +22,46 @@ #include <string.h> static void +test_write_bad_prefix(void) +{ + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdBuffer buffer = {NULL, 0}; + SerdWriter* writer = + serd_writer_new(SERD_TURTLE, 0u, env, serd_buffer_sink, &buffer); + + assert(writer); + + SerdNode* name = serd_new_string(SERD_STRING("eg")); + SerdNode* uri = serd_new_uri(SERD_STRING("rel")); + + assert(serd_writer_set_prefix(writer, name, uri) == SERD_ERR_BAD_ARG); + + char* const out = serd_buffer_sink_finish(&buffer); + + assert(!strcmp(out, "")); + serd_free(out); + + serd_node_free(uri); + serd_node_free(name); + serd_writer_free(writer); + serd_env_free(env); +} + +static void test_write_long_literal(void) { - SerdEnv* env = serd_env_new(NULL); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); SerdBuffer buffer = {NULL, 0}; SerdWriter* writer = - serd_writer_new(SERD_TURTLE, 0u, env, NULL, serd_buffer_sink, &buffer); + serd_writer_new(SERD_TURTLE, 0u, env, serd_buffer_sink, &buffer); assert(writer); - SerdNode* s = serd_new_string(SERD_URI, "http://example.org/s"); - SerdNode* p = serd_new_string(SERD_URI, "http://example.org/p"); - SerdNode* o = serd_new_string(SERD_LITERAL, "hello \"\"\"world\"\"\"!"); + SerdNode* s = serd_new_uri(SERD_STRING("http://example.org/s")); + SerdNode* p = serd_new_uri(SERD_STRING("http://example.org/p")); + SerdNode* o = serd_new_literal(SERD_STRING("hello \"\"\"world\"\"\"!"), + SERD_EMPTY_STRING(), + SERD_EMPTY_STRING()); assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o)); @@ -56,6 +84,7 @@ test_write_long_literal(void) int main(void) { + test_write_bad_prefix(); test_write_long_literal(); return 0; |