_ZN3ada7unicode14percent_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
11017|  57.3k|std::string percent_decode(const std::string_view input, size_t first_percent) {
11018|       |  // next line is for safety only, we expect users to avoid calling
11019|       |  // percent_decode when first_percent is outside the range.
11020|  57.3k|  if (first_percent == std::string_view::npos) {
  ------------------
  |  Branch (11020:7): [True: 38.5k, False: 18.7k]
  ------------------
11021|  38.5k|    return std::string(input);
11022|  38.5k|  }
11023|  18.7k|  std::string dest;
11024|  18.7k|  dest.reserve(input.length());
11025|  18.7k|  dest.append(input.substr(0, first_percent));
11026|  18.7k|  const char* pointer = input.data() + first_percent;
11027|  18.7k|  const char* end = input.data() + input.size();
11028|       |  // Optimization opportunity: if the following code gets
11029|       |  // called often, it can be optimized quite a bit.
11030|   163k|  while (pointer < end) {
  ------------------
  |  Branch (11030:10): [True: 144k, False: 18.7k]
  ------------------
11031|   144k|    const char ch = pointer[0];
11032|   144k|    size_t remaining = end - pointer - 1;
11033|   144k|    if (ch != '%' || remaining < 2 ||
  ------------------
  |  Branch (11033:9): [True: 20.2k, False: 124k]
  |  Branch (11033:22): [True: 4.38k, False: 119k]
  ------------------
11034|   119k|        (  // ch == '%' && // It is unnecessary to check that ch == '%'.
11035|   119k|            (!is_ascii_hex_digit(pointer[1]) ||
  ------------------
  |  Branch (11035:14): [True: 1.12k, False: 118k]
  ------------------
11036|   118k|             !is_ascii_hex_digit(pointer[2])))) {
  ------------------
  |  Branch (11036:14): [True: 698, False: 117k]
  ------------------
11037|  26.4k|      dest += ch;
11038|  26.4k|      pointer++;
11039|   117k|    } else {
11040|   117k|      unsigned a = convert_hex_to_binary(pointer[1]);
11041|   117k|      unsigned b = convert_hex_to_binary(pointer[2]);
11042|   117k|      char c = static_cast<char>(a * 16 + b);
11043|   117k|      dest += c;
11044|   117k|      pointer += 3;
11045|   117k|    }
11046|   144k|  }
11047|  18.7k|  return dest;
11048|  57.3k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
11051|   115k|                           const uint8_t character_set[]) {
11052|   115k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11053|   115k|    return character_sets::bit_at(character_set, c);
11054|   115k|  });
11055|       |  // Optimization: Don't iterate if percent encode is not required
11056|   115k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11056:7): [True: 77.6k, False: 38.2k]
  ------------------
11057|  77.6k|    return std::string(input);
11058|  77.6k|  }
11059|       |
11060|  38.2k|  std::string result;
11061|  38.2k|  result.reserve(input.length());  // in the worst case, percent encoding might
11062|       |                                   // produce 3 characters.
11063|  38.2k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
11064|       |
11065|   498k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11065:10): [True: 460k, False: 38.2k]
  ------------------
11066|   460k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11066:9): [True: 413k, False: 46.7k]
  ------------------
11067|   413k|      result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11068|   413k|    } else {
11069|  46.7k|      result += *pointer;
11070|  46.7k|    }
11071|   460k|  }
11072|       |
11073|  38.2k|  return result;
11074|   115k|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
11496|  2.60k|    std::string_view input, const result_type* base_url) {
11497|  2.60k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
11498|  2.60k|  if (!u.is_valid) {
  ------------------
  |  Branch (11498:7): [True: 0, False: 2.60k]
  ------------------
11499|      0|    return tl::unexpected(errors::type_error);
11500|      0|  }
11501|  2.60k|  return u;
11502|  2.60k|}
_ZN3ada20get_max_input_lengthEv:
11245|  3.95k|uint32_t get_max_input_length() {
11246|  3.95k|  return max_input_length_.load(std::memory_order_relaxed);
11247|  3.95k|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12404|  2.60k|void trim_c0_whitespace(std::string_view& input) noexcept {
12405|  2.60k|  while (!input.empty() &&
  ------------------
  |  Branch (12405:10): [True: 2.60k, False: 0]
  ------------------
12406|  2.60k|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (12406:10): [True: 0, False: 2.60k]
  ------------------
12407|      0|    input.remove_prefix(1);
12408|      0|  }
12409|  5.11k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (12409:10): [True: 5.11k, False: 0]
  |  Branch (12409:28): [True: 2.50k, False: 2.60k]
  ------------------
12410|  2.50k|    input.remove_suffix(1);
12411|  2.50k|  }
12412|  2.60k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
13654|  2.60k|                           const result_type* base_url) {
13655|       |  // We can specialize the implementation per type.
13656|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
13657|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
13658|       |  // something else } is free (at runtime). This means that ada::url_aggregator
13659|       |  // and ada::url **do not have to support the exact same API**.
13660|  2.60k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
13661|  2.60k|  constexpr bool result_type_is_ada_url_aggregator =
13662|  2.60k|      std::is_same_v<url_aggregator, result_type>;
13663|  2.60k|  static_assert(result_type_is_ada_url ||
13664|  2.60k|                result_type_is_ada_url_aggregator);  // We don't support
13665|       |                                                     // anything else for now.
13666|       |
13667|  2.60k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
13668|  2.60k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
13669|  2.60k|          ")");
13670|       |
13671|  2.60k|  state state = state::SCHEME_START;
13672|  2.60k|  result_type url{};
13673|       |
13674|  2.60k|  const uint32_t max_input_length = ada::get_max_input_length();
13675|       |
13676|       |  // We refuse to parse URL strings that exceed the maximum input length.
13677|       |  // By default, this is 4GB but can be configured via
13678|       |  // ada::set_max_input_length().
13679|  2.60k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (13679:7): [True: 0, False: 2.60k]
  ------------------
13680|      0|    url.is_valid = false;
13681|      0|  }
13682|       |  // Going forward, user_input.size() is in [0,
13683|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
13684|       |  // base, or the optional_url was invalid, we must return.
13685|  2.60k|  if (base_url != nullptr) {
  ------------------
  |  Branch (13685:7): [True: 0, False: 2.60k]
  ------------------
13686|      0|    url.is_valid &= base_url->is_valid;
13687|      0|  }
13688|  2.60k|  if (!url.is_valid) {
  ------------------
  |  Branch (13688:7): [True: 0, False: 2.60k]
  ------------------
13689|      0|    return url;
13690|      0|  }
13691|  2.60k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
13692|       |    // Most of the time, we just need user_input.size().
13693|       |    // In some instances, we may need a bit more.
13694|       |    ///////////////////////////
13695|       |    // This is *very* important. This line should *not* be removed
13696|       |    // hastily. There are principled reasons why reserve is important
13697|       |    // for performance. If you have a benchmark with small inputs,
13698|       |    // it may not matter, but in other instances, it could.
13699|       |    ////
13700|       |    // This rounds up to the next power of two.
13701|       |    // We know that user_input.size() is in [0,
13702|       |    // std::numeric_limits<uint32_t>::max).
13703|  2.60k|    uint32_t reserve_capacity =
13704|  2.60k|        (0xFFFFFFFF >>
13705|  2.60k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
13706|  2.60k|        1;
13707|  2.60k|    url.reserve(reserve_capacity);
13708|  2.60k|  }
13709|  2.60k|  std::string tmp_buffer;
13710|  2.60k|  std::string_view url_data;
13711|  2.60k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (13711:7): [True: 120, False: 2.48k]
  ------------------
13712|    120|    tmp_buffer = user_input;
13713|       |    // Optimization opportunity: Instead of copying and then pruning, we could
13714|       |    // just directly build the string from user_input.
13715|    120|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
13716|    120|    url_data = tmp_buffer;
13717|  2.48k|  } else [[likely]] {
13718|  2.48k|    url_data = user_input;
13719|  2.48k|  }
13720|       |
13721|       |  // Leading and trailing control characters are uncommon and easy to deal with
13722|       |  // (no performance concern).
13723|  2.60k|  helpers::trim_c0_whitespace(url_data);
13724|       |
13725|       |  // Optimization opportunity. Most websites do not have fragment.
13726|  2.60k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
13727|       |  // We add it last so that an implementation like ada::url_aggregator
13728|       |  // can append it last to its internal buffer, thus improving performance.
13729|       |
13730|       |  // Here url_data no longer has its fragment.
13731|       |  // We are going to access the data from url_data (it is immutable).
13732|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
13733|       |  // The input_position variable should range from 0 to input_size.
13734|       |  // It is illegal to access url_data at input_size.
13735|  2.60k|  size_t input_position = 0;
13736|  2.60k|  const size_t input_size = url_data.size();
13737|       |  // Keep running the following state machine by switching on state.
13738|       |  // If after a run pointer points to the EOF code point, go to the next step.
13739|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
13740|       |  // We never decrement input_position.
13741|  20.8k|  while (input_position <= input_size) {
  ------------------
  |  Branch (13741:10): [True: 20.8k, False: 0]
  ------------------
13742|  20.8k|    ada_log("In parsing at ", input_position, " out of ", input_size,
13743|  20.8k|            " in state ", ada::to_string(state));
13744|  20.8k|    switch (state) {
13745|  2.60k|      case state::SCHEME_START: {
  ------------------
  |  Branch (13745:7): [True: 2.60k, False: 18.2k]
  ------------------
13746|  2.60k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13747|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13748|       |        // state to scheme state.
13749|  2.60k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13749:13): [True: 2.60k, False: 0]
  ------------------
13750|  2.60k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13750:13): [True: 2.60k, False: 0]
  ------------------
13751|  2.60k|          state = state::SCHEME;
13752|  2.60k|          input_position++;
13753|  2.60k|        } else {
13754|       |          // Otherwise, if state override is not given, set state to no scheme
13755|       |          // state and decrease pointer by 1.
13756|      0|          state = state::NO_SCHEME;
13757|      0|        }
13758|  2.60k|        break;
13759|      0|      }
13760|  2.60k|      case state::SCHEME: {
  ------------------
  |  Branch (13760:7): [True: 2.60k, False: 18.2k]
  ------------------
13761|  2.60k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13762|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13763|       |        // append c, lowercased, to buffer.
13764|  13.0k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13764:16): [True: 13.0k, False: 0]
  ------------------
13765|  13.0k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13765:16): [True: 10.4k, False: 2.60k]
  ------------------
13766|  10.4k|          input_position++;
13767|  10.4k|        }
13768|       |        // Otherwise, if c is U+003A (:), then:
13769|  2.60k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13769:13): [True: 2.60k, False: 0]
  ------------------
13770|  2.60k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13770:13): [True: 2.60k, False: 0]
  ------------------
13771|  2.60k|          ada_log("SCHEME the scheme should be ",
13772|  2.60k|                  url_data.substr(0, input_position));
13773|       |          if constexpr (result_type_is_ada_url) {
13774|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
13775|       |              return url;
13776|       |            }
13777|  2.60k|          } else {
13778|       |            // we pass the colon along instead of painfully adding it back.
13779|  2.60k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (13779:17): [True: 0, False: 2.60k]
  ------------------
13780|  2.60k|                    url_data.substr(0, input_position + 1))) {
13781|      0|              return url;
13782|      0|            }
13783|  2.60k|          }
13784|  2.60k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13785|       |
13786|       |          // If url's scheme is "file", then:
13787|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13788|  2.60k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13788:15): [True: 0, False: 2.60k]
  ------------------
13789|       |            // Set state to file state.
13790|      0|            state = state::FILE;
13791|      0|          }
13792|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13793|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13794|       |          // != nullptr is false.
13795|  2.60k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13795:20): [True: 2.60k, False: 0]
  |  Branch (13795:40): [True: 0, False: 2.60k]
  ------------------
13796|      0|                   base_url->type == url.type) {
  ------------------
  |  Branch (13796:20): [True: 0, False: 0]
  ------------------
13797|       |            // Set state to special relative or authority state.
13798|      0|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13799|      0|          }
13800|       |          // Otherwise, if url is special, set state to special authority
13801|       |          // slashes state.
13802|  2.60k|          else if (url.is_special()) {
  ------------------
  |  Branch (13802:20): [True: 2.60k, False: 0]
  ------------------
13803|  2.60k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13804|  2.60k|          }
13805|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13806|       |          // path or authority state and increase pointer by 1.
13807|      0|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13807:20): [True: 0, False: 0]
  ------------------
13808|      0|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13808:20): [True: 0, False: 0]
  ------------------
13809|      0|            state = state::PATH_OR_AUTHORITY;
13810|      0|            input_position++;
13811|      0|          }
13812|       |          // Otherwise, set url's path to the empty string and set state to
13813|       |          // opaque path state.
13814|      0|          else {
13815|      0|            state = state::OPAQUE_PATH;
13816|      0|          }
13817|  2.60k|        }
13818|       |        // Otherwise, if state override is not given, set buffer to the empty
13819|       |        // string, state to no scheme state, and start over (from the first code
13820|       |        // point in input).
13821|      0|        else {
13822|      0|          state = state::NO_SCHEME;
13823|      0|          input_position = 0;
13824|      0|          break;
13825|      0|        }
13826|  2.60k|        input_position++;
13827|  2.60k|        break;
13828|  2.60k|      }
13829|      0|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13829:7): [True: 0, False: 20.8k]
  ------------------
13830|      0|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13831|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13832|       |        // validation error, return failure.
13833|      0|        if (base_url == nullptr ||
  ------------------
  |  Branch (13833:13): [True: 0, False: 0]
  ------------------
13834|      0|            (base_url->has_opaque_path && !fragment.has_value())) {
  ------------------
  |  Branch (13834:14): [True: 0, False: 0]
  |  Branch (13834:43): [True: 0, False: 0]
  ------------------
13835|      0|          ada_log("NO_SCHEME validation error");
13836|      0|          url.is_valid = false;
13837|      0|          return url;
13838|      0|        }
13839|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13840|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13841|       |        // query to base's query, and set state to fragment state.
13842|      0|        else if (base_url->has_opaque_path && fragment.has_value() &&
  ------------------
  |  Branch (13842:18): [True: 0, False: 0]
  |  Branch (13842:47): [True: 0, False: 0]
  ------------------
13843|      0|                 input_position == input_size) {
  ------------------
  |  Branch (13843:18): [True: 0, False: 0]
  ------------------
13844|      0|          ada_log("NO_SCHEME opaque base with fragment");
13845|      0|          url.copy_scheme(*base_url);
13846|      0|          url.has_opaque_path = base_url->has_opaque_path;
13847|       |
13848|       |          if constexpr (result_type_is_ada_url) {
13849|       |            url.path = base_url->path;
13850|       |            url.query = base_url->query;
13851|      0|          } else {
13852|      0|            url.update_base_pathname(base_url->get_pathname());
13853|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13853:17): [True: 0, False: 0]
  ------------------
13854|       |              // get_search() returns "" for an empty query string (URL ends
13855|       |              // with '?'). update_base_search("") would incorrectly clear the
13856|       |              // query, so pass "?" to preserve the empty query distinction.
13857|      0|              auto s = base_url->get_search();
13858|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13858:38): [True: 0, False: 0]
  ------------------
13859|      0|            }
13860|      0|          }
13861|      0|          url.update_unencoded_base_hash(*fragment);
13862|      0|          return url;
13863|      0|        }
13864|       |        // Otherwise, if base's scheme is not "file", set state to relative
13865|       |        // state and decrease pointer by 1.
13866|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13867|      0|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13867:18): [True: 0, False: 0]
  ------------------
13868|      0|          ada_log("NO_SCHEME non-file relative path");
13869|      0|          state = state::RELATIVE_SCHEME;
13870|      0|        }
13871|       |        // Otherwise, set state to file state and decrease pointer by 1.
13872|      0|        else {
13873|      0|          ada_log("NO_SCHEME file base type");
13874|      0|          state = state::FILE;
13875|      0|        }
13876|      0|        break;
13877|      0|      }
13878|  2.60k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13878:7): [True: 2.60k, False: 18.2k]
  ------------------
13879|  2.60k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13880|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13881|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13882|       |        // worry about AUTHORITY.
13883|       |        // TODO: Instead of just collecting a bool, collect the location of the
13884|       |        // '@' and do something useful with it.
13885|       |        // TODO: We could do various processing early on, using a single pass
13886|       |        // over the string to collect information about it, e.g., telling us
13887|       |        // whether there is a @ and if so, where (or how many).
13888|       |
13889|       |        // Check if url data contains an @.
13890|  2.60k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13890:13): [True: 2.52k, False: 84]
  ------------------
13891|  2.52k|          state = state::HOST;
13892|  2.52k|          break;
13893|  2.52k|        }
13894|     84|        bool at_sign_seen{false};
13895|     84|        bool password_token_seen{false};
13896|       |        /**
13897|       |         * We expect something of the sort...
13898|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13899|       |         * --------^
13900|       |         */
13901|     84|        do {
13902|     84|          std::string_view view = url_data.substr(input_position);
13903|       |          // The delimiters are @, /, ? \\.
13904|     84|          size_t location =
13905|     84|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13905:15): [True: 84, False: 0]
  ------------------
13906|     84|                               : helpers::find_authority_delimiter(view);
13907|     84|          std::string_view authority_view = view.substr(0, location);
13908|     84|          size_t end_of_authority = input_position + authority_view.size();
13909|       |          // If c is U+0040 (@), then:
13910|     84|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13910:15): [True: 84, False: 0]
  ------------------
13911|     84|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13911:15): [True: 0, False: 84]
  ------------------
13912|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13913|      0|            if (at_sign_seen) {
  ------------------
  |  Branch (13913:17): [True: 0, False: 0]
  ------------------
13914|      0|              if (password_token_seen) {
  ------------------
  |  Branch (13914:19): [True: 0, False: 0]
  ------------------
13915|       |                if constexpr (result_type_is_ada_url) {
13916|       |                  url.password += "%40";
13917|      0|                } else {
13918|      0|                  url.append_base_password("%40");
13919|      0|                }
13920|      0|              } else {
13921|       |                if constexpr (result_type_is_ada_url) {
13922|       |                  url.username += "%40";
13923|      0|                } else {
13924|      0|                  url.append_base_username("%40");
13925|      0|                }
13926|      0|              }
13927|      0|            }
13928|       |
13929|      0|            at_sign_seen = true;
13930|       |
13931|      0|            if (!password_token_seen) {
  ------------------
  |  Branch (13931:17): [True: 0, False: 0]
  ------------------
13932|      0|              size_t password_token_location = authority_view.find(':');
13933|      0|              password_token_seen =
13934|      0|                  password_token_location != std::string_view::npos;
13935|       |
13936|      0|              if constexpr (store_values) {
13937|      0|                if (!password_token_seen) {
  ------------------
  |  Branch (13937:21): [True: 0, False: 0]
  ------------------
13938|       |                  if constexpr (result_type_is_ada_url) {
13939|       |                    url.username += unicode::percent_encode(
13940|       |                        authority_view,
13941|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13942|      0|                  } else {
13943|      0|                    url.append_base_username(unicode::percent_encode(
13944|      0|                        authority_view,
13945|      0|                        character_sets::USERINFO_PERCENT_ENCODE));
13946|      0|                  }
13947|      0|                } else {
13948|       |                  if constexpr (result_type_is_ada_url) {
13949|       |                    url.username += unicode::percent_encode(
13950|       |                        authority_view.substr(0, password_token_location),
13951|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13952|       |                    url.password += unicode::percent_encode(
13953|       |                        authority_view.substr(password_token_location + 1),
13954|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13955|      0|                  } else {
13956|      0|                    url.append_base_username(unicode::percent_encode(
13957|      0|                        authority_view.substr(0, password_token_location),
13958|      0|                        character_sets::USERINFO_PERCENT_ENCODE));
13959|      0|                    url.append_base_password(unicode::percent_encode(
13960|      0|                        authority_view.substr(password_token_location + 1),
13961|      0|                        character_sets::USERINFO_PERCENT_ENCODE));
13962|      0|                  }
13963|      0|                }
13964|      0|              }
13965|      0|            } else if constexpr (store_values) {
13966|       |              if constexpr (result_type_is_ada_url) {
13967|       |                url.password += unicode::percent_encode(
13968|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13969|      0|              } else {
13970|      0|                url.append_base_password(unicode::percent_encode(
13971|      0|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13972|      0|              }
13973|      0|            }
13974|      0|          }
13975|       |          // Otherwise, if one of the following is true:
13976|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13977|       |          // - url is special and c is U+005C (\)
13978|     84|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13978:20): [True: 0, False: 84]
  ------------------
13979|     84|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13979:20): [True: 84, False: 0]
  ------------------
13980|      0|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13980:20): [True: 0, False: 0]
  ------------------
13981|     84|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13981:21): [True: 0, False: 0]
  |  Branch (13981:41): [True: 0, False: 0]
  ------------------
13982|       |            // If atSignSeen is true and authority_view is the empty string,
13983|       |            // validation error, return failure.
13984|     84|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13984:17): [True: 0, False: 84]
  |  Branch (13984:33): [True: 0, False: 0]
  ------------------
13985|      0|              url.is_valid = false;
13986|      0|              return url;
13987|      0|            }
13988|     84|            state = state::HOST;
13989|     84|            break;
13990|     84|          }
13991|      0|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13991:15): [True: 0, False: 0]
  ------------------
