// Copyright 2011-2023 David Robillard // SPDX-License-Identifier: ISC #ifndef SERD_SRC_URI_UTILS_H #define SERD_SRC_URI_UTILS_H #include "serd/serd.h" #include "string_utils.h" #include #include #include typedef struct { size_t shared; size_t root; } SlashIndexes; static inline bool chunk_equals(const SerdChunk* a, const SerdChunk* b) { return a->len == b->len && !strncmp((const char*)a->buf, (const char*)b->buf, a->len); } static inline size_t uri_path_len(const SerdURI* uri) { return uri->path_base.len + uri->path.len; } static inline uint8_t uri_path_at(const SerdURI* uri, size_t i) { return (i < uri->path_base.len) ? uri->path_base.buf[i] : uri->path.buf[i - uri->path_base.len]; } /** 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 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 indexes; } 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); 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 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).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 SlashIndexes indexes = uri_rooted_index(uri, root); return indexes.shared && indexes.shared != SIZE_MAX && indexes.shared == indexes.root; } static inline bool is_uri_scheme_char(const int c) { switch (c) { case ':': case '+': case '-': case '.': return true; default: return is_alpha(c) || is_digit(c); } } #endif // SERD_SRC_URI_UTILS_H