diff options
author | David Robillard <d@drobilla.net> | 2023-03-29 19:59:50 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2023-04-05 09:45:15 -0400 |
commit | f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913 (patch) | |
tree | 749ae66010fb5b8bfcf398c29ce252f9138cce5b /src | |
parent | dd777c54b7585823be1f977e9dd887a5110a74f3 (diff) | |
download | serd-f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913.tar.gz serd-f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913.tar.bz2 serd-f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913.zip |
Fix relative URI creation
Diffstat (limited to 'src')
-rw-r--r-- | src/uri.c | 14 | ||||
-rw-r--r-- | src/uri_utils.h | 54 |
2 files changed, 43 insertions, 25 deletions
@@ -1,4 +1,4 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> +// Copyright 2011-2023 David Robillard <d@drobilla.net> // 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 <stdint.h> #include <string.h> +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 |