13992|      0|            if constexpr (store_values) {
13993|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13993:19): [True: 0, False: 0]
  ------------------
13994|      0|                url.update_unencoded_base_hash(*fragment);
13995|      0|              }
13996|      0|            }
13997|      0|            return url;
13998|      0|          }
13999|      0|          input_position = end_of_authority + 1;
14000|      0|        } while (true);
  ------------------
  |  Branch (14000:18): [True: 0, Folded]
  ------------------
14001|       |
14002|     84|        break;
14003|     84|      }
14004|     84|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (14004:7): [True: 0, False: 20.8k]
  ------------------
14005|      0|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
14006|      0|                helpers::substring(url_data, input_position));
14007|       |
14008|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
14009|       |        // then set state to special authority ignore slashes state and increase
14010|       |        // pointer by 1.
14011|      0|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (14011:13): [True: 0, False: 0]
  ------------------
14012|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
14013|      0|          input_position += 2;
14014|      0|        } else {
14015|       |          // Otherwise, validation error, set state to relative state and
14016|       |          // decrease pointer by 1.
14017|      0|          state = state::RELATIVE_SCHEME;
14018|      0|        }
14019|       |
14020|      0|        break;
14021|     84|      }
14022|      0|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (14022:7): [True: 0, False: 20.8k]
  ------------------
14023|      0|        ada_log("PATH_OR_AUTHORITY ",
14024|      0|                helpers::substring(url_data, input_position));
14025|       |
14026|       |        // If c is U+002F (/), then set state to authority state.
14027|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14027:13): [True: 0, False: 0]
  ------------------
14028|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14028:13): [True: 0, False: 0]
  ------------------
14029|      0|          state = state::AUTHORITY;
14030|      0|          input_position++;
14031|      0|        } else {
14032|       |          // Otherwise, set state to path state, and decrease pointer by 1.
14033|      0|          state = state::PATH;
14034|      0|        }
14035|       |
14036|      0|        break;
14037|     84|      }
14038|      0|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (14038:7): [True: 0, False: 20.8k]
  ------------------
14039|      0|        ada_log("RELATIVE_SCHEME ",
14040|      0|                helpers::substring(url_data, input_position));
14041|       |
14042|       |        // Set url's scheme to base's scheme.
14043|      0|        url.copy_scheme(*base_url);
14044|       |
14045|       |        // If c is U+002F (/), then set state to relative slash state.
14046|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14046:13): [True: 0, False: 0]
  ------------------
14047|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
14048|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14048:13): [True: 0, False: 0]
  ------------------
14049|      0|          ada_log(
14050|      0|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
14051|      0|              "slash state");
14052|      0|          state = state::RELATIVE_SLASH;
14053|      0|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14053:20): [True: 0, False: 0]
  |  Branch (14053:40): [True: 0, False: 0]
  ------------------
14054|      0|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14054:20): [True: 0, False: 0]
  ------------------
14055|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
14056|       |          // set state to relative slash state.
14057|      0|          ada_log(
14058|      0|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
14059|      0|              "error, set state to relative slash state");
14060|      0|          state = state::RELATIVE_SLASH;
14061|      0|        } else {
14062|      0|          ada_log("RELATIVE_SCHEME otherwise");
14063|       |          // Set url's username to base's username, url's password to base's
14064|       |          // password, url's host to base's host, url's port to base's port,
14065|       |          // url's path to a clone of base's path, and url's query to base's
14066|       |          // query.
14067|       |          if constexpr (result_type_is_ada_url) {
14068|       |            url.username = base_url->username;
14069|       |            url.password = base_url->password;
14070|       |            url.host = base_url->host;
14071|       |            url.port = base_url->port;
14072|       |            // cloning the base path includes cloning the has_opaque_path flag
14073|       |            url.has_opaque_path = base_url->has_opaque_path;
14074|       |            url.path = base_url->path;
14075|       |            url.query = base_url->query;
14076|      0|          } else {
14077|      0|            url.update_base_authority(base_url->get_href(),
14078|      0|                                      base_url->get_components());
14079|      0|            url.update_host_to_base_host(base_url->get_hostname());
14080|      0|            url.update_base_port(base_url->retrieve_base_port());
14081|       |            // cloning the base path includes cloning the has_opaque_path flag
14082|      0|            url.has_opaque_path = base_url->has_opaque_path;
14083|      0|            url.update_base_pathname(base_url->get_pathname());
14084|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (14084:17): [True: 0, False: 0]
  ------------------
14085|       |              // get_search() returns "" for an empty query string (URL ends
14086|       |              // with '?'). update_base_search("") would incorrectly clear the
14087|       |              // query, so pass "?" to preserve the empty query distinction.
14088|      0|              auto s = base_url->get_search();
14089|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (14089:38): [True: 0, False: 0]
  ------------------
14090|      0|            }
14091|      0|          }
14092|       |
14093|      0|          url.has_opaque_path = base_url->has_opaque_path;
14094|       |
14095|       |          // If c is U+003F (?), then set url's query to the empty string, and
14096|       |          // state to query state.
14097|      0|          if ((input_position != input_size) &&
  ------------------
  |  Branch (14097:15): [True: 0, False: 0]
  ------------------
14098|      0|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14098:15): [True: 0, False: 0]
  ------------------
14099|      0|            state = state::QUERY;
14100|      0|          }
14101|       |          // Otherwise, if c is not the EOF code point:
14102|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (14102:20): [True: 0, False: 0]
  ------------------
14103|       |            // Set url's query to null.
14104|      0|            url.clear_search();
14105|       |            if constexpr (result_type_is_ada_url) {
14106|       |              // Shorten url's path.
14107|       |              helpers::shorten_path(url.path, url.type);
14108|      0|            } else {
14109|      0|              std::string_view path = url.get_pathname();
14110|      0|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (14110:19): [True: 0, False: 0]
  ------------------
14111|      0|                url.update_base_pathname(std::move(std::string(path)));
14112|      0|              }
14113|      0|            }
14114|       |            // Set state to path state and decrease pointer by 1.
14115|      0|            state = state::PATH;
14116|      0|            break;
14117|      0|          }
14118|      0|        }
14119|      0|        input_position++;
14120|      0|        break;
14121|      0|      }
14122|      0|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (14122:7): [True: 0, False: 20.8k]
  ------------------
14123|      0|        ada_log("RELATIVE_SLASH ",
14124|      0|                helpers::substring(url_data, input_position));
14125|       |
14126|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
14127|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
14128|      0|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14128:13): [True: 0, False: 0]
  |  Branch (14128:33): [True: 0, False: 0]
  ------------------
14129|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14129:14): [True: 0, False: 0]
  ------------------
14130|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14130:14): [True: 0, False: 0]
  ------------------
14131|       |          // Set state to special authority ignore slashes state.
14132|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
14133|      0|        }
14134|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
14135|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14135:18): [True: 0, False: 0]
  ------------------
14136|      0|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14136:18): [True: 0, False: 0]
  ------------------
14137|      0|          state = state::AUTHORITY;
14138|      0|        }
14139|       |        // Otherwise, set
14140|       |        // - url's username to base's username,
14141|       |        // - url's password to base's password,
14142|       |        // - url's host to base's host,
14143|       |        // - url's port to base's port,
14144|       |        // - state to path state, and then, decrease pointer by 1.
14145|      0|        else {
14146|       |          if constexpr (result_type_is_ada_url) {
14147|       |            url.username = base_url->username;
14148|       |            url.password = base_url->password;
14149|       |            url.host = base_url->host;
14150|       |            url.port = base_url->port;
14151|      0|          } else {
14152|      0|            url.update_base_authority(base_url->get_href(),
14153|      0|                                      base_url->get_components());
14154|      0|            url.update_host_to_base_host(base_url->get_hostname());
14155|      0|            url.update_base_port(base_url->retrieve_base_port());
14156|      0|          }
14157|      0|          state = state::PATH;
14158|      0|          break;
14159|      0|        }
14160|       |
14161|      0|        input_position++;
14162|      0|        break;
14163|      0|      }
14164|  2.60k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (14164:7): [True: 2.60k, False: 18.2k]
  ------------------
14165|  2.60k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
14166|  2.60k|                helpers::substring(url_data, input_position));
14167|       |
14168|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
14169|       |        // then set state to special authority ignore slashes state and increase
14170|       |        // pointer by 1.
14171|  2.60k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (14171:13): [True: 2.60k, False: 0]
  ------------------
14172|  2.60k|          input_position += 2;
14173|  2.60k|        }
14174|       |
14175|  2.60k|        [[fallthrough]];
14176|  2.60k|      }
14177|  2.60k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (14177:7): [True: 0, False: 20.8k]
  ------------------
14178|  2.60k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
14179|  2.60k|                helpers::substring(url_data, input_position));
14180|       |
14181|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
14182|       |        // authority state and decrease pointer by 1.
14183|  2.60k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (14183:16): [True: 2.60k, False: 0]
  ------------------
14184|  2.60k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (14184:17): [True: 0, False: 2.60k]
  ------------------
14185|  2.60k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (14185:17): [True: 0, False: 2.60k]
  ------------------
14186|      0|          input_position++;
14187|      0|        }
14188|  2.60k|        state = state::AUTHORITY;
14189|       |
14190|  2.60k|        break;
14191|  2.60k|      }
14192|  2.60k|      case state::QUERY: {
  ------------------
  |  Branch (14192:7): [True: 2.60k, False: 18.2k]
  ------------------
14193|  2.60k|        ada_log("QUERY ", helpers::substring(url_data, input_position));
14194|  2.60k|        if constexpr (store_values) {
14195|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
14196|       |          // if url is special; otherwise the query percent-encode set.
14197|  2.60k|          const uint8_t* query_percent_encode_set =
14198|  2.60k|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (14198:15): [True: 2.60k, False: 0]
  ------------------
14199|  2.60k|                               : character_sets::QUERY_PERCENT_ENCODE;
14200|       |
14201|       |          // Percent-encode after encoding, with encoding, buffer, and
14202|       |          // queryPercentEncodeSet, and append the result to url's query.
14203|  2.60k|          url.update_base_search(url_data.substr(input_position),
14204|  2.60k|                                 query_percent_encode_set);
14205|  2.60k|          ada_log("QUERY update_base_search completed ");
14206|  2.60k|          if (fragment.has_value()) {
  ------------------
  |  Branch (14206:15): [True: 162, False: 2.44k]
  ------------------
14207|    162|            url.update_unencoded_base_hash(*fragment);
14208|    162|          }
14209|  2.60k|        }
14210|  2.60k|        return url;
14211|  2.60k|      }
14212|  2.60k|      case state::HOST: {
  ------------------
  |  Branch (14212:7): [True: 2.60k, False: 18.2k]
  ------------------
14213|  2.60k|        ada_log("HOST ", helpers::substring(url_data, input_position));
14214|       |
14215|  2.60k|        std::string_view host_view = url_data.substr(input_position);
14216|  2.60k|        auto [location, found_colon] =
14217|  2.60k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
14218|  2.60k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (14218:26): [True: 2.60k, False: 0]
  ------------------
14219|  2.60k|                             ? input_position + location
14220|  2.60k|                             : input_size;
14221|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
14222|       |        // Note: the 'found_colon' value is true if and only if a colon was
14223|       |        // encountered while not inside brackets.
14224|  2.60k|        if (found_colon) {
  ------------------
  |  Branch (14224:13): [True: 0, False: 2.60k]
  ------------------
14225|       |          // If buffer is the empty string, validation error, return failure.
14226|       |          // Let host be the result of host parsing buffer with url is not
14227|       |          // special.
14228|      0|          ada_log("HOST parsing ", host_view);
14229|      0|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14229:15): [True: 0, False: 0]
  ------------------
14230|      0|            return url;
14231|      0|          }
14232|      0|          ada_log("HOST parsing results in ", url.get_hostname());
14233|       |          // Set url's host to host, buffer to the empty string, and state to
14234|       |          // port state.
14235|      0|          state = state::PORT;
14236|      0|          input_position++;
14237|      0|        }
14238|       |        // Otherwise, if one of the following is true:
14239|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
14240|       |        // - url is special and c is U+005C (\)
14241|       |        // The get_host_delimiter_location function either brings us to
14242|       |        // the colon outside of the bracket, or to one of those characters.
14243|  2.60k|        else {
14244|       |          // If url is special and host_view is the empty string, validation
14245|       |          // error, return failure.
14246|  2.60k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (14246:15): [True: 0, False: 2.60k]
  |  Branch (14246:36): [True: 0, False: 0]
  ------------------
14247|      0|            url.is_valid = false;
14248|      0|            return url;
14249|      0|          }
14250|  2.60k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
14251|       |          // Let host be the result of host parsing host_view with url is not
14252|       |          // special.
14253|  2.60k|          if (host_view.empty()) {
  ------------------
  |  Branch (14253:15): [True: 0, False: 2.60k]
  ------------------
14254|      0|            url.update_base_hostname("");
14255|  2.60k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14255:22): [True: 0, False: 2.60k]
  ------------------
14256|      0|            return url;
14257|      0|          }
14258|  2.60k|          ada_log("HOST parsing results in ", url.get_hostname(),
14259|  2.60k|                  " href=", url.get_href());
14260|       |
14261|       |          // Set url's host to host, and state to path start state.
14262|  2.60k|          state = state::PATH_START;
14263|  2.60k|        }
14264|       |
14265|  2.60k|        break;
14266|  2.60k|      }
14267|  2.60k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (14267:7): [True: 0, False: 20.8k]
  ------------------
14268|      0|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
14269|       |        // Opaque path, query, and fragment are structurally always valid:
14270|       |        // the parser would just percent-encode whatever is there. When we
14271|       |        // are not storing values (can_parse), we can return immediately.
14272|       |        // We must set has_opaque_path = true before returning so that when
14273|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
14274|       |        // correctly rejects relative inputs against an opaque-path base
14275|       |        // (e.g. can_parse("", &"W:") must return false).
14276|       |        if constexpr (!store_values) {
14277|       |          url.has_opaque_path = true;
14278|       |          return url;
14279|       |        }
14280|      0|        std::string_view view = url_data.substr(input_position);
14281|       |        // If c is U+003F (?), then set url's query to the empty string and
14282|       |        // state to query state.
14283|      0|        size_t location = view.find('?');
14284|      0|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (14284:13): [True: 0, False: 0]
  ------------------
14285|      0|          view.remove_suffix(view.size() - location);
14286|      0|          state = state::QUERY;
14287|      0|          input_position += location + 1;
14288|      0|        } else {
14289|      0|          input_position = input_size + 1;
14290|      0|        }
14291|      0|        url.has_opaque_path = true;
14292|       |
14293|       |        // This is a really unlikely scenario in real world. We should not seek
14294|       |        // to optimize it.
14295|      0|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (14295:13): [True: 0, False: 0]
  ------------------
14296|      0|          std::string modified_view =
14297|      0|              std::string(view.substr(0, view.size() - 1)) + "%20";
14298|      0|          url.update_base_pathname(unicode::percent_encode(
14299|      0|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14300|      0|        } else {
14301|      0|          url.update_base_pathname(unicode::percent_encode(
14302|      0|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14303|      0|        }
14304|      0|        break;
14305|  2.60k|      }
14306|      0|      case state::PORT: {
  ------------------
  |  Branch (14306:7): [True: 0, False: 20.8k]
  ------------------
14307|      0|        ada_log("PORT ", helpers::substring(url_data, input_position));
14308|      0|        std::string_view port_view = url_data.substr(input_position);
14309|      0|        input_position += url.parse_port(port_view, true);
14310|      0|        if (!url.is_valid) {
  ------------------
  |  Branch (14310:13): [True: 0, False: 0]
  ------------------
14311|      0|          return url;
14312|      0|        }
14313|      0|        state = state::PATH_START;
14314|      0|        [[fallthrough]];
14315|      0|      }
14316|  2.60k|      case state::PATH_START: {
  ------------------
  |  Branch (14316:7): [True: 2.60k, False: 18.2k]
  ------------------
14317|  2.60k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
14318|       |        // Path, query, and fragment are structurally always valid: the
14319|       |        // parser would just percent-encode whatever is there. When we are
14320|       |        // not storing values (can_parse), we can return immediately since
14321|       |        // no subsequent state can invalidate the URL.
14322|       |        if constexpr (!store_values) {
14323|       |          return url;
14324|       |        }
14325|       |
14326|       |        // If url is special, then:
14327|  2.60k|        if (url.is_special()) {
  ------------------
  |  Branch (14327:13): [True: 2.60k, False: 0]
  ------------------
14328|       |          // Set state to path state.
14329|  2.60k|          state = state::PATH;
14330|       |
14331|       |          // Optimization: Avoiding going into PATH state improves the
14332|       |          // performance of urls ending with /.
14333|  2.60k|          if (input_position == input_size) {
  ------------------
  |  Branch (14333:15): [True: 0, False: 2.60k]
  ------------------
14334|      0|            if constexpr (store_values) {
14335|      0|              url.update_base_pathname("/");
14336|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (14336:19): [True: 0, False: 0]
  ------------------
14337|      0|                url.update_unencoded_base_hash(*fragment);
14338|      0|              }
14339|      0|            }
14340|      0|            return url;
14341|      0|          }
14342|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
14343|       |          // by 1. We know that (input_position == input_size) is impossible
14344|       |          // here, because of the previous if-check.
14345|  2.60k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (14345:15): [True: 0, False: 2.60k]
  ------------------
14346|      0|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (14346:15): [True: 0, False: 0]
  ------------------
14347|      0|            break;
14348|      0|          }
14349|  2.60k|        }
14350|       |        // Otherwise, if state override is not given and c is U+003F (?),
14351|       |        // set url's query to the empty string and state to query state.
14352|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14352:18): [True: 0, False: 0]
  ------------------
14353|      0|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14353:18): [True: 0, False: 0]
  ------------------
14354|      0|          state = state::QUERY;
14355|      0|        }
14356|       |        // Otherwise, if c is not the EOF code point:
14357|      0|        else if (input_position != input_size) {
  ------------------
  |  Branch (14357:18): [True: 0, False: 0]
  ------------------
14358|       |          // Set state to path state.
14359|      0|          state = state::PATH;
14360|       |
14361|       |          // If c is not U+002F (/), then decrease pointer by 1.
14362|      0|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (14362:15): [True: 0, False: 0]
  ------------------
14363|      0|            break;
14364|      0|          }
14365|      0|        }
14366|       |
14367|  2.60k|        input_position++;
14368|  2.60k|        break;
14369|  2.60k|      }
14370|  2.60k|      case state::PATH: {
  ------------------
  |  Branch (14370:7): [True: 2.60k, False: 18.2k]
  ------------------
14371|  2.60k|        ada_log("PATH ", helpers::substring(url_data, input_position));
14372|       |        // Path, query, and fragment are structurally always valid: the
14373|       |        // parser would just percent-encode whatever is there. When we are
14374|       |        // not storing values (can_parse), we can return immediately since
14375|       |        // no subsequent state can invalidate the URL.
14376|       |        if constexpr (!store_values) {
14377|       |          return url;
14378|       |        }
14379|  2.60k|        std::string_view view = url_data.substr(input_position);
14380|       |
14381|       |        // Most time, we do not need percent encoding.
14382|       |        // Furthermore, we can immediately locate the '?'.
14383|  2.60k|        size_t locofquestionmark = view.find('?');
14384|  2.60k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (14384:13): [True: 2.60k, False: 0]
  ------------------
14385|  2.60k|          state = state::QUERY;
14386|  2.60k|          view.remove_suffix(view.size() - locofquestionmark);
14387|  2.60k|          input_position += locofquestionmark + 1;
14388|  2.60k|        } else {
14389|      0|          input_position = input_size + 1;
14390|      0|        }
14391|  2.60k|        if constexpr (store_values) {
14392|       |          if constexpr (result_type_is_ada_url) {
14393|       |            helpers::parse_prepared_path(view, url.type, url.path);
14394|  2.60k|          } else {
14395|  2.60k|            url.consume_prepared_path(view);
14396|  2.60k|            ADA_ASSERT_TRUE(url.validate());
14397|  2.60k|          }
14398|  2.60k|        }
14399|  2.60k|        break;
14400|  2.60k|      }
14401|      0|      case state::FILE_SLASH: {
  ------------------
  |  Branch (14401:7): [True: 0, False: 20.8k]
  ------------------
14402|      0|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
14403|       |
14404|       |        // If c is U+002F (/) or U+005C (\), then:
14405|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14405:13): [True: 0, False: 0]
  ------------------
14406|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14406:14): [True: 0, False: 0]
  ------------------
14407|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14407:14): [True: 0, False: 0]
  ------------------
14408|      0|          ada_log("FILE_SLASH c is U+002F or U+005C");
14409|       |          // Set state to file host state.
14410|      0|          state = state::FILE_HOST;
14411|      0|          input_position++;
14412|      0|        } else {
14413|      0|          ada_log("FILE_SLASH otherwise");
14414|       |          // If base is non-null and base's scheme is "file", then:
14415|       |          // Note: it is unsafe to do base_url->scheme unless you know that
14416|       |          // base_url_has_value() is true.
14417|      0|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14417:15): [True: 0, False: 0]
  |  Branch (14417:38): [True: 0, False: 0]
  ------------------
