| 12940 | |
| 12941 | template <class result_type, bool store_values> |
| 12942 | result_type parse_url_impl(std::string_view user_input, |
| 12943 | const result_type* base_url) { |
| 12944 | // We can specialize the implementation per type. |
| 12945 | // Important: result_type_is_ada_url is evaluated at *compile time*. This |
| 12946 | // means that doing if constexpr(result_type_is_ada_url) { something } else { |
| 12947 | // something else } is free (at runtime). This means that ada::url_aggregator |
| 12948 | // and ada::url **do not have to support the exact same API**. |
| 12949 | constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>; |
| 12950 | constexpr bool result_type_is_ada_url_aggregator = |
| 12951 | std::is_same_v<url_aggregator, result_type>; |
| 12952 | static_assert(result_type_is_ada_url || |
| 12953 | result_type_is_ada_url_aggregator); // We don't support |
| 12954 | // anything else for now. |
| 12955 | |
| 12956 | ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(), |
| 12957 | " bytes],", (base_url != nullptr ? base_url->to_string() : "null"), |
| 12958 | ")"); |
| 12959 | |
| 12960 | state state = state::SCHEME_START; |
| 12961 | result_type url{}; |
| 12962 | |
| 12963 | // We refuse to parse URL strings that exceed 4GB. Such strings are almost |
| 12964 | // surely the result of a bug or are otherwise a security concern. |
| 12965 | if (user_input.size() > std::numeric_limits<uint32_t>::max()) [[unlikely]] { |
| 12966 | url.is_valid = false; |
| 12967 | } |
| 12968 | // Going forward, user_input.size() is in [0, |
| 12969 | // std::numeric_limits<uint32_t>::max). If we are provided with an invalid |
| 12970 | // base, or the optional_url was invalid, we must return. |
| 12971 | if (base_url != nullptr) { |
| 12972 | url.is_valid &= base_url->is_valid; |
| 12973 | } |
| 12974 | if (!url.is_valid) { |
| 12975 | return url; |
| 12976 | } |
| 12977 | if constexpr (result_type_is_ada_url_aggregator && store_values) { |
| 12978 | // Most of the time, we just need user_input.size(). |
| 12979 | // In some instances, we may need a bit more. |
| 12980 | /////////////////////////// |
| 12981 | // This is *very* important. This line should *not* be removed |
| 12982 | // hastily. There are principled reasons why reserve is important |
| 12983 | // for performance. If you have a benchmark with small inputs, |
| 12984 | // it may not matter, but in other instances, it could. |
| 12985 | //// |
| 12986 | // This rounds up to the next power of two. |
| 12987 | // We know that user_input.size() is in [0, |
| 12988 | // std::numeric_limits<uint32_t>::max). |
| 12989 | uint32_t reserve_capacity = |
| 12990 | (0xFFFFFFFF >> |
| 12991 | helpers::leading_zeroes(uint32_t(1 | user_input.size()))) + |
| 12992 | 1; |
| 12993 | url.reserve(reserve_capacity); |
| 12994 | } |
| 12995 | std::string tmp_buffer; |
| 12996 | std::string_view url_data; |
| 12997 | if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] { |
| 12998 | tmp_buffer = user_input; |
| 12999 | // Optimization opportunity: Instead of copying and then pruning, we could |
nothing calls this directly
no test coverage detected