From f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 29 Mar 2023 19:59:50 -0400 Subject: Fix relative URI creation --- src/uri.c | 14 +++++++------- src/uri_utils.h | 54 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/uri.c b/src/uri.c index 8acb79b3..dcdd8083 100644 --- a/src/uri.c +++ b/src/uri.c @@ -1,4 +1,4 @@ -// Copyright 2011-2020 David Robillard +// Copyright 2011-2023 David Robillard // SPDX-License-Identifier: ISC #include "string_utils.h" @@ -428,10 +428,6 @@ write_rel_path(SerdSink sink, len += sink("../", 3, stream); } - if (last_shared_sep == 0 && up == 0) { - len += sink("/", 1, stream); - } - // Write suffix return len + write_path_tail(sink, stream, uri, last_shared_sep + 1); } @@ -468,8 +464,12 @@ serd_uri_serialise_relative(const SerdURI* const uri, 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] != '/' && + + const bool authority_ends_with_slash = + (uri->authority.len > 0 && + uri->authority.buf[uri->authority.len - 1] == '/'); + + if (!authority_ends_with_slash && serd_uri_path_starts_without_slash(uri)) { // Special case: ensure path begins with a slash // https://tools.ietf.org/html/rfc3986#section-3.2 diff --git a/src/uri_utils.h b/src/uri_utils.h index 16819191..e2f30edb 100644 --- a/src/uri_utils.h +++ b/src/uri_utils.h @@ -12,6 +12,11 @@ #include #include +typedef struct { + size_t shared; + size_t root; +} SlashIndexes; + static inline bool chunk_equals(const SerdChunk* a, const SerdChunk* b) { @@ -33,51 +38,64 @@ uri_path_at(const SerdURI* uri, size_t i) } /** - Return the index of the first differing character after the last root slash, - or zero if `uri` is not under `root`. + Return the index of the last slash shared with the root, or `SIZE_MAX`. + + The index of the next slash found in the root is also returned, so the two + can be compared to determine if the URI is within the root (if the shared + slash is the last in the root, then the URI is a child of the root, + otherwise it may merely share some leading path components). */ -static inline SERD_PURE_FUNC size_t +static inline SERD_PURE_FUNC SlashIndexes uri_rooted_index(const SerdURI* uri, const SerdURI* root) { + SlashIndexes indexes = {SIZE_MAX, SIZE_MAX}; + if (!root || !root->scheme.len || !chunk_equals(&root->scheme, &uri->scheme) || !chunk_equals(&root->authority, &uri->authority)) { - return 0; + return indexes; } - 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 size_t path_len = uri_path_len(uri); + const size_t root_len = uri_path_len(root); + const size_t min_len = path_len < root_len ? path_len : root_len; + for (size_t i = 0; i < min_len; ++i) { const uint8_t u = uri_path_at(uri, i); const uint8_t r = uri_path_at(root, i); - differ = differ || u != r; - if (r == '/') { - last_root_slash = i; - if (differ) { - return 0; + if (u == r) { + if (u == '/') { + indexes.root = indexes.shared = i; } + } else { + for (size_t j = i; j < root_len; ++j) { + if (uri_path_at(root, j) == '/') { + indexes.root = j; + break; + } + } + + return indexes; } } - return last_root_slash + 1; + return indexes; } /** Return true iff `uri` shares path components with `root` */ static inline SERD_PURE_FUNC bool uri_is_related(const SerdURI* uri, const SerdURI* root) { - return uri_rooted_index(uri, root) > 0; + return uri_rooted_index(uri, root).shared != SIZE_MAX; } /** Return true iff `uri` is within the base of `root` */ static inline SERD_PURE_FUNC bool uri_is_under(const SerdURI* uri, const SerdURI* root) { - const size_t index = uri_rooted_index(uri, root); - return index > 0 && uri->path.len > index; + const SlashIndexes indexes = uri_rooted_index(uri, root); + return indexes.shared && indexes.shared != SIZE_MAX && + indexes.shared == indexes.root; } static inline bool -- cgit v1.2.1