14418|       |            // Set url's host to base's host.
14419|       |            if constexpr (result_type_is_ada_url) {
14420|       |              url.host = base_url->host;
14421|      0|            } else {
14422|      0|              url.update_host_to_base_host(base_url->get_host());
14423|      0|            }
14424|       |            // If the code point substring from pointer to the end of input does
14425|       |            // not start with a Windows drive letter and base's path[0] is a
14426|       |            // normalized Windows drive letter, then append base's path[0] to
14427|       |            // url's path.
14428|      0|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (14428:17): [True: 0, False: 0]
  ------------------
14429|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (14429:19): [True: 0, False: 0]
  ------------------
14430|      0|                      url_data.substr(input_position))) {
14431|      0|                std::string_view first_base_url_path =
14432|      0|                    base_url->get_pathname().substr(1);
14433|      0|                size_t loc = first_base_url_path.find('/');
14434|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (14434:21): [True: 0, False: 0]
  ------------------
14435|      0|                  helpers::resize(first_base_url_path, loc);
14436|      0|                }
14437|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (14437:21): [True: 0, False: 0]
  ------------------
14438|      0|                        first_base_url_path)) {
14439|       |                  if constexpr (result_type_is_ada_url) {
14440|       |                    url.path += '/';
14441|       |                    url.path += first_base_url_path;
14442|      0|                  } else {
14443|      0|                    url.append_base_pathname(
14444|      0|                        helpers::concat("/", first_base_url_path));
14445|      0|                  }
14446|      0|                }
14447|      0|              }
14448|      0|            }
14449|      0|          }
14450|       |
14451|       |          // Set state to path state, and decrease pointer by 1.
14452|      0|          state = state::PATH;
14453|      0|        }
14454|       |
14455|      0|        break;
14456|  2.60k|      }
14457|      0|      case state::FILE_HOST: {
  ------------------
  |  Branch (14457:7): [True: 0, False: 20.8k]
  ------------------
14458|      0|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
14459|      0|        std::string_view view = url_data.substr(input_position);
14460|       |
14461|      0|        size_t location = view.find_first_of("/\\?");
14462|      0|        std::string_view file_host_buffer = view.substr(
14463|      0|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (14463:16): [True: 0, False: 0]
  ------------------
14464|       |
14465|      0|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (14465:13): [True: 0, False: 0]
  ------------------
14466|      0|          state = state::PATH;
14467|      0|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (14467:20): [True: 0, False: 0]
  ------------------
14468|       |          // Set url's host to the empty string.
14469|       |          if constexpr (result_type_is_ada_url) {
14470|       |            url.host = "";
14471|      0|          } else {
14472|      0|            url.update_base_hostname("");
14473|      0|          }
14474|       |          // Set state to path start state.
14475|      0|          state = state::PATH_START;
14476|      0|        } else {
14477|      0|          size_t consumed_bytes = file_host_buffer.size();
14478|      0|          input_position += consumed_bytes;
14479|       |          // Let host be the result of host parsing buffer with url is not
14480|       |          // special.
14481|      0|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (14481:15): [True: 0, False: 0]
  ------------------
14482|      0|            return url;
14483|      0|          }
14484|       |
14485|       |          if constexpr (result_type_is_ada_url) {
14486|       |            // If host is "localhost", then set host to the empty string.
14487|       |            if (url.host.has_value() && url.host.value() == "localhost") {
14488|       |              url.host = "";
14489|       |            }
14490|      0|          } else {
14491|      0|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (14491:17): [True: 0, False: 0]
  ------------------
14492|      0|              url.update_base_hostname("");
14493|      0|            }
14494|      0|          }
14495|       |
14496|       |          // Set buffer to the empty string and state to path start state.
14497|      0|          state = state::PATH_START;
14498|      0|        }
14499|       |
14500|      0|        break;
14501|      0|      }
14502|      0|      case state::FILE: {
  ------------------
  |  Branch (14502:7): [True: 0, False: 20.8k]
  ------------------
14503|      0|        ada_log("FILE ", helpers::substring(url_data, input_position));
14504|      0|        std::string_view file_view = url_data.substr(input_position);
14505|       |
14506|      0|        url.set_protocol_as_file();
14507|       |        if constexpr (result_type_is_ada_url) {
14508|       |          // Set url's host to the empty string.
14509|       |          url.host = "";
14510|      0|        } else {
14511|      0|          url.update_base_hostname("");
14512|      0|        }
14513|       |        // If c is U+002F (/) or U+005C (\), then:
14514|      0|        if (input_position != input_size &&
  ------------------
  |  Branch (14514:13): [True: 0, False: 0]
  ------------------
14515|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14515:14): [True: 0, False: 0]
  ------------------
14516|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14516:14): [True: 0, False: 0]
  ------------------
14517|      0|          ada_log("FILE c is U+002F or U+005C");
14518|       |          // Set state to file slash state.
14519|      0|          state = state::FILE_SLASH;
14520|      0|        }
14521|       |        // Otherwise, if base is non-null and base's scheme is "file":
14522|      0|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14522:18): [True: 0, False: 0]
  |  Branch (14522:41): [True: 0, False: 0]
  ------------------
14523|       |          // Set url's host to base's host, url's path to a clone of base's
14524|       |          // path, and url's query to base's query.
14525|      0|          ada_log("FILE base non-null");
14526|       |          if constexpr (result_type_is_ada_url) {
14527|       |            url.host = base_url->host;
14528|       |            url.path = base_url->path;
14529|       |            url.query = base_url->query;
14530|      0|          } else {
14531|      0|            url.update_host_to_base_host(base_url->get_hostname());
14532|      0|            url.update_base_pathname(base_url->get_pathname());
14533|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (14533:17): [True: 0, False: 0]
  ------------------
14534|       |              // get_search() returns "" for an empty query string (URL ends
14535|       |              // with '?'). update_base_search("") would incorrectly clear the
14536|       |              // query, so pass "?" to preserve the empty query distinction.
14537|      0|              auto s = base_url->get_search();
14538|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (14538:38): [True: 0, False: 0]
  ------------------
14539|      0|            }
14540|      0|          }
14541|      0|          url.has_opaque_path = base_url->has_opaque_path;
14542|       |
14543|       |          // If c is U+003F (?), then set url's query to the empty string and
14544|       |          // state to query state.
14545|      0|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (14545:15): [True: 0, False: 0]
  |  Branch (14545:47): [True: 0, False: 0]
  ------------------
14546|      0|            state = state::QUERY;
14547|      0|          }
14548|       |          // Otherwise, if c is not the EOF code point:
14549|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (14549:20): [True: 0, False: 0]
  ------------------
14550|       |            // Set url's query to null.
14551|      0|            url.clear_search();
14552|       |            // If the code point substring from pointer to the end of input does
14553|       |            // not start with a Windows drive letter, then shorten url's path.
14554|      0|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (14554:17): [True: 0, False: 0]
  ------------------
14555|       |              if constexpr (result_type_is_ada_url) {
14556|       |                helpers::shorten_path(url.path, url.type);
14557|      0|              } else {
14558|      0|                std::string_view path = url.get_pathname();
14559|      0|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (14559:21): [True: 0, False: 0]
  ------------------
14560|      0|                  url.update_base_pathname(std::move(std::string(path)));
14561|      0|                }
14562|      0|              }
14563|      0|            }
14564|       |            // Otherwise:
14565|      0|            else {
14566|       |              // Set url's path to an empty list.
14567|      0|              url.clear_pathname();
14568|      0|              url.has_opaque_path = true;
14569|      0|            }
14570|       |
14571|       |            // Set state to path state and decrease pointer by 1.
14572|      0|            state = state::PATH;
14573|      0|            break;
14574|      0|          }
14575|      0|        }
14576|       |        // Otherwise, set state to path state, and decrease pointer by 1.
14577|      0|        else {
14578|      0|          ada_log("FILE go to path");
14579|      0|          state = state::PATH;
14580|      0|          break;
14581|      0|        }
14582|       |
14583|      0|        input_position++;
14584|      0|        break;
14585|      0|      }
14586|      0|      default:
  ------------------
  |  Branch (14586:7): [True: 0, False: 20.8k]
  ------------------
14587|      0|        unreachable();
14588|  20.8k|    }
14589|  20.8k|  }
14590|      0|  if constexpr (store_values) {
14591|      0|    if (fragment.has_value()) {
  ------------------
  |  Branch (14591:9): [True: 0, False: 0]
  ------------------
14592|      0|      url.update_unencoded_base_hash(*fragment);
14593|      0|    }
14594|      0|  }
14595|       |  // Check the resulting (normalized) URL size against the maximum input length.
14596|       |  // Normalization (percent-encoding, IDNA, etc.) can expand the URL beyond the
14597|       |  // original input size.
14598|      0|  if constexpr (store_values) {
14599|      0|    if (url.is_valid) {
  ------------------
  |  Branch (14599:9): [True: 0, False: 0]
  ------------------
14600|      0|      if constexpr (result_type_is_ada_url_aggregator) {
14601|      0|        if (url.buffer.size() > max_input_length) {
  ------------------
  |  Branch (14601:13): [True: 0, False: 0]
  ------------------
14602|      0|          url.is_valid = false;
14603|      0|        }
14604|       |      } else {
14605|       |        if (url.get_href_size() > max_input_length) {
14606|       |          url.is_valid = false;
14607|       |        }
14608|       |      }
14609|      0|    }
14610|      0|  }
14611|      0|  return url;
14612|  2.60k|}
_ZN3ada14url_aggregator10set_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15077|  2.60k|void url_aggregator::set_search(const std::string_view input) {
15078|  2.60k|  ada_log("url_aggregator::set_search ", input);
15079|  2.60k|  ADA_ASSERT_TRUE(validate());
15080|  2.60k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15081|  2.60k|  if (input.empty()) {
  ------------------
  |  Branch (15081:7): [True: 1.26k, False: 1.34k]
  ------------------
15082|  1.26k|    clear_search();
15083|  1.26k|    helpers::strip_trailing_spaces_from_opaque_path(*this);
15084|  1.26k|    return;
15085|  1.26k|  }
15086|       |
15087|  1.34k|  std::string new_value;
15088|  1.34k|  new_value = input[0] == '?' ? input.substr(1) : input;
  ------------------
  |  Branch (15088:15): [True: 0, False: 1.34k]
  ------------------
15089|  1.34k|  helpers::remove_ascii_tab_or_newline(new_value);
15090|       |
15091|  1.34k|  auto query_percent_encode_set =
15092|  1.34k|      is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (15092:7): [True: 1.34k, False: 0]
  ------------------
15093|  1.34k|                   : ada::character_sets::QUERY_PERCENT_ENCODE;
15094|       |
15095|  1.34k|  url_aggregator saved_url(*this);
15096|  1.34k|  update_base_search(new_value, query_percent_encode_set);
15097|  1.34k|  if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (15097:7): [True: 0, False: 1.34k]
  ------------------
15098|      0|    *this = std::move(saved_url);
15099|      0|    return;
15100|      0|  }
15101|  1.34k|  ADA_ASSERT_TRUE(validate());
15102|  1.34k|}
_ZNK3ada14url_aggregator12get_hostnameEv:
15498|  2.60k|    ada_lifetime_bound {
15499|  2.60k|  ada_log("url_aggregator::get_hostname");
15500|       |  // Technically, we should check if there is a hostname, but
15501|       |  // the code below works even if there isn't.
15502|       |  // if(!has_hostname()) { return ""; }
15503|  2.60k|  size_t start = components.host_start;
15504|       |  // So host_start is not where the host begins.
15505|  2.60k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (15505:7): [True: 2.60k, False: 0]
  ------------------
15506|  2.60k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (15506:7): [True: 0, False: 2.60k]
  ------------------
15507|      0|    start++;
15508|      0|  }
15509|  2.60k|  return helpers::substring(buffer, start, components.host_end);
15510|  2.60k|}
_ZNK3ada14url_aggregator10get_searchEv:
15513|  2.60k|    ada_lifetime_bound {
15514|  2.60k|  ada_log("url_aggregator::get_search");
15515|       |  // If this's URL's query is either null or the empty string, then return the
15516|       |  // empty string. Return U+003F (?), followed by this's URL's query.
15517|  2.60k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (15517:7): [True: 0, False: 2.60k]
  ------------------
15518|      0|    return "";
15519|      0|  }
15520|  2.60k|  auto ending_index = uint32_t(buffer.size());
15521|  2.60k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15521:7): [True: 162, False: 2.44k]
  ------------------
15522|    162|    ending_index = components.hash_start;
15523|    162|  }
15524|  2.60k|  if (ending_index - components.search_start <= 1) {
  ------------------
  |  Branch (15524:7): [True: 1.22k, False: 1.38k]
  ------------------
15525|  1.22k|    return "";
15526|  1.22k|  }
15527|  1.38k|  return helpers::substring(buffer, components.search_start, ending_index);
15528|  2.60k|}
_ZN3ada7unicode18is_ascii_hex_digitEc:
10923|   238k|ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept {
10924|   238k|  return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
  ------------------
  |  Branch (10924:11): [True: 236k, False: 1.63k]
  |  Branch (10924:23): [True: 153k, False: 83.3k]
  |  Branch (10924:37): [True: 83.2k, False: 1.71k]
  |  Branch (10924:49): [True: 82.2k, False: 975]
  ------------------
10925|  2.68k|         (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (10925:11): [True: 936, False: 1.75k]
  |  Branch (10925:23): [True: 867, False: 69]
  ------------------
10926|   238k|}
_ZN3ada7unicode21convert_hex_to_binaryEc:
11013|   235k|unsigned constexpr convert_hex_to_binary(const char c) noexcept {
11014|   235k|  return hex_to_binary_table[c - '0'];
11015|   235k|}
url_search_params.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
11052|  75.7k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11053|  75.7k|    return character_sets::bit_at(character_set, c);
11054|  75.7k|  });
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10699|  2.60k|    std::string_view user_input) noexcept {
10700|       |  // first check for short strings in which case we do it naively.
10701|  2.60k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (10701:7): [True: 0, False: 2.60k]
  ------------------
10702|      0|    return std::ranges::any_of(user_input, is_tabs_or_newline);
10703|      0|  }
10704|       |  // fast path for long strings (expected to be common)
10705|  2.60k|  size_t i = 0;
10706|  2.60k|  const __m128i mask1 = _mm_set1_epi8('\r');
10707|  2.60k|  const __m128i mask2 = _mm_set1_epi8('\n');
10708|  2.60k|  const __m128i mask3 = _mm_set1_epi8('\t');
10709|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
10710|  2.60k|  __m128i running{0};
10711|  8.60k|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (10711:10): [True: 5.99k, False: 2.60k]
  ------------------
10712|  5.99k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
10713|  5.99k|    running = _mm_or_si128(
10714|  5.99k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
10715|  5.99k|                                           _mm_cmpeq_epi8(word, mask2))),
10716|  5.99k|        _mm_cmpeq_epi8(word, mask3));
10717|  5.99k|  }
10718|  2.60k|  if (i < user_input.size()) {
  ------------------
  |  Branch (10718:7): [True: 2.54k, False: 65]
  ------------------
10719|  2.54k|    __m128i word = _mm_loadu_si128(
10720|  2.54k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
10721|  2.54k|    running = _mm_or_si128(
10722|  2.54k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
10723|  2.54k|                                           _mm_cmpeq_epi8(word, mask2))),
10724|  2.54k|        _mm_cmpeq_epi8(word, mask3));
10725|  2.54k|  }
10726|  2.60k|  return _mm_movemask_epi8(running) != 0;
10727|  2.60k|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
11734|  1.46k|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
11735|       |  // if this ever becomes a performance issue, we could use an approach similar
11736|       |  // to has_tabs_or_newline
11737|  1.46k|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
11738|  1.46k|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
10944|   129k|    const char c) noexcept {
10945|   129k|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (10945:10): [True: 523, False: 129k]
  |  Branch (10945:23): [True: 559, False: 128k]
  |  Branch (10945:36): [True: 212, False: 128k]
  ------------------
10946|   129k|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
10939|  7.72k|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
10940|  7.72k|  return (unsigned char)c <= ' ';
10941|  7.72k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   87|  2.60k|    std::string_view input) noexcept {
   88|       |  // The path percent-encode set is the query percent-encode set and U+003F (?),
   89|       |  // U+0060 (`), U+007B ({), and U+007D (}). The query percent-encode set is the
   90|       |  // C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+0023 (#),
   91|       |  // U+003C (<), and U+003E (>). The C0 control percent-encode set are the C0
   92|       |  // controls and all code points greater than U+007E (~).
   93|  2.60k|  size_t i = 0;
   94|  2.60k|  uint8_t accumulator{};
   95|  2.60k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (95:10): [True: 0, False: 2.60k]
  ------------------
   96|      0|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   97|      0|                           path_signature_table[uint8_t(input[i + 1])] |
   98|      0|                           path_signature_table[uint8_t(input[i + 2])] |
   99|      0|                           path_signature_table[uint8_t(input[i + 3])] |
  100|      0|                           path_signature_table[uint8_t(input[i + 4])] |
  101|      0|                           path_signature_table[uint8_t(input[i + 5])] |
  102|      0|                           path_signature_table[uint8_t(input[i + 6])] |
  103|      0|                           path_signature_table[uint8_t(input[i + 7])]);
  104|      0|  }
  105|  2.60k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (105:10): [True: 0, False: 2.60k]
  ------------------
  106|      0|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
  107|      0|  }
  108|  2.60k|  return accumulator;
  109|  2.60k|}
_ZN3ada7unicode13is_alnum_plusEc:
10916|  13.0k|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
10917|  13.0k|  return is_alnum_plus_table[uint8_t(c)];
10918|       |  // A table is almost surely much faster than the
10919|       |  // following under most compilers: return
10920|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
10921|  13.0k|}
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
11674|  2.60k|    std::string_view& input) noexcept {
11675|       |  // compiles down to 20--30 instructions including a class to memchr (C
11676|       |  // function). this function should be quite fast.
11677|  2.60k|  size_t location_of_first = input.find('#');
11678|  2.60k|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (11678:7): [True: 2.44k, False: 162]
  ------------------
11679|  2.44k|    return std::nullopt;
11680|  2.44k|  }
11681|    162|  std::string_view hash = input;
11682|    162|  hash.remove_prefix(location_of_first + 1);
11683|    162|  input.remove_suffix(input.size() - location_of_first);
11684|    162|  return hash;
11685|  2.60k|}
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12612|     84|find_authority_delimiter_special(std::string_view view) noexcept {
12613|       |  // performance note: we might be able to gain further performance
12614|       |  // with SIMD intrinsics.
12615|  1.00k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (12615:33): [True: 1.00k, False: 0]
  ------------------
12616|  1.00k|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (12616:9): [True: 84, False: 924]
  ------------------
12617|     84|      return pos - view.begin();
12618|     84|    }
12619|  1.00k|  }
12620|      0|  return size_t(view.size());
12621|     84|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12325|  2.60k|    const bool is_special, std::string_view& view) noexcept {
12326|       |  /**
12327|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
12328|       |   * compute a variable called insideBrackets but this variable is only used
12329|       |   * once, to check whether a ':' character was found outside brackets. Exact
12330|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
12331|       |   * It is conceptually simpler and arguably more efficient to just return a
12332|       |   * Boolean indicating whether ':' was found outside brackets.
12333|       |   */
12334|  2.60k|  const size_t view_size = view.size();
12335|  2.60k|  size_t location = 0;
12336|  2.60k|  bool found_colon = false;
12337|       |  /**
12338|       |   * Performance analysis:
12339|       |   *
12340|       |   * We are basically seeking the end of the hostname which can be indicated
12341|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
12342|       |   * (where '\\' is only applicable for special URLs). However, these must
12343|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
12344|       |   * '?' does not count.
12345|       |   *
12346|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
12347|       |   * the set of delimiters, and that we handle it first.
12348|       |   *
12349|       |   * So the trick is to have a fast function that locates the next delimiter.
12350|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
12351|       |   * function would be provided by the C++ standard library, but it seems that
12352|       |   * find_first_of is not very fast, so we are forced to roll our own.
12353|       |   *
12354|       |   * We do not break into two loops for speed, but for clarity.
12355|       |   */
12356|  2.60k|  if (is_special) {
  ------------------
  |  Branch (12356:7): [True: 2.60k, False: 0]
  ------------------
12357|       |    // We move to the next delimiter.
12358|  2.60k|    location = find_next_host_delimiter_special(view, location);
12359|       |    // Unless we find '[' then we are going only going to have to call
12360|       |    // find_next_host_delimiter_special once.
12361|  2.60k|    for (; location < view_size;
  ------------------
  |  Branch (12361:12): [True: 2.60k, False: 0]
  ------------------
12362|  2.60k|         location = find_next_host_delimiter_special(view, location)) {
12363|  2.60k|      if (view[location] == '[') {
  ------------------
  |  Branch (12363:11): [True: 0, False: 2.60k]
  ------------------
12364|      0|        location = view.find(']', location);
12365|      0|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (12365:13): [True: 0, False: 0]
  ------------------
12366|       |          // performance: view.find might get translated to a memchr, which
12367|       |          // has no notion of std::string_view::npos, so the code does not
12368|       |          // reflect the assembly.
12369|      0|          location = view_size;
12370|      0|          break;
12371|      0|        }
12372|  2.60k|      } else {
12373|  2.60k|        found_colon = view[location] == ':';
12374|  2.60k|        break;
12375|  2.60k|      }
12376|  2.60k|    }
12377|  2.60k|  } else {
12378|       |    // We move to the next delimiter.
12379|      0|    location = find_next_host_delimiter(view, location);
12380|       |    // Unless we find '[' then we are going only going to have to call
12381|       |    // find_next_host_delimiter_special once.
12382|      0|    for (; location < view_size;
  ------------------
  |  Branch (12382:12): [True: 0, False: 0]
  ------------------
12383|      0|         location = find_next_host_delimiter(view, location)) {
12384|      0|      if (view[location] == '[') {
  ------------------
  |  Branch (12384:11): [True: 0, False: 0]
  ------------------
12385|      0|        location = view.find(']', location);
12386|      0|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (12386:13): [True: 0, False: 0]
  ------------------
12387|       |          // performance: view.find might get translated to a memchr, which
12388|       |          // has no notion of std::string_view::npos, so the code does not
12389|       |          // reflect the assembly.
12390|      0|          location = view_size;
12391|      0|          break;
12392|      0|        }
12393|      0|      } else {
12394|      0|        found_colon = view[location] == ':';
12395|      0|        break;
12396|      0|      }
12397|      0|    }
12398|      0|  }
12399|       |  // performance: remove_suffix may translate into a single instruction.
12400|  2.60k|  view.remove_suffix(view_size - location);
12401|  2.60k|  return {location, found_colon};
12402|  2.60k|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
11902|  2.60k|    std::string_view view, size_t location) noexcept {
11903|       |  // first check for short strings in which case we do it naively.
11904|  2.60k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (11904:7): [True: 1.68k, False: 927]
  ------------------
11905|  20.1k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (11905:31): [True: 20.1k, False: 0]
  ------------------
11906|  20.1k|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (11906:11): [True: 0, False: 20.1k]
  |  Branch (11906:29): [True: 1.68k, False: 18.5k]
  |  Branch (11906:47): [True: 0, False: 18.5k]
  ------------------
11907|  18.5k|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (11907:11): [True: 0, False: 18.5k]
  |  Branch (11907:29): [True: 0, False: 18.5k]
  ------------------
11908|  1.68k|        return i;
11909|  1.68k|      }
11910|  20.1k|    }
11911|      0|    return size_t(view.size());
11912|  1.68k|  }
11913|       |  // fast path for long strings (expected to be common)
11914|    927|  size_t i = location;
11915|    927|  const __m128i mask1 = _mm_set1_epi8(':');
11916|    927|  const __m128i mask2 = _mm_set1_epi8('/');
11917|    927|  const __m128i mask3 = _mm_set1_epi8('\\');
11918|    927|  const __m128i mask4 = _mm_set1_epi8('?');
11919|    927|  const __m128i mask5 = _mm_set1_epi8('[');
11920|       |
11921|    927|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (11921:10): [True: 927, False: 0]
  ------------------
