summaryrefslogtreecommitdiff
path: root/graphics/asymptote/LspCpp/third_party/uri/src/uri.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/asymptote/LspCpp/third_party/uri/src/uri.cpp')
-rw-r--r--graphics/asymptote/LspCpp/third_party/uri/src/uri.cpp695
1 files changed, 695 insertions, 0 deletions
diff --git a/graphics/asymptote/LspCpp/third_party/uri/src/uri.cpp b/graphics/asymptote/LspCpp/third_party/uri/src/uri.cpp
new file mode 100644
index 0000000000..b4b7188822
--- /dev/null
+++ b/graphics/asymptote/LspCpp/third_party/uri/src/uri.cpp
@@ -0,0 +1,695 @@
+// Copyright 2012-2017 Glyn Matthews.
+// Copyright 2012 Google, Inc.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cassert>
+#include <locale>
+#include <algorithm>
+#include <functional>
+#include "network/uri/uri.hpp"
+#include "detail/uri_parse.hpp"
+#include "detail/uri_advance_parts.hpp"
+#include "detail/uri_percent_encode.hpp"
+#include "detail/uri_normalize.hpp"
+#include "detail/uri_resolve.hpp"
+#include "detail/algorithm.hpp"
+
+namespace network {
+namespace {
+// With the parser, we use string_views, which are mutable. However,
+// there are times (e.g. during normalization), when we want a part
+// to be mutable. This function returns a pair of
+// std::string::iterators in the same range as the URI part.
+//
+inline std::pair<std::string::iterator, std::string::iterator> mutable_part(
+ std::string &str, detail::uri_part part) {
+ auto view = string_view(str);
+
+ auto first_index = std::distance(std::begin(view), std::begin(part));
+ auto first = std::begin(str);
+ std::advance(first, first_index);
+
+ auto last_index = std::distance(std::begin(view), std::end(part));
+ auto last = std::begin(str);
+ std::advance(last, last_index);
+
+ return std::make_pair(first, last);
+}
+
+// This is a convenience function that converts a part of a
+// std::string to a string_view.
+inline string_view to_string_view(const std::string &uri,
+ detail::uri_part part) {
+ if (!part.empty()) {
+ const char *c_str = uri.c_str();
+ const char *part_begin = &(*(std::begin(part)));
+ std::advance(c_str, std::distance(c_str, part_begin));
+ return string_view(c_str, std::distance(std::begin(part), std::end(part)));
+ }
+ return string_view();
+}
+
+inline optional<std::string> make_arg(optional<string_view> view) {
+ if (view) {
+ return view->to_string();
+ }
+ return nullopt;
+}
+
+template <class T>
+inline void ignore(T) {}
+} // namespace
+
+void uri::initialize(optional<string_type> scheme,
+ optional<string_type> user_info,
+ optional<string_type> host, optional<string_type> port,
+ optional<string_type> path, optional<string_type> query,
+ optional<string_type> fragment) {
+ if (scheme) {
+ uri_.append(*scheme);
+ }
+
+ if (user_info || host || port) {
+ if (scheme) {
+ uri_.append("://");
+ }
+
+ if (user_info) {
+ uri_.append(*user_info);
+ uri_.append("@");
+ }
+
+ if (host) {
+ uri_.append(*host);
+ } else {
+ throw uri_builder_error();
+ }
+
+ if (port) {
+ uri_.append(":");
+ uri_.append(*port);
+ }
+ } else {
+ if (scheme) {
+ if (path || query || fragment) {
+ uri_.append(":");
+ } else {
+ throw uri_builder_error();
+ }
+ }
+ }
+
+ if (path) {
+ // if the URI is not opaque and the path is not already prefixed
+ // with a '/', add one.
+ if (host && (!path->empty() && path->front() != '/')) {
+ path = "/" + *path;
+ }
+ uri_.append(*path);
+ }
+
+ if (query) {
+ uri_.append("?");
+ uri_.append(*query);
+ }
+
+ if (fragment) {
+ uri_.append("#");
+ uri_.append(*fragment);
+ }
+
+ uri_view_ = string_view(uri_);
+
+ auto it = std::begin(uri_view_);
+ if (scheme) {
+ uri_parts_.scheme = detail::copy_part(*scheme, it);
+ // ignore : and ://
+ if (*it == ':') {
+ ++it;
+ }
+ if (*it == '/' && *(it + 1) == '/') {
+ it += 2;
+ }
+ }
+
+ if (user_info) {
+ uri_parts_.hier_part.user_info = detail::copy_part(*user_info, it);
+ ++it; // ignore @
+ }
+
+ if (host) {
+ uri_parts_.hier_part.host = detail::copy_part(*host, it);
+ }
+
+ if (port) {
+ ++it; // ignore :
+ uri_parts_.hier_part.port = detail::copy_part(*port, it);
+ }
+
+ if (path) {
+ uri_parts_.hier_part.path = detail::copy_part(*path, it);
+ }
+
+ if (query) {
+ ++it; // ignore ?
+ uri_parts_.query = detail::copy_part(*query, it);
+ }
+
+ if (fragment) {
+ ++it; // ignore #
+ uri_parts_.fragment = detail::copy_part(*fragment, it);
+ }
+}
+
+uri::uri() : uri_view_(uri_) {}
+
+uri::uri(const uri &other) : uri_(other.uri_), uri_view_(uri_) {
+ detail::advance_parts(uri_view_, uri_parts_, other.uri_parts_);
+}
+
+uri::uri(const uri_builder &builder) {
+ initialize(builder.scheme_, builder.user_info_, builder.host_, builder.port_,
+ builder.path_, builder.query_, builder.fragment_);
+}
+
+uri::uri(uri &&other) noexcept
+ : uri_(std::move(other.uri_)),
+ uri_view_(uri_),
+ uri_parts_(std::move(other.uri_parts_)) {
+ detail::advance_parts(uri_view_, uri_parts_, other.uri_parts_);
+ other.uri_.clear();
+ other.uri_view_ = string_view(other.uri_);
+ other.uri_parts_ = detail::uri_parts();
+}
+
+uri::~uri() {}
+
+uri &uri::operator=(uri other) {
+ other.swap(*this);
+ return *this;
+}
+
+void uri::swap(uri &other) noexcept {
+ uri_.swap(other.uri_);
+ uri_view_ = uri_;
+ other.uri_view_ = other.uri_;
+
+ const auto this_parts = uri_parts_;
+ uri_parts_.clear();
+ detail::advance_parts(uri_view_, uri_parts_, other.uri_parts_);
+ other.uri_parts_.clear();
+ detail::advance_parts(other.uri_view_, other.uri_parts_, this_parts);
+}
+
+uri::const_iterator uri::begin() const noexcept { return uri_view_.begin(); }
+
+uri::const_iterator uri::end() const noexcept { return uri_view_.end(); }
+
+bool uri::has_scheme() const noexcept {
+ return static_cast<bool>(uri_parts_.scheme);
+}
+
+uri::string_view uri::scheme() const noexcept {
+ return has_scheme() ? to_string_view(uri_, *uri_parts_.scheme)
+ : string_view{};
+}
+
+bool uri::has_user_info() const noexcept {
+ return static_cast<bool>(uri_parts_.hier_part.user_info);
+}
+
+uri::string_view uri::user_info() const noexcept {
+ return has_user_info() ? to_string_view(uri_, *uri_parts_.hier_part.user_info)
+ : string_view{};
+}
+
+bool uri::has_host() const noexcept {
+ return static_cast<bool>(uri_parts_.hier_part.host);
+}
+
+uri::string_view uri::host() const noexcept {
+ return has_host() ? to_string_view(uri_, *uri_parts_.hier_part.host)
+ : string_view{};
+}
+
+bool uri::has_port() const noexcept {
+ return static_cast<bool>(uri_parts_.hier_part.port);
+}
+
+uri::string_view uri::port() const noexcept {
+ return has_port() ? to_string_view(uri_, *uri_parts_.hier_part.port)
+ : string_view{};
+}
+
+bool uri::has_path() const noexcept {
+ return static_cast<bool>(uri_parts_.hier_part.path);
+}
+
+uri::string_view uri::path() const noexcept {
+ return has_path() ? to_string_view(uri_, *uri_parts_.hier_part.path)
+ : string_view{};
+}
+
+bool uri::has_query() const noexcept {
+ return static_cast<bool>(uri_parts_.query);
+}
+
+uri::string_view uri::query() const noexcept {
+ return has_query() ? to_string_view(uri_, *uri_parts_.query) : string_view{};
+}
+
+uri::query_iterator::query_iterator() : query_{}, kvp_{} {}
+
+uri::query_iterator::query_iterator(optional<detail::uri_part> query)
+ : query_(query), kvp_{} {
+ if (query_ && query_->empty()) {
+ query_ = nullopt;
+ } else {
+ assign_kvp();
+ }
+}
+
+uri::query_iterator::query_iterator(const query_iterator &other)
+ : query_(other.query_), kvp_(other.kvp_) {}
+
+uri::query_iterator &uri::query_iterator::operator=(
+ const query_iterator &other) {
+ auto tmp(other);
+ swap(tmp);
+ return *this;
+}
+
+uri::query_iterator::reference uri::query_iterator::operator++() noexcept {
+ increment();
+ return kvp_;
+}
+
+uri::query_iterator::value_type uri::query_iterator::operator++(int) noexcept {
+ auto original = kvp_;
+ increment();
+ return original;
+}
+
+uri::query_iterator::reference uri::query_iterator::operator*() const noexcept {
+ return kvp_;
+}
+
+uri::query_iterator::pointer uri::query_iterator::operator->() const noexcept {
+ return std::addressof(kvp_);
+}
+
+bool uri::query_iterator::operator==(const query_iterator &other) const
+ noexcept {
+ if (!query_ && !other.query_) {
+ return true;
+ } else if (query_ && other.query_) {
+ // since we're comparing substrings, the address of the first
+ // element in each iterator must be the same
+ return std::addressof(kvp_.first) == std::addressof(other.kvp_.first);
+ }
+ return false;
+}
+
+void uri::query_iterator::swap(query_iterator &other) noexcept {
+ std::swap(query_, other.query_);
+ std::swap(kvp_, other.kvp_);
+}
+
+void uri::query_iterator::advance_to_next_kvp() noexcept {
+ auto first = std::begin(*query_), last = std::end(*query_);
+
+ auto sep_it = std::find_if(
+ first, last, [](char c) -> bool { return c == '&' || c == ';'; });
+
+ if (sep_it != last) {
+ ++sep_it; // skip next separator
+ }
+
+ // reassign query to the next element
+ query_ = detail::uri_part(sep_it, last);
+}
+
+void uri::query_iterator::assign_kvp() noexcept {
+ auto first = std::begin(*query_), last = std::end(*query_);
+
+ auto sep_it = std::find_if(
+ first, last, [](char c) -> bool { return c == '&' || c == ';'; });
+ auto eq_it =
+ std::find_if(first, sep_it, [](char c) -> bool { return c == '='; });
+
+ kvp_.first = string_view(std::addressof(*first), std::distance(first, eq_it));
+ if (eq_it != sep_it) {
+ ++eq_it; // skip '=' symbol
+ }
+ kvp_.second =
+ string_view(std::addressof(*eq_it), std::distance(eq_it, sep_it));
+}
+
+void uri::query_iterator::increment() noexcept {
+ assert(query_);
+
+ if (!query_->empty()) {
+ advance_to_next_kvp();
+ assign_kvp();
+ }
+
+ if (query_->empty()) {
+ query_ = nullopt;
+ }
+}
+
+uri::query_iterator uri::query_begin() const noexcept {
+ return has_query() ? uri::query_iterator{uri_parts_.query}
+ : uri::query_iterator{};
+}
+
+uri::query_iterator uri::query_end() const noexcept {
+ return uri::query_iterator{};
+}
+
+bool uri::has_fragment() const noexcept {
+ return static_cast<bool>(uri_parts_.fragment);
+}
+
+uri::string_view uri::fragment() const noexcept {
+ return has_fragment() ? to_string_view(uri_, *uri_parts_.fragment)
+ : string_view{};
+}
+
+bool uri::has_authority() const noexcept { return has_host(); }
+
+uri::string_view uri::authority() const noexcept {
+ if (!has_host()) {
+ return string_view{};
+ }
+
+ auto host = this->host();
+
+ auto user_info = string_view{};
+ if (has_user_info()) {
+ user_info = this->user_info();
+ }
+
+ auto port = string_view{};
+ if (has_port()) {
+ port = this->port();
+ }
+
+ auto first = std::begin(host), last = std::end(host);
+ if (has_user_info() && !user_info.empty()) {
+ first = std::begin(user_info);
+ } else if (host.empty() && has_port() && !port.empty()) {
+ first = std::begin(port);
+ --first; // include ':' before port
+ }
+
+ if (host.empty()) {
+ if (has_port() && !port.empty()) {
+ last = std::end(port);
+ } else if (has_user_info() && !user_info.empty()) {
+ last = std::end(user_info);
+ ++last; // include '@'
+ }
+ } else if (has_port()) {
+ if (port.empty()) {
+ ++last; // include ':' after host
+ } else {
+ last = std::end(port);
+ }
+ }
+
+ return string_view(first, std::distance(first, last));
+}
+
+std::string uri::string() const { return uri_; }
+
+std::wstring uri::wstring() const {
+ return std::wstring(std::begin(*this), std::end(*this));
+}
+
+std::u16string uri::u16string() const {
+ return std::u16string(std::begin(*this), std::end(*this));
+}
+
+std::u32string uri::u32string() const {
+ return std::u32string(std::begin(*this), std::end(*this));
+}
+
+uri::string_view uri::view() const noexcept { return uri_view_; }
+
+bool uri::empty() const noexcept { return uri_.empty(); }
+
+bool uri::is_absolute() const noexcept { return has_scheme(); }
+
+bool uri::is_opaque() const noexcept {
+ return (is_absolute() && !has_authority());
+}
+
+uri uri::normalize(uri_comparison_level level) const {
+ string_type normalized(uri_);
+ string_view normalized_view(normalized);
+ detail::uri_parts parts;
+ detail::advance_parts(normalized_view, parts, uri_parts_);
+
+ if (uri_comparison_level::syntax_based == level) {
+ // All alphabetic characters in the scheme and host are
+ // lower-case...
+ if (parts.scheme) {
+ std::string::iterator first, last;
+ std::tie(first, last) = mutable_part(normalized, *parts.scheme);
+ std::transform(first, last, first,
+ [](char ch) { return std::tolower(ch, std::locale()); });
+ }
+
+ // if (parts.hier_part.host) {
+ // std::string::iterator first, last;
+ // std::tie(first, last) = mutable_part(normalized,
+ // *parts.hier_part.host); std::transform(first, last, first,
+ // [](char ch) { return std::tolower(ch, std::locale());
+ // });
+ // }
+
+ // ...except when used in percent encoding
+ detail::for_each(normalized,
+ detail::percent_encoded_to_upper<std::string>());
+
+ // parts are invalidated here
+ // there's got to be a better way of doing this that doesn't
+ // mean parsing again (twice!)
+ normalized.erase(detail::decode_encoded_unreserved_chars(
+ std::begin(normalized), std::end(normalized)),
+ std::end(normalized));
+ normalized_view = string_view(normalized);
+
+ // need to parse the parts again as the underlying string has changed
+ const_iterator it = std::begin(normalized_view),
+ last = std::end(normalized_view);
+ bool is_valid = detail::parse(it, last, parts);
+ ignore(is_valid);
+ assert(is_valid);
+
+ if (parts.hier_part.path) {
+ uri::string_type path = detail::normalize_path_segments(
+ to_string_view(normalized, *parts.hier_part.path));
+
+ // put the normalized path back into the uri
+ optional<string_type> query, fragment;
+ if (parts.query) {
+ query = parts.query->to_string();
+ }
+
+ if (parts.fragment) {
+ fragment = parts.fragment->to_string();
+ }
+
+ auto path_begin = std::begin(normalized);
+ auto path_range = mutable_part(normalized, *parts.hier_part.path);
+ std::advance(path_begin, std::distance(path_begin, path_range.first));
+ normalized.erase(path_begin, std::end(normalized));
+ normalized.append(path);
+
+ if (query) {
+ normalized.append("?");
+ normalized.append(*query);
+ }
+
+ if (fragment) {
+ normalized.append("#");
+ normalized.append(*fragment);
+ }
+ }
+ }
+
+ return uri(normalized);
+}
+
+uri uri::make_relative(const uri &other) const {
+ if (is_opaque() || other.is_opaque()) {
+ return other;
+ }
+
+ if ((!has_scheme() || !other.has_scheme()) ||
+ !detail::equal(scheme(), other.scheme())) {
+ return other;
+ }
+
+ if ((!has_authority() || !other.has_authority()) ||
+ !detail::equal(authority(), other.authority())) {
+ return other;
+ }
+
+ if (!has_path() || !other.has_path()) {
+ return other;
+ }
+
+ auto path =
+ detail::normalize_path(this->path(), uri_comparison_level::syntax_based);
+ auto other_path =
+ detail::normalize_path(other.path(), uri_comparison_level::syntax_based);
+
+ optional<string_type> query, fragment;
+ if (other.has_query()) {
+ query = other.query().to_string();
+ }
+
+ if (other.has_fragment()) {
+ fragment = other.fragment().to_string();
+ }
+
+ network::uri result;
+ result.initialize(optional<string_type>(), optional<string_type>(),
+ optional<string_type>(), optional<string_type>(),
+ other_path, query, fragment);
+ return result;
+}
+
+uri uri::resolve(const uri &base) const {
+ // This implementation uses the psuedo-code given in
+ // http://tools.ietf.org/html/rfc3986#section-5.2.2
+
+ if (is_absolute() && !is_opaque()) {
+ // throw an exception ?
+ return *this;
+ }
+
+ if (is_opaque()) {
+ // throw an exception ?
+ return *this;
+ }
+
+ optional<uri::string_type> user_info, host, port, path, query, fragment;
+
+ if (has_authority()) {
+ // g -> http://g
+ if (has_user_info()) {
+ user_info = make_arg(this->user_info());
+ }
+
+ if (has_host()) {
+ host = make_arg(this->host());
+ }
+
+ if (has_port()) {
+ port = make_arg(this->port());
+ }
+
+ if (has_path()) {
+ path = detail::remove_dot_segments(this->path());
+ }
+
+ if (has_query()) {
+ query = make_arg(this->query());
+ }
+ } else {
+ if (!has_path() || this->path().empty()) {
+ if (base.has_path()) {
+ path = make_arg(base.path());
+ }
+
+ if (has_query()) {
+ query = make_arg(this->query());
+ } else if (base.has_query()) {
+ query = make_arg(base.query());
+ }
+ } else {
+ if (this->path().front() == '/') {
+ path = detail::remove_dot_segments(this->path());
+ } else {
+ path = detail::merge_paths(base, *this);
+ }
+
+ if (has_query()) {
+ query = make_arg(this->query());
+ }
+ }
+
+ if (base.has_user_info()) {
+ user_info = make_arg(base.user_info());
+ }
+
+ if (base.has_host()) {
+ host = make_arg(base.host());
+ }
+
+ if (base.has_port()) {
+ port = make_arg(base.port());
+ }
+ }
+
+ if (has_fragment()) {
+ fragment = make_arg(this->fragment());
+ }
+
+ network::uri result;
+ result.initialize(make_arg(base.scheme()), std::move(user_info),
+ std::move(host), std::move(port), std::move(path),
+ std::move(query), std::move(fragment));
+ return result;
+}
+
+int uri::compare(const uri &other, uri_comparison_level level) const noexcept {
+ // if both URIs are empty, then we should define them as equal
+ // even though they're still invalid.
+ if (empty() && other.empty()) {
+ return 0;
+ }
+
+ if (empty()) {
+ return -1;
+ }
+
+ if (other.empty()) {
+ return 1;
+ }
+
+ return normalize(level).uri_.compare(other.normalize(level).uri_);
+}
+
+bool uri::initialize(const string_type &uri) {
+ uri_ = detail::trim_copy(uri);
+ if (!uri_.empty()) {
+ uri_view_ = string_view(uri_);
+ const_iterator it = std::begin(uri_view_), last = std::end(uri_view_);
+ bool is_valid = detail::parse(it, last, uri_parts_);
+ return is_valid;
+ }
+ return true;
+}
+
+void swap(uri &lhs, uri &rhs) noexcept { lhs.swap(rhs); }
+
+bool operator==(const uri &lhs, const uri &rhs) noexcept {
+ return lhs.view() == rhs.view();
+}
+
+bool operator==(const uri &lhs, const char *rhs) noexcept {
+ return lhs.view() == string_view{rhs};
+}
+
+bool operator<(const uri &lhs, const uri &rhs) noexcept {
+ return lhs.view() < rhs.view();
+}
+
+} // namespace network