aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2023-03-29 19:59:50 -0400
committerDavid Robillard <d@drobilla.net>2023-04-05 09:45:15 -0400
commitf93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913 (patch)
tree749ae66010fb5b8bfcf398c29ce252f9138cce5b /src
parentdd777c54b7585823be1f977e9dd887a5110a74f3 (diff)
downloadserd-f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913.tar.gz
serd-f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913.tar.bz2
serd-f93c3fdd6c7d6ca61bec55d3c1ffae7e7c793913.zip
Fix relative URI creation
Diffstat (limited to 'src')
-rw-r--r--src/uri.c14
-rw-r--r--src/uri_utils.h54
2 files changed, 43 insertions, 25 deletions
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 <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