From 5fc41f56d2f98ab87fe5a0599d1f328d27a6ac63 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Tue, 25 Apr 2023 01:47:50 +0100 Subject: [PATCH] deps: update ada to 2.2.0 PR-URL: https://github.com/nodejs/node/pull/47678 Reviewed-By: Yagiz Nizipli Reviewed-By: Benjamin Gruenbaum --- deps/ada/ada.cpp | 89 +- deps/ada/ada.h | 7002 +++++++++++++++++++++++----------------------- src/node_url.cc | 6 +- 3 files changed, 3517 insertions(+), 3580 deletions(-) diff --git a/deps/ada/ada.cpp b/deps/ada/ada.cpp index be7e778bac7c72..1070ef6814f816 100644 --- a/deps/ada/ada.cpp +++ b/deps/ada/ada.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2023-04-17 12:20:41 -0400. Do not edit! */ +/* auto-generated on 2023-04-20 18:39:35 -0400. Do not edit! */ /* begin file src/ada.cpp */ #include "ada.h" /* begin file src/checkers.cpp */ @@ -10501,7 +10501,7 @@ ada_unused std::string get_state(ada::state s) { } } -ada_really_inline std::optional prune_fragment( +ada_really_inline std::optional prune_hash( std::string_view& input) noexcept { // compiles down to 20--30 instructions including a class to memchr (C // function). this function should be quite fast. @@ -10509,10 +10509,10 @@ ada_really_inline std::optional prune_fragment( if (location_of_first == std::string_view::npos) { return std::nullopt; } - std::string_view fragment = input; - fragment.remove_prefix(location_of_first + 1); + std::string_view hash = input; + hash.remove_prefix(location_of_first + 1); input.remove_suffix(input.size() - location_of_first); - return fragment; + return hash; } ada_really_inline bool shorten_path(std::string& path, @@ -10523,9 +10523,9 @@ ada_really_inline bool shorten_path(std::string& path, // If url’s scheme is "file", path’s size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos) { + first_delimiter == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( - std::string_view(path.data() + 1, first_delimiter - 1))) { + helpers::substring(path, 1))) { return false; } } @@ -10547,9 +10547,9 @@ ada_really_inline bool shorten_path(std::string_view& path, // If url’s scheme is "file", path’s size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos) { + first_delimiter == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( - std::string_view(path.data() + 1, first_delimiter - 1))) { + helpers::substring(path, 1))) { return false; } } @@ -10998,8 +10998,8 @@ ada_really_inline void strip_trailing_spaces_from_opaque_path( url_type& url) noexcept { ada_log("helpers::strip_trailing_spaces_from_opaque_path"); if (!url.has_opaque_path) return; - if (url.base_fragment_has_value()) return; - if (url.base_search_has_value()) return; + if (url.has_hash()) return; + if (url.has_search()) return; auto path = std::string(url.get_pathname()); while (!path.empty() && path.back() == ' ') { @@ -11451,7 +11451,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || port.has_value()) && + if ((has_credentials() || port.has_value()) && parsed_type == ada::scheme::type::FILE) { return true; } @@ -11496,7 +11496,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || port.has_value()) && _buffer == "file") { + if ((has_credentials() || port.has_value()) && _buffer == "file") { return true; } @@ -11648,7 +11648,7 @@ std::string url::to_string() const { answer.append("\t\"protocol\":\""); helpers::encode_json(get_protocol(), back); answer.append("\",\n"); - if (includes_credentials()) { + if (has_credentials()) { answer.append("\t\"username\":\""); helpers::encode_json(username, back); answer.append("\",\n"); @@ -11671,16 +11671,16 @@ std::string url::to_string() const { answer.append("\",\n"); answer.append("\t\"opaque path\":"); answer.append((has_opaque_path ? "true" : "false")); - if (base_search_has_value()) { + if (has_search()) { answer.append(",\n"); answer.append("\t\"query\":\""); helpers::encode_json(query.value(), back); answer.append("\""); } - if (fragment.has_value()) { + if (hash.has_value()) { answer.append(",\n"); - answer.append("\t\"fragment\":\""); - helpers::encode_json(fragment.value(), back); + answer.append("\t\"hash\":\""); + helpers::encode_json(hash.value(), back); answer.append("\""); } answer.append("\n}"); @@ -11781,9 +11781,8 @@ namespace ada { [[nodiscard]] std::string url::get_hash() const noexcept { // If this’s URL’s fragment is either null or the empty string, then return // the empty string. Return U+0023 (#), followed by this’s URL’s fragment. - return (!fragment.has_value() || (fragment.value().empty())) - ? "" - : "#" + fragment.value(); + return (!hash.has_value() || (hash.value().empty())) ? "" + : "#" + hash.value(); } } // namespace ada @@ -11839,7 +11838,7 @@ bool url::set_host_or_hostname(const std::string_view input) { // empty string, and either url includes credentials or url’s port is // non-null, return. else if (host_view.empty() && - (is_special() || includes_credentials() || port.has_value())) { + (is_special() || has_credentials() || port.has_value())) { return false; } @@ -11939,7 +11938,7 @@ bool url::set_port(const std::string_view input) { void url::set_hash(const std::string_view input) { if (input.empty()) { - fragment = std::nullopt; + hash = std::nullopt; helpers::strip_trailing_spaces_from_opaque_path(*this); return; } @@ -11947,8 +11946,8 @@ void url::set_hash(const std::string_view input) { std::string new_value; new_value = input[0] == '#' ? input.substr(1) : input; helpers::remove_ascii_tab_or_newline(new_value); - fragment = unicode::percent_encode( - new_value, ada::character_sets::FRAGMENT_PERCENT_ENCODE); + hash = unicode::percent_encode(new_value, + ada::character_sets::FRAGMENT_PERCENT_ENCODE); return; } @@ -12014,7 +12013,7 @@ bool url::set_href(const std::string_view input) { port = out->port; path = out->path; query = out->query; - fragment = out->fragment; + hash = out->hash; type = out->type; non_special_scheme = out->non_special_scheme; has_opaque_path = out->has_opaque_path; @@ -12106,7 +12105,7 @@ result_type parse_url(std::string_view user_input, helpers::trim_c0_whitespace(url_data); // Optimization opportunity. Most websites do not have fragment. - std::optional fragment = helpers::prune_fragment(url_data); + std::optional fragment = helpers::prune_hash(url_data); // We add it last so that an implementation like ada::url_aggregator // can append it last to its internal buffer, thus improving performance. @@ -12463,7 +12462,7 @@ result_type parse_url(std::string_view user_input, // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { // Set url’s query to null. - url.clear_base_search(); + url.clear_search(); if constexpr (result_type_is_ada_url) { // Shorten url’s path. helpers::shorten_path(url.path, url.type); @@ -12878,7 +12877,7 @@ result_type parse_url(std::string_view user_input, // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { // Set url’s query to null. - url.clear_base_search(); + url.clear_search(); // If the code point substring from pointer to the end of input does // not start with a Windows drive letter, then shorten url’s path. @@ -12895,11 +12894,7 @@ result_type parse_url(std::string_view user_input, // Otherwise: else { // Set url’s path to an empty list. - if constexpr (result_type_is_ada_url) { - url.path.clear(); - } else { - url.clear_base_pathname(); - } + url.clear_pathname(); url.has_opaque_path = true; } @@ -13091,8 +13086,7 @@ template // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || - components.port != url_components::omitted) && + if ((has_credentials() || components.port != url_components::omitted) && parsed_type == ada::scheme::type::FILE) { return true; } @@ -13115,7 +13109,7 @@ template // If url’s port is url’s scheme’s default port, then set url’s port to // null. if (components.port == urls_scheme_port) { - clear_base_port(); + clear_port(); } } } else { // slow path @@ -13135,8 +13129,7 @@ template // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || - components.port != url_components::omitted) && + if ((has_credentials() || components.port != url_components::omitted) && _buffer == "file") { return true; } @@ -13158,7 +13151,7 @@ template // If url’s port is url’s scheme’s default port, then set url’s port to // null. if (components.port == urls_scheme_port) { - clear_base_port(); + clear_port(); } } } @@ -13339,7 +13332,7 @@ bool url_aggregator::set_port(const std::string_view input) { std::string trimmed(input); helpers::remove_ascii_tab_or_newline(trimmed); if (trimmed.empty()) { - clear_base_port(); + clear_port(); return true; } // Input should not start with control characters. @@ -13370,7 +13363,7 @@ bool url_aggregator::set_pathname(const std::string_view input) { if (has_opaque_path) { return false; } - clear_base_pathname(); + clear_pathname(); parse_path(input); if (checkers::begins_with(input, "//") && !has_authority() && !has_dash_dot()) { @@ -13427,7 +13420,7 @@ void url_aggregator::set_search(const std::string_view input) { ADA_ASSERT_TRUE(validate()); ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); if (input.empty()) { - clear_base_search(); + clear_search(); helpers::strip_trailing_spaces_from_opaque_path(*this); return; } @@ -13623,7 +13616,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // empty string, and either url includes credentials or url’s port is // non-null, return. else if (host_view.empty() && - (is_special() || includes_credentials() || + (is_special() || has_credentials() || components.port != url_components::omitted)) { return false; } @@ -13631,7 +13624,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // Let host be the result of host parsing host_view with url is not special. if (host_view.empty()) { if (has_hostname()) { - clear_base_hostname(); // easy! + clear_hostname(); // easy! } else if (has_dash_dot()) { add_authority_slashes_if_needed(); delete_dash_dot(); @@ -13657,7 +13650,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { if (new_host.empty()) { // Set url’s host to the empty string. - clear_base_hostname(); + clear_hostname(); } else { // Let host be the result of host parsing buffer with url is not special. if (!parse_host(new_host)) { @@ -13669,7 +13662,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // If host is "localhost", then set host to the empty string. if (helpers::substring(buffer, components.host_start, components.host_end) == "localhost") { - clear_base_hostname(); + clear_hostname(); } } ADA_ASSERT_TRUE(validate()); @@ -13836,7 +13829,7 @@ std::string ada::url_aggregator::to_string() const { helpers::encode_json(get_protocol(), back); answer.append("\",\n"); - if (includes_credentials()) { + if (has_credentials()) { answer.append("\t\"username\":\""); helpers::encode_json(get_username(), back); answer.append("\",\n"); diff --git a/deps/ada/ada.h b/deps/ada/ada.h index 29e991d6ba1cd3..625194ddb423e9 100644 --- a/deps/ada/ada.h +++ b/deps/ada/ada.h @@ -1,4 +1,4 @@ -/* auto-generated on 2023-04-17 12:20:41 -0400. Do not edit! */ +/* auto-generated on 2023-04-20 18:39:35 -0400. Do not edit! */ /* begin file include/ada.h */ /** * @file ada.h @@ -1095,122 +1095,143 @@ ada_warn_unused std::string to_string(encoding_type type); #ifndef ADA_HELPERS_H #define ADA_HELPERS_H -/* begin file include/ada/url.h */ +/* begin file include/ada/state.h */ /** - * @file url.h - * @brief Declaration for the URL + * @file state.h + * @brief Definitions for the states of the URL state machine. */ -#ifndef ADA_URL_H -#define ADA_URL_H +#ifndef ADA_STATE_H +#define ADA_STATE_H -/* begin file include/ada/checkers.h */ -/** - * @file checkers.h - * @brief Declarations for URL specific checkers used within Ada. - */ -#ifndef ADA_CHECKERS_H -#define ADA_CHECKERS_H +#include -#include -#include +namespace ada { /** - * @namespace ada::checkers - * @brief Includes the definitions for validation functions + * @see https://url.spec.whatwg.org/#url-parsing */ -namespace ada::checkers { +enum class state { + AUTHORITY, + SCHEME_START, + SCHEME, + HOST, + NO_SCHEME, + FRAGMENT, + RELATIVE_SCHEME, + RELATIVE_SLASH, + FILE, + FILE_HOST, + FILE_SLASH, + PATH_OR_AUTHORITY, + SPECIAL_AUTHORITY_IGNORE_SLASHES, + SPECIAL_AUTHORITY_SLASHES, + SPECIAL_RELATIVE_OR_AUTHORITY, + QUERY, + PATH, + PATH_START, + OPAQUE_PATH, + PORT, +}; /** - * Assuming that x is an ASCII letter, this function returns the lower case - * equivalent. - * @details More likely to be inlined by the compiler and constexpr. + * Stringify a URL state machine state. */ -constexpr char to_lower(char x) noexcept; +ada_warn_unused std::string to_string(ada::state s); -/** - * Returns true if the character is an ASCII letter. Equivalent to std::isalpha - * but more likely to be inlined by the compiler. - * - * @attention std::isalpha is not constexpr generally. - */ -constexpr bool is_alpha(char x) noexcept; +} // namespace ada +#endif // ADA_STATE_H +/* end file include/ada/state.h */ +/* begin file include/ada/url_base.h */ /** - * Check whether a string starts with 0x or 0X. The function is only - * safe if input.size() >=2. - * - * @see has_hex_prefix - */ -inline bool has_hex_prefix_unsafe(std::string_view input); -/** - * Check whether a string starts with 0x or 0X. + * @file url_base.h + * @brief Declaration for the basic URL definitions */ -inline bool has_hex_prefix(std::string_view input); +#ifndef ADA_URL_BASE_H +#define ADA_URL_BASE_H +/* begin file include/ada/url_components.h */ /** - * Check whether x is an ASCII digit. More likely to be inlined than - * std::isdigit. + * @file url_components.h + * @brief Declaration for the URL Components */ -constexpr bool is_digit(char x) noexcept; +#ifndef ADA_URL_COMPONENTS_H +#define ADA_URL_COMPONENTS_H + + +#include +#include + +namespace ada { /** - * @details A string starts with a Windows drive letter if all of the following - * are true: + * @brief URL Component representations using offsets. * - * - its length is greater than or equal to 2 - * - its first two code points are a Windows drive letter - * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F - * (?), or U+0023 (#). + * @details We design the url_components struct so that it is as small + * and simple as possible. This version uses 32 bytes. * - * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter - */ -inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; - -/** - * @details A normalized Windows drive letter is a Windows drive letter of which - * the second code point is U+003A (:). + * This struct is used to extract components from a single 'href'. */ -inline constexpr bool is_normalized_windows_drive_letter( - std::string_view input) noexcept; +struct url_components { + constexpr static uint32_t omitted = uint32_t(-1); -/** - * @warning Will be removed when Ada supports C++20. - */ -ada_really_inline constexpr bool begins_with(std::string_view view, - std::string_view prefix); + url_components() = default; + url_components(const url_components &u) = default; + url_components(url_components &&u) noexcept = default; + url_components &operator=(url_components &&u) noexcept = default; + url_components &operator=(const url_components &u) = default; + ~url_components() = default; -/** - * Returns true if an input is an ipv4 address. - */ -ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept; + /* + * By using 32-bit integers, we implicitly assume that the URL string + * cannot exceed 4 GB. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + uint32_t protocol_end{0}; + /** + * Username end is not `omitted` by default to make username and password + * getters less costly to implement. + */ + uint32_t username_end{0}; + uint32_t host_start{0}; + uint32_t host_end{0}; + uint32_t port{omitted}; + uint32_t pathname_start{0}; + uint32_t search_start{omitted}; + uint32_t hash_start{omitted}; -/** - * Returns a bitset. If the first bit is set, then at least one character needs - * percent encoding. If the second bit is set, a \\ is found. If the third bit - * is set then we have a dot. If the fourth bit is set, then we have a percent - * character. - */ -ada_really_inline constexpr uint8_t path_signature( - std::string_view input) noexcept; + /** + * Check the following conditions: + * protocol_end < username_end < ... < hash_start, + * expect when a value is omitted. It also computes + * a lower bound on the possible string length that may match these + * offsets. + * @return true if the offset values are + * consistent with a possible URL string + */ + bool check_offset_consistency() const noexcept; -/** - * Returns true if the length of the domain name and its labels are according to - * the specifications. The length of the domain must be 255 octets (253 - * characters not including the last 2 which are the empty label reserved at the - * end). When the empty label is included (a dot at the end), the domain name - * can have 254 characters. The length of a label must be at least 1 and at most - * 63 characters. - * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 - * @see https://www.unicode.org/reports/tr46/#ToASCII - */ -ada_really_inline constexpr bool verify_dns_length( - std::string_view input) noexcept; + /** + * Converts a url_components to JSON stringified version. + */ + std::string to_string() const; -} // namespace ada::checkers +}; // struct url_components -#endif // ADA_CHECKERS_H -/* end file include/ada/checkers.h */ +} // namespace ada +#endif +/* end file include/ada/url_components.h */ /* begin file include/ada/scheme.h */ /** * @file scheme.h @@ -1289,3838 +1310,3806 @@ constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept; #endif // ADA_SCHEME_H /* end file include/ada/scheme.h */ -/* begin file include/ada/serializers.h */ -/** - * @file serializers.h - * @brief Definitions for the URL serializers. - */ -#ifndef ADA_SERIALIZERS_H -#define ADA_SERIALIZERS_H +#include -#include -#include -#include - -/** - * @namespace ada::serializers - * @brief Includes the definitions for URL serializers - */ -namespace ada::serializers { +namespace ada { /** - * Finds and returns the longest sequence of 0 values in a ipv6 input. + * @brief Base class of URL implementations + * + * @details A url_base contains a few attributes: is_valid, has_opaque_path and + * type. All non-trivial implementation details are in derived classes such as + * ada::url and ada::url_aggregator. + * + * It is an abstract class that cannot be instantiated directly. */ -void find_longest_sequence_of_ipv6_pieces( - const std::array& address, size_t& compress, - size_t& compress_length) noexcept; +struct url_base { + virtual ~url_base() = default; -/** - * Serializes an ipv6 address. - * @details An IPv6 address is a 128-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv6-serializer - */ -std::string ipv6(const std::array& address) noexcept; + /** + * Used for returning the validity from the result of the URL parser. + */ + bool is_valid{true}; -/** - * Serializes an ipv4 address. - * @details An IPv4 address is a 32-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv4-serializer - */ -std::string ipv4(const uint64_t address) noexcept; + /** + * A URL has an opaque path if its path is a string. + */ + bool has_opaque_path{false}; -} // namespace ada::serializers + /** + * @private + */ + ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; -#endif // ADA_SERIALIZERS_H -/* end file include/ada/serializers.h */ -/* begin file include/ada/unicode.h */ -/** - * @file unicode.h - * @brief Definitions for all unicode specific functions. - */ -#ifndef ADA_UNICODE_H -#define ADA_UNICODE_H + /** + * A URL is special if its scheme is a special scheme. A URL is not special if + * its scheme is not a special scheme. + */ + [[nodiscard]] ada_really_inline bool is_special() const noexcept; + + /** + * The origin getter steps are to return the serialization of this’s URL’s + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] virtual std::string get_origin() const noexcept = 0; + /** + * Returns true if this URL has a valid domain as per RFC 1034 and + * corresponding specifications. Among other things, it requires + * that the domain string has fewer than 255 octets. + */ + [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; -#include + /** + * @private + * + * Return the 'special port' if the URL is special and not 'file'. + * Returns 0 otherwise. + */ + [[nodiscard]] inline uint16_t get_special_port() const noexcept; + + /** + * @private + * + * Get the default port if the url's scheme has one, returns 0 otherwise. + */ + [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; + + /** + * @private + * + * Parse a port (16-bit decimal digit) from the provided input. + * We assume that the input does not contain spaces or tabs + * within the ASCII digits. + * It returns how many bytes were consumed when a number is successfully + * parsed. + * @return On failure, it returns zero. + * @see https://url.spec.whatwg.org/#host-parsing + */ + virtual ada_really_inline size_t parse_port( + std::string_view view, bool check_trailing_content = false) noexcept = 0; + + /** + * Returns a JSON string representation of this URL. + */ + virtual std::string to_string() const = 0; + + /** @private */ + virtual inline void clear_pathname() = 0; + + /** @private */ + virtual inline void clear_search() = 0; + + /** @private */ + virtual inline bool has_hash() const noexcept = 0; + + /** @private */ + virtual inline bool has_search() const noexcept = 0; + +}; // url_base + +} // namespace ada + +#endif +/* end file include/ada/url_base.h */ + +#include #include /** - * @namespace ada::unicode - * @brief Includes the definitions for unicode operations + * @private + * @namespace ada::helpers + * @brief Includes the definitions for helper functions */ -namespace ada::unicode { +namespace ada::helpers { /** - * We receive a UTF-8 string representing a domain name. - * If the string is percent encoded, we apply percent decoding. - * - * Given a domain, we need to identify its labels. - * They are separated by label-separators: - * - * U+002E ( . ) FULL STOP - * U+FF0E ( . ) FULLWIDTH FULL STOP - * U+3002 ( 。 ) IDEOGRAPHIC FULL STOP - * U+FF61 ( 。 ) HALFWIDTH IDEOGRAPHIC FULL STOP - * - * They are all mapped to U+002E. - * - * We process each label into a string that should not exceed 63 octets. - * If the string is already punycode (starts with "xn--"), then we must - * scan it to look for unallowed code points. - * Otherwise, if the string is not pure ASCII, we need to transcode it - * to punycode by following RFC 3454 which requires us to - * - Map characters (see section 3), - * - Normalize (see section 4), - * - Reject forbidden characters, - * - Check for right-to-left characters and if so, check all requirements (see - * section 6), - * - Optionally reject based on unassigned code points (section 7). - * - * The Unicode standard provides a table of code points with a mapping, a list - * of forbidden code points and so forth. This table is subject to change and - * will vary based on the implementation. For Unicode 15, the table is at - * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt - * If you use ICU, they parse this table and map it to code using a Python - * script. - * - * The resulting strings should not exceed 255 octets according to RFC 1035 - * section 2.3.4. ICU checks for label size and domain size, but these errors - * are ignored. - * - * @see https://url.spec.whatwg.org/#concept-domain-to-ascii - * + * @private */ -bool to_ascii(std::optional& out, std::string_view plain, - size_t first_percent); +template +void encode_json(std::string_view view, out_iter out); /** - * @see https://www.unicode.org/reports/tr46/#ToUnicode + * @private + * This function is used to prune a fragment from a url, and returning the + * removed string if input has fragment. + * + * @details prune_hash seeks the first '#' and returns everything after it + * as a string_view, and modifies (in place) the input so that it points at + * everything before the '#'. If no '#' is found, the input is left unchanged + * and std::nullopt is returned. + * + * @attention The function is non-allocating and it does not throw. + * @returns Note that the returned string_view might be empty! */ -std::string to_unicode(std::string_view input); +ada_really_inline std::optional prune_hash( + std::string_view& input) noexcept; /** - * Checks if the input has tab or newline characters. - * - * @attention The has_tabs_or_newline function is a bottleneck and it is simple - * enough that compilers like GCC can 'autovectorize it'. + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -ada_really_inline constexpr bool has_tabs_or_newline( - std::string_view user_input) noexcept; +ada_really_inline bool shorten_path(std::string& path, + ada::scheme::type type) noexcept; /** - * Checks if the input is a forbidden host code point. - * @see https://url.spec.whatwg.org/#forbidden-host-code-point + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -ada_really_inline constexpr bool is_forbidden_host_code_point( - const char c) noexcept; +ada_really_inline bool shorten_path(std::string_view& path, + ada::scheme::type type) noexcept; /** - * Checks if the input contains a forbidden domain code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * @private + * + * Parse the path from the provided input and append to the existing + * (possibly empty) path. The input cannot contain tabs and spaces: it + * is the user's responsibility to check. + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ */ -ada_really_inline constexpr bool contains_forbidden_domain_code_point( - const char* input, size_t length) noexcept; +ada_really_inline void parse_prepared_path(const std::string_view input, + ada::scheme::type type, + std::string& path); /** - * Checks if the input contains a forbidden domain code point in which case - * the first bit is set to 1. If the input contains an upper case ASCII letter, - * then the second bit is set to 1. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * @private + * Remove and mutate all ASCII tab or newline characters from an input. */ -ada_really_inline constexpr bool contains_forbidden_domain_code_point_or_upper( - const char* input, size_t length) noexcept; +ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; /** - * Checks if the input is a forbidden doamin code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * @private + * Return the substring from input going from index pos to the end. + * This function cannot throw. */ -ada_really_inline constexpr bool is_forbidden_domain_code_point( - const char c) noexcept; +ada_really_inline std::string_view substring(std::string_view input, + size_t pos) noexcept; /** - * Checks if the input is alphanumeric, '+', '-' or '.' + * @private + * Returns true if the string_view points within the string. */ -ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept; +bool overlaps(std::string_view input1, const std::string& input2) noexcept; /** - * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex - * digit. An ASCII upper hex digit is an ASCII digit or a code point in the - * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an - * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. + * @private + * Return the substring from input going from index pos1 to the pos2 (non + * included). The length of the substring is pos2 - pos1. */ -ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept; +ada_really_inline std::string_view substring(const std::string& input, + size_t pos1, + size_t pos2) noexcept { +#if ADA_DEVELOPMENT_CHECKS + if (pos2 < pos1) { + std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")" + << std::endl; + abort(); + } +#endif + return std::string_view(input.data() + pos1, pos2 - pos1); +} /** - * Checks if the input is a C0 control or space character. - * - * @details A C0 control or space is a C0 control or U+0020 SPACE. - * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION - * SEPARATOR ONE, inclusive. + * @private + * Modify the string_view so that it has the new size pos, assuming that pos <= + * input.size(). This function cannot throw. */ -ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept; +ada_really_inline void resize(std::string_view& input, size_t pos) noexcept; /** - * Checks if the input is a ASCII tab or newline character. - * - * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. + * @private + * Returns a host's delimiter location depending on the state of the instance, + * and whether a colon was found outside brackets. Used by the host parser. */ -ada_really_inline constexpr bool is_ascii_tab_or_newline(const char c) noexcept; +ada_really_inline std::pair get_host_delimiter_location( + const bool is_special, std::string_view& view) noexcept; /** - * @details A double-dot path segment must be ".." or an ASCII case-insensitive - * match for ".%2e", "%2e.", or "%2e%2e". + * @private + * Removes leading and trailing C0 control and whitespace characters from + * string. */ -ada_really_inline ada_constexpr bool is_double_dot_path_segment( - const std::string_view input) noexcept; +ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept; /** - * @details A single-dot path segment must be "." or an ASCII case-insensitive - * match for "%2e". + * @private + * @see + * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path */ -ada_really_inline constexpr bool is_single_dot_path_segment( - const std::string_view input) noexcept; +template +ada_really_inline void strip_trailing_spaces_from_opaque_path( + url_type& url) noexcept; /** - * @details ipv4 character might contain 0-9 or a-f character ranges. + * @private + * Reverse the order of the bytes. */ -ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept; +ada_really_inline uint64_t swap_bytes(uint64_t val) noexcept; /** - * @details Convert hex to binary. + * @private + * Reverse the order of the bytes but only if the system is big endian */ -unsigned constexpr convert_hex_to_binary(char c) noexcept; +ada_really_inline uint64_t swap_bytes_if_big_endian(uint64_t val) noexcept; /** - * first_percent should be = input.find('%') - * - * @todo It would be faster as noexcept maybe, but it could be unsafe since. - * @author Node.js - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 - * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom + * @private + * Finds the delimiter of a view in authority state. */ -std::string percent_decode(const std::string_view input, size_t first_percent); +ada_really_inline size_t +find_authority_delimiter_special(std::string_view view) noexcept; /** - * Returns a percent-encoding string whether percent encoding was needed or not. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + * @private + * Finds the delimiter of a view in authority state. */ -std::string percent_encode(const std::string_view input, - const uint8_t character_set[]); +ada_really_inline size_t +find_authority_delimiter(std::string_view view) noexcept; + /** - * Returns a percent-encoded string version of input, while starting the percent - * encoding at the provided index. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + * @private */ -std::string percent_encode(const std::string_view input, - const uint8_t character_set[], size_t index); +template +inline void inner_concat(std::string& buffer, T t) { + buffer.append(t); +} + /** - * Returns true if percent encoding was needed, in which case, we store - * the percent-encoded content in 'out'. If the boolean 'append' is set to - * true, the content is appended to 'out'. - * If percent encoding is not needed, out is left unchanged. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + * @private */ -template -bool percent_encode(const std::string_view input, const uint8_t character_set[], - std::string& out); +template +inline void inner_concat(std::string& buffer, T t, Args... args) { + buffer.append(t); + return inner_concat(buffer, args...); +} + /** - * Returns the index at which percent encoding should start, or (equivalently), - * the length of the prefix that does not require percent encoding. + * Concatenate the arguments and return a string. + * @returns a string */ -ada_really_inline size_t percent_encode_index(const std::string_view input, - const uint8_t character_set[]); +template +std::string concat(Args... args) { + std::string answer; + inner_concat(answer, args...); + return answer; +} + /** - * Lowers the string in-place, assuming that the content is ASCII. - * Return true if the content was ASCII. + * @return Number of leading zeroes. */ -constexpr bool to_lower_ascii(char* input, size_t length) noexcept; -} // namespace ada::unicode +inline int leading_zeroes(uint32_t input_num) noexcept { +#if ADA_REGULAR_VISUAL_STUDIO + unsigned long leading_zero(0); + unsigned long in(input_num); + return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32; +#else + return __builtin_clz(input_num); +#endif // ADA_REGULAR_VISUAL_STUDIO +} -#endif // ADA_UNICODE_H -/* end file include/ada/unicode.h */ -/* begin file include/ada/url_base.h */ /** - * @file url_base.h - * @brief Declaration for the basic URL definitions + * Counts the number of decimal digits necessary to represent x. + * faster than std::to_string(x).size(). + * @return digit count */ -#ifndef ADA_URL_BASE_H -#define ADA_URL_BASE_H +inline int fast_digit_count(uint32_t x) noexcept { + auto int_log2 = [](uint32_t z) -> int { + return 31 - ada::helpers::leading_zeroes(z | 1); + }; + // Compiles to very few instructions. Note that the + // table is static and thus effectively a constant. + // We leave it inside the function because it is meaningless + // outside of it (this comes at no performance cost). + const static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return int((x + table[int_log2(x)]) >> 32); +} +} // namespace ada::helpers -/* begin file include/ada/url_components.h */ +#endif // ADA_HELPERS_H +/* end file include/ada/helpers.h */ +/* begin file include/ada/parser.h */ /** - * @file url_components.h - * @brief Declaration for the URL Components + * @file parser.h + * @brief Definitions for the parser. */ -#ifndef ADA_URL_COMPONENTS_H -#define ADA_URL_COMPONENTS_H - - -#include -#include - -namespace ada { +#ifndef ADA_PARSER_H +#define ADA_PARSER_H +/* begin file include/ada/expected.h */ /** - * @brief URL Component representations using offsets. - * - * @details We design the url_components struct so that it is as small - * and simple as possible. This version uses 32 bytes. - * - * This struct is used to extract components from a single 'href'. + * @file expected.h + * @brief Definitions for std::expected + * @private Excluded from docs through the doxygen file. */ -struct url_components { - constexpr static uint32_t omitted = uint32_t(-1); - - url_components() = default; - url_components(const url_components &u) = default; - url_components(url_components &&u) noexcept = default; - url_components &operator=(url_components &&u) noexcept = default; - url_components &operator=(const url_components &u) = default; - ~url_components() = default; +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// - /* - * By using 32-bit integers, we implicitly assume that the URL string - * cannot exceed 4 GB. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - */ - uint32_t protocol_end{0}; - /** - * Username end is not `omitted` by default to make username and password - * getters less costly to implement. - */ - uint32_t username_end{0}; - uint32_t host_start{0}; - uint32_t host_end{0}; - uint32_t port{omitted}; - uint32_t pathname_start{0}; - uint32_t search_start{omitted}; - uint32_t hash_start{omitted}; +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP - /** - * Check the following conditions: - * protocol_end < username_end < ... < hash_start, - * expect when a value is omitted. It also computes - * a lower bound on the possible string length that may match these - * offsets. - * @return true if the offset values are - * consistent with a possible URL string - */ - bool check_offset_consistency() const noexcept; +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 0 +#define TL_EXPECTED_VERSION_PATCH 1 - /** - * @private - * Converts a url_components to JSON stringified version. - */ - std::string to_string() const; +#include +#include +#include +#include -}; // struct url_components +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif -} // namespace ada +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr #endif -/* end file include/ada/url_components.h */ -#include +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 +#endif -namespace ada { +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif -/** - * @brief Base class of URL implementations - * - * @details A url_base contains a few attributes: is_valid, has_opaque_path and - * type. All non-trivial implementation details are in derived classes such as - * ada::url and ada::url_aggregator. - * - * It is an abstract class that cannot be instantiated directly. - */ -struct url_base { - virtual ~url_base() = default; +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif - /** - * Used for returning the validity from the result of the URL parser. - */ - bool is_valid{true}; +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions - /** - * A URL has an opaque path if its path is a string. - */ - bool has_opaque_path{false}; +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign - /** - * @private - */ - ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; +// This one will be different for GCC 5.7 if it's ever supported +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible - /** - * A URL is special if its scheme is a special scheme. A URL is not special if - * its scheme is not a special scheme. - */ - [[nodiscard]] ada_really_inline bool is_special() const noexcept; +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif - /** - * The origin getter steps are to return the serialization of this’s URL’s - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin - */ - [[nodiscard]] virtual std::string get_origin() const noexcept = 0; +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif - /** - * Returns true if this URL has a valid domain as per RFC 1034 and - * corresponding specifications. Among other things, it requires - * that the domain string has fewer than 255 octets. - */ - [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif - /** - * @private - * - * Return the 'special port' if the URL is special and not 'file'. - * Returns 0 otherwise. - */ - [[nodiscard]] inline uint16_t get_special_port() const noexcept; +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif - /** - * @private - * - * Get the default port if the url's scheme has one, returns 0 otherwise. - */ - [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif - /** - * @private - * - * Parse a port (16-bit decimal digit) from the provided input. - * We assume that the input does not contain spaces or tabs - * within the ASCII digits. - * It returns how many bytes were consumed when a number is successfully - * parsed. - * @return On failure, it returns zero. - * @see https://url.spec.whatwg.org/#host-parsing - */ - virtual ada_really_inline size_t parse_port( - std::string_view view, bool check_trailing_content = false) noexcept = 0; +namespace tl { +template +class expected; - /** - * Returns a JSON string representation of this URL. - */ - virtual std::string to_string() const = 0; +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; - /** @private */ - virtual inline void clear_base_pathname() = 0; +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif - /** @private */ - virtual inline void clear_base_search() = 0; +template +class unexpected { + public: + static_assert(!std::is_same::value, "E must not be void"); - /** @private */ - virtual inline bool base_fragment_has_value() const = 0; + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} - /** @private */ - virtual inline bool base_search_has_value() const = 0; + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} -}; // url_base + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} -} // namespace ada + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } + + private: + E m_val; +}; +#ifdef __cpp_deduction_guides +template +unexpected(E) -> unexpected; #endif -/* end file include/ada/url_base.h */ -#include -#include -#include -#include -#include -#include +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} -namespace ada { +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} -/** - * @brief Generic URL struct reliant on std::string instantiation. - * - * @details To disambiguate from a valid URL string it can also be referred to - * as a URL record. A URL is a struct that represents a universal identifier. - * Unlike the url_aggregator, the ada::url represents the different components - * of a parsed URL as independent std::string instances. This makes the - * structure heavier and more reliant on memory allocations. When getting - * components from the parsed URL, a new std::string is typically constructed. - * - * @see https://url.spec.whatwg.org/#url-representation - */ -struct url : url_base { - url() = default; - url(const url &u) = default; - url(url &&u) noexcept = default; - url &operator=(url &&u) noexcept = default; - url &operator=(const url &u) = default; - ~url() = default; +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; - /** - * @private - * A URL’s username is an ASCII string identifying a username. It is initially - * the empty string. - */ - std::string username{}; +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif +#endif +} - /** - * @private - * A URL’s password is an ASCII string identifying a password. It is initially - * the empty string. - */ - std::string password{}; +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template +using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template +using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; - /** - * @private - * A URL’s host is null or a host. It is initially null. - */ - std::optional host{}; +// std::conjunction from C++17 +template +struct conjunction : std::true_type {}; +template +struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; - /** - * @private - * A URL’s port is either null or a 16-bit unsigned integer that identifies a - * networking port. It is initially null. - */ - std::optional port{}; +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif - /** - * @private - * A URL’s path is either an ASCII string or a list of zero or more ASCII - * strings, usually identifying a location. - */ - std::string path{}; +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; - /** - * @private - * A URL’s query is either null or an ASCII string. It is initially null. - */ - std::optional query{}; +template +struct is_const_or_const_ref : std::false_type {}; +template +struct is_const_or_const_ref : std::true_type {}; +template +struct is_const_or_const_ref : std::true_type {}; +#endif - /** - * @private - * A URL’s fragment is either null or an ASCII string that can be used for - * further processing on the resource the URL’s other components identify. It - * is initially null. - */ - std::optional fragment{}; +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template < + typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} - /** @private */ - inline void update_unencoded_base_hash(std::string_view input); - /** @private */ - inline void update_base_hostname(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input, - const uint8_t query_percent_encode_set[]); - /** @private */ - inline void update_base_search(std::optional input); - /** @private */ - inline void update_base_pathname(const std::string_view input); - /** @private */ - inline void update_base_username(const std::string_view input); - /** @private */ - inline void update_base_password(const std::string_view input); - /** @private */ - inline void update_base_port(std::optional input); - /** @private */ - inline void clear_base_pathname() override; - /** @private */ - inline void clear_base_search() override; - /** @private */ - inline bool base_fragment_has_value() const override; - /** @private */ - inline bool base_search_has_value() const override; - /** @private set this URL's type to file */ - inline void set_protocol_as_file(); - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - [[nodiscard]] bool has_valid_domain() const noexcept override; +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} - /** - * @private - * - * Parse the path from the provided input. - * Return true on success. Control characters not - * trimmed from the ends (they should have - * been removed if needed). - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ - ada_really_inline void parse_path(const std::string_view input); +// std::invoke_result from C++17 +template +struct invoke_result_impl; - /** - * Set the scheme for this URL. The provided scheme should be a valid - * scheme string, be lower-cased, not contain spaces or tabs. It should - * have no spurious trailing or leading content. - */ - inline void set_scheme(std::string &&new_scheme) noexcept; +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; - /** - * @private - * - * Take the scheme from another URL. The scheme string is moved from the - * provided url. - */ - inline void copy_scheme(ada::url &&u) noexcept; +template +using invoke_result = invoke_result_impl; - /** - * Returns a JSON string representation of this URL. - */ - std::string to_string() const override; +template +using invoke_result_t = typename invoke_result::type; - /** - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer - */ - [[nodiscard]] ada_really_inline std::string get_href() const noexcept; +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template +struct is_swappable : std::true_type {}; - /** - * The origin getter steps are to return the serialization of this’s URL’s - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin - */ - [[nodiscard]] std::string get_origin() const noexcept override; +template +struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; - /** - * The protocol getter steps are to return this’s URL’s scheme, followed by - * U+003A (:). - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-protocol - */ - [[nodiscard]] std::string get_protocol() const noexcept; +template +tag swap(T &, T &); +template +tag swap(T (&a)[N], T (&b)[N]); - /** - * Return url’s host, serialized, followed by U+003A (:) and url’s port, - * serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-host - */ - [[nodiscard]] std::string get_host() const noexcept; +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template +std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); - /** - * Return this’s URL’s host, serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-hostname - */ - [[nodiscard]] std::string get_hostname() const noexcept; +template +std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); - /** - * The pathname getter steps are to return the result of URL path serializing - * this’s URL. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-pathname - */ - [[nodiscard]] const std::string_view get_pathname() const noexcept; +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; - /** - * Compute the pathname length in bytes witout instantiating a view or a - * string. - * @return size of the pathname in bytes - * @see https://url.spec.whatwg.org/#dom-url-pathname - */ - ada_really_inline size_t get_pathname_length() const noexcept; +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; - /** - * Return U+003F (?), followed by this’s URL’s query. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - [[nodiscard]] std::string get_search() const noexcept; +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests - /** - * The username getter steps are to return this’s URL’s username. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - [[nodiscard]] const std::string &get_username() const noexcept; +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; - /** - * @return Returns true on successful operation. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - bool set_username(const std::string_view input); +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - bool set_password(const std::string_view input); +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - bool set_port(const std::string_view input); +// Trait for checking if a type is a tl::expected +template +struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template +using is_expected = is_expected_impl>; - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - void set_hash(const std::string_view input); +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - void set_search(const std::string_view input); +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - bool set_pathname(const std::string_view input); +template +using is_void_or = conditional_t::value, std::true_type, U>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-host - */ - bool set_host(const std::string_view input); +template +using is_copy_constructible_or_void = + is_void_or>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-hostname - */ - bool set_hostname(const std::string_view input); +template +using is_move_constructible_or_void = + is_void_or>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-protocol - */ - bool set_protocol(const std::string_view input); +template +using is_copy_assignable_or_void = is_void_or>; - /** - * @see https://url.spec.whatwg.org/#dom-url-href - */ - bool set_href(const std::string_view input); +template +using is_move_assignable_or_void = is_void_or>; - /** - * @private - * - * Sets the host or hostname according to override condition. - * Return true on success. - * @see https://url.spec.whatwg.org/#hostname-state - */ - template - bool set_host_or_hostname(std::string_view input); +} // namespace detail - /** - * The password getter steps are to return this’s URL’s password. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - [[nodiscard]] const std::string &get_password() const noexcept; +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; - /** - * Return this’s URL’s port, serialized. - * @return a newly constructed string representing the port. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - [[nodiscard]] std::string get_port() const noexcept; +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - /** - * Return U+0023 (#), followed by this’s URL’s fragment. - * @return a newly constructed string representing the hash. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - [[nodiscard]] std::string get_hash() const noexcept; + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} - /** - * A URL includes credentials if its username or password is not the empty - * string. - */ - [[nodiscard]] ada_really_inline bool includes_credentials() const noexcept; + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} - /** - * @private - * - * A URL cannot have a username/password/port if its host is null or the empty - * string, or its scheme is "file". - */ - [[nodiscard]] inline bool cannot_have_credentials_or_port() const; + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - /** @private */ - ada_really_inline size_t - parse_port(std::string_view view, - bool check_trailing_content = false) noexcept override; + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; - /** - * @private - * - * Take the scheme from another URL. The scheme string is copied from the - * provided url. - */ - inline void copy_scheme(const ada::url &u); +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - /** - * @private - * - * Parse the host from the provided input. We assume that - * the input does not contain spaces or tabs. Control - * characters and spaces are not trimmed (they should have - * been removed if needed). - * Return true on success. - * @see https://url.spec.whatwg.org/#host-parsing - */ - [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} - /** @private */ - template - [[nodiscard]] ada_really_inline bool parse_scheme( - const std::string_view input); + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} - /** - * Useful for implementing efficient serialization for the URL. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - * - * Inspired after servo/url - * - * @return a newly constructed component. - * - * @see - * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 - */ - [[nodiscard]] ada_really_inline ada::url_components get_components() - const noexcept; + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - private: - /** - * @private - * - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv4-parser - */ - [[nodiscard]] bool parse_ipv4(std::string_view input); + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; - /** - * @private - * - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv6-parser - */ - [[nodiscard]] bool parse_ipv6(std::string_view input); +// T is trivial, E is not. +template +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} - /** - * @private - * - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-opaque-host-parser - */ - [[nodiscard]] bool parse_opaque_host(std::string_view input); + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} - /** - * @private - * - * A URL’s scheme is an ASCII string that identifies the type of URL and can - * be used to dispatch a URL for further processing after parsing. It is - * initially the empty string. We only set non_special_scheme when the scheme - * is non-special, otherwise we avoid constructing string. - * - * Special schemes are stored in ada::scheme::details::is_special_list so we - * typically do not need to store them in each url instance. - */ - std::string non_special_scheme{}; + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -}; // struct url + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -inline std::ostream &operator<<(std::ostream &out, const ada::url &u); -} // namespace ada + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } -#endif // ADA_URL_H -/* end file include/ada/url.h */ -/* begin file include/ada/state.h */ -/** - * @file state.h - * @brief Definitions for the states of the URL state machine. - */ -#ifndef ADA_STATE_H -#define ADA_STATE_H + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} -#include + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -namespace ada { + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -/** - * @see https://url.spec.whatwg.org/#url-parsing - */ -enum class state { - AUTHORITY, - SCHEME_START, - SCHEME, - HOST, - NO_SCHEME, - FRAGMENT, - RELATIVE_SCHEME, - RELATIVE_SLASH, - FILE, - FILE_HOST, - FILE_SLASH, - PATH_OR_AUTHORITY, - SPECIAL_AUTHORITY_IGNORE_SLASHES, - SPECIAL_AUTHORITY_SLASHES, - SPECIAL_RELATIVE_OR_AUTHORITY, - QUERY, - PATH, - PATH_START, - OPAQUE_PATH, - PORT, + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; }; -/** - * Stringify a URL state machine state. - */ -ada_warn_unused std::string to_string(ada::state s); - -} // namespace ada +// `T` is `void`, `E` is trivially-destructible +template +struct expected_storage_base { +#if __GNUC__ <= 5 +// no constexpr for GCC 4/5 bug +#else + TL_EXPECTED_MSVC2015_CONSTEXPR +#endif + expected_storage_base() : m_has_val(true) {} -#endif // ADA_STATE_H -/* end file include/ada/state.h */ + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} -#include -#include + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} -/** - * @private - * @namespace ada::helpers - * @brief Includes the definitions for helper functions - */ -namespace ada::helpers { + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -/** - * @private - */ -template -void encode_json(std::string_view view, out_iter out); + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -/** - * @private - * This function is used to prune a fragment from a url, and returning the - * removed string if input has fragment. - * - * @details prune_fragment seeks the first '#' and returns everything after it - * as a string_view, and modifies (in place) the input so that it points at - * everything before the '#'. If no '#' is found, the input is left unchanged - * and std::nullopt is returned. - * - * @attention The function is non-allocating and it does not throw. - * @returns Note that the returned string_view might be empty! - */ -ada_really_inline std::optional prune_fragment( - std::string_view& input) noexcept; + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string& path, - ada::scheme::type type) noexcept; - -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string_view& path, - ada::scheme::type type) noexcept; - -/** - * @private - * - * Parse the path from the provided input and append to the existing - * (possibly empty) path. The input cannot contain tabs and spaces: it - * is the user's responsibility to check. - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ -ada_really_inline void parse_prepared_path(const std::string_view input, - ada::scheme::type type, - std::string& path); +// `T` is `void`, `E` is not trivially-destructible +template +struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} -/** - * @private - * Remove and mutate all ASCII tab or newline characters from an input. - */ -ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} -/** - * @private - * Return the substring from input going from index pos to the end. If pos > - * input.size(), it returns an empty string_view. This function cannot throw. - */ -ada_really_inline std::string_view substring(std::string_view input, - size_t pos) noexcept; + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -/** - * @private - * Returns true if the string_view points within the string. - */ -bool overlaps(std::string_view input1, const std::string& input2) noexcept; + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -/** - * @private - * Return the substring from input going from index pos1 to the pos2 (non - * included). The length of the substring is pos2 - pos1. - */ -ada_really_inline std::string_view substring(const std::string& input, - size_t pos1, - size_t pos2) noexcept { -#if ADA_DEVELOPMENT_CHECKS - if (pos2 < pos1) { - std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")" - << std::endl; - abort(); + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } } -#endif - return std::string_view(input.data() + pos1, pos2 - pos1); -} - -/** - * @private - * Modify the string_view so that it has the new size pos, assuming that pos <= - * input.size(). This function cannot throw. - */ -ada_really_inline void resize(std::string_view& input, size_t pos) noexcept; - -/** - * @private - * Returns a host's delimiter location depending on the state of the instance, - * and whether a colon was found outside brackets. Used by the host parser. - */ -ada_really_inline std::pair get_host_delimiter_location( - const bool is_special, std::string_view& view) noexcept; -/** - * @private - * Removes leading and trailing C0 control and whitespace characters from - * string. - */ -ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept; + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; -/** - * @private - * @see - * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path - */ -template -ada_really_inline void strip_trailing_spaces_from_opaque_path( - url_type& url) noexcept; +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; -/** - * @private - * Reverse the order of the bytes. - */ -ada_really_inline uint64_t swap_bytes(uint64_t val) noexcept; + template + void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } -/** - * @private - * Reverse the order of the bytes but only if the system is big endian - */ -ada_really_inline uint64_t swap_bytes_if_big_endian(uint64_t val) noexcept; + template + void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } -/** - * @private - * Finds the delimiter of a view in authority state. - */ -ada_really_inline size_t -find_authority_delimiter_special(std::string_view view) noexcept; + template + void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } -/** - * @private - * Finds the delimiter of a view in authority state. - */ -ada_really_inline size_t -find_authority_delimiter(std::string_view view) noexcept; +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED -/** - * @private - */ -template -inline void inner_concat(std::string& buffer, T t) { - buffer.append(t); -} + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } -/** - * @private - */ -template -inline void inner_concat(std::string& buffer, T t, Args... args) { - buffer.append(t); - return inner_concat(buffer, args...); -} + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } -/** - * Concatenate the arguments and return a string. - * @returns a string - */ -template -std::string concat(Args... args) { - std::string answer; - inner_concat(answer, args...); - return answer; -} + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); -/** - * @return Number of leading zeroes. - */ -inline int leading_zeroes(uint32_t input_num) noexcept { -#if ADA_REGULAR_VISUAL_STUDIO - unsigned long leading_zero(0); - unsigned long in(input_num); - return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32; +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } #else - return __builtin_clz(input_num); -#endif // ADA_REGULAR_VISUAL_STUDIO -} - -/** - * Counts the number of decimal digits necessary to represent x. - * faster than std::to_string(x).size(). - * @return digit count - */ -inline int fast_digit_count(uint32_t x) noexcept { - auto int_log2 = [](uint32_t z) -> int { - return 31 - ada::helpers::leading_zeroes(z | 1); - }; - // Compiles to very few instructions. Note that the - // table is static and thus effectively a constant. - // We leave it inside the function because it is meaningless - // outside of it (this comes at no performance cost). - const static uint64_t table[] = { - 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, - 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, - 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, - 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, - 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, - 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, - 42949672960, 42949672960}; - return int((x + table[int_log2(x)]) >> 32); -} -} // namespace ada::helpers - -#endif // ADA_HELPERS_H -/* end file include/ada/helpers.h */ -/* begin file include/ada/parser.h */ -/** - * @file parser.h - * @brief Definitions for the parser. - */ -#ifndef ADA_PARSER_H -#define ADA_PARSER_H - -/* begin file include/ada/expected.h */ -/** - * @file expected.h - * @brief Definitions for std::expected - * @private Excluded from docs through the doxygen file. - */ -/// -// expected - An implementation of std::expected with extensions -// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) -// -// Documentation available at http://tl.tartanllama.xyz/ -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to the -// public domain worldwide. This software is distributed without any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. If not, see -// . -/// - -#ifndef TL_EXPECTED_HPP -#define TL_EXPECTED_HPP - -#define TL_EXPECTED_VERSION_MAJOR 1 -#define TL_EXPECTED_VERSION_MINOR 0 -#define TL_EXPECTED_VERSION_PATCH 1 - -#include -#include -#include -#include - -#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) -#define TL_EXPECTED_EXCEPTIONS_ENABLED -#endif - -#if (defined(_MSC_VER) && _MSC_VER == 1900) -#define TL_EXPECTED_MSVC2015 -#define TL_EXPECTED_MSVC2015_CONSTEXPR -#else -#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr -#endif - -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC49 + construct(rhs.get()); #endif + } else { + assign_common(rhs); + } + } -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC54 -#endif + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC55 + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); #endif + } else { + assign_common(std::move(rhs)); + } + } -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) -// GCC < 5 doesn't support overloading on const&& for member functions +#else -#define TL_EXPECTED_NO_CONSTRR -// GCC < 5 doesn't support some standard C++11 type traits -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::has_trivial_copy_constructor -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::has_trivial_copy_assign + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } -// This one will be different for GCC 5.7 if it's ever supported -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(rhs); + } + } -// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks -// std::vector for non-copyable types -#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) -#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -namespace tl { -namespace detail { -template -struct is_trivially_copy_constructible - : std::is_trivially_copy_constructible {}; -#ifdef _GLIBCXX_VECTOR -template -struct is_trivially_copy_constructible> : std::false_type {}; -#endif -} // namespace detail -} // namespace tl #endif -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - tl::detail::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#else -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#endif + // The common part of move/copy assigning + template + void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } -#if __cplusplus > 201103L -#define TL_EXPECTED_CXX14 -#endif + bool has_value() const { return this->m_has_val; } -#ifdef TL_EXPECTED_GCC49 -#define TL_EXPECTED_GCC49_CONSTEXPR -#else -#define TL_EXPECTED_GCC49_CONSTEXPR constexpr + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } #endif -#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ - defined(TL_EXPECTED_GCC49)) -#define TL_EXPECTED_11_CONSTEXPR -#else -#define TL_EXPECTED_11_CONSTEXPR constexpr + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } #endif -namespace tl { -template -class expected; - -#ifndef TL_MONOSTATE_INPLACE_MUTEX -#define TL_MONOSTATE_INPLACE_MUTEX -class monostate {}; - -struct in_place_t { - explicit in_place_t() = default; + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } }; -static constexpr in_place_t in_place{}; -#endif +// This base class provides some handy member functions which can be used in +// further derived classes template -class unexpected { - public: - static_assert(!std::is_same::value, "E must not be void"); - - unexpected() = delete; - constexpr explicit unexpected(const E &e) : m_val(e) {} +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; - constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + template + void construct() noexcept { + this->m_has_val = true; + } - template ::value>::type * = nullptr> - constexpr explicit unexpected(Args &&...args) - : m_val(std::forward(args)...) {} - template < - class U, class... Args, - typename std::enable_if &, Args &&...>::value>::type * = nullptr> - constexpr explicit unexpected(std::initializer_list l, Args &&...args) - : m_val(l, std::forward(args)...) {} + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template + void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } - constexpr const E &value() const & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } - constexpr const E &&value() const && { return std::move(m_val); } + template + void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } - private: - E m_val; -}; + template + void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } -#ifdef __cpp_deduction_guides -template -unexpected(E) -> unexpected; -#endif + bool has_value() const { return this->m_has_val; } -template -constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() == rhs.value(); -} -template -constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() != rhs.value(); -} -template -constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() < rhs.value(); -} -template -constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() <= rhs.value(); -} -template -constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() > rhs.value(); -} -template -constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() >= rhs.value(); -} + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif -template -unexpected::type> make_unexpected(E &&e) { - return unexpected::type>(std::forward(e)); -} + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } +}; -struct unexpect_t { - unexpect_t() = default; +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; }; -static constexpr unexpect_t unexpect{}; -namespace detail { -template -[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - throw std::forward(e); -#else -#ifdef _MSC_VER - __assume(0); -#else - __builtin_unreachable(); -#endif -#endif -} +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; -#ifndef TL_TRAITS_MUTEX -#define TL_TRAITS_MUTEX -// C++14-style aliases for brevity -template -using remove_const_t = typename std::remove_const::type; -template -using remove_reference_t = typename std::remove_reference::type; -template -using decay_t = typename std::decay::type; -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } -// std::conjunction from C++17 -template -struct conjunction : std::true_type {}; -template -struct conjunction : B {}; -template -struct conjunction - : std::conditional, B>::type {}; + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; -#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L -#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template +struct expected_move_base; #endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; -// In C++11 mode, there's an issue in libc++'s std::mem_fn -// which results in a hard-error when using it in a noexcept expression -// in some cases. This is a check to workaround the common failing case. -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND -template -struct is_pointer_to_non_const_member_func : std::false_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; -template -struct is_const_or_const_ref : std::false_type {}; -template -struct is_const_or_const_ref : std::true_type {}; -template -struct is_const_or_const_ref : std::true_type {}; -#endif + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; -// std::invoke from C++17 -// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround -template < - typename Fn, typename... Args, -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND - typename = enable_if_t::value && - is_const_or_const_ref::value)>, -#endif - typename = enable_if_t>::value>, int = 0> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::mem_fn(f)(std::forward(args)...))) - -> decltype(std::mem_fn(f)(std::forward(args)...)) { - return std::mem_fn(f)(std::forward(args)...); -} +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; -template >::value>> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) { - return std::forward(f)(std::forward(args)...); -} +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; -// std::invoke_result from C++17 -template -struct invoke_result_impl; + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; -template -struct invoke_result_impl< - F, - decltype(detail::invoke(std::declval(), std::declval()...), void()), - Us...> { - using type = - decltype(detail::invoke(std::declval(), std::declval()...)); + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) = + default; }; -template -using invoke_result = invoke_result_impl; +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template +struct expected_move_assign_base; +#endif -template -using invoke_result_t = typename invoke_result::type; +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; -#if defined(_MSC_VER) && _MSC_VER <= 1900 -// TODO make a version which works with MSVC 2015 -template -struct is_swappable : std::true_type {}; + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; -template -struct is_nothrow_swappable : std::true_type {}; -#else -// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept -namespace swap_adl_tests { -// if swap ADL finds this then it would call std::swap otherwise (same -// signature) -struct tag {}; + expected_move_assign_base(expected_move_assign_base &&rhs) = default; -template -tag swap(T &, T &); -template -tag swap(T (&a)[N], T (&b)[N]); + expected_move_assign_base &operator=(const expected_move_assign_base &rhs) = + default; -// helper functions to test if an unqualified swap is possible, and if it -// becomes std::swap -template -std::false_type can_swap(...) noexcept(false); -template (), std::declval()))> -std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), - std::declval()))); + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; -template -std::false_type uses_std(...); -template -std::is_same(), std::declval())), tag> -uses_std(int); +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_std_swap_noexcept - : std::integral_constant::value && - std::is_nothrow_move_assignable::value> {}; +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_std_swap_noexcept : is_std_swap_noexcept {}; +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_adl_swap_noexcept - : std::integral_constant(0))> {}; -} // namespace swap_adl_tests +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - (std::is_move_assignable::value && - std::is_move_constructible::value))> {}; +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + default; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = default; +}; -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std( - 0))::value || - is_swappable::value)> {}; +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + default; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = delete; +}; -template -struct is_nothrow_swappable - : std::integral_constant< - bool, - is_swappable::value && - ((decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_std_swap_noexcept::value) || - (!decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; -#endif -#endif +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + delete; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = default; +}; -// Trait for checking if a type is a tl::expected -template -struct is_expected_impl : std::false_type {}; template -struct is_expected_impl> : std::true_type {}; -template -using is_expected = is_expected_impl>; +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + delete; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = delete; +}; -template -using expected_enable_forward_value = detail::enable_if_t< - std::is_constructible::value && - !std::is_same, in_place_t>::value && - !std::is_same, detail::decay_t>::value && - !std::is_same, detail::decay_t>::value>; +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; -template -using expected_enable_from_other = detail::enable_if_t< - std::is_constructible::value && - std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value>; - -template -using is_void_or = conditional_t::value, std::true_type, U>; - -template -using is_copy_constructible_or_void = - is_void_or>; - -template -using is_move_constructible_or_void = - is_void_or>; +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base &operator=( + expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base &operator=( + expected_default_ctor_base &&) noexcept = default; -template -using is_copy_assignable_or_void = is_void_or>; + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; -template -using is_move_assignable_or_void = is_void_or>; +// This specialization is for when T is not default constructible +template +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base &operator=( + expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base &operator=( + expected_default_ctor_base &&) noexcept = default; + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; } // namespace detail -namespace detail { -struct no_init_t {}; -static constexpr no_init_t no_init{}; - -// Implements the storage of the values, and ensures that the destructor is -// trivial if it can be. -// -// This specialization is for where neither `T` or `E` is trivially -// destructible, so the destructors must be called on destruction of the -// `expected` -template ::value, - bool = std::is_trivially_destructible::value> -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} +template +class bad_expected_access : public std::exception { + public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + virtual const char *what() const noexcept override { + return "Bad expected access"; + } - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } else { - m_unexpect.~unexpected(); - } - } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; + private: + E m_val; }; -// This specialization is for when both `T` and `E` are trivially-destructible, -// so the destructor of the `expected` can be trivial. +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. template -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); - ~expected_storage_base() = default; - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } -// T is trivial, E is not. -template -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) - : m_no_init(), m_has_val(false) {} + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); - } +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif -// E is trivial, T is not. -template -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} +#else + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template + constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template + constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; -// `T` is `void`, `E` is trivially-destructible -template -struct expected_storage_base { -#if __GNUC__ <= 5 -// no constexpr for GCC 4/5 bug +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template + constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } #else - TL_EXPECTED_MSVC2015_CONSTEXPR + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } #endif - expected_storage_base() : m_has_val(true) {} +#endif + template + expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } - constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + template + expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } - constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + template + expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; template ::value> * = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() = default; - struct dummy {}; - union { - unexpected m_unexpect; - dummy m_val; - }; - bool m_has_val; -}; + T, std::initializer_list &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} -// `T` is `void`, `E` is not trivially-destructible -template -struct expected_storage_base { - constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} - constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); } } - union { - unexpected m_unexpect; - char m_dummy; - }; - bool m_has_val; -}; - -// This base class provides some handy member functions which can be used in -// further derived classes -template -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; - - template - void construct(Args &&...args) noexcept { - new (std::addressof(this->m_val)) T(std::forward(args)...); - this->m_has_val = true; - } - - template - void construct_with(Rhs &&rhs) noexcept { - new (std::addressof(this->m_val)) T(std::forward(rhs).get()); - this->m_has_val = true; + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } } - template - void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(args)...); - this->m_has_val = false; + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } } -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - - // These assign overloads ensure that the most efficient assignment - // implementation is used while maintaining the strong exception guarantee. - // The problematic case is where rhs has a value, but *this does not. - // - // This overload handles the case where we can just copy-construct `T` - // directly into place without throwing. - template ::value> - * = nullptr> - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(rhs.get()); + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); } else { - assign_common(rhs); + this->construct_error(std::move(rhs.error())); } } - // This overload handles the case where we can attempt to create a copy of - // `T`, then no-throw move it into place if the copy was successful. - template ::value && - std::is_nothrow_move_constructible::value> - * = nullptr> - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - T tmp = rhs.get(); - geterr().~unexpected(); - construct(std::move(tmp)); + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); } else { - assign_common(rhs); + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; } + + return *this; } - // This overload is the worst-case, where we have to move-construct the - // unexpected value into temporary storage, then try to copy the T into place. - // If the construction succeeds, then everything is fine, but if it throws, - // then we move the old unexpected value back into place before rethrowing the - // exception. - template ::value && - !std::is_nothrow_move_constructible::value> - * = nullptr> - void assign(const expected_operations_base &rhs) { - if (!this->m_has_val && rhs.m_has_val) { - auto tmp = std::move(geterr()); - geterr().~unexpected(); + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { - construct(rhs.get()); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; } catch (...) { - geterr() = std::move(tmp); + err() = std::move(tmp); throw; } #else - construct(rhs.get()); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; #endif - } else { - assign_common(rhs); } + + return *this; } - // These overloads do the same as above, but for rvalues - template ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(std::move(rhs).get()); + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; } else { - assign_common(std::move(rhs)); + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; } + + return *this; } - template ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) { - if (!this->m_has_val && rhs.m_has_val) { - auto tmp = std::move(geterr()); - geterr().~unexpected(); + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { - construct(std::move(rhs).get()); + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; } catch (...) { - geterr() = std::move(tmp); + err() = std::move(tmp); throw; } #else - construct(std::move(rhs).get()); + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; #endif - } else { - assign_common(std::move(rhs)); } } -#else - - // If exceptions are disabled then we can just copy-construct - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(rhs.get()); + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); } else { - assign_common(rhs); + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; } } - void assign(expected_operations_base &&rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(std::move(rhs).get()); + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); } else { - assign_common(rhs); - } - } - -#endif + auto tmp = std::move(err()); + err().~unexpected(); - // The common part of move/copy assigning - template - void assign_common(Rhs &&rhs) { - if (this->m_has_val) { - if (rhs.m_has_val) { - get() = std::forward(rhs).get(); - } else { - destroy_val(); - construct_error(std::forward(rhs).geterr()); - } - } else { - if (!rhs.m_has_val) { - geterr() = std::forward(rhs).geterr(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; } +#else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; +#endif } } - bool has_value() const { return this->m_has_val; } - - TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } - constexpr const T &get() const & { return this->m_val; } - TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const T &&get() const && { return std::move(this->m_val); } -#endif + private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; - TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&geterr() const && { - return std::move(this->m_unexpect); + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { + // swapping void is a no-op } -#endif - - TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } -}; -// This base class provides some handy member functions which can be used in -// further derived classes -template -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } - template - void construct() noexcept { - this->m_has_val = true; + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); } - // This function doesn't use its argument, but needs it so that code in - // levels above this can work independently of whether T is void - template - void construct_with(Rhs &&) noexcept { - this->m_has_val = true; + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); } - template - void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(args)...); - this->m_has_val = false; + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); } - template - void assign(Rhs &&rhs) noexcept { - if (!this->m_has_val) { - if (rhs.m_has_val) { - geterr().~unexpected(); - construct(); - } else { - geterr() = std::forward(rhs).geterr(); - } - } else { - if (!rhs.m_has_val) { - construct_error(std::forward(rhs).geterr()); - } + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif } - bool has_value() const { return this->m_has_val; } - - TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&geterr() const && { - return std::move(this->m_unexpect); - } + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); #endif - - TL_EXPECTED_11_CONSTEXPR void destroy_val() { - // no-op } -}; -// This class manages conditionally having a trivial copy constructor -// This specialization is for when T and E are trivially copy constructible -template :: - value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; -}; - -// This specialization is for when T or E are not trivially copy constructible -template -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; - - expected_copy_base() = default; - expected_copy_base(const expected_copy_base &rhs) - : expected_operations_base(no_init) { - if (rhs.has_value()) { - this->construct_with(rhs); + public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); } else { - this->construct_error(rhs.geterr()); + using std::swap; + swap(err(), rhs.err()); } } - expected_copy_base(expected_copy_base &&rhs) = default; - expected_copy_base &operator=(const expected_copy_base &rhs) = default; - expected_copy_base &operator=(expected_copy_base &&rhs) = default; -}; - -// This class manages conditionally having a trivial move constructor -// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it -// doesn't implement an analogue to std::is_trivially_move_constructible. We -// have to make do with a non-trivial move constructor even if T is trivially -// move constructible -#ifndef TL_EXPECTED_GCC49 -template >::value - &&std::is_trivially_move_constructible::value> -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; -}; -#else -template -struct expected_move_base; -#endif -template -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; - - expected_move_base() = default; - expected_move_base(const expected_move_base &rhs) = default; + constexpr const T *operator->() const { return valptr(); } + TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } - expected_move_base(expected_move_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value) - : expected_copy_base(no_init) { - if (rhs.has_value()) { - this->construct_with(std::move(rhs)); - } else { - this->construct_error(std::move(rhs.geterr())); - } + template ::value> * = nullptr> + constexpr const U &operator*() const & { + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + return std::move(val()); } - expected_move_base &operator=(const expected_move_base &rhs) = default; - expected_move_base &operator=(expected_move_base &&rhs) = default; -}; -// This class manages conditionally having a trivial copy assignment operator -template >::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; -}; + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } -template -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } - expected_copy_assign_base() = default; - expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + constexpr const E &error() const & { return err().value(); } + TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } + constexpr const E &&error() const && { return std::move(err().value()); } + TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } - expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; - expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { - this->assign(rhs); - return *this; + template + constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template + TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); } - expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) = - default; }; -// This class manages conditionally having a trivial move assignment operator -// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it -// doesn't implement an analogue to std::is_trivially_move_assignable. We have -// to make do with a non-trivial move assignment operator even if T is trivially -// move assignable -#ifndef TL_EXPECTED_GCC49 -template , - std::is_trivially_move_constructible, - std::is_trivially_move_assignable>>:: - value &&std::is_trivially_destructible::value - &&std::is_trivially_move_constructible::value - &&std::is_trivially_move_assignable::value> -struct expected_move_assign_base : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; -}; -#else -template -struct expected_move_assign_base; -#endif +namespace detail { +template +using exp_t = typename detail::decay_t::value_type; +template +using err_t = typename detail::decay_t::error_type; +template +using ret_t = expected>; -template -struct expected_move_assign_base - : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); - expected_move_assign_base() = default; - expected_move_assign_base(const expected_move_assign_base &rhs) = default; + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} - expected_move_assign_base(expected_move_assign_base &&rhs) = default; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); - expected_move_assign_base &operator=(const expected_move_assign_base &rhs) = - default; + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template +struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); - expected_move_assign_base & - operator=(expected_move_assign_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value - &&std::is_nothrow_move_assignable::value) { - this->assign(std::move(rhs)); - return *this; - } -}; + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} -// expected_delete_ctor_base will conditionally delete copy and move -// constructors depending on whether T is copy/move constructible -template ::value && - std::is_copy_constructible::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::value)> -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } -// expected_delete_assign_base will conditionally delete copy and move -// constructors depending on whether T and E are copy/move constructible + -// assignable -template ::value && - std::is_copy_constructible::value && - is_copy_assignable_or_void::value && - std::is_copy_assignable::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::value && - is_move_assignable_or_void::value && - std::is_move_assignable::value)> -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - default; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = default; -}; + return result(unexpect, std::forward(exp).error()); +} -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - default; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = delete; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - delete; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = default; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - delete; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = delete; -}; + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> -// This is needed to be able to construct the expected_default_ctor_base which -// follows, while still conditionally deleting the default constructor. -struct default_constructor_tag { - explicit constexpr default_constructor_tag() = default; -}; +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; -// expected_default_ctor_base will ensure that expected has a deleted default -// consturctor if T is not default constructible. -// This specialization is for when T is default constructible -template ::value || std::is_void::value> -struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = default; - constexpr expected_default_ctor_base( - expected_default_ctor_base const &) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = - default; - expected_default_ctor_base &operator=( - expected_default_ctor_base const &) noexcept = default; - expected_default_ctor_base &operator=( - expected_default_ctor_base &&) noexcept = default; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> -// This specialization is for when T is not default constructible -template -struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = delete; - constexpr expected_default_ctor_base( - expected_default_ctor_base const &) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = - default; - expected_default_ctor_base &operator=( - expected_default_ctor_base const &) noexcept = default; - expected_default_ctor_base &operator=( - expected_default_ctor_base &&) noexcept = default; +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; -} // namespace detail + return unexpected>(std::forward(exp).error()); +} -template -class bad_expected_access : public std::exception { - public: - explicit bad_expected_access(E e) : m_val(std::move(e)) {} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> - virtual const char *what() const noexcept override { - return "Bad expected access"; - } +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; - const E &error() const & { return m_val; } - E &error() & { return m_val; } - const E &&error() const && { return std::move(m_val); } - E &&error() && { return std::move(m_val); } + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} - private: - E m_val; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> -/// An `expected` object is an object that contains the storage for -/// another object and manages the lifetime of this contained object `T`. -/// Alternatively it could contain the storage for another unexpected object -/// `E`. The contained object may not be initialized after the expected object -/// has been initialized, and may not be destroyed before the expected object -/// has been destroyed. The initialization state of the contained object is -/// tracked by the expected object. -template -class expected : private detail::expected_move_assign_base, - private detail::expected_delete_ctor_base, - private detail::expected_delete_assign_base, - private detail::expected_default_ctor_base { - static_assert(!std::is_reference::value, "T must not be a reference"); - static_assert(!std::is_same::type>::value, - "T must not be in_place_t"); - static_assert(!std::is_same::type>::value, - "T must not be unexpect_t"); - static_assert( - !std::is_same>::type>::value, - "T must not be unexpected"); - static_assert(!std::is_reference::value, "E must not be a reference"); - - T *valptr() { return std::addressof(this->m_val); } - const T *valptr() const { return std::addressof(this->m_val); } - unexpected *errptr() { return std::addressof(this->m_unexpect); } - const unexpected *errptr() const { - return std::addressof(this->m_unexpect); - } - - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &val() { - return this->m_val; - } - TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } - - template ::value> * = nullptr> - constexpr const U &val() const { - return this->m_val; +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; } - constexpr const unexpected &err() const { return this->m_unexpect; } - using impl_base = detail::expected_move_assign_base; - using ctor_base = detail::expected_default_ctor_base; - - public: - typedef T value_type; - typedef E error_type; - typedef unexpected unexpected_type; + return unexpected>(std::forward(exp).error()); +} +#endif #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { - return and_then_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { - return and_then_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto and_then(F &&f) const & { - return and_then_impl(*this, std::forward(f)); +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); } -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr auto and_then(F &&f) const && { - return and_then_impl(std::move(*this), std::forward(f)); + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); } -#endif + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} #else - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } -#endif -#endif + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template - constexpr auto map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); } -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template - constexpr auto transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - template - constexpr auto map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); } -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#endif + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} #endif - template - expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { - return or_else_impl(*this, std::forward(f)); - } - template - expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { - return or_else_impl(std::move(*this), std::forward(f)); - } +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} - template - expected constexpr or_else(F &&f) const & { - return or_else_impl(*this, std::forward(f)); - } +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} -#ifndef TL_EXPECTED_NO_CONSTRR - template - expected constexpr or_else(F &&f) const && { - return or_else_impl(std::move(*this), std::forward(f)); - } +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} #endif - constexpr expected() = default; - constexpr expected(const expected &rhs) = default; - constexpr expected(expected &&rhs) = default; - expected &operator=(const expected &rhs) = default; - expected &operator=(expected &&rhs) = default; - - template ::value> * = - nullptr> - constexpr expected(in_place_t, Args &&...args) - : impl_base(in_place, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +} // namespace detail - template &, Args &&...>::value> * = nullptr> - constexpr expected(in_place_t, std::initializer_list il, Args &&...args) - : impl_base(in_place, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} - template ::value> * = - nullptr, - detail::enable_if_t::value> * = - nullptr> - explicit constexpr expected(const unexpected &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} - template < - class G = E, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected const &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - explicit constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} +#endif +/* end file include/ada/expected.h */ - template ::value> * = - nullptr> - constexpr explicit expected(unexpect_t, Args &&...args) - : impl_base(unexpect, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +#include +#include - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected(unexpect_t, std::initializer_list il, - Args &&...args) - : impl_base(unexpect, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +/** + * @private + */ +namespace ada { +struct url_aggregator; +struct url; +} // namespace ada - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } - } +/** + * @namespace ada::parser + * @brief Includes the definitions for supported parsers + */ +namespace ada::parser { - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } - } +/** + * Parses a url. + */ +template +result_type parse_url(std::string_view user_input, + const result_type* base_url = nullptr); - template < - class U, class G, - detail::enable_if_t::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } - } +extern template url_aggregator parse_url( + std::string_view user_input, const url_aggregator* base_url); +extern template url parse_url(std::string_view user_input, + const url* base_url); - template < - class U, class G, - detail::enable_if_t<(std::is_convertible::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } - } +} // namespace ada::parser - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} +#endif // ADA_PARSER_H +/* end file include/ada/parser.h */ +/* begin file include/ada/scheme-inl.h */ +/** + * @file scheme-inl.h + * @brief Definitions for the URL scheme. + */ +#ifndef ADA_SCHEME_INL_H +#define ADA_SCHEME_INL_H - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - err().~unexpected(); - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } +namespace ada::scheme { - return *this; +/** + * @namespace ada::scheme::details + * @brief Includes the definitions for scheme specific entities + */ +namespace details { +// for use with is_special and get_special_port +// Spaces, if present, are removed from URL. +constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", + "ftp", "wss", "file", " "}; +// for use with get_special_port +constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; +} // namespace details + +ada_really_inline constexpr bool is_special(std::string_view scheme) { + if (scheme.empty()) { + return false; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); +} +constexpr uint16_t get_special_port(std::string_view scheme) noexcept { + if (scheme.empty()) { + return 0; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return details::special_ports[hash_value]; + } else { + return 0; + } +} +constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { + return details::special_ports[int(type)]; +} +constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { + if (scheme.empty()) { + return ada::scheme::NOT_SPECIAL; + } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return ada::scheme::type(hash_value); + } else { + return ada::scheme::NOT_SPECIAL; } +} - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - auto tmp = std::move(err()); - err().~unexpected(); +} // namespace ada::scheme -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; -#endif - } +#endif // ADA_SCHEME_H +/* end file include/ada/scheme-inl.h */ +/* begin file include/ada/serializers.h */ +/** + * @file serializers.h + * @brief Definitions for the URL serializers. + */ +#ifndef ADA_SERIALIZERS_H +#define ADA_SERIALIZERS_H - return *this; - } - template ::value && - std::is_assignable::value> * = nullptr> - expected &operator=(const unexpected &rhs) { - if (!has_value()) { - err() = rhs; - } else { - this->destroy_val(); - ::new (errptr()) unexpected(rhs); - this->m_has_val = false; - } +#include +#include +#include - return *this; - } +/** + * @namespace ada::serializers + * @brief Includes the definitions for URL serializers + */ +namespace ada::serializers { - template ::value && - std::is_move_assignable::value> * = nullptr> - expected &operator=(unexpected &&rhs) noexcept { - if (!has_value()) { - err() = std::move(rhs); - } else { - this->destroy_val(); - ::new (errptr()) unexpected(std::move(rhs)); - this->m_has_val = false; - } +/** + * Finds and returns the longest sequence of 0 values in a ipv6 input. + */ +void find_longest_sequence_of_ipv6_pieces( + const std::array& address, size_t& compress, + size_t& compress_length) noexcept; - return *this; - } +/** + * Serializes an ipv6 address. + * @details An IPv6 address is a 128-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv6-serializer + */ +std::string ipv6(const std::array& address) noexcept; - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - } else { - err().~unexpected(); - this->m_has_val = true; - } - ::new (valptr()) T(std::forward(args)...); - } +/** + * Serializes an ipv4 address. + * @details An IPv4 address is a 32-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv4-serializer + */ +std::string ipv4(const uint64_t address) noexcept; - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - ::new (valptr()) T(std::forward(args)...); - } else { - auto tmp = std::move(err()); - err().~unexpected(); +} // namespace ada::serializers -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; -#endif - } - } +#endif // ADA_SERIALIZERS_H +/* end file include/ada/serializers.h */ +/* begin file include/ada/unicode.h */ +/** + * @file unicode.h + * @brief Definitions for all unicode specific functions. + */ +#ifndef ADA_UNICODE_H +#define ADA_UNICODE_H - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - err().~unexpected(); - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } - } - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - auto tmp = std::move(err()); - err().~unexpected(); +#include +#include -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; -#endif - } - } +/** + * @namespace ada::unicode + * @brief Includes the definitions for unicode operations + */ +namespace ada::unicode { - private: - using t_is_void = std::true_type; - using t_is_not_void = std::false_type; - using t_is_nothrow_move_constructible = std::true_type; - using move_constructing_t_can_throw = std::false_type; - using e_is_nothrow_move_constructible = std::true_type; - using move_constructing_e_can_throw = std::false_type; +/** + * We receive a UTF-8 string representing a domain name. + * If the string is percent encoded, we apply percent decoding. + * + * Given a domain, we need to identify its labels. + * They are separated by label-separators: + * + * U+002E ( . ) FULL STOP + * U+FF0E ( . ) FULLWIDTH FULL STOP + * U+3002 ( 。 ) IDEOGRAPHIC FULL STOP + * U+FF61 ( 。 ) HALFWIDTH IDEOGRAPHIC FULL STOP + * + * They are all mapped to U+002E. + * + * We process each label into a string that should not exceed 63 octets. + * If the string is already punycode (starts with "xn--"), then we must + * scan it to look for unallowed code points. + * Otherwise, if the string is not pure ASCII, we need to transcode it + * to punycode by following RFC 3454 which requires us to + * - Map characters (see section 3), + * - Normalize (see section 4), + * - Reject forbidden characters, + * - Check for right-to-left characters and if so, check all requirements (see + * section 6), + * - Optionally reject based on unassigned code points (section 7). + * + * The Unicode standard provides a table of code points with a mapping, a list + * of forbidden code points and so forth. This table is subject to change and + * will vary based on the implementation. For Unicode 15, the table is at + * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt + * If you use ICU, they parse this table and map it to code using a Python + * script. + * + * The resulting strings should not exceed 255 octets according to RFC 1035 + * section 2.3.4. ICU checks for label size and domain size, but these errors + * are ignored. + * + * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * + */ +bool to_ascii(std::optional& out, std::string_view plain, + size_t first_percent); - void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { - // swapping void is a no-op - } +/** + * @see https://www.unicode.org/reports/tr46/#ToUnicode + */ +std::string to_unicode(std::string_view input); - void swap_where_both_have_value(expected &rhs, t_is_not_void) { - using std::swap; - swap(val(), rhs.val()); - } +/** + * Checks if the input has tab or newline characters. + * + * @attention The has_tabs_or_newline function is a bottleneck and it is simple + * enough that compilers like GCC can 'autovectorize it'. + */ +ada_really_inline constexpr bool has_tabs_or_newline( + std::string_view user_input) noexcept; - void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( - std::is_nothrow_move_constructible::value) { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - std::swap(this->m_has_val, rhs.m_has_val); - } +/** + * Checks if the input is a forbidden host code point. + * @see https://url.spec.whatwg.org/#forbidden-host-code-point + */ +ada_really_inline constexpr bool is_forbidden_host_code_point( + const char c) noexcept; - void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { - swap_where_only_one_has_value_and_t_is_not_void( - rhs, typename std::is_nothrow_move_constructible::type{}, - typename std::is_nothrow_move_constructible::type{}); - } +/** + * Checks if the input contains a forbidden domain code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool contains_forbidden_domain_code_point( + const char* input, size_t length) noexcept; - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, t_is_nothrow_move_constructible, - e_is_nothrow_move_constructible) noexcept { - auto temp = std::move(val()); - val().~T(); - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } +/** + * Checks if the input contains a forbidden domain code point in which case + * the first bit is set to 1. If the input contains an upper case ASCII letter, + * then the second bit is set to 1. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool contains_forbidden_domain_code_point_or_upper( + const char* input, size_t length) noexcept; - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, t_is_nothrow_move_constructible, - move_constructing_e_can_throw) { - auto temp = std::move(val()); - val().~T(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - val() = std::move(temp); - throw; - } -#else - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); -#endif - } +/** + * Checks if the input is a forbidden doamin code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool is_forbidden_domain_code_point( + const char c) noexcept; - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, move_constructing_t_can_throw, - e_is_nothrow_move_constructible) { - auto temp = std::move(rhs.err()); - rhs.err().~unexpected_type(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (rhs.valptr()) T(std::move(val())); - val().~T(); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - rhs.err() = std::move(temp); - throw; - } -#else - ::new (rhs.valptr()) T(std::move(val())); - val().~T(); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); -#endif - } +/** + * Checks if the input is alphanumeric, '+', '-' or '.' + */ +ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept; - public: - template - detail::enable_if_t::value && - detail::is_swappable::value && - (std::is_nothrow_move_constructible::value || - std::is_nothrow_move_constructible::value)> - swap(expected &rhs) noexcept( - std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value - &&std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value) { - if (has_value() && rhs.has_value()) { - swap_where_both_have_value(rhs, typename std::is_void::type{}); - } else if (!has_value() && rhs.has_value()) { - rhs.swap(*this); - } else if (has_value()) { - swap_where_only_one_has_value(rhs, typename std::is_void::type{}); - } else { - using std::swap; - swap(err(), rhs.err()); - } - } +/** + * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex + * digit. An ASCII upper hex digit is an ASCII digit or a code point in the + * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an + * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. + */ +ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept; - constexpr const T *operator->() const { return valptr(); } - TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } +/** + * Checks if the input is a C0 control or space character. + * + * @details A C0 control or space is a C0 control or U+0020 SPACE. + * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION + * SEPARATOR ONE, inclusive. + */ +ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept; - template ::value> * = nullptr> - constexpr const U &operator*() const & { - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &operator*() & { - return val(); - } - template ::value> * = nullptr> - constexpr const U &&operator*() const && { - return std::move(val()); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&operator*() && { - return std::move(val()); - } +/** + * Checks if the input is a ASCII tab or newline character. + * + * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. + */ +ada_really_inline constexpr bool is_ascii_tab_or_newline(const char c) noexcept; - constexpr bool has_value() const noexcept { return this->m_has_val; } - constexpr explicit operator bool() const noexcept { return this->m_has_val; } +/** + * @details A double-dot path segment must be ".." or an ASCII case-insensitive + * match for ".%2e", "%2e.", or "%2e%2e". + */ +ada_really_inline ada_constexpr bool is_double_dot_path_segment( + const std::string_view input) noexcept; - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &value() const & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &value() & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &&value() const && { - if (!has_value()) - detail::throw_exception(bad_expected_access(std::move(err()).value())); - return std::move(val()); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&value() && { - if (!has_value()) - detail::throw_exception(bad_expected_access(std::move(err()).value())); - return std::move(val()); - } +/** + * @details A single-dot path segment must be "." or an ASCII case-insensitive + * match for "%2e". + */ +ada_really_inline constexpr bool is_single_dot_path_segment( + const std::string_view input) noexcept; - constexpr const E &error() const & { return err().value(); } - TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } - constexpr const E &&error() const && { return std::move(err().value()); } - TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } +/** + * @details ipv4 character might contain 0-9 or a-f character ranges. + */ +ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept; - template - constexpr T value_or(U &&v) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy-constructible and convertible to from U&&"); - return bool(*this) ? **this : static_cast(std::forward(v)); - } - template - TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move-constructible and convertible to from U&&"); - return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); - } -}; +/** + * @details Convert hex to binary. + */ +unsigned constexpr convert_hex_to_binary(char c) noexcept; -namespace detail { -template -using exp_t = typename detail::decay_t::value_type; -template -using err_t = typename detail::decay_t::error_type; -template -using ret_t = expected>; +/** + * first_percent should be = input.find('%') + * + * @todo It would be faster as noexcept maybe, but it could be unsafe since. + * @author Node.js + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 + * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom + */ +std::string percent_decode(const std::string_view input, size_t first_percent); -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); +/** + * Returns a percent-encoding string whether percent encoding was needed or not. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(const std::string_view input, + const uint8_t character_set[]); +/** + * Returns a percent-encoded string version of input, while starting the percent + * encoding at the provided index. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(const std::string_view input, + const uint8_t character_set[], size_t index); +/** + * Returns true if percent encoding was needed, in which case, we store + * the percent-encoded content in 'out'. If the boolean 'append' is set to + * true, the content is appended to 'out'. + * If percent encoding is not needed, out is left unchanged. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +template +bool percent_encode(const std::string_view input, const uint8_t character_set[], + std::string& out); +/** + * Returns the index at which percent encoding should start, or (equivalently), + * the length of the prefix that does not require percent encoding. + */ +ada_really_inline size_t percent_encode_index(const std::string_view input, + const uint8_t character_set[]); +/** + * Lowers the string in-place, assuming that the content is ASCII. + * Return true if the content was ASCII. + */ +constexpr bool to_lower_ascii(char* input, size_t length) noexcept; +} // namespace ada::unicode - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} +#endif // ADA_UNICODE_H +/* end file include/ada/unicode.h */ +/* begin file include/ada/url_base-inl.h */ +/** + * @file url_base-inl.h + * @brief Inline functions for url base + */ +#ifndef ADA_URL_BASE_INL_H +#define ADA_URL_BASE_INL_H -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); +/* begin file include/ada/url_aggregator.h */ +/** + * @file url_aggregator.h + * @brief Declaration for the basic URL definitions + */ +#ifndef ADA_URL_AGGREGATOR_H +#define ADA_URL_AGGREGATOR_H - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#else -template -struct TC; -template (), - *std::declval())), - detail::enable_if_t>::value> * = nullptr> -auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} +#include +#include -template ())), - detail::enable_if_t>::value> * = nullptr> -constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); +namespace ada { - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#endif +/** + * @brief Lightweight URL struct. + * + * @details The url_aggregator class aims to minimize temporary memory + * allocation while representing a parsed URL. Internally, it contains a single + * normalized URL (the href), and it makes available the components, mostly + * using std::string_view. + */ +struct url_aggregator : url_base { + url_aggregator() = default; + url_aggregator(const url_aggregator &u) = default; + url_aggregator(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(const url_aggregator &u) = default; + ~url_aggregator() = default; -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} + bool set_href(const std::string_view input); + bool set_host(const std::string_view input); + bool set_hostname(const std::string_view input); + bool set_protocol(const std::string_view input); + bool set_username(const std::string_view input); + bool set_password(const std::string_view input); + bool set_port(const std::string_view input); + bool set_pathname(const std::string_view input); + void set_search(const std::string_view input); + void set_hash(const std::string_view input); -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return result(); - } + [[nodiscard]] bool has_valid_domain() const noexcept override; + /** + * The origin getter steps are to return the serialization of this’s URL’s + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] std::string get_origin() const noexcept override; + /** + * Return the normalized string. + * This function does not allocate memory. + * It is highly efficient. + * @return a constant reference to the underlying normalized URL. + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer + */ + inline std::string_view get_href() const noexcept; + /** + * The username getter steps are to return this’s URL’s username. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + [[nodiscard]] std::string_view get_username() const noexcept; + /** + * The password getter steps are to return this’s URL’s password. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] std::string_view get_password() const noexcept; + /** + * Return this’s URL’s port, serialized. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string_view get_port() const noexcept; + /** + * Return U+0023 (#), followed by this’s URL’s fragment. + * This function does not allocate memory. + * @return a lightweight std::string_view.. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string_view get_hash() const noexcept; + /** + * Return url’s host, serialized, followed by U+003A (:) and url’s port, + * serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + [[nodiscard]] std::string_view get_host() const noexcept; + /** + * Return this’s URL’s host, serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + [[nodiscard]] std::string_view get_hostname() const noexcept; + /** + * The pathname getter steps are to return the result of URL path serializing + * this’s URL. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + [[nodiscard]] std::string_view get_pathname() const noexcept; + /** + * Compute the pathname length in bytes witout instantiating a view or a + * string. + * @return size of the pathname in bytes + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + ada_really_inline uint32_t get_pathname_length() const noexcept; + /** + * Return U+003F (?), followed by this’s URL’s query. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + [[nodiscard]] std::string_view get_search() const noexcept; + /** + * The protocol getter steps are to return this’s URL’s scheme, followed by + * U+003A (:). + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + [[nodiscard]] std::string_view get_protocol() const noexcept; - return result(unexpect, std::forward(exp).error()); -} + /** + * A URL includes credentials if its username or password is not the empty + * string. + */ + [[nodiscard]] ada_really_inline bool has_credentials() const noexcept; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} + /** + * Useful for implementing efficient serialization for the URL. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + * + * Inspired after servo/url + * + * @return a constant reference to the underlying component attribute. + * + * @see + * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 + */ + [[nodiscard]] ada_really_inline const ada::url_components &get_components() + const noexcept; + /** + * Returns a string representation of this URL. + */ + std::string to_string() const override; + /** + * Returns a string diagram of this URL. + */ + std::string to_diagram() const; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return result(); - } + /** + * Verifies that the parsed URL could be valid. Useful for debugging purposes. + * @return true if the URL is valid, otherwise return true of the offsets are + * possible. + */ + bool validate() const noexcept; + + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] inline bool has_empty_hostname() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] inline bool has_hostname() const noexcept; + /** @return true if the URL has a non-empty username */ + [[nodiscard]] inline bool has_non_empty_username() const noexcept; + /** @return true if the URL has a non-empty password */ + [[nodiscard]] inline bool has_non_empty_password() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] inline bool has_port() const noexcept; + /** @return true if the URL has a password */ + [[nodiscard]] inline bool has_password() const noexcept; + /** @return true if the URL has a hash component */ + [[nodiscard]] inline bool has_hash() const noexcept override; + /** @return true if the URL has a search component */ + [[nodiscard]] inline bool has_search() const noexcept override; - return result(unexpect, std::forward(exp).error()); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> + private: + friend ada::url_aggregator ada::parser::parse_url( + std::string_view, const ada::url_aggregator *); + friend void ada::helpers::strip_trailing_spaces_from_opaque_path< + ada::url_aggregator>(ada::url_aggregator &url) noexcept; -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; + std::string buffer{}; + url_components components{}; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} + /** + * Returns true if neither the search, nor the hash nor the pathname + * have been set. + * @return true if the buffer is ready to receive the path. + */ + [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> + inline void add_authority_slashes_if_needed() noexcept; -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return {}; - } + /** + * To optimize performance, you may indicate how much memory to allocate + * within this instance. + */ + inline void reserve(uint32_t capacity); - return unexpected>(std::forward(exp).error()); -} + ada_really_inline size_t + parse_port(std::string_view view, + bool check_trailing_content = false) noexcept override; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv4-parser + */ + [[nodiscard]] bool parse_ipv4(std::string_view input); -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv6-parser + */ + [[nodiscard]] bool parse_ipv6(std::string_view input); - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-opaque-host-parser + */ + [[nodiscard]] bool parse_opaque_host(std::string_view input); -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> + ada_really_inline void parse_path(std::string_view input); -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return {}; - } + /** + * A URL cannot have a username/password/port if its host is null or the empty + * string, or its scheme is "file". + */ + [[nodiscard]] inline bool cannot_have_credentials_or_port() const; - return unexpected>(std::forward(exp).error()); -} -#endif + template + bool set_host_or_hostname(const std::string_view input); -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } + ada_really_inline bool parse_host(std::string_view input); - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } + inline void update_base_authority(std::string_view base_buffer, + const ada::url_components &base); + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t *query_percent_encode_set); + inline void update_base_pathname(const std::string_view input); + inline void update_base_username(const std::string_view input); + inline void append_base_username(const std::string_view input); + inline void update_base_password(const std::string_view input); + inline void append_base_password(const std::string_view input); + inline void update_base_port(uint32_t input); + inline void append_base_pathname(const std::string_view input); + inline uint32_t retrieve_base_port() const; + inline void clear_port(); + inline void clear_hostname(); + inline void clear_hash(); + inline void clear_pathname() override; + inline void clear_search() override; + inline void clear_password(); + inline bool has_dash_dot() const noexcept; + void delete_dash_dot(); + inline void consume_prepared_path(std::string_view input); + template + [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( + const std::string_view input); + ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, + std::string_view input); + inline bool has_authority() const noexcept; + inline void set_protocol_as_file(); + inline void set_scheme(std::string_view new_scheme) noexcept; + /** + * Fast function to set the scheme from a view with a colon in the + * buffer, does not change type. + */ + inline void set_scheme_from_view_with_colon( + std::string_view new_scheme_with_colon) noexcept; + inline void copy_scheme(const url_aggregator &u) noexcept; - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; +}; // url_aggregator - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} +inline std::ostream &operator<<(std::ostream &out, const ada::url &u); +} // namespace ada -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } +#endif +/* end file include/ada/url_aggregator.h */ +/* begin file include/ada/checkers.h */ +/** + * @file checkers.h + * @brief Declarations for URL specific checkers used within Ada. + */ +#ifndef ADA_CHECKERS_H +#define ADA_CHECKERS_H - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; +#include +#include - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} +/** + * @namespace ada::checkers + * @brief Includes the definitions for validation functions + */ +namespace ada::checkers { -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } +/** + * Assuming that x is an ASCII letter, this function returns the lower case + * equivalent. + * @details More likely to be inlined by the compiler and constexpr. + */ +constexpr char to_lower(char x) noexcept; + +/** + * Returns true if the character is an ASCII letter. Equivalent to std::isalpha + * but more likely to be inlined by the compiler. + * + * @attention std::isalpha is not constexpr generally. + */ +constexpr bool is_alpha(char x) noexcept; - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#endif +/** + * Check whether a string starts with 0x or 0X. The function is only + * safe if input.size() >=2. + * + * @see has_hex_prefix + */ +inline bool has_hex_prefix_unsafe(std::string_view input); +/** + * Check whether a string starts with 0x or 0X. + */ +inline bool has_hex_prefix(std::string_view input); -#ifdef TL_EXPECTED_CXX14 -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto or_else_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} +/** + * Check whether x is an ASCII digit. More likely to be inlined than + * std::isdigit. + */ +constexpr bool is_digit(char x) noexcept; -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#else -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto or_else_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} +/** + * @details A string starts with a Windows drive letter if all of the following + * are true: + * + * - its length is greater than or equal to 2 + * - its first two code points are a Windows drive letter + * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F + * (?), or U+0023 (#). + * + * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter + */ +inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#endif -} // namespace detail +/** + * @details A normalized Windows drive letter is a Windows drive letter of which + * the second code point is U+003A (:). + */ +inline constexpr bool is_normalized_windows_drive_letter( + std::string_view input) noexcept; -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); -} -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : true); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() == rhs.error() : false); -} +/** + * @warning Will be removed when Ada supports C++20. + */ +ada_really_inline constexpr bool begins_with(std::string_view view, + std::string_view prefix); -template -constexpr bool operator==(const expected &x, const U &v) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator==(const U &v, const expected &x) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator!=(const expected &x, const U &v) { - return x.has_value() ? *x != v : true; -} -template -constexpr bool operator!=(const U &v, const expected &x) { - return x.has_value() ? *x != v : true; -} +/** + * Returns true if an input is an ipv4 address. + */ +ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept; -template -constexpr bool operator==(const expected &x, const unexpected &e) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator==(const unexpected &e, const expected &x) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator!=(const expected &x, const unexpected &e) { - return x.has_value() ? true : x.error() != e.value(); -} -template -constexpr bool operator!=(const unexpected &e, const expected &x) { - return x.has_value() ? true : x.error() != e.value(); -} +/** + * Returns a bitset. If the first bit is set, then at least one character needs + * percent encoding. If the second bit is set, a \\ is found. If the third bit + * is set then we have a dot. If the fourth bit is set, then we have a percent + * character. + */ +ada_really_inline constexpr uint8_t path_signature( + std::string_view input) noexcept; -template ::value || - std::is_move_constructible::value) && - detail::is_swappable::value && - std::is_move_constructible::value && - detail::is_swappable::value> * = nullptr> -void swap(expected &lhs, - expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { - lhs.swap(rhs); -} -} // namespace tl +/** + * Returns true if the length of the domain name and its labels are according to + * the specifications. The length of the domain must be 255 octets (253 + * characters not including the last 2 which are the empty label reserved at the + * end). When the empty label is included (a dot at the end), the domain name + * can have 254 characters. The length of a label must be at least 1 and at most + * 63 characters. + * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 + * @see https://www.unicode.org/reports/tr46/#ToASCII + */ +ada_really_inline constexpr bool verify_dns_length( + std::string_view input) noexcept; -#endif -/* end file include/ada/expected.h */ -/* begin file include/ada/url_aggregator.h */ +} // namespace ada::checkers + +#endif // ADA_CHECKERS_H +/* end file include/ada/checkers.h */ +/* begin file include/ada/url.h */ /** - * @file url_aggregator.h - * @brief Declaration for the basic URL definitions + * @file url.h + * @brief Declaration for the URL */ -#ifndef ADA_URL_AGGREGATOR_H -#define ADA_URL_AGGREGATOR_H +#ifndef ADA_URL_H +#define ADA_URL_H +#include +#include +#include +#include #include #include namespace ada { /** - * @brief Lightweight URL struct. + * @brief Generic URL struct reliant on std::string instantiation. * - * @details The url_aggregator class aims to minimize temporary memory - * allocation while representing a parsed URL. Internally, it contains a single - * normalized URL (the href), and it makes available the components, mostly - * using std::string_view. + * @details To disambiguate from a valid URL string it can also be referred to + * as a URL record. A URL is a struct that represents a universal identifier. + * Unlike the url_aggregator, the ada::url represents the different components + * of a parsed URL as independent std::string instances. This makes the + * structure heavier and more reliant on memory allocations. When getting + * components from the parsed URL, a new std::string is typically constructed. + * + * @see https://url.spec.whatwg.org/#url-representation */ -struct url_aggregator : url_base { - url_aggregator() = default; - url_aggregator(const url_aggregator &u) = default; - url_aggregator(url_aggregator &&u) noexcept = default; - url_aggregator &operator=(url_aggregator &&u) noexcept = default; - url_aggregator &operator=(const url_aggregator &u) = default; - ~url_aggregator() = default; +struct url : url_base { + url() = default; + url(const url &u) = default; + url(url &&u) noexcept = default; + url &operator=(url &&u) noexcept = default; + url &operator=(const url &u) = default; + ~url() = default; - bool set_href(const std::string_view input); - bool set_host(const std::string_view input); - bool set_hostname(const std::string_view input); - bool set_protocol(const std::string_view input); - bool set_username(const std::string_view input); - bool set_password(const std::string_view input); - bool set_port(const std::string_view input); - bool set_pathname(const std::string_view input); - void set_search(const std::string_view input); - void set_hash(const std::string_view input); - inline void set_scheme(std::string_view new_scheme) noexcept; - /** @private fast function to set the scheme from a view with a colon in the - * buffer, does not change type */ - inline void set_scheme_from_view_with_colon( - std::string_view new_scheme_with_colon) noexcept; + /** + * @private + * A URL’s username is an ASCII string identifying a username. It is initially + * the empty string. + */ + std::string username{}; + + /** + * @private + * A URL’s password is an ASCII string identifying a password. It is initially + * the empty string. + */ + std::string password{}; - inline void copy_scheme(const url_aggregator &u) noexcept; + /** + * @private + * A URL’s host is null or a host. It is initially null. + */ + std::optional host{}; - [[nodiscard]] bool has_valid_domain() const noexcept override; + /** + * @private + * A URL’s port is either null or a 16-bit unsigned integer that identifies a + * networking port. It is initially null. + */ + std::optional port{}; - /** @private */ - inline bool has_authority() const noexcept; - /** @private set this URL's type to file */ - inline void set_protocol_as_file(); /** - * The origin getter steps are to return the serialization of this’s URL’s - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin + * @private + * A URL’s path is either an ASCII string or a list of zero or more ASCII + * strings, usually identifying a location. */ - [[nodiscard]] std::string get_origin() const noexcept override; + std::string path{}; + /** - * Return the normalized string. - * This function does not allocate memory. - * It is highly efficient. - * @return a constant reference to the underlying normalized URL. - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer + * @private + * A URL’s query is either null or an ASCII string. It is initially null. */ - inline std::string_view get_href() const noexcept; + std::optional query{}; + /** - * The username getter steps are to return this’s URL’s username. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-username + * @private + * A URL’s fragment is either null or an ASCII string that can be used for + * further processing on the resource the URL’s other components identify. It + * is initially null. */ - [[nodiscard]] std::string_view get_username() const noexcept; + std::optional hash{}; + + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] inline bool has_empty_hostname() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] inline bool has_port() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] inline bool has_hostname() const noexcept; + [[nodiscard]] bool has_valid_domain() const noexcept override; + /** - * The password getter steps are to return this’s URL’s password. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-password + * Returns a JSON string representation of this URL. */ - [[nodiscard]] std::string_view get_password() const noexcept; + std::string to_string() const override; + /** - * Return this’s URL’s port, serialized. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-port + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer */ - [[nodiscard]] std::string_view get_port() const noexcept; + [[nodiscard]] ada_really_inline std::string get_href() const noexcept; + /** - * Return U+0023 (#), followed by this’s URL’s fragment. - * This function does not allocate memory. - * @return a lightweight std::string_view.. - * @see https://url.spec.whatwg.org/#dom-url-hash + * The origin getter steps are to return the serialization of this’s URL’s + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin */ - [[nodiscard]] std::string_view get_hash() const noexcept; + [[nodiscard]] std::string get_origin() const noexcept override; + + /** + * The protocol getter steps are to return this’s URL’s scheme, followed by + * U+003A (:). + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + [[nodiscard]] std::string get_protocol() const noexcept; + /** * Return url’s host, serialized, followed by U+003A (:) and url’s port, * serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-host */ - [[nodiscard]] std::string_view get_host() const noexcept; + [[nodiscard]] std::string get_host() const noexcept; + /** * Return this’s URL’s host, serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-hostname */ - [[nodiscard]] std::string_view get_hostname() const noexcept; + [[nodiscard]] std::string get_hostname() const noexcept; + /** * The pathname getter steps are to return the result of URL path serializing * this’s URL. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-pathname */ - [[nodiscard]] std::string_view get_pathname() const noexcept; - /** - * Returns true if neither the search, nor the hash nor the pathname - * have been set. - * @return true if the buffer is ready to receive the path. - */ - [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; + [[nodiscard]] const std::string_view get_pathname() const noexcept; + /** * Compute the pathname length in bytes witout instantiating a view or a * string. * @return size of the pathname in bytes * @see https://url.spec.whatwg.org/#dom-url-pathname */ - ada_really_inline uint32_t get_pathname_length() const noexcept; + ada_really_inline size_t get_pathname_length() const noexcept; + /** * Return U+003F (?), followed by this’s URL’s query. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-search */ - [[nodiscard]] std::string_view get_search() const noexcept; + [[nodiscard]] std::string get_search() const noexcept; + /** - * The protocol getter steps are to return this’s URL’s scheme, followed by - * U+003A (:). - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-protocol + * The username getter steps are to return this’s URL’s username. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-username */ - [[nodiscard]] std::string_view get_protocol() const noexcept; + [[nodiscard]] const std::string &get_username() const noexcept; /** - * A URL includes credentials if its username or password is not the empty - * string. + * @return Returns true on successful operation. + * @see https://url.spec.whatwg.org/#dom-url-username */ - [[nodiscard]] ada_really_inline bool includes_credentials() const noexcept; + bool set_username(const std::string_view input); /** - * @private - * - * A URL cannot have a username/password/port if its host is null or the empty - * string, or its scheme is "file". + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-password */ - [[nodiscard]] inline bool cannot_have_credentials_or_port() const; + bool set_password(const std::string_view input); - /** @private */ - template - bool set_host_or_hostname(const std::string_view input); + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + bool set_port(const std::string_view input); - /** @private */ - ada_really_inline bool parse_host(std::string_view input); + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + void set_hash(const std::string_view input); - /** @private */ - inline void update_base_authority(std::string_view base_buffer, - const ada::url_components &base); - /** @private */ - inline void update_unencoded_base_hash(std::string_view input); - /** @private */ - inline void update_base_hostname(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input, - const uint8_t *query_percent_encode_set); - /** @private */ - inline void update_base_pathname(const std::string_view input); - /** @private */ - inline void update_base_username(const std::string_view input); - /** @private */ - inline void append_base_username(const std::string_view input); - /** @private */ - inline void update_base_password(const std::string_view input); - /** @private */ - inline void append_base_password(const std::string_view input); - /** @private */ - inline void update_base_port(uint32_t input); - /** @private */ - inline void append_base_pathname(const std::string_view input); - /** @private */ - inline uint32_t retrieve_base_port() const; - /** @private */ - inline void clear_base_port(); - /** @private if there is no hostname, then this function does nothing, if - * there is, we make it empty */ - inline void clear_base_hostname(); - /** @private */ - inline void clear_base_hash(); - /** @private */ - inline void clear_base_pathname() override; - /** @private */ - inline void clear_base_search() override; - /** @private */ - inline void clear_base_password(); - /** @private */ - inline bool base_fragment_has_value() const override; - /** @private */ - inline bool base_search_has_value() const override; - /** @private */ - inline bool has_dash_dot() const noexcept; - /** @private */ - void delete_dash_dot(); - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - /** @private */ - [[nodiscard]] inline bool has_non_empty_username() const noexcept; - /** @private */ - [[nodiscard]] inline bool has_non_empty_password() const noexcept; - /** @private */ - [[nodiscard]] inline bool has_password() const noexcept; - /** @return true if the URL has a (non default) port */ - [[nodiscard]] inline bool has_port() const noexcept; - /** @private */ - inline void consume_prepared_path(std::string_view input); - /** @private */ - template - [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( - const std::string_view input); + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + void set_search(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + bool set_pathname(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + bool set_host(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + bool set_hostname(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + bool set_protocol(const std::string_view input); - /** @private */ - ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, - std::string_view input); + /** + * @see https://url.spec.whatwg.org/#dom-url-href + */ + bool set_href(const std::string_view input); + + /** + * The password getter steps are to return this’s URL’s password. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] const std::string &get_password() const noexcept; + + /** + * Return this’s URL’s port, serialized. + * @return a newly constructed string representing the port. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string get_port() const noexcept; + + /** + * Return U+0023 (#), followed by this’s URL’s fragment. + * @return a newly constructed string representing the hash. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string get_hash() const noexcept; + + /** + * A URL includes credentials if its username or password is not the empty + * string. + */ + [[nodiscard]] ada_really_inline bool has_credentials() const noexcept; /** * Useful for implementing efficient serialization for the URL. @@ -5138,183 +5127,139 @@ struct url_aggregator : url_base { * * Inspired after servo/url * - * @return a constant reference to the underlying component attribute. + * @return a newly constructed component. * * @see * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 */ - [[nodiscard]] ada_really_inline const ada::url_components &get_components() + [[nodiscard]] ada_really_inline ada::url_components get_components() const noexcept; - /** - * Returns a string representation of this URL. - */ - std::string to_string() const override; - /** - * Returns a string diagram of this URL. - */ - std::string to_diagram() const; - - /** - * Verifies that the parsed URL could be valid. Useful for debugging purposes. - * @return true if the URL is valid, otherwise return true of the offsets are - * possible. - */ - bool validate() const noexcept; + /** @return true if the URL has a hash component */ + [[nodiscard]] inline bool has_hash() const noexcept override; + /** @return true if the URL has a search component */ + [[nodiscard]] inline bool has_search() const noexcept override; + private: + friend ada::url ada::parser::parse_url(std::string_view, + const ada::url *); + friend ada::url_aggregator ada::parser::parse_url( + std::string_view, const ada::url_aggregator *); + friend void ada::helpers::strip_trailing_spaces_from_opaque_path( + ada::url &url) noexcept; - /** @private */ - inline void add_authority_slashes_if_needed() noexcept; + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t query_percent_encode_set[]); + inline void update_base_search(std::optional input); + inline void update_base_pathname(const std::string_view input); + inline void update_base_username(const std::string_view input); + inline void update_base_password(const std::string_view input); + inline void update_base_port(std::optional input); /** - * @private - * To optimize performance, you may indicate how much memory to allocate - * within this instance. + * Sets the host or hostname according to override condition. + * Return true on success. + * @see https://url.spec.whatwg.org/#hostname-state */ - inline void reserve(uint32_t capacity); - - /** @private */ - ada_really_inline size_t - parse_port(std::string_view view, - bool check_trailing_content = false) noexcept override; - - private: - /** @private */ - std::string buffer{}; - - /** @private */ - url_components components{}; + template + bool set_host_or_hostname(std::string_view input); /** - * @private - * * Return true on success. * @see https://url.spec.whatwg.org/#concept-ipv4-parser */ [[nodiscard]] bool parse_ipv4(std::string_view input); /** - * @private - * * Return true on success. * @see https://url.spec.whatwg.org/#concept-ipv6-parser */ [[nodiscard]] bool parse_ipv6(std::string_view input); /** - * @private - * * Return true on success. * @see https://url.spec.whatwg.org/#concept-opaque-host-parser */ [[nodiscard]] bool parse_opaque_host(std::string_view input); - /** @private */ - ada_really_inline void parse_path(std::string_view input); - -}; // url_aggregator - -inline std::ostream &operator<<(std::ostream &out, const ada::url &u); -} // namespace ada - -#endif -/* end file include/ada/url_aggregator.h */ - -#include -#include + /** + * A URL’s scheme is an ASCII string that identifies the type of URL and can + * be used to dispatch a URL for further processing after parsing. It is + * initially the empty string. We only set non_special_scheme when the scheme + * is non-special, otherwise we avoid constructing string. + * + * Special schemes are stored in ada::scheme::details::is_special_list so we + * typically do not need to store them in each url instance. + */ + std::string non_special_scheme{}; -/** - * @namespace ada::parser - * @brief Includes the definitions for supported parsers - */ -namespace ada::parser { + /** + * A URL cannot have a username/password/port if its host is null or the empty + * string, or its scheme is "file". + */ + [[nodiscard]] inline bool cannot_have_credentials_or_port() const; -/** - * Parses a url. - */ -template -result_type parse_url(std::string_view user_input, - const result_type* base_url = nullptr); + ada_really_inline size_t + parse_port(std::string_view view, + bool check_trailing_content = false) noexcept override; -extern template url_aggregator parse_url( - std::string_view user_input, const url_aggregator* base_url); -extern template url parse_url(std::string_view user_input, - const url* base_url); + /** + * Take the scheme from another URL. The scheme string is copied from the + * provided url. + */ + inline void copy_scheme(const ada::url &u); -} // namespace ada::parser + /** + * Parse the host from the provided input. We assume that + * the input does not contain spaces or tabs. Control + * characters and spaces are not trimmed (they should have + * been removed if needed). + * Return true on success. + * @see https://url.spec.whatwg.org/#host-parsing + */ + [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); -#endif // ADA_PARSER_H -/* end file include/ada/parser.h */ -/* begin file include/ada/scheme-inl.h */ -/** - * @file scheme-inl.h - * @brief Definitions for the URL scheme. - */ -#ifndef ADA_SCHEME_INL_H -#define ADA_SCHEME_INL_H + template + [[nodiscard]] ada_really_inline bool parse_scheme( + const std::string_view input); + inline void clear_pathname() override; + inline void clear_search() override; + inline void set_protocol_as_file(); -namespace ada::scheme { + /** + * Parse the path from the provided input. + * Return true on success. Control characters not + * trimmed from the ends (they should have + * been removed if needed). + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ + */ + ada_really_inline void parse_path(const std::string_view input); -/** - * @namespace ada::scheme::details - * @brief Includes the definitions for scheme specific entities - */ -namespace details { -// for use with is_special and get_special_port -// Spaces, if present, are removed from URL. -constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", - "ftp", "wss", "file", " "}; -// for use with get_special_port -constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; -} // namespace details + /** + * Set the scheme for this URL. The provided scheme should be a valid + * scheme string, be lower-cased, not contain spaces or tabs. It should + * have no spurious trailing or leading content. + */ + inline void set_scheme(std::string &&new_scheme) noexcept; -ada_really_inline constexpr bool is_special(std::string_view scheme) { - if (scheme.empty()) { - return false; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); -} -constexpr uint16_t get_special_port(std::string_view scheme) noexcept { - if (scheme.empty()) { - return 0; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return details::special_ports[hash_value]; - } else { - return 0; - } -} -constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { - return details::special_ports[int(type)]; -} -constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { - if (scheme.empty()) { - return ada::scheme::NOT_SPECIAL; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return ada::scheme::type(hash_value); - } else { - return ada::scheme::NOT_SPECIAL; - } -} + /** + * Take the scheme from another URL. The scheme string is moved from the + * provided url. + */ + inline void copy_scheme(ada::url &&u) noexcept; -} // namespace ada::scheme +}; // struct url -#endif // ADA_SCHEME_H -/* end file include/ada/scheme-inl.h */ -/* begin file include/ada/url_base-inl.h */ -/** - * @file url_base-inl.h - * @brief Inline functions for url base - */ -#ifndef ADA_URL_BASE_INL_H -#define ADA_URL_BASE_INL_H +inline std::ostream &operator<<(std::ostream &out, const ada::url &u); +} // namespace ada +#endif // ADA_URL_H +/* end file include/ada/url.h */ #include #include @@ -5357,10 +5302,13 @@ url_base::scheme_default_port() const noexcept { #endif // ADA_REGULAR_VISUAL_STUDIO namespace ada { -[[nodiscard]] ada_really_inline bool url::includes_credentials() - const noexcept { +[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept { return !username.empty() || !password.empty(); } +[[nodiscard]] ada_really_inline bool url::has_port() + const noexcept { + return port.has_value(); +} [[nodiscard]] inline bool url::cannot_have_credentials_or_port() const { return !host.has_value() || host.value().empty() || type == ada::scheme::type::FILE; @@ -5394,7 +5342,7 @@ size_t url::get_pathname_length() const noexcept { return path.size(); } // 2 characters for "//" and 1 character for starting index out.host_start = out.protocol_end + 2; - if (includes_credentials()) { + if (has_credentials()) { out.username_end = uint32_t(out.host_start + username.size()); out.host_start += uint32_t(username.size()); @@ -5447,7 +5395,7 @@ size_t url::get_pathname_length() const noexcept { return path.size(); } } } - if (fragment.has_value()) { + if (hash.has_value()) { out.hash_start = uint32_t(running_index); } @@ -5458,8 +5406,8 @@ inline void url::update_base_hostname(std::string_view input) { host = input; } inline void url::update_unencoded_base_hash(std::string_view input) { // We do the percent encoding - fragment = unicode::percent_encode( - input, ada::character_sets::FRAGMENT_PERCENT_ENCODE); + hash = unicode::percent_encode(input, + ada::character_sets::FRAGMENT_PERCENT_ENCODE); } inline void url::update_base_search(std::string_view input, @@ -5487,15 +5435,13 @@ inline void url::update_base_port(std::optional input) { port = input; } -inline void url::clear_base_pathname() { path = ""; } +inline void url::clear_pathname() { path.clear(); } -inline void url::clear_base_search() { query = std::nullopt; } +inline void url::clear_search() { query = std::nullopt; } -inline bool url::base_fragment_has_value() const { - return fragment.has_value(); -} +[[nodiscard]] inline bool url::has_hash() const noexcept { return hash.has_value(); } -inline bool url::base_search_has_value() const { return query.has_value(); } +[[nodiscard]] inline bool url::has_search() const noexcept { return query.has_value(); } inline void url::set_protocol_as_file() { type = ada::scheme::type::FILE; } @@ -5522,7 +5468,7 @@ inline void url::copy_scheme(const ada::url &u) { if (host.has_value()) { output += "//"; - if (includes_credentials()) { + if (has_credentials()) { output += username; if (!password.empty()) { output += ":" + get_password(); @@ -5543,8 +5489,8 @@ inline void url::copy_scheme(const ada::url &u) { if (query.has_value()) { output += "?" + query.value(); } - if (fragment.has_value()) { - output += "#" + fragment.value(); + if (hash.has_value()) { + output += "#" + hash.value(); } return output; } @@ -5773,7 +5719,7 @@ inline void url_aggregator::update_base_search(std::string_view input) { ADA_ASSERT_TRUE(validate()); ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); if (input.empty()) { - clear_base_search(); + clear_search(); return; } @@ -6011,9 +5957,8 @@ inline void url_aggregator::append_base_username(const std::string_view input) { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_password() { - ada_log("url_aggregator::clear_base_password ", to_string(), "\n", - to_diagram()); +inline void url_aggregator::clear_password() { + ada_log("url_aggregator::clear_password ", to_string(), "\n", to_diagram()); ADA_ASSERT_TRUE(validate()); if (!has_password()) { return; @@ -6041,7 +5986,7 @@ inline void url_aggregator::update_base_password(const std::string_view input) { // TODO: Optimization opportunity. Merge the following removal functions. if (input.empty()) { - clear_base_password(); + clear_password(); // Remove username too, if it is empty. if (!has_non_empty_username()) { @@ -6142,7 +6087,7 @@ inline void url_aggregator::update_base_port(uint32_t input) { ada_log("url_aggregator::update_base_port"); ADA_ASSERT_TRUE(validate()); if (input == url_components::omitted) { - clear_base_port(); + clear_port(); return; } // calling std::to_string(input.value()) is unfortunate given that the port @@ -6168,8 +6113,8 @@ inline void url_aggregator::update_base_port(uint32_t input) { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_port() { - ada_log("url_aggregator::clear_base_port"); +inline void url_aggregator::clear_port() { + ada_log("url_aggregator::clear_port"); ADA_ASSERT_TRUE(validate()); if (components.port == url_components::omitted) { return; @@ -6192,8 +6137,8 @@ inline uint32_t url_aggregator::retrieve_base_port() const { return components.port; } -inline void url_aggregator::clear_base_search() { - ada_log("url_aggregator::clear_base_search"); +inline void url_aggregator::clear_search() { + ada_log("url_aggregator::clear_search"); ADA_ASSERT_TRUE(validate()); if (components.search_start == url_components::omitted) { return; @@ -6217,8 +6162,8 @@ inline void url_aggregator::clear_base_search() { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_hash() { - ada_log("url_aggregator::clear_base_hash"); +inline void url_aggregator::clear_hash() { + ada_log("url_aggregator::clear_hash"); ADA_ASSERT_TRUE(validate()); if (components.hash_start == url_components::omitted) { return; @@ -6234,8 +6179,8 @@ inline void url_aggregator::clear_base_hash() { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_pathname() { - ada_log("url_aggregator::clear_base_pathname"); +inline void url_aggregator::clear_pathname() { + ada_log("url_aggregator::clear_pathname"); ADA_ASSERT_TRUE(validate()); uint32_t ending_index = uint32_t(buffer.size()); if (components.search_start != url_components::omitted) { @@ -6259,19 +6204,18 @@ inline void url_aggregator::clear_base_pathname() { if (components.hash_start != url_components::omitted) { components.hash_start -= difference; } - ada_log("url_aggregator::clear_base_pathname completed, running checks..."); + ada_log("url_aggregator::clear_pathname completed, running checks..."); #if ADA_DEVELOPMENT_CHECKS ADA_ASSERT_EQUAL(get_pathname(), "", "pathname should have been cleared on buffer=" + buffer + " with " + components.to_string() + "\n" + to_diagram()); #endif ADA_ASSERT_TRUE(validate()); - ada_log( - "url_aggregator::clear_base_pathname completed, running checks... ok"); + ada_log("url_aggregator::clear_pathname completed, running checks... ok"); } -inline void url_aggregator::clear_base_hostname() { - ada_log("url_aggregator::clear_base_hostname"); +inline void url_aggregator::clear_hostname() { + ada_log("url_aggregator::clear_hostname"); ADA_ASSERT_TRUE(validate()); if (!has_authority()) { return; @@ -6305,18 +6249,18 @@ inline void url_aggregator::clear_base_hostname() { ADA_ASSERT_TRUE(validate()); } -inline bool url_aggregator::base_fragment_has_value() const { - ada_log("url_aggregator::base_fragment_has_value"); +[[nodiscard]] inline bool url_aggregator::has_hash() const noexcept { + ada_log("url_aggregator::has_hash"); return components.hash_start != url_components::omitted; } -inline bool url_aggregator::base_search_has_value() const { - ada_log("url_aggregator::base_search_has_value"); +[[nodiscard]] inline bool url_aggregator::has_search() const noexcept { + ada_log("url_aggregator::has_search"); return components.search_start != url_components::omitted; } -ada_really_inline bool url_aggregator::includes_credentials() const noexcept { - ada_log("url_aggregator::includes_credentials"); +ada_really_inline bool url_aggregator::has_credentials() const noexcept { + ada_log("url_aggregator::has_credentials"); return has_non_empty_username() || has_non_empty_password(); } @@ -6458,7 +6402,7 @@ ada_really_inline size_t url_aggregator::parse_port( if (r.ec == std::errc() && scheme_default_port() != parsed_port) { update_base_port(parsed_port); } else { - clear_base_port(); + clear_port(); } } return consumed; @@ -6512,13 +6456,13 @@ inline std::ostream &operator<<(std::ostream &out, #ifndef ADA_ADA_VERSION_H #define ADA_ADA_VERSION_H -#define ADA_VERSION "2.1.0" +#define ADA_VERSION "2.2.0" namespace ada { enum { ADA_VERSION_MAJOR = 2, - ADA_VERSION_MINOR = 1, + ADA_VERSION_MINOR = 2, ADA_VERSION_REVISION = 0, }; diff --git a/src/node_url.cc b/src/node_url.cc index 551e726145f06b..2911e833f77c88 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -148,7 +148,7 @@ void BindingData::Format(const FunctionCallbackInfo& args) { Isolate* isolate = env->isolate(); Utf8Value href(isolate, args[0].As()); - const bool fragment = args[1]->IsTrue(); + const bool hash = args[1]->IsTrue(); const bool unicode = args[2]->IsTrue(); const bool search = args[3]->IsTrue(); const bool auth = args[4]->IsTrue(); @@ -159,8 +159,8 @@ void BindingData::Format(const FunctionCallbackInfo& args) { auto out = ada::parse(href.ToStringView()); CHECK(out); - if (!fragment) { - out->fragment = std::nullopt; + if (!hash) { + out->hash = std::nullopt; } if (unicode) {