11922|    927|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
11923|    927|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
11924|    927|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
11925|    927|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
11926|    927|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
11927|    927|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
11928|    927|    __m128i m = _mm_or_si128(
11929|    927|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
11930|    927|    int mask = _mm_movemask_epi8(m);
11931|    927|    if (mask != 0) {
  ------------------
  |  Branch (11931:9): [True: 927, False: 0]
  ------------------
11932|    927|      return i + trailing_zeroes(mask);
11933|    927|    }
11934|    927|  }
11935|      0|  if (i < view.size()) {
  ------------------
  |  Branch (11935:7): [True: 0, False: 0]
  ------------------
11936|      0|    __m128i word =
11937|      0|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
11938|      0|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
11939|      0|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
11940|      0|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
11941|      0|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
11942|      0|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
11943|      0|    __m128i m = _mm_or_si128(
11944|      0|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
11945|      0|    int mask = _mm_movemask_epi8(m);
11946|      0|    if (mask != 0) {
  ------------------
  |  Branch (11946:9): [True: 0, False: 0]
  ------------------
11947|      0|      return view.length() - 16 + trailing_zeroes(mask);
11948|      0|    }
11949|      0|  }
11950|      0|  return size_t(view.length());
11951|      0|}
_ZN3ada7helpers15trailing_zeroesEj:
11755|    927|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
11756|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
11757|       |  unsigned long ret;
11758|       |  // Search the mask data from least significant bit (LSB)
11759|       |  // to the most significant bit (MSB) for a set bit (1).
11760|       |  _BitScanForward(&ret, input_num);
11761|       |  return (int)ret;
11762|       |#else   // ADA_REGULAR_VISUAL_STUDIO
11763|    927|  return __builtin_ctzl(input_num);
11764|    927|#endif  // ADA_REGULAR_VISUAL_STUDIO
11765|    927|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   12|  2.60k|ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept {
   13|       |  // The string is not empty and does not contain upper case ASCII characters.
   14|       |  //
   15|       |  // Optimization. To be considered as a possible ipv4, the string must end
   16|       |  // with 'x' or a lowercase hex character.
   17|       |  // Most of the time, this will be false so this simple check will save a lot
   18|       |  // of effort.
   19|       |  // If the address ends with a dot, we need to prune it (special case).
   20|  2.60k|  if (view.ends_with('.')) {
  ------------------
  |  Branch (20:7): [True: 0, False: 2.60k]
  ------------------
   21|      0|    view.remove_suffix(1);
   22|      0|    if (view.empty()) {
  ------------------
  |  Branch (22:9): [True: 0, False: 0]
  ------------------
   23|      0|      return false;
   24|      0|    }
   25|      0|  }
   26|  2.60k|  char last_char = view.back();
   27|  2.60k|  bool possible_ipv4 = (last_char >= '0' && last_char <= '9') ||
  ------------------
  |  Branch (27:25): [True: 2.60k, False: 0]
  |  Branch (27:45): [True: 0, False: 2.60k]
  ------------------
   28|  2.60k|                       (last_char >= 'a' && last_char <= 'f') ||
  ------------------
  |  Branch (28:25): [True: 2.60k, False: 0]
  |  Branch (28:45): [True: 0, False: 2.60k]
  ------------------
   29|  2.60k|                       last_char == 'x';
  ------------------
  |  Branch (29:24): [True: 0, False: 2.60k]
  ------------------
   30|  2.60k|  if (!possible_ipv4) {
  ------------------
  |  Branch (30:7): [True: 2.60k, False: 0]
  ------------------
   31|  2.60k|    return false;
   32|  2.60k|  }
   33|       |  // From the last character, find the last dot.
   34|      0|  size_t last_dot = view.rfind('.');
   35|      0|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (35:7): [True: 0, False: 0]
  ------------------
   36|       |    // We have at least one dot.
   37|      0|    view.remove_prefix(last_dot + 1);
   38|      0|  }
   39|       |  /** Optimization opportunity: we have basically identified the last number of
   40|       |     the ipv4 if we return true here. We might as well parse it and have at
   41|       |     least one number parsed when we get to parse_ipv4. */
   42|      0|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (42:7): [True: 0, False: 0]
  ------------------
   43|      0|    return true;
   44|      0|  }
   45|       |  // It could be hex (0x), but not if there is a single character.
   46|      0|  if (view.size() == 1) {
  ------------------
  |  Branch (46:7): [True: 0, False: 0]
  ------------------
   47|      0|    return false;
   48|      0|  }
   49|       |  // It must start with 0x.
   50|      0|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (50:7): [True: 0, False: 0]
  ------------------
   51|      0|    return false;
   52|      0|  }
   53|       |  // We must allow "0x".
   54|      0|  if (view.size() == 2) {
  ------------------
  |  Branch (54:7): [True: 0, False: 0]
  ------------------
   55|      0|    return true;
   56|      0|  }
   57|       |  // We have 0x followed by some characters, we need to check that they are
   58|       |  // hexadecimals.
   59|      0|  view.remove_prefix(2);
   60|      0|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   61|      0|}
_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
11078|  4.00k|                    std::string& out) {
11079|  4.00k|  ada_log("percent_encode ", input, " to output string while ",
11080|  4.00k|          append ? "appending" : "overwriting");
11081|  4.00k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11082|  4.00k|    return character_sets::bit_at(character_set, c);
11083|  4.00k|  });
11084|  4.00k|  ada_log("percent_encode done checking, moved to ",
11085|  4.00k|          std::distance(input.begin(), pointer));
11086|       |
11087|       |  // Optimization: Don't iterate if percent encode is not required
11088|  4.00k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11088:7): [True: 2.99k, False: 1.00k]
  ------------------
11089|  2.99k|    ada_log("percent_encode encoding not needed.");
11090|  2.99k|    return false;
11091|  2.99k|  }
11092|       |  if constexpr (!append) {
11093|       |    out.clear();
11094|       |  }
11095|  1.00k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
11096|  1.00k|          " bytes");
11097|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
11098|  1.00k|  out.append(input.data(), std::distance(input.begin(), pointer));
11099|  1.00k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
11100|  1.00k|          " bytes");
11101|  43.0k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11101:10): [True: 42.0k, False: 1.00k]
  ------------------
11102|  42.0k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11102:9): [True: 32.7k, False: 9.32k]
  ------------------
11103|  32.7k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11104|  32.7k|    } else {
11105|  9.32k|      out += *pointer;
11106|  9.32k|    }
11107|  42.0k|  }
11108|  1.00k|  return true;
11109|  4.00k|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
11081|   115k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11082|   115k|    return character_sets::bit_at(character_set, c);
11083|   115k|  });
url_search_params.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
14693|  2.60k|    ada::url_components& components, uint32_t new_difference) {
14694|  2.60k|  components.username_end += new_difference;
14695|  2.60k|  components.host_start += new_difference;
14696|  2.60k|  components.host_end += new_difference;
14697|  2.60k|  components.pathname_start += new_difference;
14698|  2.60k|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (14698:7): [True: 0, False: 2.60k]
  ------------------
14699|      0|    components.search_start += new_difference;
14700|      0|  }
14701|  2.60k|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (14701:7): [True: 0, False: 2.60k]
  ------------------
14702|      0|    components.hash_start += new_difference;
14703|      0|  }
14704|  2.60k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15149|  2.60k|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
15150|  2.60k|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
15151|  2.60k|          " bytes]");
15152|  2.60k|  ADA_ASSERT_TRUE(validate());
15153|  2.60k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15154|  2.60k|  if (input.empty()) {
  ------------------
  |  Branch (15154:7): [True: 0, False: 2.60k]
  ------------------
15155|      0|    return is_valid = false;
15156|      0|  }  // technically unnecessary.
15157|       |  // If input starts with U+005B ([), then:
15158|  2.60k|  if (input[0] == '[') {
  ------------------
  |  Branch (15158:7): [True: 0, False: 2.60k]
  ------------------
15159|       |    // If input does not end with U+005D (]), validation error, return failure.
15160|      0|    if (input.back() != ']') {
  ------------------
  |  Branch (15160:9): [True: 0, False: 0]
  ------------------
15161|      0|      return is_valid = false;
15162|      0|    }
15163|      0|    ada_log("parse_host ipv6");
15164|       |
15165|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
15166|       |    // trailing U+005D (]) removed.
15167|      0|    input.remove_prefix(1);
15168|      0|    input.remove_suffix(1);
15169|      0|    return parse_ipv6(input);
15170|      0|  }
15171|       |
15172|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
15173|       |  // input.
15174|  2.60k|  if (!is_special()) {
  ------------------
  |  Branch (15174:7): [True: 0, False: 2.60k]
  ------------------
15175|      0|    return parse_opaque_host(input);
15176|      0|  }
15177|       |  // Let domain be the result of running UTF-8 decode without BOM on the
15178|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
15179|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
15180|       |  // which case we do not need to call the expensive 'to_ascii' if a few
15181|       |  // conditions are met: no '%' and no 'xn-' subsequence.
15182|       |
15183|       |  // Often, the input does not contain any forbidden code points, and no upper
15184|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
15185|       |  // optimize for such a common case.
15186|       |
15187|       |  // Fast path: try to parse as pure decimal IPv4(a.b.c.d) first.
15188|  2.60k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
15189|  2.60k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (15189:7): [True: 0, False: 2.60k]
  ------------------
15190|       |    // Fast path succeeded - input is pure decimal IPv4
15191|      0|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (15191:9): [True: 0, False: 0]
  |  Branch (15191:27): [True: 0, False: 0]
  ------------------
15192|      0|      update_base_hostname(input.substr(0, input.size() - 1));
15193|      0|    } else {
15194|      0|      update_base_hostname(input);
15195|      0|    }
15196|      0|    host_type = IPV4;
15197|      0|    is_valid = true;
15198|      0|    ada_log("parse_host fast path decimal ipv4");
15199|      0|    ADA_ASSERT_TRUE(validate());
15200|      0|    return true;
15201|      0|  }
15202|  2.60k|  uint8_t is_forbidden_or_upper =
15203|  2.60k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
15204|  2.60k|                                                             input.size());
15205|       |  // Minor optimization opportunity:
15206|       |  // contains_forbidden_domain_code_point_or_upper could be extend to check for
15207|       |  // the presence of characters that cannot appear in the ipv4 address and we
15208|       |  // could also check whether x and n and - are present, and so we could skip
15209|       |  // some of the checks below. However, the gains are likely to be small, and
15210|       |  // the code would be more complex.
15211|  2.60k|  if (is_forbidden_or_upper == 0 &&
  ------------------
  |  Branch (15211:7): [True: 2.60k, False: 0]
  ------------------
15212|  2.60k|      input.find("xn-") == std::string_view::npos) {
  ------------------
  |  Branch (15212:7): [True: 2.60k, False: 0]
  ------------------
15213|       |    // fast path
15214|  2.60k|    update_base_hostname(input);
15215|       |
15216|       |    // Check for other IPv4 formats (hex, octal, etc.)
15217|  2.60k|    if (checkers::is_ipv4(get_hostname())) {
  ------------------
  |  Branch (15217:9): [True: 0, False: 2.60k]
  ------------------
15218|      0|      ada_log("parse_host fast path ipv4");
15219|      0|      return parse_ipv4(get_hostname(), true);
15220|      0|    }
15221|  2.60k|    ada_log("parse_host fast path ", get_hostname());
15222|  2.60k|    is_valid = true;
15223|  2.60k|    return true;
15224|  2.60k|  }
15225|       |  // We have encountered at least one forbidden code point or the input contains
15226|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
15227|       |  // conversion.
15228|       |
15229|      0|  ada_log("parse_host calling to_ascii");
15230|      0|  std::optional<std::string> host = std::string(get_hostname());
15231|      0|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
15232|      0|  if (!is_valid) {
  ------------------
  |  Branch (15232:7): [True: 0, False: 0]
  ------------------
15233|      0|    ada_log("parse_host to_ascii returns false");
15234|      0|    return is_valid = false;
15235|      0|  }
15236|      0|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
15237|      0|          " bytes]");
15238|       |
15239|      0|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (15239:7): [True: 0, False: 0]
  ------------------
15240|      0|                          ada::unicode::is_forbidden_domain_code_point)) {
15241|      0|    return is_valid = false;
15242|      0|  }
15243|       |
15244|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
15245|       |  // asciiDomain.
15246|      0|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (15246:7): [True: 0, False: 0]
  ------------------
15247|      0|    ada_log("parse_host got ipv4 ", *host);
15248|      0|    return parse_ipv4(host.value(), false);
15249|      0|  }
15250|       |
15251|      0|  update_base_hostname(host.value());
15252|      0|  ADA_ASSERT_TRUE(validate());
15253|      0|  return true;
15254|      0|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
10886|  2.60k|                                              size_t length) noexcept {
10887|  2.60k|  size_t i = 0;
10888|  2.60k|  uint8_t accumulator{};
10889|  7.82k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (10889:10): [True: 5.21k, False: 2.60k]
  ------------------
10890|  5.21k|    accumulator |=
10891|  5.21k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
10892|  5.21k|    accumulator |=
10893|  5.21k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
10894|  5.21k|    accumulator |=
10895|  5.21k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
10896|  5.21k|    accumulator |=
10897|  5.21k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
10898|  5.21k|  }
10899|  10.4k|  for (; i < length; i++) {
  ------------------
  |  Branch (10899:10): [True: 7.82k, False: 2.60k]
  ------------------
10900|  7.82k|    accumulator |=
10901|  7.82k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
10902|  7.82k|  }
10903|  2.60k|  return accumulator;
10904|  2.60k|}
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
16176|  2.60k|inline void url_aggregator::consume_prepared_path(std::string_view input) {
16177|  2.60k|  ada_log("url_aggregator::consume_prepared_path ", input);
16178|       |  /***
16179|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
16180|       |   * unfortunate. This particular function is nearly identical, except that it
16181|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
16182|       |   * very common) merely appends to the buffer. This is the same trivial path as
16183|       |   * with helpers::parse_prepared_path, except that we have the additional check
16184|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
16185|       |   * modify it, and then insert it back into the buffer.
16186|       |   */
16187|  2.60k|  uint8_t accumulator = checkers::path_signature(input);
16188|       |  // Let us first detect a trivial case.
16189|       |  // If it is special, we check that we have no dot, no %,  no \ and no
16190|       |  // character needing percent encoding. Otherwise, we check that we have no %,
16191|       |  // no dot, and no character needing percent encoding.
16192|  2.60k|  constexpr uint8_t need_encoding = 1;
16193|  2.60k|  constexpr uint8_t backslash_char = 2;
16194|  2.60k|  constexpr uint8_t dot_char = 4;
16195|  2.60k|  constexpr uint8_t percent_char = 8;
16196|  2.60k|  bool special = type != ada::scheme::NOT_SPECIAL;
16197|  2.60k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (16197:39): [True: 0, False: 2.60k]
  ------------------
16198|      0|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (16198:39): [True: 0, False: 0]
  ------------------
16199|  2.60k|  bool trivial_path =
16200|  2.60k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (16200:7): [True: 2.60k, False: 0]
  |  Branch (16200:8): [True: 2.60k, False: 0]
  ------------------
16201|  2.60k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
16202|      0|                  0)) &&
16203|  2.60k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (16203:7): [True: 2.60k, False: 0]
  ------------------
16204|  2.60k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (16204:7): [True: 0, False: 2.60k]
  |  Branch (16204:34): [True: 0, False: 0]
  ------------------
16205|       |    // '4' means that we have at least one dot, but nothing that requires
16206|       |    // percent encoding or decoding. The only part that is not trivial is
16207|       |    // that we may have single dots and double dots path segments.
16208|       |    // If we have such segments, then we either have a path that begins
16209|       |    // with '.' (easy to check), or we have the sequence './'.
16210|       |    // Note: input cannot be empty, it must at least contain one character ('.')
16211|       |    // Note: we know that '\' is not present.
16212|      0|    if (input[0] != '.') {
  ------------------
  |  Branch (16212:9): [True: 0, False: 0]
  ------------------
16213|      0|      size_t slashdot = 0;
16214|      0|      bool dot_is_file = true;
16215|      0|      for (;;) {
16216|      0|        slashdot = input.find("/.", slashdot);
16217|      0|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (16217:13): [True: 0, False: 0]
  ------------------
16218|      0|          break;
16219|      0|        } else {  // uncommon
16220|       |          // only three cases matter: /./, /.. or a final /
16221|      0|          slashdot += 2;
16222|      0|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (16222:28): [True: 0, False: 0]
  |  Branch (16222:56): [True: 0, False: 0]
  ------------------
16223|      0|                           input[slashdot] == '/');
  ------------------
  |  Branch (16223:28): [True: 0, False: 0]
  ------------------
16224|      0|        }
16225|      0|      }
16226|      0|      trivial_path = dot_is_file;
16227|      0|    }
16228|      0|  }
16229|  2.60k|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (16229:7): [True: 2.60k, False: 0]
  |  Branch (16229:23): [True: 2.60k, False: 0]
  ------------------
