diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/env.c | 32 | ||||
-rw-r--r-- | src/node.c | 121 | ||||
-rw-r--r-- | src/node.h | 17 | ||||
-rw-r--r-- | src/reader.c | 2 | ||||
-rw-r--r-- | src/serdi.c | 5 | ||||
-rw-r--r-- | src/uri.c | 376 | ||||
-rw-r--r-- | src/uri_utils.h | 22 | ||||
-rw-r--r-- | src/writer.c | 27 |
8 files changed, 290 insertions, 312 deletions
@@ -85,15 +85,16 @@ serd_env_set_base_uri(SerdEnv* env, const SerdNode* uri) return SERD_SUCCESS; } - // Resolve base URI and create a new node and URI for it - SerdURIView base_uri; - SerdNode* base_uri_node = - serd_new_uri_from_node(uri, &env->base_uri, &base_uri); + // 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); // Replace the current base URI serd_node_free(env->base_uri_node); - env->base_uri_node = base_uri_node; - env->base_uri = base_uri; + env->base_uri_node = new_base_node; + env->base_uri = serd_node_uri_view(env->base_uri_node); return SERD_SUCCESS; } @@ -142,15 +143,17 @@ serd_env_set_prefix(SerdEnv* env, const SerdNode* name, const SerdNode* uri) if (serd_uri_string_has_scheme(serd_node_string(uri))) { // Set prefix to absolute URI serd_env_add(env, name, uri); + } else if (!env->base_uri_node) { + return SERD_ERR_BAD_ARG; } else { // Resolve relative URI and create a new node and URI for it - SerdURIView abs_uri; - SerdNode* abs_uri_node = - serd_new_uri_from_node(uri, &env->base_uri, &abs_uri); + SerdNode* const abs_uri = + serd_new_resolved_uri(serd_node_string_view(uri), env->base_uri); // Set prefix to resolved (absolute) URI - serd_env_add(env, name, abs_uri_node); - serd_node_free(abs_uri_node); + serd_env_add(env, name, abs_uri); + + serd_node_free(abs_uri); } return SERD_SUCCESS; @@ -224,16 +227,15 @@ serd_env_expand_node(const SerdEnv* env, const SerdNode* node) switch (node->type) { case SERD_LITERAL: break; - case SERD_URI: { - SerdURIView ignored; - return serd_new_uri_from_node(node, &env->base_uri, &ignored); - } + case SERD_URI: + return serd_new_resolved_uri(serd_node_string_view(node), env->base_uri); case SERD_CURIE: { SerdStringView prefix; SerdStringView suffix; if (serd_env_expand(env, node, &prefix, &suffix)) { return NULL; } + const size_t len = prefix.len + suffix.len; SerdNode* ret = serd_node_malloc(len, 0, SERD_URI); char* buf = serd_node_buffer(ret); @@ -191,7 +191,7 @@ serd_node_equals(const SerdNode* a, const SerdNode* b) static size_t serd_uri_string_length(const SerdURIView* uri) { - size_t len = uri->path_base.len; + size_t len = uri->path_prefix.len; #define ADD_LEN(field, n_delims) \ if ((field).len) { \ @@ -217,29 +217,61 @@ string_sink(const void* buf, size_t len, void* stream) } SerdNode* -serd_new_uri_from_node(const SerdNode* uri_node, - const SerdURIView* base, - SerdURIView* out) +serd_new_uri(const char* str) { - const char* uri_str = serd_node_string(uri_node); - return (uri_node && uri_node->type == SERD_URI && uri_str) - ? serd_new_uri_from_string(uri_str, base, out) - : NULL; + const size_t n_bytes = strlen(str); + SerdNode* node = serd_node_malloc(n_bytes, 0, SERD_URI); + memcpy(serd_node_buffer(node), str, n_bytes); + node->n_bytes = n_bytes; + return node; +} + +SerdNode* +serd_new_parsed_uri(const SerdURIView uri) +{ + const size_t len = serd_uri_string_length(&uri); + SerdNode* const node = serd_node_malloc(len, 0, SERD_URI); + char* ptr = serd_node_buffer(node); + const size_t actual_len = serd_write_uri(uri, string_sink, &ptr); + + serd_node_buffer(node)[actual_len] = '\0'; + node->n_bytes = actual_len; + + return node; +} + +static SerdNode* +serd_new_from_uri(const SerdURIView uri, const SerdURIView base) +{ + const SerdURIView abs_uri = serd_resolve_uri(uri, base); + const size_t len = serd_uri_string_length(&abs_uri); + SerdNode* node = serd_node_malloc(len, 0, SERD_URI); + char* ptr = serd_node_buffer(node); + const size_t actual_len = serd_write_uri(abs_uri, string_sink, &ptr); + + serd_node_buffer(node)[actual_len] = '\0'; + node->n_bytes = actual_len; + + return node; } SerdNode* -serd_new_uri_from_string(const char* str, - const SerdURIView* base, - SerdURIView* out) +serd_new_resolved_uri(const SerdStringView string, const SerdURIView base) { - if (!str || str[0] == '\0') { + if (!string.len || !string.buf[0]) { // Empty URI => Base URI, or nothing if no base is given - return base ? serd_new_uri(base, NULL, out) : NULL; + return base.scheme.buf ? serd_new_from_uri(base, SERD_URI_NULL) : NULL; } - SerdURIView uri; - serd_uri_parse(str, &uri); - return serd_new_uri(&uri, base, out); // Resolve/Serialise + const SerdURIView uri = serd_parse_uri(string.buf); + SerdNode* const result = serd_new_from_uri(uri, base); + + if (!serd_uri_string_has_scheme(serd_node_string(result))) { + serd_node_free(result); + return NULL; + } + + return result; } static inline bool @@ -321,59 +353,13 @@ serd_new_file_uri(const char* path, const char* hostname, SerdURIView* out) SerdNode* node = serd_new_substring(SERD_URI, (const char*)buffer.buf, buffer.len); if (out) { - serd_uri_parse(serd_node_buffer(node), out); + *out = serd_parse_uri(serd_node_buffer(node)); } free(buffer.buf); return node; } -SerdNode* -serd_new_uri(const SerdURIView* uri, const SerdURIView* base, SerdURIView* out) -{ - SerdURIView abs_uri = *uri; - if (base) { - serd_uri_resolve(uri, base, &abs_uri); - } - - const size_t len = serd_uri_string_length(&abs_uri); - SerdNode* node = serd_node_malloc(len, 0, SERD_URI); - char* ptr = serd_node_buffer(node); - const size_t actual_len = serd_uri_serialise(&abs_uri, string_sink, &ptr); - - serd_node_buffer(node)[actual_len] = '\0'; - node->n_bytes = actual_len; - - if (out) { - serd_uri_parse(serd_node_buffer(node), out); // TODO: avoid double parse - } - - return node; -} - -SerdNode* -serd_new_relative_uri(const SerdURIView* uri, - const SerdURIView* base, - const SerdURIView* root, - SerdURIView* out) -{ - const size_t uri_len = serd_uri_string_length(uri); - const size_t base_len = serd_uri_string_length(base); - SerdNode* node = serd_node_malloc(uri_len + base_len, 0, SERD_URI); - char* ptr = serd_node_buffer(node); - const size_t actual_len = - serd_uri_serialise_relative(uri, base, root, string_sink, &ptr); - - serd_node_buffer(node)[actual_len] = '\0'; - node->n_bytes = actual_len; - - if (out) { - serd_uri_parse(serd_node_buffer(node), out); // TODO: avoid double parse - } - - return node; -} - static inline unsigned serd_digits(double abs) { @@ -510,13 +496,8 @@ serd_node_string_view(const SerdNode* SERD_NONNULL node) SerdURIView serd_node_uri_view(const SerdNode* SERD_NONNULL node) { - SerdURIView result = SERD_URI_NULL; - - if (node->type == SERD_URI) { - serd_uri_parse(serd_node_string(node), &result); - } - - return result; + return (node->type == SERD_URI) ? serd_parse_uri(serd_node_string(node)) + : SERD_URI_NULL; } const SerdNode* @@ -27,22 +27,27 @@ struct SerdNodeImpl { SerdNodeType type; /**< Node type */ }; -static inline char* -serd_node_buffer(SerdNode* node) +static inline char* SERD_NONNULL +serd_node_buffer(SerdNode* SERD_NONNULL node) { return (char*)(node + 1); } -static inline const char* -serd_node_buffer_c(const SerdNode* node) +static inline const char* SERD_NONNULL +serd_node_buffer_c(const SerdNode* SERD_NONNULL node) { return (const char*)(node + 1); } -SerdNode* +SerdNode* SERD_ALLOCATED serd_node_malloc(size_t n_bytes, SerdNodeFlags flags, SerdNodeType type); void -serd_node_set(SerdNode** dst, const SerdNode* src); +serd_node_set(SerdNode* SERD_NONNULL* SERD_NONNULL dst, + const SerdNode* SERD_NONNULL src); + +/// Create a new URI from a string, resolved against a base URI +SerdNode* SERD_ALLOCATED +serd_new_resolved_uri(SerdStringView string, SerdURIView base_uri); #endif // SERD_NODE_H diff --git a/src/reader.c b/src/reader.c index 06fb25ea..6e814d93 100644 --- a/src/reader.c +++ b/src/reader.c @@ -270,7 +270,7 @@ serd_reader_set_default_graph(SerdReader* reader, const SerdNode* graph) SerdStatus serd_reader_read_file(SerdReader* reader, const char* uri) { - char* const path = serd_file_uri_parse(uri, NULL); + char* const path = serd_parse_file_uri(uri, NULL); if (!path) { return SERD_ERR_BAD_ARG; } diff --git a/src/serdi.c b/src/serdi.c index 3d3b414c..b391f549 100644 --- a/src/serdi.c +++ b/src/serdi.c @@ -286,7 +286,7 @@ main(int argc, char** argv) in_name = in_name ? in_name : input; if (!in_fd) { if (!strncmp(input, "file:", 5)) { - input_path = serd_file_uri_parse(input, NULL); + input_path = serd_parse_file_uri(input, NULL); input = input_path; } if (!input || !(in_fd = serd_fopen(input, "rb"))) { @@ -312,7 +312,8 @@ main(int argc, char** argv) SerdURIView base_uri = SERD_URI_NULL; SerdNode* base = NULL; if (a < argc) { // Base URI given on command line - base = serd_new_uri_from_string((const char*)argv[a], NULL, &base_uri); + base_uri = serd_parse_uri(argv[a]); + base = serd_new_parsed_uri(base_uri); } else if (from_file && in_fd != stdin) { // Use input file URI base = serd_new_file_uri(input, NULL, &base_uri); } @@ -26,12 +26,13 @@ #include <string.h> char* -serd_file_uri_parse(const char* uri, char** hostname) +serd_parse_file_uri(const char* uri, char** hostname) { const char* path = uri; if (hostname) { *hostname = NULL; } + if (!strncmp(uri, "file://", 7)) { const char* auth = uri + 7; if (*auth == '/') { // No hostname @@ -40,6 +41,7 @@ serd_file_uri_parse(const char* uri, char** hostname) if (!(path = strchr(auth, '/'))) { return NULL; } + if (hostname) { *hostname = (char*)calloc((size_t)(path - auth + 1), 1); memcpy(*hostname, auth, (size_t)(path - auth)); @@ -69,36 +71,34 @@ serd_file_uri_parse(const char* uri, char** hostname) serd_buffer_sink(s, 1, &buffer); } } + return serd_buffer_sink_finish(&buffer); } +/// RFC3986: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) bool -serd_uri_string_has_scheme(const char* utf8) +serd_uri_string_has_scheme(const char* const string) { - // RFC3986: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - if (!utf8 || !is_alpha(utf8[0])) { - return false; // Invalid scheme initial character, URI is relative - } - - for (char c = 0; (c = *++utf8) != '\0';) { - if (!is_uri_scheme_char(c)) { - return false; - } + if (is_alpha(string[0])) { + for (size_t i = 1; string[i]; ++i) { + if (!is_uri_scheme_char(string[i])) { + return false; // Non-scheme character before a ':' + } - if (c == ':') { - return true; // End of scheme + if (string[i] == ':') { + return true; // Valid scheme terminated by a ':' + } } } - return false; + return false; // String doesn't start with a scheme } -SerdStatus -serd_uri_parse(const char* utf8, SerdURIView* out) +SerdURIView +serd_parse_uri(const char* const string) { - *out = SERD_URI_NULL; - - const char* ptr = utf8; + SerdURIView result = SERD_URI_NULL; + const char* ptr = string; /* See http://tools.ietf.org/html/rfc3986#section-3 URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] @@ -112,11 +112,11 @@ serd_uri_parse(const char* utf8, SerdURIView* out) case '/': case '?': case '#': - ptr = utf8; + ptr = string; goto path; // Relative URI (starts with path by definition) case ':': - out->scheme.buf = utf8; - out->scheme.len = (size_t)((ptr++) - utf8); + result.scheme.buf = string; + result.scheme.len = (size_t)((ptr++) - string); goto maybe_authority; // URI with scheme case '+': case '-': @@ -137,7 +137,7 @@ serd_uri_parse(const char* utf8, SerdURIView* out) maybe_authority: if (*ptr == '/' && *(ptr + 1) == '/') { ptr += 2; - out->authority.buf = ptr; + result.authority.buf = ptr; for (char c = 0; (c = *ptr) != '\0'; ++ptr) { switch (c) { case '/': @@ -147,7 +147,7 @@ maybe_authority: case '#': goto fragment; default: - ++out->authority.len; + ++result.authority.len; } } } @@ -166,8 +166,8 @@ path: default: break; } - out->path.buf = ptr; - out->path.len = 0; + result.path.buf = ptr; + result.path.len = 0; for (char c = 0; (c = *ptr) != '\0'; ++ptr) { switch (c) { case '?': @@ -175,7 +175,7 @@ path: case '#': goto fragment; default: - ++out->path.len; + ++result.path.len; } } @@ -185,12 +185,12 @@ path: */ query: if (*ptr == '?') { - out->query.buf = ++ptr; + result.query.buf = ++ptr; for (char c = 0; (c = *ptr) != '\0'; ++ptr) { if (c == '#') { goto fragment; } - ++out->query.len; + ++result.query.len; } } @@ -200,14 +200,14 @@ query: */ fragment: if (*ptr == '#') { - out->fragment.buf = ptr; + result.fragment.buf = ptr; while (*ptr++ != '\0') { - ++out->fragment.len; + ++result.fragment.len; } } end: - return SERD_SUCCESS; + return result; } /** @@ -217,64 +217,33 @@ end: @return A pointer to the new start of `path` */ static const char* -remove_dot_segments(const char* path, size_t len, size_t* up) +remove_dot_segments(const char* path, const size_t len, size_t* up) { - const char* begin = path; - const char* const end = path + len; - *up = 0; - while (begin < end) { - switch (begin[0]) { - case '.': - switch (begin[1]) { - case '/': - begin += 2; // Chop leading "./" - break; - case '.': - switch (begin[2]) { - case '\0': - ++*up; - begin += 2; // Chop input ".." - break; - case '/': - ++*up; - begin += 3; // Chop leading "../" - break; - default: - return begin; - } - break; - case '\0': - ++begin; // Chop input "." - // fallthru - default: - return begin; - } - break; - case '/': - switch (begin[1]) { - case '.': - switch (begin[2]) { - case '/': - begin += 2; // Leading "/./" => "/" - break; - case '.': - switch (begin[3]) { - case '/': - ++*up; - begin += 3; // Leading "/../" => "/" - } - break; - default: - return begin; - } - } // else fall through - default: - return begin; // Finished chopping dot components + + for (size_t i = 0; i < len;) { + const char* const p = path + i; + if (!strncmp(p, "./", 2)) { + i += 2; // Chop leading "./" + } else if (!strncmp(p, "..", 3)) { + ++*up; + i += 2; // Chop input ".." + } else if (!strncmp(p, "../", 3)) { + ++*up; + i += 3; // Chop leading "../" + } else if (!strncmp(p, ".", 2)) { + ++i; // Chop input "." + } else if (!strncmp(p, "/./", 3)) { + i += 2; // Leading "/./" => "/" + } else if (!strncmp(p, "/../", 4)) { + ++*up; + i += 3; // Leading "/../" => "/" + } else { + return p; } } - return begin; + return path + len; } /// Merge `base` and `path` in-place @@ -305,172 +274,189 @@ merge(SerdStringView* base, SerdStringView* path) } /// See http://tools.ietf.org/html/rfc3986#section-5.2.2 -void -serd_uri_resolve(const SerdURIView* r, const SerdURIView* base, SerdURIView* t) +SerdURIView +serd_resolve_uri(const SerdURIView r, const SerdURIView base) { - if (!base->scheme.len) { - *t = *r; // Don't resolve against non-absolute URIs - return; + if (r.scheme.len || !base.scheme.len) { + return r; // No resolution necessary || possible (respectively) } - t->path_base.buf = NULL; - t->path_base.len = 0; - if (r->scheme.len) { - *t = *r; + SerdURIView t = SERD_URI_NULL; + + t.path_prefix.buf = NULL; + t.path_prefix.len = 0; + + if (r.authority.len) { + t.authority = r.authority; + t.path = r.path; + t.query = r.query; } else { - if (r->authority.len) { - t->authority = r->authority; - t->path = r->path; - t->query = r->query; + t.path = r.path; + if (!r.path.len) { + t.path_prefix = base.path; + t.query = r.query.len ? r.query : base.query; } else { - t->path = r->path; - if (!r->path.len) { - t->path_base = base->path; - if (r->query.len) { - t->query = r->query; - } else { - t->query = base->query; - } - } else { - if (r->path.buf[0] != '/') { - t->path_base = base->path; - } - merge(&t->path_base, &t->path); - t->query = r->query; + if (r.path.buf[0] != '/') { + t.path_prefix = base.path; } - t->authority = base->authority; + + merge(&t.path_prefix, &t.path); + t.query = r.query; } - t->scheme = base->scheme; - t->fragment = r->fragment; + + t.authority = base.authority; } + + t.scheme = base.scheme; + t.fragment = r.fragment; + + return t; } -/** Write the path of `uri` starting at index `i` */ -static size_t -write_path_tail(SerdSink sink, void* stream, const SerdURIView* uri, size_t i) +SerdURIView +serd_relative_uri(const SerdURIView uri, const SerdURIView base) { - size_t len = 0; - if (i < uri->path_base.len) { - len += sink(uri->path_base.buf + i, uri->path_base.len - i, stream); + if (!uri_is_related(&uri, &base)) { + return uri; } - if (uri->path.buf) { - if (i < uri->path_base.len) { - len += sink(uri->path.buf, uri->path.len, stream); - } else { - const size_t j = (i - uri->path_base.len); - len += sink(uri->path.buf + j, uri->path.len - j, stream); - } - } + SerdURIView result = SERD_URI_NULL; - return len; -} + // Regardless of the path, the query and/or fragment come along + result.query = uri.query; + result.fragment = uri.fragment; -/** Write the path of `uri` relative to the path of `base`. */ -static size_t -write_rel_path(SerdSink sink, - void* stream, - const SerdURIView* uri, - const SerdURIView* base) -{ - const size_t path_len = uri_path_len(uri); - const size_t base_len = uri_path_len(base); + const size_t path_len = uri_path_len(&uri); + const size_t base_len = uri_path_len(&base); const size_t min_len = (path_len < base_len) ? path_len : base_len; // Find the last separator common to both paths size_t last_shared_sep = 0; size_t i = 0; - for (; i < min_len && uri_path_at(uri, i) == uri_path_at(base, i); ++i) { - if (uri_path_at(uri, i) == '/') { + for (; i < min_len && uri_path_at(&uri, i) == uri_path_at(&base, i); ++i) { + if (uri_path_at(&uri, i) == '/') { last_shared_sep = i; } } - if (i == path_len && i == base_len) { // Paths are identical - return 0; + // If the URI and base URI have identical paths, the relative path is empty + if (i == path_len && i == base_len) { + result.path.buf = uri.path.buf; + result.path.len = 0; + return result; } + // Otherwise, we need to build the relative path out of string slices + // Find the number of up references ("..") required size_t up = 0; for (size_t s = last_shared_sep + 1; s < base_len; ++s) { - if (uri_path_at(base, s) == '/') { + if (uri_path_at(&base, s) == '/') { ++up; } } - // Write up references - size_t len = 0; - for (size_t u = 0; u < up; ++u) { - len += sink("../", 3, stream); + if (up > 0) { + if (last_shared_sep < uri.path_prefix.len) { + return SERD_URI_NULL; + } + + // Special representation: NULL buffer and len set to the depth + result.path_prefix.len = up; } - if (last_shared_sep == 0 && up == 0) { - len += sink("/", 1, stream); + if (last_shared_sep < uri.path_prefix.len) { + result.path_prefix.buf = uri.path_prefix.buf + last_shared_sep + 1; + result.path_prefix.len = uri.path_prefix.len - last_shared_sep - 1; + result.path = uri.path; + } else { + result.path.buf = uri.path.buf + last_shared_sep + 1; + result.path.len = uri.path.len - last_shared_sep - 1; } - // Write suffix - return len + write_path_tail(sink, stream, uri, last_shared_sep + 1); + return result; } -static uint8_t -serd_uri_path_starts_without_slash(const SerdURIView* uri) +SERD_API +bool +serd_uri_is_within(const SerdURIView uri, const SerdURIView base) { - return ((uri->path_base.len || uri->path.len) && - ((!uri->path_base.len || uri->path_base.buf[0] != '/') && - (!uri->path.len || uri->path.buf[0] != '/'))); + if (!base.scheme.len || !slice_equals(&base.scheme, &uri.scheme) || + !slice_equals(&base.authority, &uri.authority)) { + return false; + } + + bool differ = false; + const size_t path_len = uri_path_len(&uri); + const size_t base_len = uri_path_len(&base); + + size_t last_base_slash = 0; + for (size_t i = 0; i < path_len && i < base_len; ++i) { + const char u = uri_path_at(&uri, i); + const char b = uri_path_at(&base, i); + + differ = differ || u != b; + if (b == '/') { + last_base_slash = i; + if (differ) { + return false; + } + } + } + + for (size_t i = last_base_slash + 1; i < base_len; ++i) { + if (uri_path_at(&base, i) == '/') { + return false; + } + } + + return true; } /// See http://tools.ietf.org/html/rfc3986#section-5.3 size_t -serd_uri_serialise_relative(const SerdURIView* uri, - const SerdURIView* base, - const SerdURIView* root, - SerdSink sink, - void* stream) +serd_write_uri(const SerdURIView uri, SerdSink sink, void* stream) { - size_t len = 0; - const bool relative = - root ? uri_is_under(uri, root) : uri_is_related(uri, base); + size_t len = 0; - if (relative) { - len = write_rel_path(sink, stream, uri, base); + if (uri.scheme.buf) { + len += sink(uri.scheme.buf, uri.scheme.len, stream); + len += sink(":", 1, stream); } - if (!relative || (!len && base->query.buf)) { - if (uri->scheme.buf) { - len += sink(uri->scheme.buf, uri->scheme.len, stream); - len += sink(":", 1, stream); + if (uri.authority.buf) { + len += sink("//", 2, stream); + len += sink(uri.authority.buf, uri.authority.len, stream); + + if (uri.authority.len > 0 && uri_path_len(&uri) > 0 && + uri_path_at(&uri, 0) != '/') { + // Special case: ensure path begins with a slash + // https://tools.ietf.org/html/rfc3986#section-3.2 + len += sink("/", 1, stream); } - if (uri->authority.buf) { - len += sink("//", 2, stream); - len += sink(uri->authority.buf, uri->authority.len, stream); - if (uri->authority.len > 0 && - uri->authority.buf[uri->authority.len - 1] != '/' && - serd_uri_path_starts_without_slash(uri)) { - // Special case: ensure path begins with a slash - // https://tools.ietf.org/html/rfc3986#section-3.2 - len += sink("/", 1, stream); - } + } + + if (uri.path_prefix.buf) { + len += sink(uri.path_prefix.buf, uri.path_prefix.len, stream); + } else if (uri.path_prefix.len) { + for (size_t i = 0; i < uri.path_prefix.len; ++i) { + len += sink("../", 3, stream); } - len += write_path_tail(sink, stream, uri, 0); } - if (uri->query.buf) { + if (uri.path.buf) { + len += sink(uri.path.buf, uri.path.len, stream); + } + + if (uri.query.buf) { len += sink("?", 1, stream); - len += sink(uri->query.buf, uri->query.len, stream); + len += sink(uri.query.buf, uri.query.len, stream); } - if (uri->fragment.buf) { - // Note uri->fragment.buf includes the leading `#' - len += sink(uri->fragment.buf, uri->fragment.len, stream); + if (uri.fragment.buf) { + // Note that uri.fragment.buf includes the leading `#' + len += sink(uri.fragment.buf, uri.fragment.len, stream); } return len; } - -/// See http://tools.ietf.org/html/rfc3986#section-5.3 -size_t -serd_uri_serialise(const SerdURIView* uri, SerdSink sink, void* stream) -{ - return serd_uri_serialise_relative(uri, NULL, NULL, sink, stream); -} diff --git a/src/uri_utils.h b/src/uri_utils.h index 6b07ec48..fa1619d9 100644 --- a/src/uri_utils.h +++ b/src/uri_utils.h @@ -33,17 +33,14 @@ slice_equals(const SerdStringView* a, const SerdStringView* b) static inline size_t uri_path_len(const SerdURIView* uri) { - return uri->path_base.len + uri->path.len; + return uri->path_prefix.len + uri->path.len; } static inline char uri_path_at(const SerdURIView* uri, size_t i) { - if (i < uri->path_base.len) { - return uri->path_base.buf[i]; - } - - return uri->path.buf[i - uri->path_base.len]; + return (i < uri->path_prefix.len) ? uri->path_prefix.buf[i] + : uri->path.buf[i - uri->path_prefix.len]; } /** @@ -60,10 +57,11 @@ uri_rooted_index(const SerdURIView* uri, const SerdURIView* root) return 0; } - bool differ = false; - const size_t path_len = uri_path_len(uri); - const size_t root_len = uri_path_len(root); - size_t last_root_slash = 0; + bool differ = false; + const size_t path_len = uri_path_len(uri); + const size_t root_len = uri_path_len(root); + + size_t last_root_slash = 0; for (size_t i = 0; i < path_len && i < root_len; ++i) { const char u = uri_path_at(uri, i); const char r = uri_path_at(root, i); @@ -85,7 +83,9 @@ static inline SERD_PURE_FUNC bool uri_is_related(const SerdURIView* uri, const SerdURIView* root) { - return uri_rooted_index(uri, root) > 0; + return root && root->scheme.len && + slice_equals(&root->scheme, &uri->scheme) && + slice_equals(&root->authority, &uri->authority); } /** Return true iff `uri` is within the base of `root` */ diff --git a/src/writer.c b/src/writer.c index 5437f659..5205c7d1 100644 --- a/src/writer.c +++ b/src/writer.c @@ -614,29 +614,32 @@ 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; - SerdURIView uri; - SerdURIView abs_uri; serd_env_base_uri(writer->env, &in_base_uri); - serd_uri_parse(node_str, &uri); - serd_uri_resolve(&uri, &in_base_uri, &abs_uri); - bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri); - SerdURIView* root = rooted ? &writer->root_uri : &writer->base_uri; - if (!uri_is_under(&abs_uri, root) || writer->syntax == SERD_NTRIPLES || - writer->syntax == SERD_NQUADS) { - serd_uri_serialise(&abs_uri, uri_sink, writer); + + 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->syntax == SERD_NTRIPLES || writer->syntax == SERD_NQUADS || + !uri_is_under(&abs_uri, root) || + !uri_is_related(&abs_uri, &in_base_uri)) { + serd_write_uri(abs_uri, uri_sink, writer); } else { - serd_uri_serialise_relative( - &uri, &writer->base_uri, root, uri_sink, writer); + serd_write_uri(serd_relative_uri(uri, in_base_uri), uri_sink, writer); } } else { write_uri_from_node(writer, node); } write_sep(writer, SEP_URI_END); + if (is_inline_start(writer, field, flags)) { sink(" ;", 2, writer); write_newline(writer); } + return true; } @@ -1031,7 +1034,7 @@ serd_writer_set_root_uri(SerdWriter* writer, const SerdNode* uri) serd_node_free(writer->root_node); if (uri) { writer->root_node = serd_node_copy(uri); - serd_uri_parse(serd_node_string(writer->root_node), &writer->root_uri); + writer->root_uri = serd_parse_uri(serd_node_string(writer->root_node)); } else { writer->root_node = NULL; writer->root_uri = SERD_URI_NULL; |