| 12 | |
| 13 | template <class result_type, bool store_values> |
| 14 | result_type parse_url_impl(std::string_view user_input, |
| 15 | const result_type* base_url) { |
| 16 | // We can specialize the implementation per type. |
| 17 | // Important: result_type_is_ada_url is evaluated at *compile time*. This |
| 18 | // means that doing if constexpr(result_type_is_ada_url) { something } else { |
| 19 | // something else } is free (at runtime). This means that ada::url_aggregator |
| 20 | // and ada::url **do not have to support the exact same API**. |
| 21 | constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>; |
| 22 | constexpr bool result_type_is_ada_url_aggregator = |
| 23 | std::is_same_v<url_aggregator, result_type>; |
| 24 | static_assert(result_type_is_ada_url || |
| 25 | result_type_is_ada_url_aggregator); // We don't support |
| 26 | // anything else for now. |
| 27 | |
| 28 | ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(), |
| 29 | " bytes],", (base_url != nullptr ? base_url->to_string() : "null"), |
| 30 | ")"); |
| 31 | |
| 32 | state state = state::SCHEME_START; |
| 33 | result_type url{}; |
| 34 | |
| 35 | // We refuse to parse URL strings that exceed 4GB. Such strings are almost |
| 36 | // surely the result of a bug or are otherwise a security concern. |
| 37 | if (user_input.size() > std::numeric_limits<uint32_t>::max()) [[unlikely]] { |
| 38 | url.is_valid = false; |
| 39 | } |
| 40 | // Going forward, user_input.size() is in [0, |
| 41 | // std::numeric_limits<uint32_t>::max). If we are provided with an invalid |
| 42 | // base, or the optional_url was invalid, we must return. |
| 43 | if (base_url != nullptr) { |
| 44 | url.is_valid &= base_url->is_valid; |
| 45 | } |
| 46 | if (!url.is_valid) { |
| 47 | return url; |
| 48 | } |
| 49 | if constexpr (result_type_is_ada_url_aggregator && store_values) { |
| 50 | // Most of the time, we just need user_input.size(). |
| 51 | // In some instances, we may need a bit more. |
| 52 | /////////////////////////// |
| 53 | // This is *very* important. This line should *not* be removed |
| 54 | // hastily. There are principled reasons why reserve is important |
| 55 | // for performance. If you have a benchmark with small inputs, |
| 56 | // it may not matter, but in other instances, it could. |
| 57 | //// |
| 58 | // This rounds up to the next power of two. |
| 59 | // We know that user_input.size() is in [0, |
| 60 | // std::numeric_limits<uint32_t>::max). |
| 61 | uint32_t reserve_capacity = |
| 62 | (0xFFFFFFFF >> |
| 63 | helpers::leading_zeroes(uint32_t(1 | user_input.size()))) + |
| 64 | 1; |
| 65 | url.reserve(reserve_capacity); |
| 66 | } |
| 67 | std::string tmp_buffer; |
| 68 | std::string_view url_data; |
| 69 | if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] { |
| 70 | tmp_buffer = user_input; |
| 71 | // Optimization opportunity: Instead of copying and then pruning, we could |
nothing calls this directly
no test coverage detected