16230|  2.60k|    ada_log("parse_path trivial");
16231|  2.60k|    buffer += '/';
16232|  2.60k|    buffer += input;
16233|  2.60k|    return;
16234|  2.60k|  }
16235|      0|  std::string path = std::string(get_pathname());
16236|       |  // We are going to need to look a bit at the path, but let us see if we can
16237|       |  // ignore percent encoding *and* backslashes *and* percent characters.
16238|       |  // Except for the trivial case, this is likely to capture 99% of paths out
16239|       |  // there.
16240|      0|  bool fast_path =
16241|      0|      (special &&
  ------------------
  |  Branch (16241:8): [True: 0, False: 0]
  ------------------
16242|      0|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (16242:8): [True: 0, False: 0]
  ------------------
16243|      0|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (16243:7): [True: 0, False: 0]
  ------------------
16244|      0|  if (fast_path) {
  ------------------
  |  Branch (16244:7): [True: 0, False: 0]
  ------------------
16245|      0|    ada_log("parse_prepared_path fast");
16246|       |    // Here we don't need to worry about \ or percent encoding.
16247|       |    // We also do not have a file protocol. We might have dots, however,
16248|       |    // but dots must as appear as '.', and they cannot be encoded because
16249|       |    // the symbol '%' is not present.
16250|      0|    size_t previous_location = 0;  // We start at 0.
16251|      0|    do {
16252|      0|      size_t new_location = input.find('/', previous_location);
16253|       |      // std::string_view path_view = input;
16254|       |      //  We process the last segment separately:
16255|      0|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (16255:11): [True: 0, False: 0]
  ------------------
16256|      0|        std::string_view path_view = input.substr(previous_location);
16257|      0|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (16257:13): [True: 0, False: 0]
  ------------------
16258|       |          // e.g., if you receive ".." with an empty path, you go to "/".
16259|      0|          if (path.empty()) {
  ------------------
  |  Branch (16259:15): [True: 0, False: 0]
  ------------------
16260|      0|            path = '/';
16261|      0|            update_base_pathname(path);
16262|      0|            return;
16263|      0|          }
16264|       |          // Fast case where we have nothing to do:
16265|      0|          if (path.back() == '/') {
  ------------------
  |  Branch (16265:15): [True: 0, False: 0]
  ------------------
16266|      0|            update_base_pathname(path);
16267|      0|            return;
16268|      0|          }
16269|       |          // If you have the path "/joe/myfriend",
16270|       |          // then you delete 'myfriend'.
16271|      0|          path.resize(path.rfind('/') + 1);
16272|      0|          update_base_pathname(path);
16273|      0|          return;
16274|      0|        }
16275|      0|        path += '/';
16276|      0|        if (path_view != ".") {
  ------------------
  |  Branch (16276:13): [True: 0, False: 0]
  ------------------
16277|      0|          path.append(path_view);
16278|      0|        }
16279|      0|        update_base_pathname(path);
16280|      0|        return;
16281|      0|      } else {
16282|       |        // This is a non-final segment.
16283|      0|        std::string_view path_view =
16284|      0|            input.substr(previous_location, new_location - previous_location);
16285|      0|        previous_location = new_location + 1;
16286|      0|        if (path_view == "..") {
  ------------------
  |  Branch (16286:13): [True: 0, False: 0]
  ------------------
16287|      0|          size_t last_delimiter = path.rfind('/');
16288|      0|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (16288:15): [True: 0, False: 0]
  ------------------
16289|      0|            path.erase(last_delimiter);
16290|      0|          }
16291|      0|        } else if (path_view != ".") {
  ------------------
  |  Branch (16291:20): [True: 0, False: 0]
  ------------------
16292|      0|          path += '/';
16293|      0|          path.append(path_view);
16294|      0|        }
16295|      0|      }
16296|      0|    } while (true);
  ------------------
  |  Branch (16296:14): [True: 0, Folded]
  ------------------
16297|      0|  } else {
16298|      0|    ada_log("parse_path slow");
16299|       |    // we have reached the general case
16300|      0|    bool needs_percent_encoding = (accumulator & 1);
16301|      0|    std::string path_buffer_tmp;
16302|      0|    do {
16303|      0|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (16303:26): [True: 0, False: 0]
  |  Branch (16303:37): [True: 0, False: 0]
  ------------------
16304|      0|                            ? input.find_first_of("/\\")
16305|      0|                            : input.find('/');
16306|      0|      std::string_view path_view = input;
16307|      0|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (16307:11): [True: 0, False: 0]
  ------------------
16308|      0|        path_view.remove_suffix(path_view.size() - location);
16309|      0|        input.remove_prefix(location + 1);
16310|      0|      }
16311|       |      // path_buffer is either path_view or it might point at a percent encoded
16312|       |      // temporary string.
16313|      0|      std::string_view path_buffer =
16314|      0|          (needs_percent_encoding &&
  ------------------
  |  Branch (16314:12): [True: 0, False: 0]
  ------------------
16315|      0|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (16315:12): [True: 0, False: 0]
  ------------------
16316|      0|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
16317|      0|              ? path_buffer_tmp
16318|      0|              : path_view;
16319|      0|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (16319:11): [True: 0, False: 0]
  ------------------
16320|      0|        helpers::shorten_path(path, type);
16321|      0|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (16321:13): [True: 0, False: 0]
  ------------------
16322|      0|          path += '/';
16323|      0|        }
16324|      0|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (16324:18): [True: 0, False: 0]
  ------------------
16325|      0|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (16325:18): [True: 0, False: 0]
  ------------------
16326|      0|        path += '/';
16327|      0|      }
16328|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
16329|      0|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (16329:16): [True: 0, False: 0]
  ------------------
16330|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
16331|       |        // Windows drive letter, then replace the second code point in
16332|       |        // path_buffer with U+003A (:).
16333|      0|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (16333:13): [True: 0, False: 0]
  |  Branch (16333:48): [True: 0, False: 0]
  ------------------
16334|      0|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (16334:13): [True: 0, False: 0]
  ------------------
16335|      0|          path += '/';
16336|      0|          path += path_buffer[0];
16337|      0|          path += ':';
16338|      0|          path_buffer.remove_prefix(2);
16339|      0|          path.append(path_buffer);
16340|      0|        } else {
16341|       |          // Append path_buffer to url's path.
16342|      0|          path += '/';
16343|      0|          path.append(path_buffer);
16344|      0|        }
16345|      0|      }
16346|      0|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (16346:11): [True: 0, False: 0]
  ------------------
16347|      0|        update_base_pathname(path);
16348|      0|        return;
16349|      0|      }
16350|      0|    } while (true);
  ------------------
  |  Branch (16350:14): [True: 0, Folded]
  ------------------
16351|      0|  }
16352|      0|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
14711|  2.60k|    const std::string_view input_with_colon) {
14712|  2.60k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
14713|  2.60k|  ADA_ASSERT_TRUE(validate());
14714|  2.60k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
14715|  2.60k|  std::string_view input{input_with_colon};
14716|  2.60k|  input.remove_suffix(1);
14717|  2.60k|  auto parsed_type = ada::scheme::get_scheme_type(input);
14718|  2.60k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
14719|       |  /**
14720|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
14721|       |   *http, https), in which case, we can go really fast.
14722|       |   **/
14723|  2.60k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (14723:7): [True: 2.60k, False: 0]
  ------------------
14724|       |    if constexpr (has_state_override) {
14725|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
14726|       |      // then return.
14727|       |      if (is_special() != is_input_special) {
14728|       |        return false;
14729|       |      }
14730|       |
14731|       |      // If url includes credentials or has a non-null port, and buffer is
14732|       |      // "file", then return.
14733|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14734|       |          parsed_type == ada::scheme::type::FILE) {
14735|       |        return false;
14736|       |      }
14737|       |
14738|       |      // If url's scheme is "file" and its host is an empty host, then return.
14739|       |      // An empty host is the empty string.
14740|       |      if (type == ada::scheme::type::FILE &&
14741|       |          components.host_start == components.host_end) {
14742|       |        return false;
14743|       |      }
14744|       |    }
14745|       |
14746|  2.60k|    type = parsed_type;
14747|  2.60k|    set_scheme_from_view_with_colon(input_with_colon);
14748|       |
14749|       |    if constexpr (has_state_override) {
14750|       |      // This is uncommon.
14751|       |      uint16_t urls_scheme_port = get_special_port();
14752|       |
14753|       |      // If url's port is url's scheme's default port, then set url's port to
14754|       |      // null.
14755|       |      if (components.port == urls_scheme_port) {
14756|       |        clear_port();
14757|       |      }
14758|       |    }
14759|  2.60k|  } else {  // slow path
14760|      0|    std::string _buffer(input);
14761|       |    // Next function is only valid if the input is ASCII and returns false
14762|       |    // otherwise, but it seems that we always have ascii content so we do not
14763|       |    // need to check the return value.
14764|      0|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
14765|       |
14766|       |    if constexpr (has_state_override) {
14767|       |      // The state-override validation errors below ("return" in the WHATWG URL
14768|       |      // parser) leave the URL unchanged. The setter contract is
14769|       |      // "true on success, false if the scheme is invalid" -- the fast path
14770|       |      // above already returns false here, so the slow path must agree.
14771|       |
14772|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
14773|       |      // then return. If url's scheme is not a special scheme and buffer is a
14774|       |      // special scheme, then return.
14775|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
14776|       |        return false;
14777|       |      }
14778|       |
14779|       |      // If url includes credentials or has a non-null port, and buffer is
14780|       |      // "file", then return.
14781|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14782|       |          _buffer == "file") {
14783|       |        return false;
14784|       |      }
14785|       |
14786|       |      // If url's scheme is "file" and its host is an empty host, then return.
14787|       |      // An empty host is the empty string.
14788|       |      if (type == ada::scheme::type::FILE &&
14789|       |          components.host_start == components.host_end) {
14790|       |        return false;
14791|       |      }
14792|       |    }
14793|       |
14794|      0|    set_scheme(_buffer);
14795|       |
14796|       |    if constexpr (has_state_override) {
14797|       |      // This is uncommon.
14798|       |      uint16_t urls_scheme_port = get_special_port();
14799|       |
14800|       |      // If url's port is url's scheme's default port, then set url's port to
14801|       |      // null.
14802|       |      if (components.port == urls_scheme_port) {
14803|       |        clear_port();
14804|       |      }
14805|       |    }
14806|      0|  }
14807|  2.60k|  ADA_ASSERT_TRUE(validate());
14808|  2.60k|  return true;
14809|  2.60k|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14833|  2.60k|    std::string_view new_scheme_with_colon) {
14834|  2.60k|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
14835|  2.60k|          new_scheme_with_colon);
14836|  2.60k|  ADA_ASSERT_TRUE(validate());
14837|  2.60k|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
14838|  2.60k|                  new_scheme_with_colon.back() == ':');
14839|       |  // next line could overflow but unsigned arithmetic has well-defined
14840|       |  // overflows.
14841|  2.60k|  uint32_t new_difference =
14842|  2.60k|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
14843|       |
14844|  2.60k|  if (buffer.empty()) {
  ------------------
  |  Branch (14844:7): [True: 2.60k, False: 0]
  ------------------
14845|  2.60k|    buffer.append(new_scheme_with_colon);
14846|  2.60k|  } else {
14847|      0|    buffer.erase(0, components.protocol_end);
14848|      0|    buffer.insert(0, new_scheme_with_colon);
14849|      0|  }
14850|  2.60k|  components.protocol_end += new_difference;
14851|       |
14852|  2.60k|  apply_shifted_non_scheme_offsets(components, new_difference);
14853|  2.60k|  ADA_ASSERT_TRUE(validate());
14854|  2.60k|}
_ZN3ada7helpers38strip_trailing_spaces_from_opaque_pathINS_14url_aggregatorEEEvRT_:
12588|  1.26k|ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url) {
12589|  1.26k|  ada_log("helpers::strip_trailing_spaces_from_opaque_path");
12590|  1.26k|  if (!url.has_opaque_path) return;
  ------------------
  |  Branch (12590:7): [True: 1.26k, False: 0]
  ------------------
12591|      0|  if (url.has_hash()) return;
  ------------------
  |  Branch (12591:7): [True: 0, False: 0]
  ------------------
12592|      0|  if (url.has_search()) return;
  ------------------
  |  Branch (12592:7): [True: 0, False: 0]
  ------------------
12593|       |
12594|      0|  auto path = std::string(url.get_pathname());
12595|      0|  while (!path.empty() && path.back() == ' ') {
  ------------------
  |  Branch (12595:10): [True: 0, False: 0]
  |  Branch (12595:27): [True: 0, False: 0]
  ------------------
12596|      0|    path.resize(path.size() - 1);
12597|      0|  }
12598|      0|  url.update_base_pathname(path);
12599|      0|}

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5559|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1031|   710k|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1032|   710k|  return !!(a[i >> 3] & (1 << (i & 7)));
 1033|   710k|}
_ZN3ada8url_baseD2Ev:
 1515|  6.56k|  virtual ~url_base() = default;
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3101|  2.60k|  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_:
 3583|  2.60k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3456|  2.60k|      : impl_base(in_place, std::forward<Args>(args)...),
 3457|  2.60k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2471|  2.60k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorC2EOS0_:
 7509|  2.60k|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZN3ada14url_aggregatorD2Ev:
 7512|  6.56k|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6590|  2.60k|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6591|  2.60k|  if (scheme.empty()) {
  ------------------
  |  Branch (6591:7): [True: 0, False: 2.60k]
  ------------------
 6592|      0|    return ada::scheme::NOT_SPECIAL;
 6593|      0|  }
 6594|  2.60k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6595|  2.60k|  const std::string_view target = details::is_special_list[hash_value];
 6596|  2.60k|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6596:7): [True: 2.60k, False: 0]
  ------------------
 6597|  2.60k|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6597:7): [True: 2.60k, False: 0]
  ------------------
 6598|  2.60k|          details::scheme_keys[hash_value]) {
 6599|  2.60k|    return ada::scheme::type(hash_value);
 6600|  2.60k|  } else {
 6601|      0|    return ada::scheme::NOT_SPECIAL;
 6602|      0|  }
 6603|  2.60k|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6531|  2.60k|inline uint64_t branchless_load5(const char* p, size_t n) {
 6532|  2.60k|  uint64_t input = (uint8_t)p[0];
 6533|  2.60k|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6534|  2.60k|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6535|  2.60k|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6536|  2.60k|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6537|  2.60k|  return input;
 6538|  2.60k|}
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1226|  2.60k|    std::string_view input) noexcept {
 1227|  2.60k|  const char* p = input.data();
 1228|  2.60k|  const char* const pend = p + input.size();
 1229|       |
 1230|  2.60k|  uint32_t ipv4 = 0;
 1231|       |
 1232|  2.60k|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1232:19): [True: 2.60k, False: 0]
  ------------------
 1233|  2.60k|    if (p == pend) {
  ------------------
  |  Branch (1233:9): [True: 0, False: 2.60k]
  ------------------
 1234|      0|      return ipv4_fast_fail;
 1235|      0|    }
 1236|       |
 1237|  2.60k|    uint32_t val;
 1238|  2.60k|    char c = *p;
 1239|  2.60k|    if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1239:9): [True: 2.60k, False: 0]
  |  Branch (1239:21): [True: 0, False: 2.60k]
  ------------------
 1240|      0|      val = c - '0';
 1241|      0|      p++;
 1242|  2.60k|    } else {
 1243|  2.60k|      return ipv4_fast_fail;
 1244|  2.60k|    }
 1245|       |
 1246|      0|    if (p < pend) {
  ------------------
  |  Branch (1246:9): [True: 0, False: 0]
  ------------------
 1247|      0|      c = *p;
 1248|      0|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1248:11): [True: 0, False: 0]
  |  Branch (1248:23): [True: 0, False: 0]
  ------------------
 1249|      0|        if (val == 0) return ipv4_fast_fail;
  ------------------
  |  Branch (1249:13): [True: 0, False: 0]
  ------------------
 1250|      0|        val = val * 10 + (c - '0');
 1251|      0|        p++;
 1252|      0|        if (p < pend) {
  ------------------
  |  Branch (1252:13): [True: 0, False: 0]
  ------------------
 1253|      0|          c = *p;
 1254|      0|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1254:15): [True: 0, False: 0]
  |  Branch (1254:27): [True: 0, False: 0]
  ------------------
 1255|      0|            val = val * 10 + (c - '0');
 1256|      0|            p++;
 1257|      0|            if (val > 255) return ipv4_fast_fail;
  ------------------
  |  Branch (1257:17): [True: 0, False: 0]
  ------------------
 1258|      0|          }
 1259|      0|        }
 1260|      0|      }
 1261|      0|    }
 1262|       |
 1263|      0|    ipv4 = (ipv4 << 8) | val;
 1264|       |
 1265|      0|    if (i < 3) {
  ------------------
  |  Branch (1265:9): [True: 0, False: 0]
  ------------------
 1266|      0|      if (p == pend || *p != '.') {
  ------------------
  |  Branch (1266:11): [True: 0, False: 0]
  |  Branch (1266:24): [True: 0, False: 0]
  ------------------
 1267|      0|        return ipv4_fast_fail;
 1268|      0|      }
 1269|      0|      p++;
 1270|      0|    }
 1271|      0|  }
 1272|       |
 1273|      0|  if (p != pend) {
  ------------------
  |  Branch (1273:7): [True: 0, False: 0]
  ------------------
 1274|      0|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1274:9): [True: 0, False: 0]
  |  Branch (1274:26): [True: 0, False: 0]
  ------------------
 1275|      0|      return ipv4;
 1276|      0|    }
 1277|      0|    return ipv4_fast_fail;
 1278|      0|  }
 1279|       |
 1280|      0|  return ipv4;
 1281|      0|}
_ZN3ada14url_aggregatorC2Ev:
 7507|  2.60k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 4757|  2.60k|  url_components() = default;
_ZNK3ada8url_base10is_specialEv:
 7063|  17.0k|    const noexcept {
 7064|  17.0k|  return type != ada::scheme::NOT_SPECIAL;
 7065|  17.0k|}
_ZN3ada8checkers8is_alphaEc:
 1209|  2.60k|constexpr bool is_alpha(char x) noexcept {
 1210|  2.60k|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1210:10): [True: 2.60k, False: 0]
  |  Branch (1210:34): [True: 2.60k, False: 0]
  ------------------
 1211|  2.60k|}
_ZN3ada8checkers8to_lowerEc:
 1207|  5.21k|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZN3ada7helpers14leading_zeroesEj:
 1800|  2.60k|inline int leading_zeroes(uint32_t input_num) noexcept {
 1801|       |#if ADA_REGULAR_VISUAL_STUDIO
 1802|       |  unsigned long leading_zero(0);
 1803|       |  unsigned long in(input_num);
 1804|       |  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
 1805|       |#else
 1806|  2.60k|  return __builtin_clz(input_num);
 1807|  2.60k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 1808|  2.60k|}
_ZN3ada14url_aggregator7reserveEj:
 8717|  2.60k|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 8718|  2.60k|  buffer.reserve(capacity);
 8719|  2.60k|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8050|  2.60k|    uint32_t start, uint32_t end, std::string_view input) {
 8051|  2.60k|  uint32_t current_length = end - start;
 8052|  2.60k|  uint32_t input_size = uint32_t(input.size());
 8053|  2.60k|  uint32_t new_difference = input_size - current_length;
 8054|       |
 8055|  2.60k|  if (current_length == 0) {
  ------------------
  |  Branch (8055:7): [True: 2.60k, False: 0]
  ------------------
 8056|  2.60k|    buffer.insert(start, input);
 8057|  2.60k|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8057:14): [True: 0, False: 0]
  ------------------
 8058|      0|    buffer.replace(start, input_size, input);
 8059|      0|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8059:14): [True: 0, False: 0]
  ------------------
 8060|      0|    buffer.erase(start, current_length - input_size);
 8061|      0|    buffer.replace(start, input_size, input);
 8062|      0|  } else {
 8063|      0|    buffer.replace(start, current_length, input.substr(0, current_length));
 8064|      0|    buffer.insert(start + current_length, input.substr(current_length));
 8065|      0|  }
 8066|       |
 8067|  2.60k|  return new_difference;
 8068|  2.60k|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8026|    162|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8027|    162|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8028|    162|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8029|    162|          " bytes] components.hash_start = ", components.hash_start);
 8030|    162|  ADA_ASSERT_TRUE(validate());
 8031|    162|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8032|    162|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8032:7): [True: 0, False: 162]
  ------------------
 8033|      0|    buffer.resize(components.hash_start);
 8034|      0|  }
 8035|    162|  components.hash_start = uint32_t(buffer.size());
 8036|    162|  buffer += "#";
 8037|    162|  bool encoding_required = unicode::percent_encode<true>(
 8038|    162|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8039|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8040|       |  // encoding was not deemed required.
 8041|    162|  if (!encoding_required) {
  ------------------
  |  Branch (8041:7): [True: 102, False: 60]
  ------------------
 8042|    102|    buffer.append(input);
 8043|    102|  }
 8044|    162|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8045|    162|          buffer, "' [", buffer.size(), " bytes]");
 8046|    162|  ADA_ASSERT_TRUE(validate());
 8047|    162|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 8691|  2.60k|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 8692|  2.60k|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 8693|  2.60k|  ADA_ASSERT_TRUE(validate());
 8694|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 8695|       |  // to insert
 8696|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 8697|  2.60k|  if (has_authority()) {
  ------------------
  |  Branch (8697:7): [True: 0, False: 2.60k]
  ------------------
 8698|      0|    return;
 8699|      0|  }
 8700|       |  // Performance: the common case is components.protocol_end == buffer.size()
 8701|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 8702|       |  // the insert could be fused with another insert.
 8703|  2.60k|  buffer.insert(components.protocol_end, "//");
 8704|  2.60k|  components.username_end += 2;
 8705|  2.60k|  components.host_start += 2;
 8706|  2.60k|  components.host_end += 2;
 8707|  2.60k|  components.pathname_start += 2;
 8708|  2.60k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8708:7): [True: 0, False: 2.60k]
  ------------------
 8709|      0|    components.search_start += 2;
 8710|      0|  }
 8711|  2.60k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8711:7): [True: 0, False: 2.60k]
  ------------------
 8712|      0|    components.hash_start += 2;
 8713|      0|  }
 8714|  2.60k|  ADA_ASSERT_TRUE(validate());
 8715|  2.60k|}
_ZN3ada14url_aggregator12clear_searchEv:
 8541|  1.26k|inline void url_aggregator::clear_search() {
 8542|  1.26k|  ada_log("url_aggregator::clear_search");
 8543|  1.26k|  ADA_ASSERT_TRUE(validate());
 8544|  1.26k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8544:7): [True: 0, False: 1.26k]
  ------------------
 8545|      0|    return;
 8546|      0|  }
 8547|       |
 8548|  1.26k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8548:7): [True: 1.21k, False: 49]
  ------------------
 8549|  1.21k|    buffer.resize(components.search_start);
 8550|  1.21k|  } else {
 8551|     49|    buffer.erase(components.search_start,
 8552|     49|                 components.hash_start - components.search_start);
 8553|     49|    components.hash_start = components.search_start;
 8554|     49|  }
 8555|       |
 8556|  1.26k|  components.search_start = url_components::omitted;
 8557|       |
 8558|       |#if ADA_DEVELOPMENT_CHECKS
 8559|       |  ADA_ASSERT_EQUAL(get_search(), "",
 8560|       |                   "search should have been cleared on buffer=" + buffer +
 8561|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8562|       |#endif
 8563|  1.26k|  ADA_ASSERT_TRUE(validate());
 8564|  1.26k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8155|  3.95k|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8156|  3.95k|  ada_log("url_aggregator::update_base_search ", input,
 8157|  3.95k|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8158|  3.95k|  ADA_ASSERT_TRUE(validate());
 8159|  3.95k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8160|       |
 8161|  3.95k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8161:7): [True: 3.84k, False: 113]
  ------------------
 8162|  3.84k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8162:9): [True: 2.60k, False: 1.23k]
  ------------------
 8163|  2.60k|      components.search_start = uint32_t(buffer.size());
 8164|  2.60k|      buffer += "?";
 8165|  2.60k|    } else {
 8166|  1.23k|      buffer.resize(components.search_start + 1);
 8167|  1.23k|    }
 8168|       |
 8169|  3.84k|    bool encoding_required =
 8170|  3.84k|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8171|       |    // When encoding_required is false, then buffer is left unchanged, and
 8172|       |    // percent encoding was not deemed required.
 8173|  3.84k|    if (!encoding_required) {
  ------------------
  |  Branch (8173:9): [True: 2.89k, False: 948]
  ------------------
 8174|  2.89k|      buffer.append(input);
 8175|  2.89k|    }
 8176|  3.84k|  } else {
 8177|    113|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8177:9): [True: 0, False: 113]
  ------------------
 8178|      0|      components.search_start = components.hash_start;
 8179|    113|    } else {
 8180|    113|      buffer.erase(components.search_start,
 8181|    113|                   components.hash_start - components.search_start);
 8182|    113|      components.hash_start = components.search_start;
 8183|    113|    }
 8184|       |
 8185|    113|    buffer.insert(components.search_start, "?");
 8186|    113|    size_t idx =
 8187|    113|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8188|    113|    if (idx == input.size()) {
  ------------------
  |  Branch (8188:9): [True: 113, False: 0]
  ------------------
 8189|    113|      buffer.insert(components.search_start + 1, input);
 8190|    113|      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8191|    113|    } else {
 8192|      0|      buffer.insert(components.search_start + 1, input, 0, idx);
 8193|      0|      input.remove_prefix(idx);
 8194|       |      // We only create a temporary string if we need percent encoding and
 8195|       |      // we attempt to create as small a temporary string as we can.
 8196|      0|      std::string encoded =
 8197|      0|          ada::unicode::percent_encode(input, query_percent_encode_set);
 8198|      0|      buffer.insert(components.search_start + idx + 1, encoded);
 8199|      0|      components.hash_start +=
 8200|      0|          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
 8201|      0|    }
 8202|    113|  }
 8203|       |
 8204|  3.95k|  ADA_ASSERT_TRUE(validate());
 8205|  3.95k|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8070|  2.60k|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8071|  2.60k|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8072|  2.60k|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8073|  2.60k|  ADA_ASSERT_TRUE(validate());
 8074|  2.60k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8075|       |
 8076|       |  // This next line is required for when parsing a URL like `foo://`
 8077|  2.60k|  add_authority_slashes_if_needed();
 8078|       |
 8079|  2.60k|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8080|  2.60k|  uint32_t new_difference =
 8081|  2.60k|      replace_and_resize(components.host_start, components.host_end, input);
 8082|       |
 8083|  2.60k|  if (has_credentials) {
  ------------------
  |  Branch (8083:7): [True: 0, False: 2.60k]
  ------------------
 8084|      0|    buffer.insert(components.host_start, "@");
 8085|      0|    new_difference++;
 8086|      0|  }
 8087|  2.60k|  components.host_end += new_difference;
 8088|  2.60k|  components.pathname_start += new_difference;
 8089|  2.60k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8089:7): [True: 0, False: 2.60k]
  ------------------
 8090|      0|    components.search_start += new_difference;
 8091|      0|  }
 8092|  2.60k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8092:7): [True: 0, False: 2.60k]
  ------------------
 8093|      0|    components.hash_start += new_difference;
 8094|      0|  }
 8095|  2.60k|  ADA_ASSERT_TRUE(validate());
 8096|  2.60k|}
_ZN3ada14url_aggregatorC2ERKS0_:
 7508|  1.34k|  url_aggregator(const url_aggregator& u) = default;
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7928|    113|                                              const uint8_t character_set[]) {
 7929|    113|  const char* data = input.data();
 7930|    113|  const size_t size = input.size();
 7931|       |
 7932|       |  // Process 8 bytes at a time using unrolled loop
 7933|    113|  size_t i = 0;
 7934|  2.18k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (7934:10): [True: 2.07k, False: 113]
  ------------------
 7935|  2.07k|    unsigned char chunk[8];
 7936|  2.07k|    std::memcpy(&chunk, data + i,
 7937|  2.07k|                8);  // entices compiler to unconditionally process 8 characters
 7938|       |
 7939|       |    // Check 8 characters at once
 7940|  18.6k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (7940:24): [True: 16.5k, False: 2.07k]
  ------------------
 7941|  16.5k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (7941:11): [True: 0, False: 16.5k]
  ------------------
 7942|      0|        return i + j;
 7943|      0|      }
 7944|  16.5k|    }
 7945|  2.07k|  }
 7946|       |
 7947|       |  // Handle remaining bytes
 7948|    534|  for (; i < size; i++) {
  ------------------
  |  Branch (7948:10): [True: 421, False: 113]
  ------------------
 7949|    421|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (7949:9): [True: 0, False: 421]
  ------------------
 7950|      0|      return i;
 7951|      0|    }
 7952|    421|  }
 7953|       |
 7954|    113|  return size;
 7955|    113|}
_ZNK3ada14url_aggregator13has_authorityEv:
 8682|  2.60k|    const noexcept {
 8683|  2.60k|  ada_log("url_aggregator::has_authority");
 8684|       |  // Performance: instead of doing this potentially expensive check, we could
 8685|       |  // have a boolean in the struct.
 8686|  2.60k|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (8686:10): [True: 0, False: 2.60k]
  ------------------
 8687|      0|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (8687:10): [True: 0, False: 0]
  ------------------
 8688|      0|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (8688:10): [True: 0, False: 0]
  ------------------
 8689|  2.60k|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 3896|  2.60k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 3865|  7.82k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 3866|  7.82k|    TL_ASSERT(has_value());
  ------------------
  |  | 1918|  7.82k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3866:5): [True: 7.82k, False: 0]
  ------------------
 3867|  7.82k|    return valptr();
 3868|  7.82k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3161|  7.82k|  T* valptr() { return std::addressof(this->m_val); }
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 3895|  7.82k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2493|  2.60k|  ~expected_storage_base() {
 2494|  2.60k|    if (m_has_val) {
  ------------------
  |  Branch (2494:9): [True: 2.60k, False: 0]
  ------------------
 2495|  2.60k|      m_val.~T();
 2496|  2.60k|    }
 2497|  2.60k|  }
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1712|  3.99k|                                                       size_t pos2) {
 1713|       |#if ADA_DEVELOPMENT_CHECKS
 1714|       |  if (pos2 < pos1) {
 1715|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1716|       |              << std::endl;
 1717|       |    abort();
 1718|       |  }
 1719|       |#endif
 1720|  3.99k|  return input.substr(pos1, pos2 - pos1);
 1721|  3.99k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8111|  2.60k|    const noexcept {
 8112|  2.60k|  return buffer.size() == components.pathname_start;
 8113|  2.60k|}
_ZN3ada17url_search_paramsC2ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9154|  13.0k|  explicit url_search_params(const std::string_view input) {
 9155|  13.0k|    initialize(input);
 9156|  13.0k|  }
_ZN3ada17url_search_params10initializeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9372|  15.6k|inline void url_search_params::initialize(std::string_view input) {
 9373|  15.6k|  if (!input.empty() && input.front() == '?') {
  ------------------
  |  Branch (9373:7): [True: 5.70k, False: 9.95k]
  |  Branch (9373:25): [True: 17, False: 5.68k]
  ------------------
 9374|     17|    input.remove_prefix(1);
 9375|     17|  }
 9376|       |
 9377|  15.6k|  auto process_key_value = [&](const std::string_view current) {
 9378|  15.6k|    auto equal = current.find('=');
 9379|       |
 9380|  15.6k|    if (equal == std::string_view::npos) {
 9381|  15.6k|      std::string name(current);
 9382|  15.6k|      std::ranges::replace(name, '+', ' ');
 9383|  15.6k|      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
 9384|  15.6k|    } else {
 9385|  15.6k|      std::string name(current.substr(0, equal));
 9386|  15.6k|      std::string value(current.substr(equal + 1));
 9387|       |
 9388|  15.6k|      std::ranges::replace(name, '+', ' ');
 9389|  15.6k|      std::ranges::replace(value, '+', ' ');
 9390|       |
 9391|  15.6k|      params.emplace_back(unicode::percent_decode(name, name.find('%')),
 9392|  15.6k|                          unicode::percent_decode(value, value.find('%')));
 9393|  15.6k|    }
 9394|  15.6k|  };
 9395|       |
 9396|  47.3k|  while (!input.empty()) {
  ------------------
  |  Branch (9396:10): [True: 37.2k, False: 10.0k]
  ------------------
 9397|  37.2k|    auto ampersand_index = input.find('&');
 9398|       |
 9399|  37.2k|    if (ampersand_index == std::string_view::npos) {
  ------------------
  |  Branch (9399:9): [True: 5.56k, False: 31.6k]
  ------------------
 9400|  5.56k|      if (!input.empty()) {
  ------------------
  |  Branch (9400:11): [True: 5.56k, False: 0]
  ------------------
 9401|  5.56k|        process_key_value(input);
 9402|  5.56k|      }
 9403|  5.56k|      break;
 9404|  31.6k|    } else if (ampersand_index != 0) {
  ------------------
  |  Branch (9404:16): [True: 29.0k, False: 2.59k]
  ------------------
 9405|  29.0k|      process_key_value(input.substr(0, ampersand_index));
 9406|  29.0k|    }
 9407|       |
 9408|  31.6k|    input.remove_prefix(ampersand_index + 1);
 9409|  31.6k|  }
 9410|  15.6k|}
_ZZN3ada17url_search_params10initializeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_E_clES5_:
 9377|  34.6k|  auto process_key_value = [&](const std::string_view current) {
 9378|  34.6k|    auto equal = current.find('=');
 9379|       |
 9380|  34.6k|    if (equal == std::string_view::npos) {
  ------------------
  |  Branch (9380:9): [True: 11.9k, False: 22.6k]
  ------------------
 9381|  11.9k|      std::string name(current);
 9382|  11.9k|      std::ranges::replace(name, '+', ' ');
 9383|  11.9k|      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
 9384|  22.6k|    } else {
 9385|  22.6k|      std::string name(current.substr(0, equal));
 9386|  22.6k|      std::string value(current.substr(equal + 1));
 9387|       |
 9388|  22.6k|      std::ranges::replace(name, '+', ' ');
 9389|  22.6k|      std::ranges::replace(value, '+', ' ');
 9390|       |
 9391|  22.6k|      params.emplace_back(unicode::percent_decode(name, name.find('%')),
 9392|  22.6k|                          unicode::percent_decode(value, value.find('%')));
 9393|  22.6k|    }
 9394|  34.6k|  };
_ZN3ada17url_search_paramsD2Ev:
 9162|  26.0k|  ~url_search_params() = default;
_ZNK3ada17url_search_params9to_stringEv:
 9458|  23.4k|inline std::string url_search_params::to_string() const {
 9459|  23.4k|  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
 9460|  23.4k|  std::string out{};
 9461|  81.4k|  for (size_t i = 0; i < params.size(); i++) {
  ------------------
  |  Branch (9461:22): [True: 57.9k, False: 23.4k]
  ------------------
 9462|  57.9k|    auto key = ada::unicode::percent_encode(params[i].first, character_set);
 9463|  57.9k|    auto value = ada::unicode::percent_encode(params[i].second, character_set);
 9464|       |
 9465|       |    // Performance optimization: Move this inside percent_encode.
 9466|  57.9k|    std::ranges::replace(key, ' ', '+');
 9467|  57.9k|    std::ranges::replace(value, ' ', '+');
 9468|       |
 9469|  57.9k|    if (i != 0) {
  ------------------
  |  Branch (9469:9): [True: 45.9k, False: 11.9k]
  ------------------
 9470|  45.9k|      out += "&";
 9471|  45.9k|    }
 9472|  57.9k|    out.append(key);
 9473|  57.9k|    out += "=";
 9474|  57.9k|    out.append(value);
 9475|  57.9k|  }
 9476|  23.4k|  return out;
 9477|  23.4k|}
_ZNK3ada17url_search_params4sizeEv:
 9417|  23.4k|inline size_t url_search_params::size() const noexcept { return params.size(); }
_ZN3ada17url_search_params4sortEv:
 9506|  5.21k|inline void url_search_params::sort() {
 9507|       |  // Keys are expected to be valid UTF-8, but percent_decode can produce
 9508|       |  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
 9509|  5.21k|  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
 9510|  5.21k|                                      const key_value_pair& rhs) {
 9511|  5.21k|    size_t i = 0, j = 0;
 9512|  5.21k|    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
 9513|  5.21k|    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
 9514|  5.21k|           (j < rhs.first.size() || low_surrogate2 != 0)) {
 9515|  5.21k|      uint32_t codePoint1 = 0, codePoint2 = 0;
 9516|       |
 9517|  5.21k|      if (low_surrogate1 != 0) {
 9518|  5.21k|        codePoint1 = low_surrogate1;
 9519|  5.21k|        low_surrogate1 = 0;
 9520|  5.21k|      } else {
 9521|  5.21k|        uint8_t c1 = uint8_t(lhs.first[i]);
 9522|  5.21k|        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
 9523|  5.21k|          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
 9524|  5.21k|          i += 2;
 9525|  5.21k|        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
 9526|  5.21k|          codePoint1 = ((c1 & 0x0F) << 12) |
 9527|  5.21k|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
 9528|  5.21k|                       (uint8_t(lhs.first[i + 2]) & 0x3F);
 9529|  5.21k|          i += 3;
 9530|  5.21k|        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
 9531|  5.21k|          codePoint1 = ((c1 & 0x07) << 18) |
 9532|  5.21k|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
 9533|  5.21k|                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
 9534|  5.21k|                       (uint8_t(lhs.first[i + 3]) & 0x3F);
 9535|  5.21k|          i += 4;
 9536|       |
 9537|  5.21k|          codePoint1 -= 0x10000;
 9538|  5.21k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
 9539|  5.21k|          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
 9540|  5.21k|          codePoint1 = high_surrogate;
 9541|  5.21k|        } else {
 9542|       |          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
 9543|  5.21k|          codePoint1 = c1;
 9544|  5.21k|          i++;
 9545|  5.21k|        }
 9546|  5.21k|      }
 9547|       |
 9548|  5.21k|      if (low_surrogate2 != 0) {
 9549|  5.21k|        codePoint2 = low_surrogate2;
 9550|  5.21k|        low_surrogate2 = 0;
 9551|  5.21k|      } else {
 9552|  5.21k|        uint8_t c2 = uint8_t(rhs.first[j]);
 9553|  5.21k|        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
 9554|  5.21k|          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
 9555|  5.21k|          j += 2;
 9556|  5.21k|        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
 9557|  5.21k|          codePoint2 = ((c2 & 0x0F) << 12) |
 9558|  5.21k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
 9559|  5.21k|                       (uint8_t(rhs.first[j + 2]) & 0x3F);
 9560|  5.21k|          j += 3;
 9561|  5.21k|        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
 9562|  5.21k|          codePoint2 = ((c2 & 0x07) << 18) |
 9563|  5.21k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
 9564|  5.21k|                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
 9565|  5.21k|                       (uint8_t(rhs.first[j + 3]) & 0x3F);
 9566|  5.21k|          j += 4;
 9567|  5.21k|          codePoint2 -= 0x10000;
 9568|  5.21k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
 9569|  5.21k|          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
 9570|  5.21k|          codePoint2 = high_surrogate;
 9571|  5.21k|        } else {
 9572|       |          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
 9573|  5.21k|          codePoint2 = c2;
 9574|  5.21k|          j++;
 9575|  5.21k|        }
 9576|  5.21k|      }
 9577|       |
 9578|  5.21k|      if (codePoint1 != codePoint2) {
 9579|  5.21k|        return (codePoint1 < codePoint2);
 9580|  5.21k|      }
 9581|  5.21k|    }
 9582|  5.21k|    return (j < rhs.first.size() || low_surrogate2 != 0);
 9583|  5.21k|  });
 9584|  5.21k|}
_ZZN3ada17url_search_params4sortEvENKUlRKNSt3__14pairINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_EESB_E_clESB_SB_:
 9510|  39.7k|                                      const key_value_pair& rhs) {
 9511|  39.7k|    size_t i = 0, j = 0;
 9512|  39.7k|    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
 9513|  63.8k|    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
  ------------------
  |  Branch (9513:13): [True: 39.2k, False: 24.6k]
  |  Branch (9513:37): [True: 330, False: 24.2k]
  ------------------
 9514|  39.5k|           (j < rhs.first.size() || low_surrogate2 != 0)) {
  ------------------
  |  Branch (9514:13): [True: 37.3k, False: 2.16k]
  |  Branch (9514:37): [True: 333, False: 1.82k]
  ------------------
 9515|  37.7k|      uint32_t codePoint1 = 0, codePoint2 = 0;
 9516|       |
 9517|  37.7k|      if (low_surrogate1 != 0) {
  ------------------
  |  Branch (9517:11): [True: 977, False: 36.7k]
  ------------------
 9518|    977|        codePoint1 = low_surrogate1;
 9519|    977|        low_surrogate1 = 0;
 9520|  36.7k|      } else {
 9521|  36.7k|        uint8_t c1 = uint8_t(lhs.first[i]);
 9522|  36.7k|        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
  ------------------
  |  Branch (9522:13): [True: 10.2k, False: 26.4k]
  |  Branch (9522:26): [True: 4.27k, False: 5.96k]
  |  Branch (9522:40): [True: 1.48k, False: 2.78k]
  ------------------
 9523|  1.48k|          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
 9524|  1.48k|          i += 2;
 9525|  35.2k|        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
  ------------------
  |  Branch (9525:20): [True: 5.96k, False: 29.2k]
  |  Branch (9525:33): [True: 1.63k, False: 4.32k]
  |  Branch (9525:47): [True: 980, False: 659]
  ------------------
 9526|    980|          codePoint1 = ((c1 & 0x0F) << 12) |
 9527|    980|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
 9528|    980|                       (uint8_t(lhs.first[i + 2]) & 0x3F);
 9529|    980|          i += 3;
 9530|  34.2k|        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
  ------------------
  |  Branch (9530:20): [True: 4.32k, False: 29.9k]
  |  Branch (9530:33): [True: 2.66k, False: 1.66k]
  |  Branch (9530:47): [True: 1.25k, False: 1.40k]
  ------------------
 9531|  1.25k|          codePoint1 = ((c1 & 0x07) << 18) |
 9532|  1.25k|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
 9533|  1.25k|                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
 9534|  1.25k|                       (uint8_t(lhs.first[i + 3]) & 0x3F);
 9535|  1.25k|          i += 4;
 9536|       |
 9537|  1.25k|          codePoint1 -= 0x10000;
 9538|  1.25k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
 9539|  1.25k|          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
 9540|  1.25k|          codePoint1 = high_surrogate;
 9541|  33.0k|        } else {
 9542|       |          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
 9543|  33.0k|          codePoint1 = c1;
 9544|  33.0k|          i++;
 9545|  33.0k|        }
 9546|  36.7k|      }
 9547|       |
 9548|  37.7k|      if (low_surrogate2 != 0) {
  ------------------
  |  Branch (9548:11): [True: 977, False: 36.7k]
  ------------------
 9549|    977|        codePoint2 = low_surrogate2;
 9550|    977|        low_surrogate2 = 0;
 9551|  36.7k|      } else {
 9552|  36.7k|        uint8_t c2 = uint8_t(rhs.first[j]);
 9553|  36.7k|        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
  ------------------
  |  Branch (9553:13): [True: 12.6k, False: 24.1k]
  |  Branch (9553:26): [True: 5.79k, False: 6.81k]
  |  Branch (9553:40): [True: 2.16k, False: 3.63k]
  ------------------
 9554|  2.16k|          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
 9555|  2.16k|          j += 2;
 9556|  34.5k|        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
  ------------------
  |  Branch (9556:20): [True: 6.81k, False: 27.7k]
  |  Branch (9556:33): [True: 1.90k, False: 4.91k]
  |  Branch (9556:47): [True: 1.01k, False: 889]
  ------------------
 9557|  1.01k|          codePoint2 = ((c2 & 0x0F) << 12) |
 9558|  1.01k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
 9559|  1.01k|                       (uint8_t(rhs.first[j + 2]) & 0x3F);
 9560|  1.01k|          j += 3;
 9561|  33.5k|        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
  ------------------
  |  Branch (9561:20): [True: 4.91k, False: 28.6k]
  |  Branch (9561:33): [True: 2.85k, False: 2.06k]
  |  Branch (9561:47): [True: 1.34k, False: 1.50k]
  ------------------
 9562|  1.34k|          codePoint2 = ((c2 & 0x07) << 18) |
 9563|  1.34k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
 9564|  1.34k|                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
 9565|  1.34k|                       (uint8_t(rhs.first[j + 3]) & 0x3F);
 9566|  1.34k|          j += 4;
 9567|  1.34k|          codePoint2 -= 0x10000;
 9568|  1.34k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
 9569|  1.34k|          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
 9570|  1.34k|          codePoint2 = high_surrogate;
 9571|  32.1k|        } else {
 9572|       |          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
 9573|  32.1k|          codePoint2 = c2;
 9574|  32.1k|          j++;
 9575|  32.1k|        }
 9576|  36.7k|      }
 9577|       |
 9578|  37.7k|      if (codePoint1 != codePoint2) {
  ------------------
  |  Branch (9578:11): [True: 13.6k, False: 24.0k]
  ------------------
 9579|  13.6k|        return (codePoint1 < codePoint2);
 9580|  13.6k|      }
 9581|  37.7k|    }
 9582|  26.1k|    return (j < rhs.first.size() || low_surrogate2 != 0);
  ------------------
  |  Branch (9582:13): [True: 5.47k, False: 20.6k]
  |  Branch (9582:37): [True: 11, False: 20.6k]
  ------------------
 9583|  39.7k|  });
_ZN3ada17url_search_params5resetENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9367|  2.60k|inline void url_search_params::reset(std::string_view input) {
 9368|  2.60k|  params.clear();
 9369|  2.60k|  initialize(input);
 9370|  2.60k|}
_ZN3ada17url_search_params6appendENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9413|  14.4k|                                      const std::string_view value) {
 9414|  14.4k|  params.emplace_back(key, value);
 9415|  14.4k|}
_ZN3ada17url_search_params3setENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9480|  2.60k|                                   const std::string_view value) {
 9481|  2.60k|  const auto find = [&key](const auto& param) { return param.first == key; };
 9482|       |
 9483|  2.60k|  auto it = std::ranges::find_if(params, find);
 9484|       |
 9485|  2.60k|  if (it == params.end()) {
  ------------------
  |  Branch (9485:7): [True: 0, False: 2.60k]
  ------------------
 9486|      0|    params.emplace_back(key, value);
 9487|  2.60k|  } else {
 9488|  2.60k|    it->second = value;
 9489|  2.60k|    params.erase(std::remove_if(std::next(it), params.end(), find),
 9490|  2.60k|                 params.end());
 9491|  2.60k|  }
 9492|  2.60k|}
_ZZN3ada17url_search_params3setENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_ENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9481|  5.21k|  const auto find = [&key](const auto& param) { return param.first == key; };
_ZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9494|  3.59k|inline void url_search_params::remove(const std::string_view key) {
 9495|  3.59k|  std::erase_if(params,
 9496|  3.59k|                [&key](const auto& param) { return param.first == key; });
 9497|  3.59k|}
_ZZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9496|  7.86k|                [&key](const auto& param) { return param.first == key; });
_ZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9500|  3.59k|                                      const std::string_view value) {
 9501|  3.59k|  std::erase_if(params, [&key, &value](const auto& param) {
 9502|  3.59k|    return param.first == key && param.second == value;
 9503|  3.59k|  });
 9504|  3.59k|}
_ZZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_ENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9501|  2.84k|  std::erase_if(params, [&key, &value](const auto& param) {
 9502|  2.84k|    return param.first == key && param.second == value;
  ------------------
  |  Branch (9502:12): [True: 0, False: 2.84k]
  |  Branch (9502:34): [True: 0, False: 0]
  ------------------
 9503|  2.84k|  });
_ZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9444|  22.9k|inline bool url_search_params::has(const std::string_view key) noexcept {
 9445|  22.9k|  auto entry = std::ranges::find_if(
 9446|  22.9k|      params, [&key](const auto& param) { return param.first == key; });
 9447|  22.9k|  return entry != params.end();
 9448|  22.9k|}
_ZZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9446|   227k|      params, [&key](const auto& param) { return param.first == key; });
_ZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9451|  12.7k|                                   std::string_view value) noexcept {
 9452|  12.7k|  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
 9453|  12.7k|    return param.first == key && param.second == value;
 9454|  12.7k|  });
 9455|  12.7k|  return entry != params.end();
 9456|  12.7k|}
_ZZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_ENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9452|   117k|  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
 9453|   117k|    return param.first == key && param.second == value;
  ------------------
  |  Branch (9453:12): [True: 15.4k, False: 102k]
  |  Branch (9453:34): [True: 11.1k, False: 4.26k]
  ------------------
 9454|   117k|  });
_ZN3ada17url_search_params3getENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9420|  18.0k|    const std::string_view key) {
 9421|  18.0k|  auto entry = std::ranges::find_if(
 9422|  18.0k|      params, [&key](const auto& param) { return param.first == key; });
 9423|       |
 9424|  18.0k|  if (entry == params.end()) {
  ------------------
  |  Branch (9424:7): [True: 4.90k, False: 13.0k]
  ------------------
 9425|  4.90k|    return std::nullopt;
 9426|  4.90k|  }
 9427|       |
 9428|  13.0k|  return entry->second;
 9429|  18.0k|}
_ZZN3ada17url_search_params3getENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9422|   122k|      params, [&key](const auto& param) { return param.first == key; });
_ZN3ada17url_search_params7get_allENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9432|  5.21k|    const std::string_view key) {
 9433|  5.21k|  std::vector<std::string> out{};
 9434|       |
 9435|  16.0k|  for (auto& param : params) {
  ------------------
  |  Branch (9435:20): [True: 16.0k, False: 5.21k]
  ------------------
 9436|  16.0k|    if (param.first == key) {
  ------------------
  |  Branch (9436:9): [True: 6.04k, False: 10.0k]
  ------------------
 9437|  6.04k|      out.emplace_back(param.second);
 9438|  6.04k|    }
 9439|  16.0k|  }
 9440|       |
 9441|  5.21k|  return out;
 9442|  5.21k|}
_ZN3ada17url_search_params8get_keysEv:
 9586|  2.60k|inline url_search_params_keys_iter url_search_params::get_keys() {
 9587|  2.60k|  return url_search_params_keys_iter(*this);
 9588|  2.60k|}
_ZN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE0EEC2ERNS_17url_search_paramsE:
 9334|  2.60k|  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
_ZN3ada17url_search_params10get_valuesEv:
 9593|  2.60k|inline url_search_params_values_iter url_search_params::get_values() {
 9594|  2.60k|  return url_search_params_values_iter(*this);
 9595|  2.60k|}
_ZN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE1EEC2ERNS_17url_search_paramsE:
 9334|  2.60k|  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
_ZN3ada17url_search_params11get_entriesEv:
 9600|  2.60k|inline url_search_params_entries_iter url_search_params::get_entries() {
 9601|  2.60k|  return url_search_params_entries_iter(*this);
 9602|  2.60k|}
_ZN3ada22url_search_params_iterINSt3__14pairINS1_17basic_string_viewIcNS1_11char_traitsIcEEEES6_EELNS_27url_search_params_iter_typeE2EEC2ERNS_17url_search_paramsE:
 9334|  2.60k|  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
_ZN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE0EE4nextEv:
 9610|  8.68k|inline std::optional<std::string_view> url_search_params_keys_iter::next() {
 9611|  8.68k|  if (!has_next()) {
  ------------------
  |  Branch (9611:7): [True: 0, False: 8.68k]
  ------------------
 9612|      0|    return std::nullopt;
 9613|      0|  }
 9614|  8.68k|  return params.params[pos++].first;
 9615|  8.68k|}
_ZN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE1EE4nextEv:
 9618|  8.68k|inline std::optional<std::string_view> url_search_params_values_iter::next() {
 9619|  8.68k|  if (!has_next()) {
  ------------------
  |  Branch (9619:7): [True: 0, False: 8.68k]
  ------------------
 9620|      0|    return std::nullopt;
 9621|      0|  }
 9622|  8.68k|  return params.params[pos++].second;
 9623|  8.68k|}
_ZN3ada22url_search_params_iterINSt3__14pairINS1_17basic_string_viewIcNS1_11char_traitsIcEEEES6_EELNS_27url_search_params_iter_typeE2EE4nextEv:
 9627|  8.68k|url_search_params_entries_iter::next() {
 9628|  8.68k|  if (!has_next()) {
  ------------------
  |  Branch (9628:7): [True: 0, False: 8.68k]
  ------------------
 9629|      0|    return std::nullopt;
 9630|      0|  }
 9631|  8.68k|  return params.params[pos++];
 9632|  8.68k|}
_ZNK3ada17url_search_params5beginEv:
 9271|  10.4k|  inline auto begin() const { return params.begin(); }
_ZNK3ada17url_search_params3endEv:
 9272|  10.4k|  inline auto end() const { return params.end(); }
_ZNK3ada17url_search_params5frontEv:
 9273|  1.12k|  inline auto front() const { return params.front(); }
_ZNK3ada17url_search_params4backEv:
 9274|  1.12k|  inline auto back() const { return params.back(); }
_ZNK3ada17url_search_paramsixEm:
 9275|  1.12k|  inline auto operator[](size_t index) const { return params[index]; }
_ZN3ada17url_search_paramsC2Ev:
 9148|  7.82k|  url_search_params() = default;
_ZN3ada17url_search_paramsC2ERKS0_:
 9158|  2.60k|  url_search_params(const url_search_params& u) = default;
_ZN3ada17url_search_paramsaSERKS0_:
 9161|  2.60k|  url_search_params& operator=(const url_search_params& u) = default;
_ZN3ada17url_search_paramsC2EOS0_:
 9159|  2.60k|  url_search_params(url_search_params&& u) noexcept = default;
_ZN3ada17url_search_paramsaSEOS0_:
 9160|  2.60k|  url_search_params& operator=(url_search_params&& u) noexcept = default;
_ZNK3ada14url_aggregator8validateEv:
 8873|  2.60k|[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
 8874|  2.60k|  if (!is_valid) {
  ------------------
  |  Branch (8874:7): [True: 0, False: 2.60k]
  ------------------
 8875|      0|    return true;
 8876|      0|  }
 8877|  2.60k|  if (!components.check_offset_consistency()) {
  ------------------
  |  Branch (8877:7): [True: 0, False: 2.60k]
  ------------------
 8878|      0|    ada_log("url_aggregator::validate inconsistent components \n",
 8879|      0|            to_diagram());
 8880|      0|    return false;
 8881|      0|  }
 8882|       |  // We have a credible components struct, but let us investivate more
 8883|       |  // carefully:
 8884|       |  /**
 8885|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 8886|       |   *       |     |    |          | ^^^^|       |   |
 8887|       |   *       |     |    |          | |   |       |   `----- hash_start
 8888|       |   *       |     |    |          | |   |       `--------- search_start
 8889|       |   *       |     |    |          | |   `----------------- pathname_start
 8890|       |   *       |     |    |          | `--------------------- port
 8891|       |   *       |     |    |          `----------------------- host_end
 8892|       |   *       |     |    `---------------------------------- host_start
 8893|       |   *       |     `--------------------------------------- username_end
 8894|       |   *       `--------------------------------------------- protocol_end
 8895|       |   */
 8896|  2.60k|  if (components.protocol_end == url_components::omitted) {
  ------------------
  |  Branch (8896:7): [True: 0, False: 2.60k]
  ------------------
 8897|      0|    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
 8898|      0|    return false;
 8899|      0|  }
 8900|  2.60k|  if (components.username_end == url_components::omitted) {
  ------------------
  |  Branch (8900:7): [True: 0, False: 2.60k]
  ------------------
 8901|      0|    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
 8902|      0|    return false;
 8903|      0|  }
 8904|  2.60k|  if (components.host_start == url_components::omitted) {
  ------------------
  |  Branch (8904:7): [True: 0, False: 2.60k]
  ------------------
 8905|      0|    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
 8906|      0|    return false;
 8907|      0|  }
 8908|  2.60k|  if (components.host_end == url_components::omitted) {
  ------------------
  |  Branch (8908:7): [True: 0, False: 2.60k]
  ------------------
 8909|      0|    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
 8910|      0|    return false;
 8911|      0|  }
 8912|  2.60k|  if (components.pathname_start == url_components::omitted) {
  ------------------
  |  Branch (8912:7): [True: 0, False: 2.60k]
  ------------------
 8913|      0|    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
 8914|      0|    return false;
 8915|      0|  }
 8916|       |
 8917|  2.60k|  if (components.protocol_end > buffer.size()) {
  ------------------
  |  Branch (8917:7): [True: 0, False: 2.60k]
  ------------------
 8918|      0|    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
 8919|      0|    return false;
 8920|      0|  }
 8921|  2.60k|  if (components.username_end > buffer.size()) {
  ------------------
  |  Branch (8921:7): [True: 0, False: 2.60k]
  ------------------
 8922|      0|    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
 8923|      0|    return false;
 8924|      0|  }
 8925|  2.60k|  if (components.host_start > buffer.size()) {
  ------------------
  |  Branch (8925:7): [True: 0, False: 2.60k]
  ------------------
 8926|      0|    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
 8927|      0|    return false;
 8928|      0|  }
 8929|  2.60k|  if (components.host_end > buffer.size()) {
  ------------------
  |  Branch (8929:7): [True: 0, False: 2.60k]
  ------------------
 8930|      0|    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
 8931|      0|    return false;
 8932|      0|  }
 8933|  2.60k|  if (components.pathname_start > buffer.size()) {
  ------------------
  |  Branch (8933:7): [True: 0, False: 2.60k]
  ------------------
 8934|      0|    ada_log("url_aggregator::validate pathname_start overflow \n",
 8935|      0|            to_diagram());
 8936|      0|    return false;
 8937|      0|  }
 8938|       |
 8939|  2.60k|  if (components.protocol_end > 0) {
  ------------------
  |  Branch (8939:7): [True: 2.60k, False: 0]
  ------------------
 8940|  2.60k|    if (buffer[components.protocol_end - 1] != ':') {
  ------------------
  |  Branch (8940:9): [True: 0, False: 2.60k]
  ------------------
 8941|      0|      ada_log(
 8942|      0|          "url_aggregator::validate missing : at the end of the protocol \n",
 8943|      0|          to_diagram());
 8944|      0|      return false;
 8945|      0|    }
 8946|  2.60k|  }
 8947|       |
 8948|  2.60k|  if (components.username_end != buffer.size() &&
  ------------------
  |  Branch (8948:7): [True: 2.60k, False: 0]
  ------------------
 8949|  2.60k|      components.username_end > components.protocol_end + 2) {
  ------------------
  |  Branch (8949:7): [True: 0, False: 2.60k]
  ------------------
 8950|      0|    if (buffer[components.username_end] != ':' &&
  ------------------
  |  Branch (8950:9): [True: 0, False: 0]
  ------------------
 8951|      0|        buffer[components.username_end] != '@') {
  ------------------
  |  Branch (8951:9): [True: 0, False: 0]
  ------------------
 8952|      0|      ada_log(
 8953|      0|          "url_aggregator::validate missing : or @ at the end of the username "
 8954|      0|          "\n",
 8955|      0|          to_diagram());
 8956|      0|      return false;
 8957|      0|    }
 8958|      0|  }
 8959|       |
 8960|  2.60k|  if (components.host_start != buffer.size()) {
  ------------------
  |  Branch (8960:7): [True: 2.60k, False: 0]
  ------------------
 8961|  2.60k|    if (components.host_start > components.username_end) {
  ------------------
  |  Branch (8961:9): [True: 0, False: 2.60k]
  ------------------
 8962|      0|      if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8962:11): [True: 0, False: 0]
  ------------------
 8963|      0|        ada_log(
 8964|      0|            "url_aggregator::validate missing @ at the end of the password \n",
 8965|      0|            to_diagram());
 8966|      0|        return false;
 8967|      0|      }
 8968|  2.60k|    } else if (components.host_start == components.username_end &&
  ------------------
  |  Branch (8968:16): [True: 2.60k, False: 0]
  ------------------
 8969|  2.60k|               components.host_end > components.host_start) {
  ------------------
  |  Branch (8969:16): [True: 2.60k, False: 0]
  ------------------
 8970|  2.60k|      if (components.host_start == components.protocol_end + 2) {
  ------------------
  |  Branch (8970:11): [True: 2.60k, False: 0]
  ------------------
 8971|  2.60k|        if (buffer[components.protocol_end] != '/' ||
  ------------------
  |  Branch (8971:13): [True: 0, False: 2.60k]
  ------------------
 8972|  2.60k|            buffer[components.protocol_end + 1] != '/') {
  ------------------
  |  Branch (8972:13): [True: 0, False: 2.60k]
  ------------------
 8973|      0|          ada_log(
 8974|      0|              "url_aggregator::validate missing // between protocol and host "
 8975|      0|              "\n",
 8976|      0|              to_diagram());
 8977|      0|          return false;
 8978|      0|        }
 8979|  2.60k|      } else {
 8980|      0|        if (components.host_start > components.protocol_end &&
  ------------------
  |  Branch (8980:13): [True: 0, False: 0]
  ------------------
 8981|      0|            buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8981:13): [True: 0, False: 0]
  ------------------
 8982|      0|          ada_log(
 8983|      0|              "url_aggregator::validate missing @ at the end of the username "
 8984|      0|              "\n",
 8985|      0|              to_diagram());
 8986|      0|          return false;
 8987|      0|        }
 8988|      0|      }
 8989|  2.60k|    } else {
 8990|      0|      if (components.host_end != components.host_start) {
  ------------------
  |  Branch (8990:11): [True: 0, False: 0]
  ------------------
 8991|      0|        ada_log("url_aggregator::validate expected omitted host \n",
 8992|      0|                to_diagram());
 8993|      0|        return false;
 8994|      0|      }
 8995|      0|    }
 8996|  2.60k|  }
 8997|  2.60k|  if (components.host_end != buffer.size() &&
  ------------------
  |  Branch (8997:7): [True: 2.60k, False: 0]
  ------------------
 8998|  2.60k|      components.pathname_start > components.host_end) {
  ------------------
  |  Branch (8998:7): [True: 0, False: 2.60k]
  ------------------
 8999|      0|    if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8999:9): [True: 0, False: 0]
  ------------------
 9000|      0|        buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9000:9): [True: 0, False: 0]
  ------------------
 9001|      0|        buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (9001:9): [True: 0, False: 0]
  ------------------
 9002|      0|      if (components.pathname_start + 1 >= buffer.size() ||
  ------------------
  |  Branch (9002:11): [True: 0, False: 0]
  ------------------
 9003|      0|          buffer[components.pathname_start] != '/' ||
  ------------------
  |  Branch (9003:11): [True: 0, False: 0]
  ------------------
 9004|      0|          buffer[components.pathname_start + 1] != '/') {
  ------------------
  |  Branch (9004:11): [True: 0, False: 0]
  ------------------
 9005|      0|        ada_log(
 9006|      0|            "url_aggregator::validate expected the path to begin with // \n",
 9007|      0|            to_diagram());
 9008|      0|        return false;
 9009|      0|      }
 9010|      0|    } else if (buffer[components.host_end] != ':') {
  ------------------
  |  Branch (9010:16): [True: 0, False: 0]
  ------------------
 9011|      0|      ada_log("url_aggregator::validate missing : at the port \n",
 9012|      0|              to_diagram());
 9013|      0|      return false;
 9014|      0|    }
 9015|      0|  }
 9016|  2.60k|  if (components.pathname_start != buffer.size() &&
  ------------------
  |  Branch (9016:7): [True: 2.60k, False: 0]
  ------------------
 9017|  2.60k|      components.pathname_start < components.search_start &&
  ------------------
  |  Branch (9017:7): [True: 2.60k, False: 0]
  ------------------
 9018|  2.60k|      components.pathname_start < components.hash_start && !has_opaque_path) {
  ------------------
  |  Branch (9018:7): [True: 2.60k, False: 0]
  |  Branch (9018:60): [True: 2.60k, False: 0]
  ------------------
 9019|  2.60k|    if (buffer[components.pathname_start] != '/') {
  ------------------
  |  Branch (9019:9): [True: 0, False: 2.60k]
  ------------------
 9020|      0|      ada_log("url_aggregator::validate missing / at the path \n",
 9021|      0|              to_diagram());
 9022|      0|      return false;
 9023|      0|    }
 9024|  2.60k|  }
 9025|  2.60k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9025:7): [True: 1.34k, False: 1.26k]
  ------------------
 9026|  1.34k|    if (buffer[components.search_start] != '?') {
  ------------------
  |  Branch (9026:9): [True: 0, False: 1.34k]
  ------------------
 9027|      0|      ada_log("url_aggregator::validate missing ? at the search \n",
 9028|      0|              to_diagram());
 9029|      0|      return false;
 9030|      0|    }
 9031|  1.34k|  }
 9032|  2.60k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9032:7): [True: 162, False: 2.44k]
  ------------------
 9033|    162|    if (buffer[components.hash_start] != '#') {
  ------------------
  |  Branch (9033:9): [True: 0, False: 162]
  ------------------
 9034|      0|      ada_log("url_aggregator::validate missing # at the hash \n",
 9035|      0|              to_diagram());
 9036|      0|      return false;
 9037|      0|    }
 9038|    162|  }
 9039|       |
 9040|  2.60k|  return true;
 9041|  2.60k|}
_ZNK3ada14url_components24check_offset_consistencyEv:
 7390|  2.60k|    const noexcept {
 7391|       |  /**
 7392|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 7393|       |   *       |     |    |          | ^^^^|       |   |
 7394|       |   *       |     |    |          | |   |       |   `----- hash_start
 7395|       |   *       |     |    |          | |   |       `--------- search_start
 7396|       |   *       |     |    |          | |   `----------------- pathname_start
 7397|       |   *       |     |    |          | `--------------------- port
 7398|       |   *       |     |    |          `----------------------- host_end
 7399|       |   *       |     |    `---------------------------------- host_start
 7400|       |   *       |     `--------------------------------------- username_end
 7401|       |   *       `--------------------------------------------- protocol_end
 7402|       |   */
 7403|       |  // These conditions can be made more strict.
 7404|  2.60k|  if (protocol_end == url_components::omitted) {
  ------------------
  |  Branch (7404:7): [True: 0, False: 2.60k]
  ------------------
 7405|      0|    return false;
 7406|      0|  }
 7407|  2.60k|  uint32_t index = protocol_end;
 7408|       |
 7409|  2.60k|  if (username_end == url_components::omitted) {
  ------------------
  |  Branch (7409:7): [True: 0, False: 2.60k]
  ------------------
 7410|      0|    return false;
 7411|      0|  }
 7412|  2.60k|  if (username_end < index) {
  ------------------
  |  Branch (7412:7): [True: 0, False: 2.60k]
  ------------------
 7413|      0|    return false;
 7414|      0|  }
 7415|  2.60k|  index = username_end;
 7416|       |
 7417|  2.60k|  if (host_start == url_components::omitted) {
  ------------------
  |  Branch (7417:7): [True: 0, False: 2.60k]
  ------------------
 7418|      0|    return false;
 7419|      0|  }
 7420|  2.60k|  if (host_start < index) {
  ------------------
  |  Branch (7420:7): [True: 0, False: 2.60k]
  ------------------
 7421|      0|    return false;
 7422|      0|  }
 7423|  2.60k|  index = host_start;
 7424|       |
 7425|  2.60k|  if (port != url_components::omitted) {
  ------------------
  |  Branch (7425:7): [True: 0, False: 2.60k]
  ------------------
 7426|      0|    if (port > 0xffff) {
  ------------------
  |  Branch (7426:9): [True: 0, False: 0]
  ------------------
 7427|      0|      return false;
 7428|      0|    }
 7429|      0|    uint32_t port_length = helpers::fast_digit_count(port) + 1;
 7430|      0|    if (index + port_length < index) {
  ------------------
  |  Branch (7430:9): [True: 0, False: 0]
  ------------------
 7431|      0|      return false;
 7432|      0|    }
 7433|      0|    index += port_length;
 7434|      0|  }
 7435|       |
 7436|  2.60k|  if (pathname_start == url_components::omitted) {
  ------------------
  |  Branch (7436:7): [True: 0, False: 2.60k]
  ------------------
 7437|      0|    return false;
 7438|      0|  }
 7439|  2.60k|  if (pathname_start < index) {
  ------------------
  |  Branch (7439:7): [True: 0, False: 2.60k]
  ------------------
 7440|      0|    return false;
 7441|      0|  }
 7442|  2.60k|  index = pathname_start;
 7443|       |
 7444|  2.60k|  if (search_start != url_components::omitted) {
  ------------------
  |  Branch (7444:7): [True: 1.34k, False: 1.26k]
  ------------------
 7445|  1.34k|    if (search_start < index) {
  ------------------
  |  Branch (7445:9): [True: 0, False: 1.34k]
  ------------------
 7446|      0|      return false;
 7447|      0|    }
 7448|  1.34k|    index = search_start;
 7449|  1.34k|  }
 7450|       |
 7451|  2.60k|  if (hash_start != url_components::omitted) {
  ------------------
  |  Branch (7451:7): [True: 162, False: 2.44k]
  ------------------
 7452|    162|    if (hash_start < index) {
  ------------------
  |  Branch (7452:9): [True: 0, False: 162]
  ------------------
 7453|      0|      return false;
 7454|      0|    }
 7455|    162|  }
 7456|       |
 7457|  2.60k|  return true;
 7458|  2.60k|}
_ZNK3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE0EE8has_nextEv:
 9605|  19.9k|inline bool url_search_params_iter<T, Type>::has_next() const {
 9606|  19.9k|  return pos < params.params.size();
 9607|  19.9k|}
_ZNK3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE1EE8has_nextEv:
 9605|  19.9k|inline bool url_search_params_iter<T, Type>::has_next() const {
 9606|  19.9k|  return pos < params.params.size();
 9607|  19.9k|}
_ZNK3ada22url_search_params_iterINSt3__14pairINS1_17basic_string_viewIcNS1_11char_traitsIcEEEES6_EELNS_27url_search_params_iter_typeE2EE8has_nextEv:
 9605|  19.9k|inline bool url_search_params_iter<T, Type>::has_next() const {
 9606|  19.9k|  return pos < params.params.size();
 9607|  19.9k|}

LLVMFuzzerTestOneInput:
    9|  2.60k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   10|  2.60k|  FuzzedDataProvider fdp(data, size);
   11|  2.60k|  std::string source = fdp.ConsumeRandomLengthString(256);
   12|  2.60k|  std::string base_source = fdp.ConsumeRandomLengthString(256);
   13|  2.60k|  std::string key3 = fdp.ConsumeRandomLengthString(64);
   14|  2.60k|  std::string value3 = fdp.ConsumeRandomLengthString(64);
   15|       |
   16|       |  /**
   17|       |   * ada::url_search_params
   18|       |   */
   19|  2.60k|  volatile size_t length = 0;
   20|       |
   21|  2.60k|  auto base_source_view =
   22|  2.60k|      std::string_view(base_source.data(), base_source.length());
   23|       |
   24|       |  // Test constructor with initial value
   25|  2.60k|  auto initialized = ada::url_search_params(base_source_view);
   26|  2.60k|  length += initialized.size();
   27|  2.60k|  initialized.to_string();
   28|       |
   29|       |  // Test get() and get_all() on initialized params
   30|  2.60k|  {
   31|  2.60k|    auto val = initialized.get(source);
   32|  2.60k|    if (val.has_value()) {
  ------------------
  |  Branch (32:9): [True: 272, False: 2.33k]
  ------------------
   33|    272|      length += val->size();
   34|    272|    }
   35|  2.60k|    auto all_vals = initialized.get_all(source);
   36|  2.99k|    for (const auto& v : all_vals) {
  ------------------
  |  Branch (36:24): [True: 2.99k, False: 2.60k]
  ------------------
   37|  2.99k|      length += v.size();
   38|  2.99k|    }
   39|  2.60k|  }
   40|       |
   41|       |  // Test sort() on initialized params
   42|  2.60k|  initialized.sort();
   43|       |
   44|       |  // Test C++ range-for iteration; also verify has(k) and has(k,v) consistency.
   45|  10.1k|  for (const auto& pair : initialized) {
  ------------------
  |  Branch (45:25): [True: 10.1k, False: 2.60k]
  ------------------
   46|  10.1k|    length += pair.first.size();
   47|  10.1k|    length += pair.second.size();
   48|       |
   49|       |    // Every key seen in iteration must be reported by has(key).
   50|  10.1k|    if (!initialized.has(pair.first)) {
  ------------------
  |  Branch (50:9): [True: 0, False: 10.1k]
  ------------------
   51|      0|      printf(
   52|      0|          "url_search_params: iteration yielded key '%s' but has(key) is "
   53|      0|          "false\n",
   54|      0|          std::string(pair.first).c_str());
   55|      0|      abort();
   56|      0|    }
   57|       |
   58|       |    // has(key, value) → has(key)
   59|  10.1k|    if (initialized.has(pair.first, pair.second) &&
  ------------------
  |  Branch (59:9): [True: 10.1k, False: 0]
  ------------------
   60|  10.1k|        !initialized.has(pair.first)) {
  ------------------
  |  Branch (60:9): [True: 0, False: 10.1k]
  ------------------
   61|      0|      printf(
   62|      0|          "url_search_params: has(key,value) is true but has(key) is false "
   63|      0|          "for key '%s'\n",
   64|      0|          std::string(pair.first).c_str());
   65|      0|      abort();
   66|      0|    }
   67|       |
   68|       |    // get(key) must return a value when the key exists.
   69|  10.1k|    auto got = initialized.get(pair.first);
   70|  10.1k|    if (!got.has_value()) {
  ------------------
  |  Branch (70:9): [True: 0, False: 10.1k]
  ------------------
   71|      0|      printf(
   72|      0|          "url_search_params: has(key) is true but get(key) returned nullopt "
   73|      0|          "for key '%s'\n",
   74|      0|          std::string(pair.first).c_str());
   75|      0|      abort();
   76|      0|    }
   77|  10.1k|  }
   78|       |
   79|       |  // Test index-based access
   80|  2.60k|  if (initialized.size() > 0) {
  ------------------
  |  Branch (80:7): [True: 1.12k, False: 1.48k]
  ------------------
   81|  1.12k|    auto front = initialized.front();
   82|  1.12k|    length += front.first.size();
   83|  1.12k|    auto back = initialized.back();
   84|  1.12k|    length += back.first.size();
   85|  1.12k|    auto first = initialized[0];
   86|  1.12k|    length += first.first.size();
   87|  1.12k|  }
   88|       |
   89|       |  // Test default-constructed params with various mutations
   90|  2.60k|  auto search_params = ada::url_search_params();
   91|  2.60k|  search_params.append(source, base_source);
   92|  2.60k|  search_params.append(key3, value3);
   93|  2.60k|  search_params.set(source, base_source);
   94|  2.60k|  search_params.to_string();
   95|       |
   96|       |  // Test size()
   97|  2.60k|  length += search_params.size();
   98|       |
   99|       |  // Test has() and has(key, value) overloads
  100|  2.60k|  volatile bool has_key = search_params.has(base_source);
  101|  2.60k|  if (has_key) {
  ------------------
  |  Branch (101:7): [True: 1.42k, False: 1.18k]
  ------------------
  102|  1.42k|    search_params.append(base_source, source);
  103|  1.42k|  }
  104|       |
  105|       |  // Test get() - returns first matching value
  106|  2.60k|  {
  107|  2.60k|    auto val = search_params.get(source);
  108|  2.60k|    if (val.has_value()) {
  ------------------
  |  Branch (108:9): [True: 2.60k, False: 0]
  ------------------
  109|  2.60k|      length += val->size();
  110|  2.60k|    }
  111|  2.60k|  }
  112|       |
  113|       |  // Test get_all() - returns all matching values
  114|  2.60k|  {
  115|  2.60k|    auto all_vals = search_params.get_all(source);
  116|  2.60k|    length += all_vals.size();
  117|  3.04k|    for (const auto& v : all_vals) {
  ------------------
  |  Branch (117:24): [True: 3.04k, False: 2.60k]
  ------------------
  118|  3.04k|      length += v.size();
  119|  3.04k|    }
  120|  2.60k|  }
  121|       |
  122|       |  // Test remove(key) and remove(key, value) overloads
  123|  2.60k|  search_params.remove(source);
  124|  2.60k|  search_params.remove(source, base_source);
  125|       |
  126|       |  // Test has(key, value) after remove
  127|  2.60k|  if (search_params.has(base_source, source)) {
  ------------------
  |  Branch (127:7): [True: 990, False: 1.61k]
  ------------------
  128|    990|    search_params.remove(base_source);
  129|    990|    search_params.remove(base_source, source);
  130|    990|  }
  131|       |
  132|       |  // Append more pairs for iteration testing
  133|  2.60k|  search_params.append(key3, value3);
  134|  2.60k|  search_params.append(source, key3);
  135|  2.60k|  search_params.append(base_source, value3);
  136|       |
  137|       |  // Test sort()
  138|  2.60k|  search_params.sort();
  139|       |
  140|       |  // Test serialization after sort
  141|  2.60k|  std::string serialized = search_params.to_string();
  142|  2.60k|  length += serialized.size();
  143|       |
  144|       |  // Test JavaScript-style iterator: keys
  145|  2.60k|  auto keys = search_params.get_keys();
  146|  11.2k|  while (keys.has_next()) {
  ------------------
  |  Branch (146:10): [True: 8.68k, False: 2.60k]
  ------------------
  147|  8.68k|    auto k = keys.next();
  148|  8.68k|    if (k.has_value()) {
  ------------------
  |  Branch (148:9): [True: 8.68k, False: 0]
  ------------------
  149|  8.68k|      length += k->size();
  150|  8.68k|    }
  151|  8.68k|  }
  152|       |
  153|       |  // Test JavaScript-style iterator: values
  154|  2.60k|  auto values = search_params.get_values();
  155|  11.2k|  while (values.has_next()) {
  ------------------
  |  Branch (155:10): [True: 8.68k, False: 2.60k]
  ------------------
  156|  8.68k|    auto v = values.next();
  157|  8.68k|    if (v.has_value()) {
  ------------------
  |  Branch (157:9): [True: 8.68k, False: 0]
  ------------------
  158|  8.68k|      length += v->size();
  159|  8.68k|    }
  160|  8.68k|  }
  161|       |
  162|       |  // Test JavaScript-style iterator: entries
  163|  2.60k|  auto entries = search_params.get_entries();
  164|  11.2k|  while (entries.has_next()) {
  ------------------
  |  Branch (164:10): [True: 8.68k, False: 2.60k]
  ------------------
  165|  8.68k|    auto e = entries.next();
  166|  8.68k|    if (e.has_value()) {
  ------------------
  |  Branch (166:9): [True: 8.68k, False: 0]
  ------------------
  167|  8.68k|      length += e->first.size();
  168|  8.68k|      length += e->second.size();
  169|  8.68k|    }
  170|  8.68k|  }
  171|       |
  172|       |  // Test C++ range-for on the mutated params
  173|  8.68k|  for (const auto& pair : search_params) {
  ------------------
  |  Branch (173:25): [True: 8.68k, False: 2.60k]
  ------------------
  174|  8.68k|    length += pair.first.size();
  175|  8.68k|    length += pair.second.size();
  176|  8.68k|  }
  177|       |
  178|       |  // Test reset() - private method used by C API
  179|  2.60k|  std::string resetted_value = fdp.ConsumeRandomLengthString(256);
  180|  2.60k|  search_params.reset(resetted_value);
  181|  2.60k|  length += search_params.size();
  182|       |
  183|       |  // Test that reset() followed by iteration doesn't crash
  184|  2.60k|  for (const auto& pair : search_params) {
  ------------------
  |  Branch (184:25): [True: 2.46k, False: 2.60k]
  ------------------
  185|  2.46k|    length += pair.first.size();
  186|  2.46k|    length += pair.second.size();
  187|  2.46k|  }
  188|       |
  189|       |  // Test get() after reset
  190|  2.60k|  {
  191|  2.60k|    auto val = search_params.get(source);
  192|  2.60k|    if (val.has_value()) {
  ------------------
  |  Branch (192:9): [True: 40, False: 2.56k]
  ------------------
  193|     40|      length += val->size();
  194|     40|    }
  195|  2.60k|  }
  196|       |
  197|       |  // Test copy constructor and copy assignment
  198|  2.60k|  ada::url_search_params copied = search_params;
  199|  2.60k|  length += copied.size();
  200|  2.60k|  ada::url_search_params assigned;
  201|  2.60k|  assigned = search_params;
  202|  2.60k|  length += assigned.size();
  203|       |
  204|       |  // Test move constructor and move assignment
  205|  2.60k|  ada::url_search_params move_constructed = std::move(copied);
  206|  2.60k|  length += move_constructed.size();
  207|  2.60k|  ada::url_search_params move_assigned;
  208|  2.60k|  move_assigned = std::move(assigned);
  209|  2.60k|  length += move_assigned.size();
  210|       |
  211|       |  /**
  212|       |   * Serialisation idempotency.
  213|       |   *
  214|       |   * The application/x-www-form-urlencoded serialiser must be a fixed point:
  215|       |   * parsing the serialised form of any url_search_params and serialising
  216|       |   * again must produce the same string. This invariant is mandated by the
  217|       |   * WHATWG spec and must hold for all inputs.
  218|       |   *
  219|       |   * We test this on both the initialised params (constructed from fuzz input)
  220|       |   * and the mutated params (after append / set / remove / sort).
  221|       |   */
  222|       |
  223|       |  // Initialised params idempotency.
  224|  2.60k|  {
  225|  2.60k|    std::string s1 = initialized.to_string();
  226|  2.60k|    ada::url_search_params reparsed(s1);
  227|  2.60k|    std::string s2 = reparsed.to_string();
  228|  2.60k|    if (s1 != s2) {
  ------------------
  |  Branch (228:9): [True: 0, False: 2.60k]
  ------------------
  229|      0|      printf(
  230|      0|          "url_search_params serialisation not idempotent (initialized)!\n"
  231|      0|          "  first:  %s\n  second: %s\n",
  232|      0|          s1.c_str(), s2.c_str());
  233|      0|      abort();
  234|      0|    }
  235|  2.60k|  }
  236|       |
  237|       |  // Mutated params idempotency.
  238|  2.60k|  {
  239|  2.60k|    std::string s1 = move_assigned.to_string();
  240|  2.60k|    ada::url_search_params reparsed(s1);
  241|  2.60k|    std::string s2 = reparsed.to_string();
  242|  2.60k|    if (s1 != s2) {
  ------------------
  |  Branch (242:9): [True: 0, False: 2.60k]
  ------------------
  243|      0|      printf(
  244|      0|          "url_search_params serialisation not idempotent (mutated)!\n"
  245|      0|          "  first:  %s\n  second: %s\n",
  246|      0|          s1.c_str(), s2.c_str());
  247|      0|      abort();
  248|      0|    }
  249|  2.60k|  }
  250|       |
  251|       |  /**
  252|       |   * Round-trip via URL.
  253|       |   *
  254|       |   * Embed the fuzz input as the query component of a real URL, extract the
  255|       |   * search params from the parsed URL's search string, and verify that the
  256|       |   * resulting params serialise idempotently. This exercises the interface
  257|       |   * between URL parsing and search-params parsing.
  258|       |   */
  259|  2.60k|  {
  260|  2.60k|    std::string url_str = "https://example.com/?" + source;
  261|  2.60k|    auto parsed_url = ada::parse<ada::url_aggregator>(url_str);
  262|  2.60k|    if (parsed_url) {
  ------------------
  |  Branch (262:9): [True: 2.60k, False: 0]
  ------------------
  263|  2.60k|      std::string search_raw = std::string(parsed_url->get_search());
  264|  2.60k|      std::string_view search_view = search_raw;
  265|  2.60k|      if (!search_view.empty() && search_view[0] == '?') {
  ------------------
  |  Branch (265:11): [True: 1.38k, False: 1.22k]
  |  Branch (265:35): [True: 1.38k, False: 0]
  ------------------
  266|  1.38k|        search_view = search_view.substr(1);
  267|  1.38k|      }
  268|       |
  269|  2.60k|      ada::url_search_params sp_from_url(search_view);
  270|       |
  271|       |      // Size and iteration must not crash.
  272|  2.60k|      length += sp_from_url.size();
  273|  4.68k|      for (const auto& pair : sp_from_url) {
  ------------------
  |  Branch (273:29): [True: 4.68k, False: 2.60k]
  ------------------
  274|  4.68k|        length += pair.first.size();
  275|  4.68k|        length += pair.second.size();
  276|  4.68k|      }
  277|       |
  278|       |      // Idempotency check.
  279|  2.60k|      std::string t1 = sp_from_url.to_string();
  280|  2.60k|      ada::url_search_params sp2(t1);
  281|  2.60k|      std::string t2 = sp2.to_string();
  282|  2.60k|      if (t1 != t2) {
  ------------------
  |  Branch (282:11): [True: 0, False: 2.60k]
  ------------------
  283|      0|        printf(
  284|      0|            "url_search_params via URL round-trip not idempotent!\n"
  285|      0|            "  first:  %s\n  second: %s\n",
  286|      0|            t1.c_str(), t2.c_str());
  287|      0|        abort();
  288|      0|      }
  289|       |
  290|       |      // Set the serialised params back on the URL – must not crash.
  291|  2.60k|      parsed_url->set_search(t1);
  292|  2.60k|      volatile bool v = parsed_url->validate();
  293|  2.60k|      (void)v;
  294|  2.60k|    }
  295|  2.60k|  }
  296|       |
  297|  2.60k|  return 0;
  298|  2.60k|}

