_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7618|  3.19k|                    std::string& out) {
 7619|  3.19k|  ada_log("percent_encode ", input, " to output string while ",
 7620|  3.19k|          append ? "appending" : "overwriting");
 7621|  3.19k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7622|  3.19k|    return character_sets::bit_at(character_set, c);
 7623|  3.19k|  });
 7624|  3.19k|  ada_log("percent_encode done checking, moved to ",
 7625|  3.19k|          std::distance(input.begin(), pointer));
 7626|       |
 7627|       |  // Optimization: Don't iterate if percent encode is not required
 7628|  3.19k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7628:7): [True: 1.83k, False: 1.35k]
  ------------------
 7629|  1.83k|    ada_log("percent_encode encoding not needed.");
 7630|  1.83k|    return false;
 7631|  1.83k|  }
 7632|       |  if constexpr (!append) {
 7633|       |    out.clear();
 7634|       |  }
 7635|  1.35k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7636|  1.35k|          " bytes");
 7637|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7638|  1.35k|  out.append(input.data(), std::distance(input.begin(), pointer));
 7639|  1.35k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7640|  1.35k|          " bytes");
 7641|  56.0k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7641:10): [True: 54.6k, False: 1.35k]
  ------------------
 7642|  54.6k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7642:9): [True: 37.2k, False: 17.4k]
  ------------------
 7643|  37.2k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7644|  37.2k|    } else {
 7645|  17.4k|      out += *pointer;
 7646|  17.4k|    }
 7647|  54.6k|  }
 7648|  1.35k|  return true;
 7649|  3.19k|}
_ZN3ada7unicode22form_urlencoded_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7518|  54.9k|std::string form_urlencoded_decode(const std::string_view input) {
 7519|  54.9k|  const size_t len = input.size();
 7520|  54.9k|  if (len == 0) [[unlikely]] {
  ------------------
  |  Branch (7520:7): [True: 26.6k, False: 28.3k]
  ------------------
 7521|  26.6k|    return {};
 7522|  26.6k|  }
 7523|       |
 7524|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7525|  28.3k|  const char* const src = input.data();
 7526|  28.3k|  const char* const end = src + len;
 7527|  28.3k|  const char* p = src;
 7528|       |
 7529|       |  // Advance over the untransformed prefix.
 7530|   114k|  while (p < end && *p != '+' && *p != '%') {
  ------------------
  |  Branch (7530:10): [True: 103k, False: 10.4k]
  |  Branch (7530:21): [True: 101k, False: 1.95k]
  |  Branch (7530:34): [True: 85.9k, False: 15.9k]
  ------------------
 7531|  85.9k|    ++p;
 7532|  85.9k|  }
 7533|  28.3k|  if (p == end) {
  ------------------
  |  Branch (7533:7): [True: 10.4k, False: 17.8k]
  ------------------
 7534|  10.4k|    return std::string(input);
 7535|  10.4k|  }
 7536|       |
 7537|       |  // Output is always at most as long as the input: write into a single
 7538|       |  // pre-sized buffer, then shrink to the final length.
 7539|  17.8k|  std::string out(len, '\0');
 7540|  17.8k|  char* d = out.data();
 7541|  17.8k|  char* const d0 = d;
 7542|       |
 7543|  17.8k|  const size_t prefix = static_cast<size_t>(p - src);
 7544|  17.8k|  std::memcpy(d, src, prefix);
 7545|  17.8k|  d += prefix;
 7546|       |
 7547|  60.2k|  while (p < end) {
  ------------------
  |  Branch (7547:10): [True: 42.3k, False: 17.8k]
  ------------------
 7548|  42.3k|    const char c = *p;
 7549|  42.3k|    if (c == '+') {
  ------------------
  |  Branch (7549:9): [True: 9.42k, False: 32.9k]
  ------------------
 7550|  9.42k|      *d++ = ' ';
 7551|  9.42k|      ++p;
 7552|  32.9k|    } else if (c == '%') {
  ------------------
  |  Branch (7552:16): [True: 23.1k, False: 9.81k]
  ------------------
 7553|       |      // Decode runs of valid %XX tightly (common for nested URL query values).
 7554|   142k|      while (p + 2 < end && *p == '%') {
  ------------------
  |  Branch (7554:14): [True: 127k, False: 15.5k]
  |  Branch (7554:29): [True: 121k, False: 6.11k]
  ------------------
 7555|   121k|        const uint8_t hi = unhex_table[static_cast<uint8_t>(p[1])];
 7556|   121k|        const uint8_t lo = unhex_table[static_cast<uint8_t>(p[2])];
 7557|   121k|        if ((hi | lo) >= 16) {
  ------------------
  |  Branch (7557:13): [True: 1.53k, False: 119k]
  ------------------
 7558|  1.53k|          break;
 7559|  1.53k|        }
 7560|   119k|        *d++ = static_cast<char>((hi << 4) | lo);
 7561|   119k|        p += 3;
 7562|   119k|      }
 7563|  23.1k|      if (p < end && *p == '%') {
  ------------------
  |  Branch (7563:11): [True: 11.1k, False: 12.0k]
  |  Branch (7563:22): [True: 4.02k, False: 7.09k]
  ------------------
 7564|       |        // Invalid escape: copy '%' literally and continue.
 7565|  4.02k|        *d++ = *p++;
 7566|  4.02k|      }
 7567|  23.1k|    } else {
 7568|       |      // Copy a plain run until the next '+' or '%'.
 7569|  9.81k|      const char* start = p;
 7570|  9.81k|      ++p;
 7571|  40.0k|      while (p < end && *p != '+' && *p != '%') {
  ------------------
  |  Branch (7571:14): [True: 38.3k, False: 1.65k]
  |  Branch (7571:25): [True: 35.8k, False: 2.53k]
  |  Branch (7571:38): [True: 30.2k, False: 5.62k]
  ------------------
 7572|  30.2k|        ++p;
 7573|  30.2k|      }
 7574|  9.81k|      const size_t n = static_cast<size_t>(p - start);
 7575|  9.81k|      std::memcpy(d, start, n);
 7576|  9.81k|      d += n;
 7577|  9.81k|    }
 7578|  42.3k|  }
 7579|       |
 7580|  17.8k|  out.resize(static_cast<size_t>(d - d0));
 7581|  17.8k|  return out;
 7582|  28.3k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7588|   117k|                           const uint8_t character_set[]) {
 7589|   117k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7590|   117k|    return character_sets::bit_at(character_set, c);
 7591|   117k|  });
 7592|       |  // Optimization: Don't iterate if percent encode is not required
 7593|   117k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7593:7): [True: 80.6k, False: 37.3k]
  ------------------
 7594|  80.6k|    return std::string(input);
 7595|  80.6k|  }
 7596|       |
 7597|  37.3k|  std::string result;
 7598|  37.3k|  result.reserve(input.length());  // in the worst case, percent encoding might
 7599|       |                                   // produce 3 characters.
 7600|  37.3k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
 7601|  37.3k|  if (static_cast<size_t>(input.end() - pointer) >= 48) {
  ------------------
  |  Branch (7601:7): [True: 3.37k, False: 34.0k]
  ------------------
 7602|  3.37k|    percent_encode_suffix(&*pointer, input.data() + input.size(), character_set,
 7603|  3.37k|                          result);
 7604|  34.0k|  } else {
 7605|   189k|    for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7605:12): [True: 155k, False: 34.0k]
  ------------------
 7606|   155k|      if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7606:11): [True: 135k, False: 20.5k]
  ------------------
 7607|   135k|        result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7608|   135k|      } else {
 7609|  20.5k|        result += *pointer;
 7610|  20.5k|      }
 7611|   155k|    }
 7612|  34.0k|  }
 7613|  37.3k|  return result;
 7614|   117k|}
_ZN3ada7unicode21percent_encode_suffixEPKcS2_PKhRNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEE:
 7993|  3.37k|                           const uint8_t character_set[], std::string& out) {
 7994|  3.37k|#if ADA_UNICODE_SSSE3 || ADA_NEON || ADA_RVV
 7995|  3.37k|#ifdef ADA_UNICODE_NEED_SSSE3_TARGET
 7996|  3.37k|  static const bool ssse3_supported = __builtin_cpu_supports("ssse3") != 0;
 7997|  3.37k|  const bool use_wide = ssse3_supported;
 7998|       |#else
 7999|       |  const bool use_wide = true;
 8000|       |#endif
 8001|  3.37k|  if (use_wide && static_cast<size_t>(end - p) >= kPercentEncodeSimdMin) {
  ------------------
  |  Branch (8001:7): [True: 3.37k, False: 0]
  |  Branch (8001:19): [True: 3.37k, False: 0]
  ------------------
 8002|  3.37k|    percent_encode_to_wide(p, end, character_set, out);
 8003|  3.37k|    return;
 8004|  3.37k|  }
 8005|      0|#endif
 8006|      0|  percent_encode_to_scalar(p, end, character_set, out);
 8007|      0|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 8549|  2.99k|    std::string_view input, const result_type* base_url) {
 8550|  2.99k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 8551|  2.99k|  if (!u.is_valid) {
  ------------------
  |  Branch (8551:7): [True: 0, False: 2.99k]
  ------------------
 8552|      0|    return tl::unexpected(errors::type_error);
 8553|      0|  }
 8554|  2.99k|  return u;
 8555|  2.99k|}
_ZN3ada20get_max_input_lengthEv:
 8170|  10.8k|uint32_t get_max_input_length() {
 8171|  10.8k|  return max_input_length_.load(std::memory_order_relaxed);
 8172|  10.8k|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9539|    503|void trim_c0_whitespace(std::string_view& input) noexcept {
 9540|    503|  while (!input.empty() &&
  ------------------
  |  Branch (9540:10): [True: 503, False: 0]
  ------------------
 9541|    503|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (9541:10): [True: 0, False: 503]
  ------------------
 9542|      0|    input.remove_prefix(1);
 9543|      0|  }
 9544|  2.78k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (9544:10): [True: 2.78k, False: 0]
  |  Branch (9544:28): [True: 2.28k, False: 503]
  ------------------
 9545|  2.28k|    input.remove_suffix(1);
 9546|  2.28k|  }
 9547|    503|}
_ZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
12269|  2.99k|                                                result_type& out) {
12270|  2.99k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12271|  2.99k|  constexpr bool is_aggregator =
12272|  2.99k|      std::is_same_v<result_type, ada::url_aggregator>;
12273|  2.99k|  static_assert(is_ada_url || is_aggregator);
12274|       |
12275|  2.99k|  const size_t len = input.size();
12276|       |  // Shortest accepted form is "ws://x" (6).
12277|  2.99k|  if (len < 6) [[unlikely]] {
  ------------------
  |  Branch (12277:7): [True: 0, False: 2.99k]
  ------------------
12278|      0|    return false;
12279|      0|  }
12280|  2.99k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12281|       |
12282|  2.99k|  size_t pos = 0;
12283|  2.99k|  ada::scheme::type scheme_type = ada::scheme::type::NOT_SPECIAL;
12284|  2.99k|  uint32_t protocol_end = 0;
12285|  2.99k|#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
12286|  2.99k|    defined(_M_X64) || defined(_M_IX86) || defined(_M_AMD64)
12287|       |  // One 8-byte load + integer compare is a single cmp/jcc on x86-64.
12288|       |  // Masked compares cover the shorter special schemes without extra loads.
12289|  2.99k|  if (len >= 8) {
  ------------------
  |  Branch (12289:7): [True: 2.99k, False: 0]
  ------------------
12290|  2.99k|    uint64_t first8 = 0;
12291|  2.99k|    std::memcpy(&first8, b, 8);
12292|  2.99k|    if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (12292:9): [True: 2.99k, False: 0]
  ------------------
12293|  2.99k|      pos = 8;
12294|  2.99k|      scheme_type = ada::scheme::type::HTTPS;
12295|  2.99k|      protocol_end = 6;
12296|  2.99k|    } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (12296:16): [True: 0, False: 0]
  ------------------
12297|      0|               0x002f2f3a70747468ull) {  // "http://"
12298|      0|      pos = 7;
12299|      0|      scheme_type = ada::scheme::type::HTTP;
12300|      0|      protocol_end = 5;
12301|      0|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12301:16): [True: 0, False: 0]
  ------------------
12302|      0|               0x00002f2f3a737377ull) {  // "wss://"
12303|      0|      pos = 6;
12304|      0|      scheme_type = ada::scheme::type::WSS;
12305|      0|      protocol_end = 4;
12306|      0|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12306:16): [True: 0, False: 0]
  ------------------
12307|      0|               0x00002f2f3a707466ull) {  // "ftp://"
12308|      0|      pos = 6;
12309|      0|      scheme_type = ada::scheme::type::FTP;
12310|      0|      protocol_end = 4;
12311|      0|    } else if ((first8 & 0x000000ffffffffffull) ==
  ------------------
  |  Branch (12311:16): [True: 0, False: 0]
  ------------------
12312|      0|               0x0000002f2f3a7377ull) {  // "ws://"
12313|      0|      pos = 5;
12314|      0|      scheme_type = ada::scheme::type::WS;
12315|      0|      protocol_end = 3;
12316|      0|    } else {
12317|      0|      return false;
12318|      0|    }
12319|  2.99k|  } else if (b[0] == 'w' && b[1] == 's') {
  ------------------
  |  Branch (12319:14): [True: 0, False: 0]
  |  Branch (12319:29): [True: 0, False: 0]
  ------------------
12320|      0|    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
  ------------------
  |  Branch (12320:9): [True: 0, False: 0]
  |  Branch (12320:24): [True: 0, False: 0]
  |  Branch (12320:39): [True: 0, False: 0]
  ------------------
12321|      0|      pos = 5;
12322|      0|      scheme_type = ada::scheme::type::WS;
12323|      0|      protocol_end = 3;
12324|      0|    } else if (b[2] == 's' && b[3] == ':' && b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12324:16): [True: 0, False: 0]
  |  Branch (12324:31): [True: 0, False: 0]
  |  Branch (12324:46): [True: 0, False: 0]
  |  Branch (12324:61): [True: 0, False: 0]
  ------------------
12325|      0|      pos = 6;
12326|      0|      scheme_type = ada::scheme::type::WSS;
12327|      0|      protocol_end = 4;
12328|      0|    } else {
12329|      0|      return false;
12330|      0|    }
12331|      0|  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
  ------------------
  |  Branch (12331:14): [True: 0, False: 0]
  |  Branch (12331:29): [True: 0, False: 0]
  |  Branch (12331:44): [True: 0, False: 0]
  |  Branch (12331:59): [True: 0, False: 0]
  ------------------
12332|      0|             b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12332:14): [True: 0, False: 0]
  |  Branch (12332:29): [True: 0, False: 0]
  ------------------
12333|      0|    pos = 6;
12334|      0|    scheme_type = ada::scheme::type::FTP;
12335|      0|    protocol_end = 4;
12336|      0|  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
  ------------------
  |  Branch (12336:14): [True: 0, False: 0]
  |  Branch (12336:26): [True: 0, False: 0]
  |  Branch (12336:41): [True: 0, False: 0]
  |  Branch (12336:56): [True: 0, False: 0]
  ------------------
12337|      0|             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (12337:14): [True: 0, False: 0]
  |  Branch (12337:29): [True: 0, False: 0]
  |  Branch (12337:44): [True: 0, False: 0]
  |  Branch (12337:59): [True: 0, False: 0]
  ------------------
12338|      0|    pos = 7;
12339|      0|    scheme_type = ada::scheme::type::HTTP;
12340|      0|    protocol_end = 5;
12341|      0|  } else {
12342|      0|    return false;
12343|      0|  }
12344|       |#else
12345|       |  // Big-endian / uncommon arches: byte-wise special-scheme match.
12346|       |  if (len >= 8 && b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p' &&
12347|       |      b[4] == 's' && b[5] == ':' && b[6] == '/' && b[7] == '/') {
12348|       |    pos = 8;
12349|       |    scheme_type = ada::scheme::type::HTTPS;
12350|       |    protocol_end = 6;
12351|       |  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
12352|       |             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
12353|       |    pos = 7;
12354|       |    scheme_type = ada::scheme::type::HTTP;
12355|       |    protocol_end = 5;
12356|       |  } else if (b[0] == 'w' && b[1] == 's') {
12357|       |    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
12358|       |      pos = 5;
12359|       |      scheme_type = ada::scheme::type::WS;
12360|       |      protocol_end = 3;
12361|       |    } else if (len >= 6 && b[2] == 's' && b[3] == ':' && b[4] == '/' &&
12362|       |               b[5] == '/') {
12363|       |      pos = 6;
12364|       |      scheme_type = ada::scheme::type::WSS;
12365|       |      protocol_end = 4;
12366|       |    } else {
12367|       |      return false;
12368|       |    }
12369|       |  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
12370|       |             b[4] == '/' && b[5] == '/') {
12371|       |    pos = 6;
12372|       |    scheme_type = ada::scheme::type::FTP;
12373|       |    protocol_end = 4;
12374|       |  } else {
12375|       |    return false;
12376|       |  }
12377|       |#endif
12378|  2.99k|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (12378:7): [True: 2.99k, False: 0]
  |  Branch (12378:21): [True: 0, False: 2.99k]
  |  Branch (12378:38): [True: 0, False: 2.99k]
  ------------------
12379|      0|    return false;
12380|      0|  }
12381|       |
12382|       |  // Digit-led hosts are IPv4/numeric; '[' is IPv6. Skip before scanning.
12383|  2.99k|  if (pos < len && ((b[pos] >= '0' && b[pos] <= '9') || b[pos] == '[')) {
  ------------------
  |  Branch (12383:7): [True: 2.99k, False: 0]
  |  Branch (12383:22): [True: 2.99k, False: 0]
  |  Branch (12383:39): [True: 0, False: 2.99k]
  |  Branch (12383:57): [True: 0, False: 2.99k]
  ------------------
12384|      0|    return false;
12385|      0|  }
12386|       |
12387|  2.99k|  const size_t host_start = pos;
12388|  2.99k|  bool has_upper = false;
12389|  2.99k|  bool has_x = false;
12390|  2.99k|  size_t host_end = pos;
12391|  2.99k|  if (!scan_plain_host(b, pos, len, host_end, has_upper, has_x)) {
  ------------------
  |  Branch (12391:7): [True: 0, False: 2.99k]
  ------------------
12392|      0|    return false;
12393|      0|  }
12394|  2.99k|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (12394:7): [True: 0, False: 2.99k]
  ------------------
12395|      0|    return false;
12396|      0|  }
12397|  2.99k|  const size_t host_len = host_end - host_start;
12398|  2.99k|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (12398:7): [True: 0, False: 2.99k]
  ------------------
12399|      0|    return false;
12400|      0|  }
12401|  2.99k|  {
12402|  2.99k|    std::string_view hv(input.data() + host_start, host_len);
12403|  2.99k|    char host_buf[256];
12404|  2.99k|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (12404:9): [True: 0, False: 2.99k]
  ------------------
12405|      0|      std::memcpy(host_buf, input.data() + host_start, host_len);
12406|      0|      unicode::to_lower_ascii(host_buf, host_len);
12407|      0|      hv = std::string_view(host_buf, host_len);
12408|      0|    }
12409|  2.99k|    if (ada::checkers::last_label_may_be_a_number(hv)) [[unlikely]] {
  ------------------
  |  Branch (12409:9): [True: 0, False: 2.99k]
  ------------------
12410|      0|      return false;
12411|      0|    }
12412|       |    // Punycode is "xn--...". The host scan already notes a lowercase 'x';
12413|       |    // uppercase 'X' is covered by has_upper after the in-place lower.
12414|  2.99k|    static constexpr std::string_view xn{"xn-", 3};
12415|  2.99k|    if ((has_x || has_upper) && hv.find(xn) != std::string_view::npos)
  ------------------
  |  Branch (12415:10): [True: 2.99k, False: 0]
  |  Branch (12415:19): [True: 0, False: 0]
  |  Branch (12415:33): [True: 0, False: 2.99k]
  ------------------
12416|      0|        [[unlikely]] {
12417|      0|      return false;
12418|      0|    }
12419|  2.99k|  }
12420|       |
12421|  2.99k|  if (host_end < len && b[host_end] == ':') [[unlikely]] {
  ------------------
  |  Branch (12421:7): [True: 2.99k, False: 0]
  |  Branch (12421:25): [True: 0, False: 2.99k]
  ------------------
12422|      0|    return finish_simple_absolute_with_port(input, out, scheme_type,
12423|      0|                                            protocol_end, host_start, host_end,
12424|      0|                                            host_len, has_upper);
12425|      0|  }
12426|       |
12427|  2.99k|  size_t i = host_end;
12428|  2.99k|  size_t path_start = host_end;
12429|  2.99k|  size_t path_end = host_end;
12430|  2.99k|  size_t query_start = std::string_view::npos;
12431|  2.99k|  size_t hash_start = std::string_view::npos;
12432|  2.99k|  bool has_path = false;
12433|  2.99k|  bool maybe_dot_segment = false;
12434|  2.99k|  bool rest_simple = true;
12435|       |
12436|  2.99k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12436:7): [True: 2.99k, False: 0]
  |  Branch (12436:18): [True: 2.99k, False: 0]
  ------------------
12437|  2.99k|    has_path = true;
12438|  2.99k|    path_start = i;
12439|  2.99k|    ++i;
12440|  2.99k|    scan_path_run(b, i, len, maybe_dot_segment);
12441|  2.99k|    if (i < len) {
  ------------------
  |  Branch (12441:9): [True: 2.99k, False: 0]
  ------------------
12442|  2.99k|      const uint8_t cls = k_path[b[i]];
12443|  2.99k|      if (cls == 1) {
  ------------------
  |  Branch (12443:11): [True: 2.99k, False: 0]
  ------------------
12444|  2.99k|        path_end = i;
12445|  2.99k|        if (b[i] == '?') {
  ------------------
  |  Branch (12445:13): [True: 2.99k, False: 0]
  ------------------
12446|  2.99k|          query_start = i;
12447|  2.99k|          ++i;
12448|  2.99k|          goto scan_query;
12449|  2.99k|        }
12450|      0|        hash_start = i;
12451|      0|        ++i;
12452|      0|        goto scan_hash;
12453|  2.99k|      }
12454|      0|      rest_simple = false;
12455|      0|      for (; i < len; ++i) {
  ------------------
  |  Branch (12455:14): [True: 0, False: 0]
  ------------------
12456|      0|        if (b[i] == '?') {
  ------------------
  |  Branch (12456:13): [True: 0, False: 0]
  ------------------
12457|      0|          path_end = i;
12458|      0|          query_start = i;
12459|      0|          ++i;
12460|      0|          goto scan_query_boundary;
12461|      0|        }
12462|      0|        if (b[i] == '#') {
  ------------------
  |  Branch (12462:13): [True: 0, False: 0]
  ------------------
12463|      0|          path_end = i;
12464|      0|          hash_start = i;
12465|      0|          ++i;
12466|      0|          goto after_rest;
12467|      0|        }
12468|      0|      }
12469|      0|      path_end = i;
12470|      0|      goto after_rest;
12471|      0|    }
12472|      0|    path_end = i;
12473|      0|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12473:14): [True: 0, False: 0]
  |  Branch (12473:25): [True: 0, False: 0]
  ------------------
12474|      0|    query_start = i;
12475|      0|    ++i;
12476|      0|    goto scan_query;
12477|      0|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12477:14): [True: 0, False: 0]
  |  Branch (12477:25): [True: 0, False: 0]
  ------------------
12478|      0|    hash_start = i;
12479|      0|    ++i;
12480|      0|    goto scan_hash;
12481|      0|  }
12482|      0|  goto after_rest;
12483|       |
12484|  2.99k|scan_query:
12485|  2.99k|  scan_query_run(b, i, len);
12486|  2.99k|  if (i < len) {
  ------------------
  |  Branch (12486:7): [True: 1.61k, False: 1.37k]
  ------------------
12487|  1.61k|    if (b[i] == '#') {
  ------------------
  |  Branch (12487:9): [True: 249, False: 1.36k]
  ------------------
12488|    249|      hash_start = i;
12489|    249|      ++i;
12490|    249|      goto scan_hash;
12491|    249|    }
12492|  1.36k|    rest_simple = false;
12493|  1.36k|    goto scan_query_boundary;
12494|  1.61k|  }
12495|  1.37k|  goto after_rest;
12496|       |
12497|  1.37k|scan_query_boundary:
12498|  54.3k|  for (; i < len; ++i) {
  ------------------
  |  Branch (12498:10): [True: 53.1k, False: 1.19k]
  ------------------
12499|  53.1k|    if (b[i] == '#') {
  ------------------
  |  Branch (12499:9): [True: 164, False: 53.0k]
  ------------------
12500|    164|      hash_start = i;
12501|    164|      ++i;
12502|    164|      goto after_rest;
12503|    164|    }
12504|  53.1k|  }
12505|  1.19k|  goto after_rest;
12506|       |
12507|  1.19k|scan_hash:
12508|    249|  scan_hash_run(b, i, len);
12509|    249|  if (i < len) {
  ------------------
  |  Branch (12509:7): [True: 169, False: 80]
  ------------------
12510|    169|    rest_simple = false;
12511|    169|  }
12512|       |
12513|  2.99k|after_rest:
12514|  2.99k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12514:7): [True: 1.45k, False: 1.53k]
  |  Branch (12514:22): [True: 0, False: 1.45k]
  ------------------
12515|      0|    const std::string_view path_body(input.data() + path_start,
12516|      0|                                     path_end - path_start);
12517|      0|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12517:9): [True: 0, False: 0]
  ------------------
12518|      0|      rest_simple = false;
12519|      0|    }
12520|      0|  }
12521|       |
12522|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12523|       |  // trailing C0 control or space; this fast path does neither. A query or
12524|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12525|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12526|       |  // inputs back to the slow path.
12527|  2.99k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12527:7): [True: 1.53k, False: 1.45k]
  |  Branch (12527:24): [True: 364, False: 1.16k]
  ------------------
12528|  1.16k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12528:24): [True: 139, False: 1.02k]
  ------------------
12529|    503|    return false;
12530|    503|  }
12531|       |
12532|  2.48k|  out.type = scheme_type;
12533|  2.48k|  out.is_valid = true;
12534|  2.48k|  out.has_opaque_path = false;
12535|  2.48k|  out.host_type = DEFAULT;
12536|       |
12537|  2.48k|  if (!rest_simple) {
  ------------------
  |  Branch (12537:7): [True: 1.02k, False: 1.45k]
  ------------------
12538|       |    // Host is a plain domain. Finish path/query/hash with
12539|       |    // the regular helpers
12540|       |    // so percent-encoding and dot segments do not re-parse the authority.
12541|  1.02k|    const std::string_view path_view =
12542|  1.02k|        has_path
  ------------------
  |  Branch (12542:9): [True: 1.02k, False: 0]
  ------------------
12543|  1.02k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12544|  1.02k|            : std::string_view{};
12545|  1.02k|    auto apply_query_and_hash = [&]() {
12546|  1.02k|      if (query_start != std::string_view::npos) {
12547|  1.02k|        const size_t q_end =
12548|  1.02k|            (hash_start != std::string_view::npos) ? hash_start : len;
12549|  1.02k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12550|  1.02k|                                                q_end - query_start - 1),
12551|  1.02k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12552|  1.02k|      }
12553|  1.02k|      if (hash_start != std::string_view::npos) {
12554|  1.02k|        out.update_unencoded_base_hash(std::string_view(
12555|  1.02k|            input.data() + hash_start + 1, len - hash_start - 1));
12556|  1.02k|      }
12557|  1.02k|    };
12558|  1.02k|    if constexpr (is_aggregator) {
12559|  1.02k|      out.buffer.assign(input.substr(0, host_end));
12560|  1.02k|      if (has_upper) {
  ------------------
  |  Branch (12560:11): [True: 0, False: 1.02k]
  ------------------
12561|      0|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12562|      0|      }
12563|  1.02k|      out.components.protocol_end = protocol_end;
12564|  1.02k|      out.components.username_end = protocol_end + 2;
12565|  1.02k|      out.components.host_start = protocol_end + 2;
12566|  1.02k|      out.components.host_end = static_cast<uint32_t>(host_end);
12567|  1.02k|      out.components.port = url_components::omitted;
12568|  1.02k|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12569|  1.02k|      out.components.search_start = url_components::omitted;
12570|  1.02k|      out.components.hash_start = url_components::omitted;
12571|  1.02k|      out.parse_path(path_view);
12572|  1.02k|      apply_query_and_hash();
12573|       |    } else {
12574|       |      std::string host_str(input.substr(host_start, host_len));
12575|       |      if (has_upper) {
12576|       |        unicode::to_lower_ascii(host_str.data(), host_str.size());
12577|       |      }
12578|       |      out.host = std::move(host_str);
12579|       |      out.parse_path(path_view);
12580|       |      apply_query_and_hash();
12581|       |    }
12582|  1.02k|    return true;
12583|  1.02k|  }
12584|       |
12585|  1.45k|  const bool need_slash = !has_path;
12586|  1.45k|  if constexpr (is_aggregator) {
12587|  1.45k|    if (!need_slash) {
  ------------------
  |  Branch (12587:9): [True: 1.45k, False: 0]
  ------------------
12588|       |      // assign copies once. resize()+memcpy would value-init then overwrite.
12589|  1.45k|      out.buffer.assign(input);
12590|  1.45k|      if (has_upper) {
  ------------------
  |  Branch (12590:11): [True: 0, False: 1.45k]
  ------------------
12591|      0|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12592|      0|      }
12593|  1.45k|      out.components.protocol_end = protocol_end;
12594|  1.45k|      out.components.username_end = protocol_end + 2;
12595|  1.45k|      out.components.host_start = protocol_end + 2;
12596|  1.45k|      out.components.host_end = static_cast<uint32_t>(host_end);
12597|  1.45k|      out.components.port = url_components::omitted;
12598|  1.45k|      out.components.pathname_start = static_cast<uint32_t>(path_start);
12599|  1.45k|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12599:37): [True: 1.45k, False: 0]
  ------------------
12600|  1.45k|                                        ? static_cast<uint32_t>(query_start)
12601|  1.45k|                                        : url_components::omitted;
12602|  1.45k|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12602:35): [True: 80, False: 1.37k]
  ------------------
12603|  1.45k|                                      ? static_cast<uint32_t>(hash_start)
12604|  1.45k|                                      : url_components::omitted;
12605|  1.45k|    } else {
12606|      0|      out.buffer.clear();
12607|      0|      out.buffer.reserve(len + 1);
12608|      0|      out.buffer.append(input.substr(0, host_end));
12609|      0|      out.buffer.push_back('/');
12610|      0|      if (host_end < len) {
  ------------------
  |  Branch (12610:11): [True: 0, False: 0]
  ------------------
12611|      0|        out.buffer.append(input.substr(host_end));
12612|      0|      }
12613|      0|      if (has_upper) {
  ------------------
  |  Branch (12613:11): [True: 0, False: 0]
  ------------------
12614|      0|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12615|      0|      }
12616|      0|      out.components.protocol_end = protocol_end;
12617|      0|      out.components.username_end = protocol_end + 2;
12618|      0|      out.components.host_start = protocol_end + 2;
12619|      0|      out.components.host_end = static_cast<uint32_t>(host_end);
12620|      0|      out.components.port = url_components::omitted;
12621|      0|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12622|      0|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12622:37): [True: 0, False: 0]
  ------------------
12623|      0|                                        ? static_cast<uint32_t>(query_start + 1)
12624|      0|                                        : url_components::omitted;
12625|      0|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12625:35): [True: 0, False: 0]
  ------------------
12626|      0|                                      ? static_cast<uint32_t>(hash_start + 1)
12627|      0|                                      : url_components::omitted;
12628|      0|    }
12629|       |  } else {
12630|       |    std::string host_str(input.substr(host_start, host_len));
12631|       |    if (has_upper) {
12632|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
12633|       |    }
12634|       |    out.host = std::move(host_str);
12635|       |    if (need_slash) {
12636|       |      out.path = "/";
12637|       |    } else {
12638|       |      out.path.assign(input.data() + path_start, path_end - path_start);
12639|       |    }
12640|       |    if (query_start != std::string_view::npos) {
12641|       |      const size_t q_end =
12642|       |          (hash_start != std::string_view::npos) ? hash_start : len;
12643|       |      out.query.emplace(input.data() + query_start + 1,
12644|       |                        q_end - query_start - 1);
12645|       |    }
12646|       |    if (hash_start != std::string_view::npos) {
12647|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12648|       |    }
12649|       |  }
12650|  1.45k|  return true;
12651|  2.48k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12840|  2.99k|                           const result_type* base_url) {
12841|       |  // We can specialize the implementation per type.
12842|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12843|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12844|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12845|       |  // and ada::url **do not have to support the exact same API**.
12846|  2.99k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12847|  2.99k|  constexpr bool result_type_is_ada_url_aggregator =
12848|  2.99k|      std::is_same_v<url_aggregator, result_type>;
12849|  2.99k|  static_assert(result_type_is_ada_url ||
12850|  2.99k|                result_type_is_ada_url_aggregator);  // We don't support
12851|       |                                                     // anything else for now.
12852|       |
12853|  2.99k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12854|  2.99k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12855|  2.99k|          ")");
12856|       |
12857|  2.99k|  result_type url{};
12858|       |
12859|  2.99k|  const uint32_t max_input_length = ada::get_max_input_length();
12860|       |
12861|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12862|       |  // max_input_length even when the input fit under it. The check at the end of
12863|       |  // the state machine covers URLs that run to completion, but several states
12864|       |  // return early after appending a query or fragment, so run the same check on
12865|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12866|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12867|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12868|  2.99k|  const auto enforce_max_input_length = [&]() {
12869|  2.99k|    if constexpr (store_values) {
12870|  2.99k|      if (url.is_valid) {
12871|  2.99k|        if constexpr (result_type_is_ada_url_aggregator) {
12872|  2.99k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12873|  2.99k|            url.is_valid = false;
12874|  2.99k|          }
12875|  2.99k|        } else {
12876|  2.99k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12877|  2.99k|            url.is_valid = false;
12878|  2.99k|          }
12879|  2.99k|        }
12880|  2.99k|      }
12881|  2.99k|    }
12882|  2.99k|  };
12883|       |
12884|       |  // We refuse to parse URL strings that exceed the maximum input length.
12885|       |  // By default, this is 4GB but can be configured via
12886|       |  // ada::set_max_input_length().
12887|  2.99k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12887:7): [True: 0, False: 2.99k]
  ------------------
12888|      0|    url.is_valid = false;
12889|      0|  }
12890|       |  // Going forward, user_input.size() is in [0,
12891|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12892|       |  // base, or the optional_url was invalid, we must return.
12893|  2.99k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12893:7): [True: 0, False: 2.99k]
  ------------------
12894|      0|    url.is_valid &= base_url->is_valid;
12895|      0|  }
12896|  2.99k|  if (!url.is_valid) {
  ------------------
  |  Branch (12896:7): [True: 0, False: 2.99k]
  ------------------
12897|      0|    return url;
12898|      0|  }
12899|       |
12900|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12901|  2.99k|  if constexpr (store_values) {
12902|  2.99k|    bool hit_fast_path = false;
12903|  2.99k|    if (base_url == nullptr) {
  ------------------
  |  Branch (12903:9): [True: 2.99k, False: 0]
  ------------------
12904|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12905|       |      // call so those URLs (including SetHref) do not pay for a miss.
12906|  2.99k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12907|  2.99k|      const size_t n = user_input.size();
12908|  2.99k|      size_t host_start = 0;
12909|  2.99k|      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
  ------------------
  |  Branch (12909:11): [True: 2.99k, False: 0]
  |  Branch (12909:21): [True: 0, False: 2.99k]
  |  Branch (12909:36): [True: 0, False: 0]
  |  Branch (12909:51): [True: 0, False: 0]
  ------------------
12910|      0|        host_start = 7;
12911|  2.99k|      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
  ------------------
  |  Branch (12911:18): [True: 2.99k, False: 0]
  |  Branch (12911:28): [True: 2.99k, False: 0]
  |  Branch (12911:43): [True: 2.99k, False: 0]
  |  Branch (12911:58): [True: 2.99k, False: 0]
  ------------------
12912|  2.99k|        host_start = 8;
12913|  2.99k|      }
12914|  2.99k|      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
  ------------------
  |  Branch (12914:34): [True: 2.99k, False: 0]
  ------------------
12915|  2.99k|      const bool skip_ip =
12916|  2.99k|          host_first == '[' || (host_first >= '0' && host_first <= '9');
  ------------------
  |  Branch (12916:11): [True: 0, False: 2.99k]
  |  Branch (12916:33): [True: 2.99k, False: 0]
  |  Branch (12916:54): [True: 0, False: 2.99k]
  ------------------
12917|  2.99k|      const bool skip_userinfo =
12918|  2.99k|          host_start != 0 && authority_has_at(p, host_start, n);
  ------------------
  |  Branch (12918:11): [True: 2.99k, False: 0]
  |  Branch (12918:30): [True: 0, False: 2.99k]
  ------------------
12919|  2.99k|      hit_fast_path = !skip_ip && !skip_userinfo &&
  ------------------
  |  Branch (12919:23): [True: 2.99k, False: 0]
  |  Branch (12919:35): [True: 2.99k, False: 0]
  ------------------
12920|  2.99k|                      try_parse_simple_absolute(user_input, url);
  ------------------
  |  Branch (12920:23): [True: 2.48k, False: 503]
  ------------------
12921|  2.99k|    } else {
12922|      0|      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12923|      0|    }
12924|  2.99k|    if (hit_fast_path) {
  ------------------
  |  Branch (12924:9): [True: 2.48k, False: 503]
  ------------------
12925|  2.48k|      if constexpr (result_type_is_ada_url_aggregator) {
12926|  2.48k|        if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12926:13): [True: 0, False: 2.48k]
  ------------------
12927|      0|          url.is_valid = false;
12928|      0|        }
12929|       |      } else {
12930|       |        // For a small absolute input, even worst-case normalization cannot
12931|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12932|       |        // most 4.5x. Avoid traversing every stored component on the common
12933|       |        // default-limit path.
12934|       |        if ((base_url != nullptr ||
12935|       |             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
12936|       |            url.get_href_size() > max_input_length) [[unlikely]] {
12937|       |          url.is_valid = false;
12938|       |        }
12939|       |      }
12940|  2.48k|      return url;
12941|  2.48k|    }
12942|  2.99k|  }
12943|       |
12944|    503|  state state = state::SCHEME_START;
12945|       |
12946|  2.99k|  std::string tmp_buffer;
12947|  2.99k|  std::string_view url_data;
12948|  2.99k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12948:7): [True: 244, False: 2.74k]
  ------------------
12949|    244|    tmp_buffer = user_input;
12950|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12951|       |    // just directly build the string from user_input.
12952|    244|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12953|    244|    url_data = tmp_buffer;
12954|  2.74k|  } else [[likely]] {
12955|  2.74k|    url_data = user_input;
12956|  2.74k|  }
12957|       |
12958|       |  // Leading and trailing control characters are uncommon and easy to deal with
12959|       |  // (no performance concern).
12960|  2.99k|  helpers::trim_c0_whitespace(url_data);
12961|       |
12962|  2.99k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12963|       |    // Most of the time, we just need user_input.size().
12964|       |    // In some instances, we may need a bit more.
12965|       |    ///////////////////////////
12966|       |    // This is *very* important. This line should *not* be removed
12967|       |    // hastily. There are principled reasons why reserve is important
12968|       |    // for performance. If you have a benchmark with small inputs,
12969|       |    // it may not matter, but in other instances, it could.
12970|       |    ////
12971|       |    // This rounds up to the next power of two.
12972|       |    // We know that user_input.size() is in [0,
12973|       |    // std::numeric_limits<uint32_t>::max).
12974|  2.99k|    uint32_t reserve_capacity =
12975|  2.99k|        (0xFFFFFFFF >>
12976|  2.99k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12977|  2.99k|        1;
12978|  2.99k|    url.reserve(reserve_capacity);
12979|  2.99k|  }
12980|       |
12981|       |  // Optimization opportunity. Most websites do not have fragment.
12982|  2.99k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12983|       |  // We add it last so that an implementation like ada::url_aggregator
12984|       |  // can append it last to its internal buffer, thus improving performance.
12985|       |
12986|       |  // Here url_data no longer has its fragment.
12987|       |  // We are going to access the data from url_data (it is immutable).
12988|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12989|       |  // The input_position variable should range from 0 to input_size.
12990|       |  // It is illegal to access url_data at input_size.
12991|  2.99k|  size_t input_position = 0;
12992|  2.99k|  const size_t input_size = url_data.size();
12993|       |  // Keep running the following state machine by switching on state.
12994|       |  // If after a run pointer points to the EOF code point, go to the next step.
12995|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12996|       |  // We never decrement input_position.
12997|  6.51k|  while (input_position <= input_size) {
  ------------------
  |  Branch (12997:10): [True: 4.02k, False: 2.48k]
  ------------------
12998|  4.02k|    ada_log("In parsing at ", input_position, " out of ", input_size,
12999|  4.02k|            " in state ", ada::to_string(state));
13000|  4.02k|    switch (state) {
13001|    503|      case state::SCHEME_START: {
  ------------------
  |  Branch (13001:7): [True: 503, False: 3.52k]
  ------------------
13002|    503|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13003|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13004|       |        // state to scheme state.
13005|    503|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13005:13): [True: 503, False: 0]
  ------------------
13006|    503|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13006:13): [True: 503, False: 0]
  ------------------
13007|    503|          state = state::SCHEME;
13008|    503|          input_position++;
13009|    503|        } else {
13010|       |          // Otherwise, if state override is not given, set state to no scheme
13011|       |          // state and decrease pointer by 1.
13012|      0|          state = state::NO_SCHEME;
13013|      0|        }
13014|    503|        break;
13015|      0|      }
13016|    503|      case state::SCHEME: {
  ------------------
  |  Branch (13016:7): [True: 503, False: 3.52k]
  ------------------
13017|    503|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13018|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13019|       |        // append c, lowercased, to buffer.
13020|  2.51k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13020:16): [True: 2.51k, False: 0]
  ------------------
13021|  2.51k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13021:16): [True: 2.01k, False: 503]
  ------------------
13022|  2.01k|          input_position++;
13023|  2.01k|        }
13024|       |        // Otherwise, if c is U+003A (:), then:
13025|    503|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13025:13): [True: 503, False: 0]
  ------------------
13026|    503|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13026:13): [True: 503, False: 0]
  ------------------
13027|    503|          ada_log("SCHEME the scheme should be ",
13028|    503|                  url_data.substr(0, input_position));
13029|       |          if constexpr (result_type_is_ada_url) {
13030|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
13031|       |              return url;
13032|       |            }
13033|    503|          } else {
13034|       |            // we pass the colon along instead of painfully adding it back.
13035|    503|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (13035:17): [True: 0, False: 503]
  ------------------
13036|    503|                    url_data.substr(0, input_position + 1))) {
13037|      0|              return url;
13038|      0|            }
13039|    503|          }
13040|    503|          ada_log("SCHEME the scheme is ", url.get_protocol());
13041|       |
13042|       |          // If url's scheme is "file", then:
13043|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13044|    503|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13044:15): [True: 0, False: 503]
  ------------------
13045|       |            // Set state to file state.
13046|      0|            state = state::FILE;
13047|      0|          }
13048|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13049|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13050|       |          // != nullptr is false.
13051|    503|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13051:20): [True: 503, False: 0]
  |  Branch (13051:40): [True: 0, False: 503]
  ------------------
13052|      0|                   base_url->type == url.type) {
  ------------------
  |  Branch (13052:20): [True: 0, False: 0]
  ------------------
13053|       |            // Set state to special relative or authority state.
13054|      0|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13055|      0|          }
13056|       |          // Otherwise, if url is special, set state to special authority
13057|       |          // slashes state.
13058|    503|          else if (url.is_special()) {
  ------------------
  |  Branch (13058:20): [True: 503, False: 0]
  ------------------
13059|    503|            state = state::SPECIAL_AUTHORITY_SLASHES;
13060|    503|          }
13061|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13062|       |          // path or authority state and increase pointer by 1.
13063|      0|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13063:20): [True: 0, False: 0]
  ------------------
13064|      0|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13064:20): [True: 0, False: 0]
  ------------------
13065|      0|            state = state::PATH_OR_AUTHORITY;
13066|      0|            input_position++;
13067|      0|          }
13068|       |          // Otherwise, set url's path to the empty string and set state to
13069|       |          // opaque path state.
13070|      0|          else {
13071|      0|            state = state::OPAQUE_PATH;
13072|      0|          }
13073|    503|        }
13074|       |        // Otherwise, if state override is not given, set buffer to the empty
13075|       |        // string, state to no scheme state, and start over (from the first code
13076|       |        // point in input).
13077|      0|        else {
13078|      0|          state = state::NO_SCHEME;
13079|      0|          input_position = 0;
13080|      0|          break;
13081|      0|        }
13082|    503|        input_position++;
13083|    503|        break;
13084|    503|      }
13085|      0|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13085:7): [True: 0, False: 4.02k]
  ------------------
13086|      0|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13087|       |        // The fragment was pruned from url_data before the state machine ran,
13088|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
13089|       |        // nothing else remains in front of it.
13090|      0|        const bool c_is_hash =
13091|      0|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (13091:13): [True: 0, False: 0]
  |  Branch (13091:37): [True: 0, False: 0]
  ------------------
13092|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13093|       |        // validation error, return failure.
13094|      0|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (13094:13): [True: 0, False: 0]
  |  Branch (13094:37): [True: 0, False: 0]
  |  Branch (13094:66): [True: 0, False: 0]
  ------------------
13095|      0|          ada_log("NO_SCHEME validation error");
13096|      0|          url.is_valid = false;
13097|      0|          return url;
13098|      0|        }
13099|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13100|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13101|       |        // query to base's query, and set state to fragment state.
13102|      0|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (13102:18): [True: 0, False: 0]
  |  Branch (13102:47): [True: 0, False: 0]
  ------------------
13103|      0|          ada_log("NO_SCHEME opaque base with fragment");
13104|      0|          url.copy_scheme(*base_url);
13105|      0|          url.has_opaque_path = base_url->has_opaque_path;
13106|       |
13107|       |          if constexpr (result_type_is_ada_url) {
13108|       |            url.path = base_url->path;
13109|       |            url.query = base_url->query;
13110|      0|          } else {
13111|      0|            url.update_base_pathname(base_url->get_pathname());
13112|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13112:17): [True: 0, False: 0]
  ------------------
13113|       |              // get_search() returns "" for an empty query string (URL ends
13114|       |              // with '?'). update_base_search("") would incorrectly clear the
13115|       |              // query, so pass "?" to preserve the empty query distinction.
13116|      0|              auto s = base_url->get_search();
13117|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13117:38): [True: 0, False: 0]
  ------------------
13118|      0|            }
13119|      0|          }
13120|      0|          url.update_unencoded_base_hash(*fragment);
13121|      0|          enforce_max_input_length();
13122|      0|          return url;
13123|      0|        }
13124|       |        // Otherwise, if base's scheme is not "file", set state to relative
13125|       |        // state and decrease pointer by 1.
13126|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13127|      0|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13127:18): [True: 0, False: 0]
  ------------------
13128|      0|          ada_log("NO_SCHEME non-file relative path");
13129|      0|          state = state::RELATIVE_SCHEME;
13130|      0|        }
13131|       |        // Otherwise, set state to file state and decrease pointer by 1.
13132|      0|        else {
13133|      0|          ada_log("NO_SCHEME file base type");
13134|      0|          state = state::FILE;
13135|      0|        }
13136|      0|        break;
13137|      0|      }
13138|    503|      case state::AUTHORITY: {
  ------------------
  |  Branch (13138:7): [True: 503, False: 3.52k]
  ------------------
13139|    503|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13140|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13141|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13142|       |        // worry about AUTHORITY.
13143|       |        // TODO: Instead of just collecting a bool, collect the location of the
13144|       |        // '@' and do something useful with it.
13145|       |        // TODO: We could do various processing early on, using a single pass
13146|       |        // over the string to collect information about it, e.g., telling us
13147|       |        // whether there is a @ and if so, where (or how many).
13148|       |
13149|       |        // Check if url data contains an @.
13150|    503|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13150:13): [True: 443, False: 60]
  ------------------
13151|    443|          state = state::HOST;
13152|    443|          break;
13153|    443|        }
13154|     60|        bool at_sign_seen{false};
13155|     60|        bool password_token_seen{false};
13156|       |        /**
13157|       |         * We expect something of the sort...
13158|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13159|       |         * --------^
13160|       |         */
13161|     60|        do {
13162|     60|          std::string_view view = url_data.substr(input_position);
13163|       |          // The delimiters are @, /, ? \\.
13164|     60|          size_t location =
13165|     60|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13165:15): [True: 60, False: 0]
  ------------------
13166|     60|                               : helpers::find_authority_delimiter(view);
13167|     60|          std::string_view authority_view = view.substr(0, location);
13168|     60|          size_t end_of_authority = input_position + authority_view.size();
13169|       |          // If c is U+0040 (@), then:
13170|     60|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13170:15): [True: 60, False: 0]
  ------------------
13171|     60|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13171:15): [True: 0, False: 60]
  ------------------
13172|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13173|      0|            if (at_sign_seen) {
  ------------------
  |  Branch (13173:17): [True: 0, False: 0]
  ------------------
13174|      0|              if (password_token_seen) {
  ------------------
  |  Branch (13174:19): [True: 0, False: 0]
  ------------------
13175|       |                if constexpr (result_type_is_ada_url) {
13176|       |                  url.password += "%40";
13177|      0|                } else {
13178|      0|                  url.append_base_password("%40");
13179|      0|                }
13180|      0|              } else {
13181|       |                if constexpr (result_type_is_ada_url) {
13182|       |                  url.username += "%40";
13183|      0|                } else {
13184|      0|                  url.append_base_username("%40");
13185|      0|                }
13186|      0|              }
13187|      0|            }
13188|       |
13189|      0|            at_sign_seen = true;
13190|       |
13191|      0|            if (!password_token_seen) {
  ------------------
  |  Branch (13191:17): [True: 0, False: 0]
  ------------------
13192|      0|              size_t password_token_location = authority_view.find(':');
13193|      0|              password_token_seen =
13194|      0|                  password_token_location != std::string_view::npos;
13195|       |
13196|      0|              if constexpr (store_values) {
13197|      0|                if (!password_token_seen) {
  ------------------
  |  Branch (13197:21): [True: 0, False: 0]
  ------------------
13198|       |                  if constexpr (result_type_is_ada_url) {
13199|       |                    url.username += unicode::percent_encode(
13200|       |                        authority_view,
13201|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13202|      0|                  } else {
13203|      0|                    url.append_base_username(unicode::percent_encode(
13204|      0|                        authority_view,
13205|      0|                        character_sets::USERINFO_PERCENT_ENCODE));
13206|      0|                  }
13207|      0|                } else {
13208|       |                  if constexpr (result_type_is_ada_url) {
13209|       |                    url.username += unicode::percent_encode(
13210|       |                        authority_view.substr(0, password_token_location),
13211|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13212|       |                    url.password += unicode::percent_encode(
13213|       |                        authority_view.substr(password_token_location + 1),
13214|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13215|      0|                  } else {
13216|      0|                    url.append_base_username(unicode::percent_encode(
13217|      0|                        authority_view.substr(0, password_token_location),
13218|      0|                        character_sets::USERINFO_PERCENT_ENCODE));
13219|      0|                    url.append_base_password(unicode::percent_encode(
13220|      0|                        authority_view.substr(password_token_location + 1),
13221|      0|                        character_sets::USERINFO_PERCENT_ENCODE));
13222|      0|                  }
13223|      0|                }
13224|      0|              }
13225|      0|            } else if constexpr (store_values) {
13226|       |              if constexpr (result_type_is_ada_url) {
13227|       |                url.password += unicode::percent_encode(
13228|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13229|      0|              } else {
13230|      0|                url.append_base_password(unicode::percent_encode(
13231|      0|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13232|      0|              }
13233|      0|            }
13234|      0|          }
13235|       |          // Otherwise, if one of the following is true:
13236|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13237|       |          // - url is special and c is U+005C (\)
13238|     60|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13238:20): [True: 0, False: 60]
  ------------------
13239|     60|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13239:20): [True: 60, False: 0]
  ------------------
13240|      0|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13240:20): [True: 0, False: 0]
  ------------------
13241|     60|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13241:21): [True: 0, False: 0]
  |  Branch (13241:41): [True: 0, False: 0]
  ------------------
13242|       |            // If atSignSeen is true and authority_view is the empty string,
13243|       |            // validation error, return failure.
13244|     60|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13244:17): [True: 0, False: 60]
  |  Branch (13244:33): [True: 0, False: 0]
  ------------------
13245|      0|              url.is_valid = false;
13246|      0|              return url;
13247|      0|            }
13248|     60|            state = state::HOST;
13249|     60|            break;
13250|     60|          }
13251|      0|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13251:15): [True: 0, False: 0]
  ------------------
13252|      0|            if constexpr (store_values) {
13253|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13253:19): [True: 0, False: 0]
  ------------------
13254|      0|                url.update_unencoded_base_hash(*fragment);
13255|      0|              }
13256|      0|            }
13257|      0|            enforce_max_input_length();
13258|      0|            return url;
13259|      0|          }
13260|      0|          input_position = end_of_authority + 1;
13261|      0|        } while (true);
  ------------------
  |  Branch (13261:18): [True: 0, Folded]
  ------------------
13262|       |
13263|     60|        break;
13264|     60|      }
13265|     60|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13265:7): [True: 0, False: 4.02k]
  ------------------
13266|      0|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13267|      0|                helpers::substring(url_data, input_position));
13268|       |
13269|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13270|       |        // then set state to special authority ignore slashes state and increase
13271|       |        // pointer by 1.
13272|      0|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13272:13): [True: 0, False: 0]
  ------------------
13273|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13274|      0|          input_position += 2;
13275|      0|        } else {
13276|       |          // Otherwise, validation error, set state to relative state and
13277|       |          // decrease pointer by 1.
13278|      0|          state = state::RELATIVE_SCHEME;
13279|      0|        }
13280|       |
13281|      0|        break;
13282|     60|      }
13283|      0|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (13283:7): [True: 0, False: 4.02k]
  ------------------
13284|      0|        ada_log("PATH_OR_AUTHORITY ",
13285|      0|                helpers::substring(url_data, input_position));
13286|       |
13287|       |        // If c is U+002F (/), then set state to authority state.
13288|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13288:13): [True: 0, False: 0]
  ------------------
13289|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13289:13): [True: 0, False: 0]
  ------------------
13290|      0|          state = state::AUTHORITY;
13291|      0|          input_position++;
13292|      0|        } else {
13293|       |          // Otherwise, set state to path state, and decrease pointer by 1.
13294|      0|          state = state::PATH;
13295|      0|        }
13296|       |
13297|      0|        break;
13298|     60|      }
13299|      0|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (13299:7): [True: 0, False: 4.02k]
  ------------------
13300|      0|        ada_log("RELATIVE_SCHEME ",
13301|      0|                helpers::substring(url_data, input_position));
13302|       |
13303|       |        // Set url's scheme to base's scheme.
13304|      0|        url.copy_scheme(*base_url);
13305|       |
13306|       |        // If c is U+002F (/), then set state to relative slash state.
13307|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13307:13): [True: 0, False: 0]
  ------------------
13308|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
13309|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13309:13): [True: 0, False: 0]
  ------------------
13310|      0|          ada_log(
13311|      0|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13312|      0|              "slash state");
13313|      0|          state = state::RELATIVE_SLASH;
13314|      0|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13314:20): [True: 0, False: 0]
  |  Branch (13314:40): [True: 0, False: 0]
  ------------------
13315|      0|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13315:20): [True: 0, False: 0]
  ------------------
13316|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13317|       |          // set state to relative slash state.
13318|      0|          ada_log(
13319|      0|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13320|      0|              "error, set state to relative slash state");
13321|      0|          state = state::RELATIVE_SLASH;
13322|      0|        } else {
13323|      0|          ada_log("RELATIVE_SCHEME otherwise");
13324|       |          // Set url's username to base's username, url's password to base's
13325|       |          // password, url's host to base's host, url's port to base's port,
13326|       |          // url's path to a clone of base's path, and url's query to base's
13327|       |          // query.
13328|       |          if constexpr (result_type_is_ada_url) {
13329|       |            url.username = base_url->username;
13330|       |            url.password = base_url->password;
13331|       |            url.host = base_url->host;
13332|       |            url.port = base_url->port;
13333|       |            // cloning the base path includes cloning the has_opaque_path flag
13334|       |            url.has_opaque_path = base_url->has_opaque_path;
13335|       |            url.path = base_url->path;
13336|       |            url.query = base_url->query;
13337|      0|          } else {
13338|      0|            url.update_base_authority(base_url->get_href(),
13339|      0|                                      base_url->get_components());
13340|      0|            url.update_host_to_base_host(base_url->get_hostname());
13341|      0|            url.update_base_port(base_url->retrieve_base_port());
13342|       |            // cloning the base path includes cloning the has_opaque_path flag
13343|      0|            url.has_opaque_path = base_url->has_opaque_path;
13344|      0|            url.update_base_pathname(base_url->get_pathname());
13345|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13345:17): [True: 0, False: 0]
  ------------------
13346|       |              // get_search() returns "" for an empty query string (URL ends
13347|       |              // with '?'). update_base_search("") would incorrectly clear the
13348|       |              // query, so pass "?" to preserve the empty query distinction.
13349|      0|              auto s = base_url->get_search();
13350|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13350:38): [True: 0, False: 0]
  ------------------
13351|      0|            }
13352|      0|          }
13353|       |
13354|      0|          url.has_opaque_path = base_url->has_opaque_path;
13355|       |
13356|       |          // If c is U+003F (?), then set url's query to the empty string, and
13357|       |          // state to query state.
13358|      0|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13358:15): [True: 0, False: 0]
  ------------------
13359|      0|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13359:15): [True: 0, False: 0]
  ------------------
13360|      0|            state = state::QUERY;
13361|      0|          }
13362|       |          // Otherwise, if c is not the EOF code point:
13363|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (13363:20): [True: 0, False: 0]
  ------------------
13364|       |            // Set url's query to null.
13365|      0|            url.clear_search();
13366|       |            if constexpr (result_type_is_ada_url) {
13367|       |              // Shorten url's path.
13368|       |              helpers::shorten_path(url.path, url.type);
13369|      0|            } else {
13370|      0|              std::string_view path = url.get_pathname();
13371|      0|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13371:19): [True: 0, False: 0]
  ------------------
13372|      0|                url.update_base_pathname(std::move(std::string(path)));
13373|      0|              }
13374|      0|            }
13375|       |            // Set state to path state and decrease pointer by 1.
13376|      0|            state = state::PATH;
13377|      0|            break;
13378|      0|          }
13379|      0|        }
13380|      0|        input_position++;
13381|      0|        break;
13382|      0|      }
13383|      0|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13383:7): [True: 0, False: 4.02k]
  ------------------
13384|      0|        ada_log("RELATIVE_SLASH ",
13385|      0|                helpers::substring(url_data, input_position));
13386|       |
13387|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13388|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13389|      0|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13389:13): [True: 0, False: 0]
  |  Branch (13389:33): [True: 0, False: 0]
  ------------------
13390|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13390:14): [True: 0, False: 0]
  ------------------
13391|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13391:14): [True: 0, False: 0]
  ------------------
13392|       |          // Set state to special authority ignore slashes state.
13393|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13394|      0|        }
13395|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13396|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13396:18): [True: 0, False: 0]
  ------------------
13397|      0|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13397:18): [True: 0, False: 0]
  ------------------
13398|      0|          state = state::AUTHORITY;
13399|      0|        }
13400|       |        // Otherwise, set
13401|       |        // - url's username to base's username,
13402|       |        // - url's password to base's password,
13403|       |        // - url's host to base's host,
13404|       |        // - url's port to base's port,
13405|       |        // - state to path state, and then, decrease pointer by 1.
13406|      0|        else {
13407|       |          if constexpr (result_type_is_ada_url) {
13408|       |            url.username = base_url->username;
13409|       |            url.password = base_url->password;
13410|       |            url.host = base_url->host;
13411|       |            url.port = base_url->port;
13412|      0|          } else {
13413|      0|            url.update_base_authority(base_url->get_href(),
13414|      0|                                      base_url->get_components());
13415|      0|            url.update_host_to_base_host(base_url->get_hostname());
13416|      0|            url.update_base_port(base_url->retrieve_base_port());
13417|      0|          }
13418|      0|          state = state::PATH;
13419|      0|          break;
13420|      0|        }
13421|       |
13422|      0|        input_position++;
13423|      0|        break;
13424|      0|      }
13425|    503|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13425:7): [True: 503, False: 3.52k]
  ------------------
13426|    503|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13427|    503|                helpers::substring(url_data, input_position));
13428|       |
13429|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13430|       |        // then set state to special authority ignore slashes state and increase
13431|       |        // pointer by 1.
13432|    503|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13432:13): [True: 503, False: 0]
  ------------------
13433|    503|          input_position += 2;
13434|    503|        }
13435|       |
13436|    503|        [[fallthrough]];
13437|    503|      }
13438|    503|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13438:7): [True: 0, False: 4.02k]
  ------------------
13439|    503|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13440|    503|                helpers::substring(url_data, input_position));
13441|       |
13442|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13443|       |        // authority state and decrease pointer by 1.
13444|    503|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13444:16): [True: 503, False: 0]
  ------------------
13445|    503|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13445:17): [True: 0, False: 503]
  ------------------
13446|    503|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13446:17): [True: 0, False: 503]
  ------------------
13447|      0|          input_position++;
13448|      0|        }
13449|    503|        state = state::AUTHORITY;
13450|       |
13451|    503|        break;
13452|    503|      }
13453|    503|      case state::QUERY: {
  ------------------
  |  Branch (13453:7): [True: 503, False: 3.52k]
  ------------------
13454|    503|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13455|    503|        if constexpr (store_values) {
13456|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13457|       |          // if url is special; otherwise the query percent-encode set.
13458|    503|          const uint8_t* query_percent_encode_set =
13459|    503|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13459:15): [True: 503, False: 0]
  ------------------
13460|    503|                               : character_sets::QUERY_PERCENT_ENCODE;
13461|       |
13462|       |          // Percent-encode after encoding, with encoding, buffer, and
13463|       |          // queryPercentEncodeSet, and append the result to url's query.
13464|    503|          url.update_base_search(url_data.substr(input_position),
13465|    503|                                 query_percent_encode_set);
13466|    503|          ada_log("QUERY update_base_search completed ");
13467|    503|          if (fragment.has_value()) {
  ------------------
  |  Branch (13467:15): [True: 107, False: 396]
  ------------------
13468|    107|            url.update_unencoded_base_hash(*fragment);
13469|    107|          }
13470|    503|        }
13471|    503|        enforce_max_input_length();
13472|    503|        return url;
13473|    503|      }
13474|    503|      case state::HOST: {
  ------------------
  |  Branch (13474:7): [True: 503, False: 3.52k]
  ------------------
13475|    503|        ada_log("HOST ", helpers::substring(url_data, input_position));
13476|       |
13477|    503|        std::string_view host_view = url_data.substr(input_position);
13478|    503|        auto [location, found_colon] =
13479|    503|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13480|    503|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13480:26): [True: 503, False: 0]
  ------------------
13481|    503|                             ? input_position + location
13482|    503|                             : input_size;
13483|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13484|       |        // Note: the 'found_colon' value is true if and only if a colon was
13485|       |        // encountered while not inside brackets.
13486|    503|        if (found_colon) {
  ------------------
  |  Branch (13486:13): [True: 0, False: 503]
  ------------------
13487|       |          // If buffer is the empty string, validation error, return failure.
13488|       |          // Let host be the result of host parsing buffer with url is not
13489|       |          // special.
13490|      0|          ada_log("HOST parsing ", host_view);
13491|      0|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13491:15): [True: 0, False: 0]
  ------------------
13492|      0|            return url;
13493|      0|          }
13494|      0|          ada_log("HOST parsing results in ", url.get_hostname());
13495|       |          // Set url's host to host, buffer to the empty string, and state to
13496|       |          // port state.
13497|      0|          state = state::PORT;
13498|      0|          input_position++;
13499|      0|        }
13500|       |        // Otherwise, if one of the following is true:
13501|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13502|       |        // - url is special and c is U+005C (\)
13503|       |        // The get_host_delimiter_location function either brings us to
13504|       |        // the colon outside of the bracket, or to one of those characters.
13505|    503|        else {
13506|       |          // If url is special and host_view is the empty string, validation
13507|       |          // error, return failure.
13508|    503|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13508:15): [True: 0, False: 503]
  |  Branch (13508:36): [True: 0, False: 0]
  ------------------
13509|      0|            url.is_valid = false;
13510|      0|            return url;
13511|      0|          }
13512|    503|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13513|       |          // Let host be the result of host parsing host_view with url is not
13514|       |          // special.
13515|    503|          if (host_view.empty()) {
  ------------------
  |  Branch (13515:15): [True: 0, False: 503]
  ------------------
13516|      0|            url.update_base_hostname("");
13517|    503|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13517:22): [True: 0, False: 503]
  ------------------
13518|      0|            return url;
13519|      0|          }
13520|    503|          ada_log("HOST parsing results in ", url.get_hostname(),
13521|    503|                  " href=", url.get_href());
13522|       |
13523|       |          // Set url's host to host, and state to path start state.
13524|    503|          state = state::PATH_START;
13525|    503|        }
13526|       |
13527|    503|        break;
13528|    503|      }
13529|    503|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13529:7): [True: 0, False: 4.02k]
  ------------------
13530|      0|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13531|       |        // Opaque path, query, and fragment are structurally always valid:
13532|       |        // the parser would just percent-encode whatever is there. When we
13533|       |        // are not storing values (can_parse), we can return immediately.
13534|       |        // We must set has_opaque_path = true before returning so that when
13535|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13536|       |        // correctly rejects relative inputs against an opaque-path base
13537|       |        // (e.g. can_parse("", &"W:") must return false).
13538|       |        if constexpr (!store_values) {
13539|       |          url.has_opaque_path = true;
13540|       |          return url;
13541|       |        }
13542|      0|        std::string_view view = url_data.substr(input_position);
13543|       |        // If c is U+003F (?), then set url's query to the empty string and
13544|       |        // state to query state.
13545|      0|        size_t location = view.find('?');
13546|      0|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13546:13): [True: 0, False: 0]
  ------------------
13547|      0|          view.remove_suffix(view.size() - location);
13548|      0|          state = state::QUERY;
13549|      0|          input_position += location + 1;
13550|      0|        } else {
13551|      0|          input_position = input_size + 1;
13552|      0|        }
13553|      0|        url.has_opaque_path = true;
13554|       |
13555|       |        // This is a really unlikely scenario in real world. We should not seek
13556|       |        // to optimize it.
13557|      0|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13557:13): [True: 0, False: 0]
  ------------------
13558|      0|          std::string modified_view =
13559|      0|              std::string(view.substr(0, view.size() - 1)) + "%20";
13560|      0|          url.update_base_pathname(unicode::percent_encode(
13561|      0|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13562|      0|        } else {
13563|      0|          url.update_base_pathname(unicode::percent_encode(
13564|      0|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13565|      0|        }
13566|      0|        break;
13567|    503|      }
13568|      0|      case state::PORT: {
  ------------------
  |  Branch (13568:7): [True: 0, False: 4.02k]
  ------------------
13569|      0|        ada_log("PORT ", helpers::substring(url_data, input_position));
13570|      0|        std::string_view port_view = url_data.substr(input_position);
13571|      0|        input_position += url.parse_port(port_view, true);
13572|      0|        if (!url.is_valid) {
  ------------------
  |  Branch (13572:13): [True: 0, False: 0]
  ------------------
13573|      0|          return url;
13574|      0|        }
13575|      0|        state = state::PATH_START;
13576|      0|        [[fallthrough]];
13577|      0|      }
13578|    503|      case state::PATH_START: {
  ------------------
  |  Branch (13578:7): [True: 503, False: 3.52k]
  ------------------
13579|    503|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13580|       |        // Path, query, and fragment are structurally always valid: the
13581|       |        // parser would just percent-encode whatever is there. When we are
13582|       |        // not storing values (can_parse), we can return immediately since
13583|       |        // no subsequent state can invalidate the URL.
13584|       |        if constexpr (!store_values) {
13585|       |          return url;
13586|       |        }
13587|       |
13588|       |        // If url is special, then:
13589|    503|        if (url.is_special()) {
  ------------------
  |  Branch (13589:13): [True: 503, False: 0]
  ------------------
13590|       |          // Set state to path state.
13591|    503|          state = state::PATH;
13592|       |
13593|       |          // Optimization: Avoiding going into PATH state improves the
13594|       |          // performance of urls ending with /.
13595|    503|          if (input_position == input_size) {
  ------------------
  |  Branch (13595:15): [True: 0, False: 503]
  ------------------
13596|      0|            if constexpr (store_values) {
13597|      0|              url.update_base_pathname("/");
13598|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13598:19): [True: 0, False: 0]
  ------------------
13599|      0|                url.update_unencoded_base_hash(*fragment);
13600|      0|              }
13601|      0|            }
13602|      0|            enforce_max_input_length();
13603|      0|            return url;
13604|      0|          }
13605|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13606|       |          // by 1. We know that (input_position == input_size) is impossible
13607|       |          // here, because of the previous if-check.
13608|    503|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13608:15): [True: 0, False: 503]
  ------------------
13609|      0|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13609:15): [True: 0, False: 0]
  ------------------
13610|      0|            break;
13611|      0|          }
13612|    503|        }
13613|       |        // Otherwise, if state override is not given and c is U+003F (?),
13614|       |        // set url's query to the empty string and state to query state.
13615|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13615:18): [True: 0, False: 0]
  ------------------
13616|      0|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13616:18): [True: 0, False: 0]
  ------------------
13617|      0|          state = state::QUERY;
13618|      0|        }
13619|       |        // Otherwise, if c is not the EOF code point:
13620|      0|        else if (input_position != input_size) {
  ------------------
  |  Branch (13620:18): [True: 0, False: 0]
  ------------------
13621|       |          // Set state to path state.
13622|      0|          state = state::PATH;
13623|       |
13624|       |          // If c is not U+002F (/), then decrease pointer by 1.
13625|      0|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13625:15): [True: 0, False: 0]
  ------------------
13626|      0|            break;
13627|      0|          }
13628|      0|        }
13629|       |
13630|    503|        input_position++;
13631|    503|        break;
13632|    503|      }
13633|    503|      case state::PATH: {
  ------------------
  |  Branch (13633:7): [True: 503, False: 3.52k]
  ------------------
13634|    503|        ada_log("PATH ", helpers::substring(url_data, input_position));
13635|       |        // Path, query, and fragment are structurally always valid: the
13636|       |        // parser would just percent-encode whatever is there. When we are
13637|       |        // not storing values (can_parse), we can return immediately since
13638|       |        // no subsequent state can invalidate the URL.
13639|       |        if constexpr (!store_values) {
13640|       |          return url;
13641|       |        }
13642|    503|        std::string_view view = url_data.substr(input_position);
13643|       |
13644|       |        // Most time, we do not need percent encoding.
13645|       |        // Furthermore, we can immediately locate the '?'.
13646|    503|        size_t locofquestionmark = view.find('?');
13647|    503|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13647:13): [True: 503, False: 0]
  ------------------
13648|    503|          state = state::QUERY;
13649|    503|          view.remove_suffix(view.size() - locofquestionmark);
13650|    503|          input_position += locofquestionmark + 1;
13651|    503|        } else {
13652|      0|          input_position = input_size + 1;
13653|      0|        }
13654|    503|        if constexpr (store_values) {
13655|       |          if constexpr (result_type_is_ada_url) {
13656|       |            helpers::parse_prepared_path(view, url.type, url.path);
13657|    503|          } else {
13658|    503|            url.consume_prepared_path(view);
13659|    503|            ADA_ASSERT_TRUE(url.validate());
13660|    503|          }
13661|    503|        }
13662|    503|        break;
13663|    503|      }
13664|      0|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13664:7): [True: 0, False: 4.02k]
  ------------------
13665|      0|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13666|       |
13667|       |        // If c is U+002F (/) or U+005C (\), then:
13668|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13668:13): [True: 0, False: 0]
  ------------------
13669|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13669:14): [True: 0, False: 0]
  ------------------
13670|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13670:14): [True: 0, False: 0]
  ------------------
13671|      0|          ada_log("FILE_SLASH c is U+002F or U+005C");
13672|       |          // Set state to file host state.
13673|      0|          state = state::FILE_HOST;
13674|      0|          input_position++;
13675|      0|        } else {
13676|      0|          ada_log("FILE_SLASH otherwise");
13677|       |          // If base is non-null and base's scheme is "file", then:
13678|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13679|       |          // base_url_has_value() is true.
13680|      0|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13680:15): [True: 0, False: 0]
  |  Branch (13680:38): [True: 0, False: 0]
  ------------------
13681|       |            // Set url's host to base's host.
13682|       |            if constexpr (result_type_is_ada_url) {
13683|       |              url.host = base_url->host;
13684|      0|            } else {
13685|      0|              url.update_host_to_base_host(base_url->get_host());
13686|      0|            }
13687|       |            // If the code point substring from pointer to the end of input does
13688|       |            // not start with a Windows drive letter and base's path[0] is a
13689|       |            // normalized Windows drive letter, then append base's path[0] to
13690|       |            // url's path.
13691|      0|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13691:17): [True: 0, False: 0]
  ------------------
13692|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13692:19): [True: 0, False: 0]
  ------------------
13693|      0|                      url_data.substr(input_position))) {
13694|      0|                std::string_view first_base_url_path =
13695|      0|                    base_url->get_pathname().substr(1);
13696|      0|                size_t loc = first_base_url_path.find('/');
13697|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13697:21): [True: 0, False: 0]
  ------------------
13698|      0|                  helpers::resize(first_base_url_path, loc);
13699|      0|                }
13700|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13700:21): [True: 0, False: 0]
  ------------------
13701|      0|                        first_base_url_path)) {
13702|       |                  if constexpr (result_type_is_ada_url) {
13703|       |                    url.path += '/';
13704|       |                    url.path += first_base_url_path;
13705|      0|                  } else {
13706|      0|                    url.append_base_pathname(
13707|      0|                        helpers::concat("/", first_base_url_path));
13708|      0|                  }
13709|      0|                }
13710|      0|              }
13711|      0|            }
13712|      0|          }
13713|       |
13714|       |          // Set state to path state, and decrease pointer by 1.
13715|      0|          state = state::PATH;
13716|      0|        }
13717|       |
13718|      0|        break;
13719|    503|      }
13720|      0|      case state::FILE_HOST: {
  ------------------
  |  Branch (13720:7): [True: 0, False: 4.02k]
  ------------------
13721|      0|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13722|      0|        std::string_view view = url_data.substr(input_position);
13723|       |
13724|      0|        size_t location = view.find_first_of("/\\?");
13725|      0|        std::string_view file_host_buffer = view.substr(
13726|      0|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13726:16): [True: 0, False: 0]
  ------------------
13727|       |
13728|      0|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13728:13): [True: 0, False: 0]
  ------------------
13729|      0|          state = state::PATH;
13730|      0|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13730:20): [True: 0, False: 0]
  ------------------
13731|       |          // Set url's host to the empty string.
13732|       |          if constexpr (result_type_is_ada_url) {
13733|       |            url.host = "";
13734|      0|          } else {
13735|      0|            url.update_base_hostname("");
13736|      0|          }
13737|       |          // Set state to path start state.
13738|      0|          state = state::PATH_START;
13739|      0|        } else {
13740|      0|          size_t consumed_bytes = file_host_buffer.size();
13741|      0|          input_position += consumed_bytes;
13742|       |          // Let host be the result of host parsing buffer with url is not
13743|       |          // special.
13744|      0|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13744:15): [True: 0, False: 0]
  ------------------
13745|      0|            return url;
13746|      0|          }
13747|       |
13748|       |          if constexpr (result_type_is_ada_url) {
13749|       |            // If host is "localhost", then set host to the empty string.
13750|       |            if (url.host.has_value() && url.host.value() == "localhost") {
13751|       |              url.host = "";
13752|       |            }
13753|      0|          } else {
13754|      0|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (13754:17): [True: 0, False: 0]
  ------------------
13755|      0|              url.update_base_hostname("");
13756|      0|            }
13757|      0|          }
13758|       |
13759|       |          // Set buffer to the empty string and state to path start state.
13760|      0|          state = state::PATH_START;
13761|      0|        }
13762|       |
13763|      0|        break;
13764|      0|      }
13765|      0|      case state::FILE: {
  ------------------
  |  Branch (13765:7): [True: 0, False: 4.02k]
  ------------------
13766|      0|        ada_log("FILE ", helpers::substring(url_data, input_position));
13767|      0|        std::string_view file_view = url_data.substr(input_position);
13768|       |
13769|      0|        url.set_protocol_as_file();
13770|       |        if constexpr (result_type_is_ada_url) {
13771|       |          // Set url's host to the empty string.
13772|       |          url.host = "";
13773|      0|        } else {
13774|      0|          url.update_base_hostname("");
13775|      0|        }
13776|       |        // If c is U+002F (/) or U+005C (\), then:
13777|      0|        if (input_position != input_size &&
  ------------------
  |  Branch (13777:13): [True: 0, False: 0]
  ------------------
13778|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13778:14): [True: 0, False: 0]
  ------------------
13779|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13779:14): [True: 0, False: 0]
  ------------------
13780|      0|          ada_log("FILE c is U+002F or U+005C");
13781|       |          // Set state to file slash state.
13782|      0|          state = state::FILE_SLASH;
13783|      0|        }
13784|       |        // Otherwise, if base is non-null and base's scheme is "file":
13785|      0|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13785:18): [True: 0, False: 0]
  |  Branch (13785:41): [True: 0, False: 0]
  ------------------
13786|       |          // Set url's host to base's host, url's path to a clone of base's
13787|       |          // path, and url's query to base's query.
13788|      0|          ada_log("FILE base non-null");
13789|       |          if constexpr (result_type_is_ada_url) {
13790|       |            url.host = base_url->host;
13791|       |            url.path = base_url->path;
13792|       |            url.query = base_url->query;
13793|      0|          } else {
13794|      0|            url.update_host_to_base_host(base_url->get_hostname());
13795|      0|            url.update_base_pathname(base_url->get_pathname());
13796|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13796:17): [True: 0, False: 0]
  ------------------
13797|       |              // get_search() returns "" for an empty query string (URL ends
13798|       |              // with '?'). update_base_search("") would incorrectly clear the
13799|       |              // query, so pass "?" to preserve the empty query distinction.
13800|      0|              auto s = base_url->get_search();
13801|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13801:38): [True: 0, False: 0]
  ------------------
13802|      0|            }
13803|      0|          }
13804|      0|          url.has_opaque_path = base_url->has_opaque_path;
13805|       |
13806|       |          // If c is U+003F (?), then set url's query to the empty string and
13807|       |          // state to query state.
13808|      0|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13808:15): [True: 0, False: 0]
  |  Branch (13808:47): [True: 0, False: 0]
  ------------------
13809|      0|            state = state::QUERY;
13810|      0|          }
13811|       |          // Otherwise, if c is not the EOF code point:
13812|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (13812:20): [True: 0, False: 0]
  ------------------
13813|       |            // Set url's query to null.
13814|      0|            url.clear_search();
13815|       |            // If the code point substring from pointer to the end of input does
13816|       |            // not start with a Windows drive letter, then shorten url's path.
13817|      0|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13817:17): [True: 0, False: 0]
  ------------------
13818|       |              if constexpr (result_type_is_ada_url) {
13819|       |                helpers::shorten_path(url.path, url.type);
13820|      0|              } else {
13821|      0|                std::string_view path = url.get_pathname();
13822|      0|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13822:21): [True: 0, False: 0]
  ------------------
13823|      0|                  url.update_base_pathname(std::move(std::string(path)));
13824|      0|                }
13825|      0|              }
13826|      0|            }
13827|       |            // Otherwise:
13828|      0|            else {
13829|       |              // Set url's path to an empty list.
13830|      0|              url.clear_pathname();
13831|      0|              url.has_opaque_path = true;
13832|      0|            }
13833|       |
13834|       |            // Set state to path state and decrease pointer by 1.
13835|      0|            state = state::PATH;
13836|      0|            break;
13837|      0|          }
13838|      0|        }
13839|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13840|      0|        else {
13841|      0|          ada_log("FILE go to path");
13842|      0|          state = state::PATH;
13843|      0|          break;
13844|      0|        }
13845|       |
13846|      0|        input_position++;
13847|      0|        break;
13848|      0|      }
13849|      0|      default:
  ------------------
  |  Branch (13849:7): [True: 0, False: 4.02k]
  ------------------
13850|      0|        unreachable();
13851|  4.02k|    }
13852|  4.02k|  }
13853|  2.48k|  if constexpr (store_values) {
13854|  2.48k|    if (fragment.has_value()) {
  ------------------
  |  Branch (13854:9): [True: 0, False: 2.48k]
  ------------------
13855|      0|      url.update_unencoded_base_hash(*fragment);
13856|      0|    }
13857|  2.48k|  }
13858|  2.48k|  enforce_max_input_length();
13859|  2.48k|  return url;
13860|  2.99k|}
_ZNK3ada14url_aggregator23needs_rollback_snapshotEm:
14152|  1.57k|bool url_aggregator::needs_rollback_snapshot(size_t input_len) const noexcept {
14153|       |  // 3x should cover worst-case percent-encoding of every input byte; the
14154|       |  // constant covers the handful of delimiter bytes a setter may add ("/.", "?",
14155|       |  // "#" and so on)
14156|  1.57k|  return buffer.size() + input_len * 3 + 16 > ada::get_max_input_length();
14157|  1.57k|}
_ZN3ada14url_aggregator10set_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14374|  2.99k|void url_aggregator::set_search(const std::string_view input) {
14375|  2.99k|  ada_log("url_aggregator::set_search ", input);
14376|  2.99k|  ADA_ASSERT_TRUE(validate());
14377|  2.99k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14378|  2.99k|  if (input.empty()) {
  ------------------
  |  Branch (14378:7): [True: 1.41k, False: 1.57k]
  ------------------
14379|  1.41k|    clear_search();
14380|  1.41k|    helpers::strip_trailing_spaces_from_opaque_path(*this);
14381|  1.41k|    return;
14382|  1.41k|  }
14383|       |
14384|  1.57k|  std::string_view stripped = input[0] == '?' ? input.substr(1) : input;
  ------------------
  |  Branch (14384:31): [True: 0, False: 1.57k]
  ------------------
14385|       |  // Fast path: no tabs/newlines (the common case) avoids the std::string
14386|       |  // allocation entirely by passing the view straight to update_base_search.
14387|  1.57k|  std::string cleaned;
14388|  1.57k|  std::string_view effective = stripped;
14389|  1.57k|  if (unicode::has_tabs_or_newline(stripped)) {
  ------------------
  |  Branch (14389:7): [True: 0, False: 1.57k]
  ------------------
14390|      0|    cleaned = stripped;
14391|      0|    helpers::remove_ascii_tab_or_newline(cleaned);
14392|      0|    effective = cleaned;
14393|      0|  }
14394|       |
14395|  1.57k|  auto query_percent_encode_set =
14396|  1.57k|      is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (14396:7): [True: 1.57k, False: 0]
  ------------------
14397|  1.57k|                   : ada::character_sets::QUERY_PERCENT_ENCODE;
14398|       |
14399|  1.57k|  std::optional<url_aggregator> saved_url;
14400|  1.57k|  if (needs_rollback_snapshot(effective.size())) {
  ------------------
  |  Branch (14400:7): [True: 0, False: 1.57k]
  ------------------
14401|      0|    saved_url = *this;
14402|      0|  }
14403|  1.57k|  update_base_search(effective, query_percent_encode_set);
14404|  1.57k|  if (saved_url && buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (14404:7): [True: 0, False: 1.57k]
  |  Branch (14404:20): [True: 0, False: 0]
  ------------------
14405|      0|    *this = std::move(*saved_url);
14406|      0|    return;
14407|      0|  }
14408|  1.57k|  ADA_ASSERT_TRUE(validate());
14409|  1.57k|}
_ZNK3ada14url_aggregator10get_searchEv:
14891|  2.99k|    ada_lifetime_bound {
14892|  2.99k|  ada_log("url_aggregator::get_search");
14893|       |  // If this's URL's query is either null or the empty string, then return the
14894|       |  // empty string. Return U+003F (?), followed by this's URL's query.
14895|  2.99k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (14895:7): [True: 0, False: 2.99k]
  ------------------
14896|      0|    return "";
14897|      0|  }
14898|  2.99k|  auto ending_index = uint32_t(buffer.size());
14899|  2.99k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (14899:7): [True: 413, False: 2.57k]
  ------------------
14900|    413|    ending_index = components.hash_start;
14901|    413|  }
14902|  2.99k|  if (ending_index - components.search_start <= 1) {
  ------------------
  |  Branch (14902:7): [True: 1.36k, False: 1.62k]
  ------------------
14903|  1.36k|    return "";
14904|  1.36k|  }
14905|  1.62k|  return helpers::substring(buffer, components.search_start, ending_index);
14906|  2.99k|}
_ZN3ada4idna7deflate22make_fixed_litlen_huffEv:
  480|      2|inline Huff make_fixed_litlen_huff() {
  481|      2|  Huff h;
  482|      2|  uint8_t lens[288];
  483|       |  // Fixed Huffman: 0-143:8, 144-255:9, 256-279:7, 280-287:8
  484|    290|  for (int i = 0; i <= 143; i++) lens[i] = 8;
  ------------------
  |  Branch (484:19): [True: 288, False: 2]
  ------------------
  485|    226|  for (int i = 144; i <= 255; i++) lens[i] = 9;
  ------------------
  |  Branch (485:21): [True: 224, False: 2]
  ------------------
  486|     50|  for (int i = 256; i <= 279; i++) lens[i] = 7;
  ------------------
  |  Branch (486:21): [True: 48, False: 2]
  ------------------
  487|     18|  for (int i = 280; i <= 287; i++) lens[i] = 8;
  ------------------
  |  Branch (487:21): [True: 16, False: 2]
  ------------------
  488|      2|  h.build(lens, 288);
  489|      2|  return h;
  490|      2|}
_ZN3ada4idna7deflate4Huff5buildEPKhi:
  429|      4|  bool build(const uint8_t* lens, int n) {
  430|      4|    std::memset(counts, 0, sizeof(counts));
  431|      4|    std::memset(first_code, 0, sizeof(first_code));
  432|      4|    std::memset(base_idx, 0, sizeof(base_idx));
  433|      4|    nsymbols = n;
  434|      4|    max_bits = 0;
  435|    644|    for (int i = 0; i < n; i++) {
  ------------------
  |  Branch (435:21): [True: 640, False: 4]
  ------------------
  436|    640|      lengths[i] = lens[i];
  437|    640|      if (lens[i]) {
  ------------------
  |  Branch (437:11): [True: 640, False: 0]
  ------------------
  438|    640|        counts[lens[i]]++;
  439|    640|        if (lens[i] > max_bits) max_bits = lens[i];
  ------------------
  |  Branch (439:13): [True: 6, False: 634]
  ------------------
  440|    640|      }
  441|    640|    }
  442|       |    // Canonical first code for each bit length (RFC 1951).
  443|      4|    int code = 0;
  444|     64|    for (int bits = 1; bits <= MAXBITS; bits++) {
  ------------------
  |  Branch (444:24): [True: 60, False: 4]
  ------------------
  445|     60|      code = (code + counts[bits - 1]) << 1;
  446|     60|      first_code[bits] = code;
  447|     60|    }
  448|      4|    int idx = 0;
  449|     32|    for (int bits = 1; bits <= max_bits; bits++) {
  ------------------
  |  Branch (449:24): [True: 28, False: 4]
  ------------------
  450|     28|      base_idx[bits] = idx;
  451|       |      // assign symbols in symbol order for codes of this length
  452|  5.53k|      for (int sym = 0; sym < n; sym++) {
  ------------------
  |  Branch (452:25): [True: 5.50k, False: 28]
  ------------------
  453|  5.50k|        if (lengths[sym] == bits) {
  ------------------
  |  Branch (453:13): [True: 640, False: 4.86k]
  ------------------
  454|    640|          symbols[idx++] = static_cast<int16_t>(sym);
  455|    640|        }
  456|  5.50k|      }
  457|     28|    }
  458|      4|    return true;
  459|      4|  }
_ZN3ada4idna7deflate20make_fixed_dist_huffEv:
  492|      2|inline Huff make_fixed_dist_huff() {
  493|      2|  Huff h;
  494|      2|  uint8_t lens[32];
  495|     66|  for (int i = 0; i < 32; i++) lens[i] = 5;
  ------------------
  |  Branch (495:19): [True: 64, False: 2]
  ------------------
  496|      2|  h.build(lens, 32);
  497|      2|  return h;
  498|      2|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7621|   124k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7622|   124k|    return character_sets::bit_at(character_set, c);
 7623|   124k|  });
url_search_params.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
 7589|  95.7k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7590|  95.7k|    return character_sets::bit_at(character_set, c);
 7591|  95.7k|  });
url_search_params.cc:_ZN3ada7unicode12_GLOBAL__N_122percent_encode_to_wideEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7975|  3.37k|                            const uint8_t character_set[], std::string& out) {
 7976|       |  // Worst case every byte becomes %XX. Avoids realloc while walking windows.
 7977|  3.37k|  out.reserve(out.size() + static_cast<size_t>(end - p) * 3);
 7978|  3.37k|#if ADA_UNICODE_SSSE3
 7979|  3.37k|  const ssse3_percent_tables tables = load_ssse3_percent_tables(character_set);
 7980|  3.37k|  percent_encode_to_ssse3(p, end, character_set, tables, out);
 7981|       |#elif ADA_NEON
 7982|       |  percent_encode_to_neon(p, end, character_set,
 7983|       |                         load_neon_percent_table(character_set), out);
 7984|       |#elif ADA_RVV
 7985|       |  percent_encode_to_rvv(p, end, character_set, out);
 7986|       |#endif
 7987|  3.37k|}
url_search_params.cc:_ZN3ada7unicode12_GLOBAL__N_125load_ssse3_percent_tablesEPKh:
 7802|  3.37k|load_ssse3_percent_tables(const uint8_t character_set[]) noexcept {
 7803|  3.37k|  ssse3_percent_tables t{};
 7804|  3.37k|  t.cs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set));
 7805|  3.37k|  t.cs_hi =
 7806|  3.37k|      _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set + 16));
 7807|       |  // 1 << (0..7), duplicated so pshufb(index & 7) works for every lane.
 7808|  3.37k|  t.pow2 =
 7809|  3.37k|      _mm_setr_epi8(1, 2, 4, 8, 16, 32, 64, -128, 1, 2, 4, 8, 16, 32, 64, -128);
 7810|  3.37k|  t.mask_0f = _mm_set1_epi8(0x0F);
 7811|  3.37k|  t.mask_07 = _mm_set1_epi8(0x07);
 7812|  3.37k|  t.zero = _mm_setzero_si128();
 7813|  3.37k|  return t;
 7814|  3.37k|}
url_search_params.cc:_ZN3ada7unicode12_GLOBAL__N_123percent_encode_to_ssse3EPKcS3_PKhRKNS1_20ssse3_percent_tablesERNSt3__112basic_stringIcNS9_11char_traitsIcEENS9_9allocatorIcEEEE:
 7833|  3.37k|    const ssse3_percent_tables& tables, std::string& out) {
 7834|       |  // Pair 16-byte windows so a fully clean 32-byte run is one append.
 7835|  14.1k|  while (p + 32 <= end) {
  ------------------
  |  Branch (7835:10): [True: 10.7k, False: 3.37k]
  ------------------
 7836|  10.7k|    const __m128i word0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7837|  10.7k|    const __m128i word1 =
 7838|  10.7k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
 7839|  10.7k|    const int mask0 = ssse3_percent_mask(word0, tables);
 7840|  10.7k|    const int mask1 = ssse3_percent_mask(word1, tables);
 7841|  10.7k|    if ((mask0 | mask1) == 0) {
  ------------------
  |  Branch (7841:9): [True: 550, False: 10.1k]
  ------------------
 7842|    550|      out.append(p, 32);
 7843|  10.1k|    } else {
 7844|  10.1k|      if (mask0 == 0) {
  ------------------
  |  Branch (7844:11): [True: 361, False: 9.83k]
  ------------------
 7845|    361|        out.append(p, 16);
 7846|  9.83k|      } else {
 7847|  9.83k|        encode_mask_window(p, static_cast<uint32_t>(mask0), 16, out);
 7848|  9.83k|      }
 7849|  10.1k|      if (mask1 == 0) {
  ------------------
  |  Branch (7849:11): [True: 626, False: 9.57k]
  ------------------
 7850|    626|        out.append(p + 16, 16);
 7851|  9.57k|      } else {
 7852|  9.57k|        encode_mask_window(p + 16, static_cast<uint32_t>(mask1), 16, out);
 7853|  9.57k|      }
 7854|  10.1k|    }
 7855|  10.7k|    p += 32;
 7856|  10.7k|  }
 7857|  3.37k|  if (p + 16 <= end) {
  ------------------
  |  Branch (7857:7): [True: 1.78k, False: 1.59k]
  ------------------
 7858|  1.78k|    const __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7859|  1.78k|    const int mask = ssse3_percent_mask(word, tables);
 7860|  1.78k|    if (mask == 0) {
  ------------------
  |  Branch (7860:9): [True: 259, False: 1.52k]
  ------------------
 7861|    259|      out.append(p, 16);
 7862|  1.52k|    } else {
 7863|  1.52k|      encode_mask_window(p, static_cast<uint32_t>(mask), 16, out);
 7864|  1.52k|    }
 7865|  1.78k|    p += 16;
 7866|  1.78k|  }
 7867|  3.37k|  percent_encode_to_scalar(p, end, character_set, out);
 7868|  3.37k|}
url_search_params.cc:_ZN3ada7unicode12_GLOBAL__N_118ssse3_percent_maskEDv2_xRKNS1_20ssse3_percent_tablesE:
 7817|  23.2k|    __m128i word, const ssse3_percent_tables& tables) noexcept {
 7818|  23.2k|  const __m128i idx = _mm_and_si128(_mm_srli_epi16(word, 3), tables.mask_0f);
 7819|  23.2k|  const __m128i lo = _mm_shuffle_epi8(tables.cs_lo, idx);
 7820|  23.2k|  const __m128i hi = _mm_shuffle_epi8(tables.cs_hi, idx);
 7821|       |  // Bytes 0x80-0xFF are signed-negative; select the high half of the bitmap.
 7822|  23.2k|  const __m128i high_byte = _mm_cmpgt_epi8(tables.zero, word);
 7823|  23.2k|  const __m128i cs_byte = _mm_or_si128(_mm_and_si128(hi, high_byte),
 7824|  23.2k|                                       _mm_andnot_si128(high_byte, lo));
 7825|  23.2k|  const __m128i bits =
 7826|  23.2k|      _mm_shuffle_epi8(tables.pow2, _mm_and_si128(word, tables.mask_07));
 7827|  23.2k|  const __m128i hits = _mm_and_si128(cs_byte, bits);
 7828|  23.2k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hits, tables.zero)) ^ 0xFFFF;
 7829|  23.2k|}
url_search_params.cc:_ZN3ada7unicode12_GLOBAL__N_118encode_mask_windowEPKcjmRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE:
 7757|  20.9k|                                          size_t width, std::string& out) {
 7758|  20.9k|  uint64_t bits = mask;
 7759|  20.9k|  size_t off = 0;
 7760|   299k|  while (bits != 0) {
  ------------------
  |  Branch (7760:10): [True: 278k, False: 20.9k]
  ------------------
 7761|   278k|    const int zero_run = trailing_zeroes32(static_cast<uint32_t>(bits));
 7762|   278k|    if (zero_run != 0) {
  ------------------
  |  Branch (7762:9): [True: 15.3k, False: 262k]
  ------------------
 7763|  15.3k|      out.append(p + off, static_cast<size_t>(zero_run));
 7764|  15.3k|    }
 7765|   278k|    off += static_cast<size_t>(zero_run);
 7766|   278k|    out.append(character_sets::hex + uint8_t(p[off]) * 4, 3);
 7767|   278k|    ++off;
 7768|   278k|    bits >>= static_cast<unsigned>(zero_run + 1);
 7769|   278k|  }
 7770|  20.9k|  if (off < width) {
  ------------------
  |  Branch (7770:7): [True: 3.73k, False: 17.1k]
  ------------------
 7771|  3.73k|    out.append(p + off, width - off);
 7772|  3.73k|  }
 7773|  20.9k|}
url_search_params.cc:_ZN3ada7unicode12_GLOBAL__N_117trailing_zeroes32Ej:
 7745|   278k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
 7746|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 7747|       |  unsigned long ret;
 7748|       |  _BitScanForward(&ret, input_num);
 7749|       |  return static_cast<int>(ret);
 7750|       |#else
 7751|   278k|  return __builtin_ctz(input_num);
 7752|   278k|#endif
 7753|   278k|}
url_search_params.cc:_ZN3ada7unicode12_GLOBAL__N_124percent_encode_to_scalarEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7778|  3.37k|                                                std::string& out) {
 7779|  19.9k|  for (; p != end; ++p) {
  ------------------
  |  Branch (7779:10): [True: 16.6k, False: 3.37k]
  ------------------
 7780|  16.6k|    if (character_sets::bit_at(character_set, *p)) {
  ------------------
  |  Branch (7780:9): [True: 12.4k, False: 4.14k]
  ------------------
 7781|  12.4k|      out.append(character_sets::hex + uint8_t(*p) * 4, 3);
 7782|  12.4k|    } else {
 7783|  4.14k|      out += *p;
 7784|  4.14k|    }
 7785|  16.6k|  }
 7786|  3.37k|}
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7131|  4.51k|    std::string_view user_input) noexcept {
 7132|       |  // first check for short strings in which case we do it naively.
 7133|  4.51k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (7133:7): [True: 1.72k, False: 2.79k]
  ------------------
 7134|  1.72k|    return std::ranges::any_of(user_input, is_tabs_or_newline);
 7135|  1.72k|  }
 7136|       |  // fast path for long strings (expected to be common)
 7137|  2.79k|  size_t i = 0;
 7138|  2.79k|  const __m128i mask1 = _mm_set1_epi8('\r');
 7139|  2.79k|  const __m128i mask2 = _mm_set1_epi8('\n');
 7140|  2.79k|  const __m128i mask3 = _mm_set1_epi8('\t');
 7141|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
 7142|  2.79k|  __m128i running{0};
 7143|  18.6k|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (7143:10): [True: 15.8k, False: 2.79k]
  ------------------
 7144|  15.8k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
 7145|  15.8k|    running = _mm_or_si128(
 7146|  15.8k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 7147|  15.8k|                                           _mm_cmpeq_epi8(word, mask2))),
 7148|  15.8k|        _mm_cmpeq_epi8(word, mask3));
 7149|  15.8k|  }
 7150|  2.79k|  if (i < user_input.size()) {
  ------------------
  |  Branch (7150:7): [True: 2.60k, False: 181]
  ------------------
 7151|  2.60k|    __m128i word = _mm_loadu_si128(
 7152|  2.60k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
 7153|  2.60k|    running = _mm_or_si128(
 7154|  2.60k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 7155|  2.60k|                                           _mm_cmpeq_epi8(word, mask2))),
 7156|  2.60k|        _mm_cmpeq_epi8(word, mask3));
 7157|  2.60k|  }
 7158|  2.79k|  return _mm_movemask_epi8(running) != 0;
 7159|  4.51k|}
_ZN3ada7unicode18is_tabs_or_newlineEc:
 7016|  5.00k|constexpr bool is_tabs_or_newline(char c) noexcept {
 7017|  5.00k|  return c == '\r' || c == '\n' || c == '\t';
  ------------------
  |  Branch (7017:10): [True: 0, False: 5.00k]
  |  Branch (7017:23): [True: 0, False: 5.00k]
  |  Branch (7017:36): [True: 0, False: 5.00k]
  ------------------
 7018|  5.00k|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 8844|    244|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
 8845|       |  // Fast path: inputs with no tab/newline (the common case) avoid the
 8846|       |  // per-character erase_if scan. has_tabs_or_newline uses SIMD for inputs
 8847|       |  // >= 16 bytes, making this check much cheaper than erase_if's scalar
 8848|       |  // predicate walk when there is nothing to remove.
 8849|    244|  if (!ada::unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (8849:7): [True: 0, False: 244]
  ------------------
 8850|      0|    return;
 8851|      0|  }
 8852|    244|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
 8853|    244|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
 7376|  23.0k|    const char c) noexcept {
 7377|  23.0k|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (7377:10): [True: 568, False: 22.4k]
  |  Branch (7377:23): [True: 1.13k, False: 21.2k]
  |  Branch (7377:36): [True: 155, False: 21.1k]
  ------------------
 7378|  23.0k|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
 7371|  4.82k|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
 7372|  4.82k|  return (unsigned char)c <= ' ';
 7373|  4.82k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   77|  1.53k|    std::string_view input) noexcept {
   78|       |  // The path percent-encode set is the query percent-encode set and U+003F (?),
   79|       |  // U+0060 (`), U+007B ({), and U+007D (}). The query percent-encode set is the
   80|       |  // C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+0023 (#),
   81|       |  // U+003C (<), and U+003E (>). The C0 control percent-encode set are the C0
   82|       |  // controls and all code points greater than U+007E (~).
   83|  1.53k|  size_t i = 0;
   84|  1.53k|  uint8_t accumulator{};
   85|  1.53k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (85:10): [True: 0, False: 1.53k]
  ------------------
   86|      0|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   87|      0|                           path_signature_table[uint8_t(input[i + 1])] |
   88|      0|                           path_signature_table[uint8_t(input[i + 2])] |
   89|      0|                           path_signature_table[uint8_t(input[i + 3])] |
   90|      0|                           path_signature_table[uint8_t(input[i + 4])] |
   91|      0|                           path_signature_table[uint8_t(input[i + 5])] |
   92|      0|                           path_signature_table[uint8_t(input[i + 6])] |
   93|      0|                           path_signature_table[uint8_t(input[i + 7])]);
   94|      0|  }
   95|  1.53k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (95:10): [True: 0, False: 1.53k]
  ------------------
   96|      0|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
   97|      0|  }
   98|  1.53k|  return accumulator;
   99|  1.53k|}
_ZN3ada7unicode13is_alnum_plusEc:
 7348|  2.51k|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
 7349|  2.51k|  return is_alnum_plus_table[uint8_t(c)];
 7350|       |  // A table is almost surely much faster than the
 7351|       |  // following under most compilers: return
 7352|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
 7353|  2.51k|}
_ZZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_ENKUlvE_clEv:
12545|  1.02k|    auto apply_query_and_hash = [&]() {
12546|  1.02k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12546:11): [True: 1.02k, False: 0]
  ------------------
12547|  1.02k|        const size_t q_end =
12548|  1.02k|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12548:13): [True: 226, False: 802]
  ------------------
12549|  1.02k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12550|  1.02k|                                                q_end - query_start - 1),
12551|  1.02k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12552|  1.02k|      }
12553|  1.02k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12553:11): [True: 226, False: 802]
  ------------------
12554|    226|        out.update_unencoded_base_hash(std::string_view(
12555|    226|            input.data() + hash_start + 1, len - hash_start - 1));
12556|    226|      }
12557|  1.02k|    };
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8784|    503|    std::string_view& input) noexcept {
 8785|       |  // compiles down to 20--30 instructions including a class to memchr (C
 8786|       |  // function). this function should be quite fast.
 8787|    503|  size_t location_of_first = input.find('#');
 8788|    503|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (8788:7): [True: 396, False: 107]
  ------------------
 8789|    396|    return std::nullopt;
 8790|    396|  }
 8791|    107|  std::string_view hash = input;
 8792|    107|  hash.remove_prefix(location_of_first + 1);
 8793|    107|  input.remove_suffix(input.size() - location_of_first);
 8794|    107|  return hash;
 8795|    503|}
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9747|     60|find_authority_delimiter_special(std::string_view view) noexcept {
 9748|       |  // performance note: we might be able to gain further performance
 9749|       |  // with SIMD intrinsics.
 9750|    720|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9750:33): [True: 720, False: 0]
  ------------------
 9751|    720|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (9751:9): [True: 60, False: 660]
  ------------------
 9752|     60|      return pos - view.begin();
 9753|     60|    }
 9754|    720|  }
 9755|      0|  return size_t(view.size());
 9756|     60|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9460|    503|    const bool is_special, std::string_view& view) noexcept {
 9461|       |  /**
 9462|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
 9463|       |   * compute a variable called insideBrackets but this variable is only used
 9464|       |   * once, to check whether a ':' character was found outside brackets. Exact
 9465|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
 9466|       |   * It is conceptually simpler and arguably more efficient to just return a
 9467|       |   * Boolean indicating whether ':' was found outside brackets.
 9468|       |   */
 9469|    503|  const size_t view_size = view.size();
 9470|    503|  size_t location = 0;
 9471|    503|  bool found_colon = false;
 9472|       |  /**
 9473|       |   * Performance analysis:
 9474|       |   *
 9475|       |   * We are basically seeking the end of the hostname which can be indicated
 9476|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
 9477|       |   * (where '\\' is only applicable for special URLs). However, these must
 9478|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
 9479|       |   * '?' does not count.
 9480|       |   *
 9481|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
 9482|       |   * the set of delimiters, and that we handle it first.
 9483|       |   *
 9484|       |   * So the trick is to have a fast function that locates the next delimiter.
 9485|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
 9486|       |   * function would be provided by the C++ standard library, but it seems that
 9487|       |   * find_first_of is not very fast, so we are forced to roll our own.
 9488|       |   *
 9489|       |   * We do not break into two loops for speed, but for clarity.
 9490|       |   */
 9491|    503|  if (is_special) {
  ------------------
  |  Branch (9491:7): [True: 503, False: 0]
  ------------------
 9492|       |    // We move to the next delimiter.
 9493|    503|    location = find_next_host_delimiter_special(view, location);
 9494|       |    // Unless we find '[' then we are going only going to have to call
 9495|       |    // find_next_host_delimiter_special once.
 9496|    503|    for (; location < view_size;
  ------------------
  |  Branch (9496:12): [True: 503, False: 0]
  ------------------
 9497|    503|         location = find_next_host_delimiter_special(view, location)) {
 9498|    503|      if (view[location] == '[') {
  ------------------
  |  Branch (9498:11): [True: 0, False: 503]
  ------------------
 9499|      0|        location = view.find(']', location);
 9500|      0|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9500:13): [True: 0, False: 0]
  ------------------
 9501|       |          // performance: view.find might get translated to a memchr, which
 9502|       |          // has no notion of std::string_view::npos, so the code does not
 9503|       |          // reflect the assembly.
 9504|      0|          location = view_size;
 9505|      0|          break;
 9506|      0|        }
 9507|    503|      } else {
 9508|    503|        found_colon = view[location] == ':';
 9509|    503|        break;
 9510|    503|      }
 9511|    503|    }
 9512|    503|  } else {
 9513|       |    // We move to the next delimiter.
 9514|      0|    location = find_next_host_delimiter(view, location);
 9515|       |    // Unless we find '[' then we are going only going to have to call
 9516|       |    // find_next_host_delimiter_special once.
 9517|      0|    for (; location < view_size;
  ------------------
  |  Branch (9517:12): [True: 0, False: 0]
  ------------------
 9518|      0|         location = find_next_host_delimiter(view, location)) {
 9519|      0|      if (view[location] == '[') {
  ------------------
  |  Branch (9519:11): [True: 0, False: 0]
  ------------------
 9520|      0|        location = view.find(']', location);
 9521|      0|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9521:13): [True: 0, False: 0]
  ------------------
 9522|       |          // performance: view.find might get translated to a memchr, which
 9523|       |          // has no notion of std::string_view::npos, so the code does not
 9524|       |          // reflect the assembly.
 9525|      0|          location = view_size;
 9526|      0|          break;
 9527|      0|        }
 9528|      0|      } else {
 9529|      0|        found_colon = view[location] == ':';
 9530|      0|        break;
 9531|      0|      }
 9532|      0|    }
 9533|      0|  }
 9534|       |  // performance: remove_suffix may translate into a single instruction.
 9535|    503|  view.remove_suffix(view_size - location);
 9536|    503|  return {location, found_colon};
 9537|    503|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 9050|    503|    std::string_view view, size_t location) noexcept {
 9051|       |  // first check for short strings in which case we do it naively.
 9052|    503|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (9052:7): [True: 247, False: 256]
  ------------------
 9053|  2.96k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (9053:31): [True: 2.96k, False: 0]
  ------------------
 9054|  2.96k|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (9054:11): [True: 0, False: 2.96k]
  |  Branch (9054:29): [True: 247, False: 2.71k]
  |  Branch (9054:47): [True: 0, False: 2.71k]
  ------------------
 9055|  2.71k|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (9055:11): [True: 0, False: 2.71k]
  |  Branch (9055:29): [True: 0, False: 2.71k]
  ------------------
 9056|    247|        return i;
 9057|    247|      }
 9058|  2.96k|    }
 9059|      0|    return size_t(view.size());
 9060|    247|  }
 9061|       |  // fast path for long strings (expected to be common)
 9062|    256|  size_t i = location;
 9063|    256|  const __m128i mask1 = _mm_set1_epi8(':');
 9064|    256|  const __m128i mask2 = _mm_set1_epi8('/');
 9065|    256|  const __m128i mask3 = _mm_set1_epi8('\\');
 9066|    256|  const __m128i mask4 = _mm_set1_epi8('?');
 9067|    256|  const __m128i mask5 = _mm_set1_epi8('[');
 9068|       |
 9069|    256|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (9069:10): [True: 256, False: 0]
  ------------------
 9070|    256|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 9071|    256|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9072|    256|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9073|    256|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 9074|    256|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9075|    256|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9076|    256|    __m128i m = _mm_or_si128(
 9077|    256|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 9078|    256|    int mask = _mm_movemask_epi8(m);
 9079|    256|    if (mask != 0) {
  ------------------
  |  Branch (9079:9): [True: 256, False: 0]
  ------------------
 9080|    256|      return i + trailing_zeroes(mask);
 9081|    256|    }
 9082|    256|  }
 9083|      0|  if (i < view.size()) {
  ------------------
  |  Branch (9083:7): [True: 0, False: 0]
  ------------------
 9084|      0|    __m128i word =
 9085|      0|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 9086|      0|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9087|      0|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9088|      0|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 9089|      0|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9090|      0|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9091|      0|    __m128i m = _mm_or_si128(
 9092|      0|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 9093|      0|    int mask = _mm_movemask_epi8(m);
 9094|      0|    if (mask != 0) {
  ------------------
  |  Branch (9094:9): [True: 0, False: 0]
  ------------------
 9095|      0|      return view.length() - 16 + trailing_zeroes(mask);
 9096|      0|    }
 9097|      0|  }
 9098|      0|  return size_t(view.length());
 9099|      0|}
_ZN3ada7helpers15trailing_zeroesEj:
 8870|    256|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
 8871|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 8872|       |  unsigned long ret;
 8873|       |  // Search the mask data from least significant bit (LSB)
 8874|       |  // to the most significant bit (MSB) for a set bit (1).
 8875|       |  _BitScanForward(&ret, input_num);
 8876|       |  return (int)ret;
 8877|       |#else   // ADA_REGULAR_VISUAL_STUDIO
 8878|    256|  return __builtin_ctzl(input_num);
 8879|    256|#endif  // ADA_REGULAR_VISUAL_STUDIO
 8880|    256|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
 7318|    503|                                              size_t length) noexcept {
 7319|    503|  size_t i = 0;
 7320|    503|  uint8_t accumulator{};
 7321|  1.50k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7321:10): [True: 1.00k, False: 503]
  ------------------
 7322|  1.00k|    accumulator |=
 7323|  1.00k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7324|  1.00k|    accumulator |=
 7325|  1.00k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
 7326|  1.00k|    accumulator |=
 7327|  1.00k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
 7328|  1.00k|    accumulator |=
 7329|  1.00k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
 7330|  1.00k|  }
 7331|  2.01k|  for (; i < length; i++) {
  ------------------
  |  Branch (7331:10): [True: 1.50k, False: 503]
  ------------------
 7332|  1.50k|    accumulator |=
 7333|  1.50k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7334|  1.50k|  }
 7335|    503|  return accumulator;
 7336|    503|}
_ZZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12868|    503|  const auto enforce_max_input_length = [&]() {
12869|    503|    if constexpr (store_values) {
12870|    503|      if (url.is_valid) {
  ------------------
  |  Branch (12870:11): [True: 503, False: 0]
  ------------------
12871|    503|        if constexpr (result_type_is_ada_url_aggregator) {
12872|    503|          if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12872:15): [True: 0, False: 503]
  ------------------
12873|      0|            url.is_valid = false;
12874|      0|          }
12875|       |        } else {
12876|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
12877|       |            url.is_valid = false;
12878|       |          }
12879|       |        }
12880|    503|      }
12881|    503|    }
12882|    503|  };
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_116authority_has_atEPKhmm:
11342|  2.99k|                                        size_t n) noexcept {
11343|  2.99k|  const size_t rem = n - host_start;
11344|  2.99k|  if (rem == 0) {
  ------------------
  |  Branch (11344:7): [True: 0, False: 2.99k]
  ------------------
11345|      0|    return false;
11346|      0|  }
11347|       |#if ADA_NEON
11348|       |  if (rem >= 16) {
11349|       |    const uint8x16_t w = vld1q_u8(p + host_start);
11350|       |    const uint64_t at = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('@')));
11351|       |    if (at == 0) {
11352|       |      return false;
11353|       |    }
11354|       |    const uint64_t delim = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('/'))) |
11355|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('?'))) |
11356|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('#')));
11357|       |    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
11358|       |  }
11359|       |#elif ADA_SSE2
11360|  2.99k|  if (rem >= 16) {
  ------------------
  |  Branch (11360:7): [True: 1.37k, False: 1.61k]
  ------------------
11361|  1.37k|    const __m128i w =
11362|  1.37k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + host_start));
11363|  1.37k|    const uint16_t at = static_cast<uint16_t>(
11364|  1.37k|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('@'))));
11365|  1.37k|    if (at == 0) {
  ------------------
  |  Branch (11365:9): [True: 1.33k, False: 40]
  ------------------
11366|  1.33k|      return false;
11367|  1.33k|    }
11368|     40|    const uint16_t delim = static_cast<uint16_t>(
11369|     40|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) |
11370|     40|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('?'))) |
11371|     40|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('#'))));
11372|     40|    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
  ------------------
  |  Branch (11372:12): [True: 0, False: 40]
  |  Branch (11372:26): [True: 0, False: 40]
  ------------------
11373|  1.37k|  }
11374|  1.61k|#endif
11375|  1.61k|  const size_t lim = host_start + rem;
11376|  19.4k|  for (size_t i = host_start; i < lim; ++i) {
  ------------------
  |  Branch (11376:31): [True: 19.4k, False: 0]
  ------------------
11377|  19.4k|    const uint8_t c = p[i];
11378|  19.4k|    if (c == '@') {
  ------------------
  |  Branch (11378:9): [True: 0, False: 19.4k]
  ------------------
11379|      0|      return true;
11380|      0|    }
11381|  19.4k|    if (c == '/' || c == '?' || c == '#') {
  ------------------
  |  Branch (11381:9): [True: 1.61k, False: 17.8k]
  |  Branch (11381:21): [True: 0, False: 17.8k]
  |  Branch (11381:33): [True: 0, False: 17.8k]
  ------------------
11382|  1.61k|      return false;
11383|  1.61k|    }
11384|  19.4k|  }
11385|      0|  return false;
11386|  1.61k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_:
11407|  2.99k|                                     bool& has_x) noexcept {
11408|  2.99k|  has_upper = false;
11409|  2.99k|  has_x = false;
11410|  2.99k|  size_t i = start;
11411|  2.99k|#if ADA_PARSER_SSSE3
11412|  2.99k|  if (parser_use_ssse3() && (len - start >= 16)) {
  ------------------
  |  Branch (11412:7): [True: 2.99k, False: 0]
  |  Branch (11412:29): [True: 1.37k, False: 1.61k]
  ------------------
11413|  1.37k|    const __m128i lo_tbl = nibble_load(k_host_nibbles.low);
11414|  1.37k|    const __m128i hi_tbl = nibble_load(k_host_nibbles.high);
11415|  1.37k|    const __m128i x_splat = _mm_set1_epi8('x');
11416|  1.37k|    auto visit = [&](size_t at) noexcept -> bool {
11417|  1.37k|      const __m128i w =
11418|  1.37k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11419|  1.37k|      const int up = sse2_uppercase(w);
11420|  1.37k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11421|  1.37k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11422|  1.37k|      if (mask == 0) {
11423|  1.37k|        if (up != 0) {
11424|  1.37k|          has_upper = true;
11425|  1.37k|        }
11426|  1.37k|        if (xs != 0) {
11427|  1.37k|          has_x = true;
11428|  1.37k|        }
11429|  1.37k|        return false;
11430|  1.37k|      }
11431|  1.37k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11432|  1.37k|      const int valid = (1 << hit) - 1;
11433|  1.37k|      if ((up & valid) != 0) {
11434|  1.37k|        has_upper = true;
11435|  1.37k|      }
11436|  1.37k|      if ((xs & valid) != 0) {
11437|  1.37k|        has_x = true;
11438|  1.37k|      }
11439|  1.37k|      end = at + static_cast<size_t>(hit);
11440|  1.37k|      return true;
11441|  1.37k|    };
11442|  1.37k|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11442:12): [True: 809, False: 562]
  ------------------
11443|    809|      if (visit(i) || visit(i + 16)) {
  ------------------
  |  Branch (11443:11): [True: 809, False: 0]
  |  Branch (11443:23): [True: 0, False: 0]
  ------------------
11444|    809|        return k_host_class[b[end]] == 1;
11445|    809|      }
11446|    809|    }
11447|    562|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11447:12): [True: 562, False: 0]
  ------------------
11448|    562|      if (visit(i)) {
  ------------------
  |  Branch (11448:11): [True: 562, False: 0]
  ------------------
11449|    562|        return k_host_class[b[end]] == 1;
11450|    562|      }
11451|    562|    }
11452|       |    // Overlapping tail only after a full 16-byte step so the window cannot
11453|       |    // start before `start` (a prior '/' would otherwise look like a host stop).
11454|      0|    if (i > start && i < len) {
  ------------------
  |  Branch (11454:9): [True: 0, False: 0]
  |  Branch (11454:22): [True: 0, False: 0]
  ------------------
11455|      0|      if (visit(len - 16) && end >= i) {
  ------------------
  |  Branch (11455:11): [True: 0, False: 0]
  |  Branch (11455:30): [True: 0, False: 0]
  ------------------
11456|      0|        return k_host_class[b[end]] == 1;
11457|      0|      }
11458|      0|      end = len;
11459|      0|      return true;
11460|      0|    }
11461|      0|  }
11462|       |#elif ADA_SSE2
11463|       |  const __m128i x_splat = _mm_set1_epi8('x');
11464|       |  for (; i + 32 <= len; i += 32) {
11465|       |    for (size_t off = 0; off < 32; off += 16) {
11466|       |      const __m128i w =
11467|       |          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + off));
11468|       |      const int up = sse2_uppercase(w);
11469|       |      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11470|       |      const int mask = sse2_host_stop(w);
11471|       |      if (mask != 0) {
11472|       |        const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11473|       |        const int valid = (1 << hit) - 1;
11474|       |        if ((up & valid) != 0) {
11475|       |          has_upper = true;
11476|       |        }
11477|       |        if ((xs & valid) != 0) {
11478|       |          has_x = true;
11479|       |        }
11480|       |        end = i + off + static_cast<size_t>(hit);
11481|       |        return k_host_class[b[end]] == 1;
11482|       |      }
11483|       |      if (up != 0) {
11484|       |        has_upper = true;
11485|       |      }
11486|       |      if (xs != 0) {
11487|       |        has_x = true;
11488|       |      }
11489|       |    }
11490|       |  }
11491|       |  for (; i + 16 <= len; i += 16) {
11492|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11493|       |    const int up = sse2_uppercase(w);
11494|       |    const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11495|       |    const int mask = sse2_host_stop(w);
11496|       |    if (mask != 0) {
11497|       |      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11498|       |      const int valid = (1 << hit) - 1;
11499|       |      if ((up & valid) != 0) {
11500|       |        has_upper = true;
11501|       |      }
11502|       |      if ((xs & valid) != 0) {
11503|       |        has_x = true;
11504|       |      }
11505|       |      end = i + static_cast<size_t>(hit);
11506|       |      return k_host_class[b[end]] == 1;
11507|       |    }
11508|       |    if (up != 0) {
11509|       |      has_upper = true;
11510|       |    }
11511|       |    if (xs != 0) {
11512|       |      has_x = true;
11513|       |    }
11514|       |  }
11515|       |#elif ADA_NEON
11516|       |  if (len - start >= 16) {
11517|       |    const uint8x16_t lo_tbl = vld1q_u8(k_host_nibbles.low);
11518|       |    const uint8x16_t hi_tbl = vld1q_u8(k_host_nibbles.high);
11519|       |    const uint8x16_t x_splat = vdupq_n_u8('x');
11520|       |    auto visit = [&](size_t at) noexcept -> bool {
11521|       |      const uint8x16_t w = vld1q_u8(b + at);
11522|       |      const uint64_t up = neon_uppercase(w);
11523|       |      const uint64_t xs = neon_nibble_bits(vceqq_u8(w, x_splat));
11524|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11525|       |      if (bits == 0) {
11526|       |        if (up != 0) {
11527|       |          has_upper = true;
11528|       |        }
11529|       |        if (xs != 0) {
11530|       |          has_x = true;
11531|       |        }
11532|       |        return false;
11533|       |      }
11534|       |      const size_t hit = static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11535|       |      const uint64_t valid = (hit == 0) ? 0 : (uint64_t{1} << (hit * 4)) - 1;
11536|       |      if ((up & valid) != 0) {
11537|       |        has_upper = true;
11538|       |      }
11539|       |      if ((xs & valid) != 0) {
11540|       |        has_x = true;
11541|       |      }
11542|       |      end = at + hit;
11543|       |      return true;
11544|       |    };
11545|       |    for (; i + 32 <= len; i += 32) {
11546|       |      if (visit(i) || visit(i + 16)) {
11547|       |        return k_host_class[b[end]] == 1;
11548|       |      }
11549|       |    }
11550|       |    for (; i + 16 <= len; i += 16) {
11551|       |      if (visit(i)) {
11552|       |        return k_host_class[b[end]] == 1;
11553|       |      }
11554|       |    }
11555|       |    if (i > start && i < len) {
11556|       |      if (visit(len - 16) && end >= i) {
11557|       |        return k_host_class[b[end]] == 1;
11558|       |      }
11559|       |      end = len;
11560|       |      return true;
11561|       |    }
11562|       |  }
11563|       |#endif
11564|  19.4k|  for (; i < len; ++i) {
  ------------------
  |  Branch (11564:10): [True: 19.4k, False: 0]
  ------------------
11565|  19.4k|    const uint8_t c = b[i];
11566|  19.4k|    const uint8_t cls = k_host_class[c];
11567|  19.4k|    if (cls == 1) {
  ------------------
  |  Branch (11567:9): [True: 1.61k, False: 17.8k]
  ------------------
11568|  1.61k|      end = i;
11569|  1.61k|      return true;
11570|  1.61k|    }
11571|  17.8k|    if (cls == 2) {
  ------------------
  |  Branch (11571:9): [True: 0, False: 17.8k]
  ------------------
11572|      0|      return false;
11573|      0|    }
11574|  17.8k|    if (c >= 'A' && c <= 'Z') {
  ------------------
  |  Branch (11574:9): [True: 16.1k, False: 1.61k]
  |  Branch (11574:21): [True: 0, False: 16.1k]
  ------------------
11575|      0|      has_upper = true;
11576|  17.8k|    } else if (c == 'x') {
  ------------------
  |  Branch (11576:16): [True: 1.61k, False: 16.1k]
  ------------------
11577|  1.61k|      has_x = true;
11578|  1.61k|    }
11579|  17.8k|  }
11580|      0|  end = len;
11581|      0|  return true;
11582|  1.61k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_116parser_use_ssse3Ev:
11392|  9.21k|ada_really_inline bool parser_use_ssse3() noexcept {
11393|  9.21k|#ifdef ADA_PARSER_NEED_SSSE3_TARGET
11394|  9.21k|  static const bool supported = __builtin_cpu_supports("ssse3") != 0;
11395|  9.21k|  return supported;
11396|       |#else
11397|       |  return true;
11398|       |#endif
11399|  9.21k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_111nibble_loadEPKh:
11223|  6.72k|ADA_PARSER_SIMD __m128i nibble_load(const uint8_t* t) noexcept {
11224|  6.72k|  return _mm_load_si128(reinterpret_cast<const __m128i*>(t));
11225|  6.72k|}
url_search_params.cc:_ZZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_ENKUlmE_clEm:
11416|  1.37k|    auto visit = [&](size_t at) noexcept -> bool {
11417|  1.37k|      const __m128i w =
11418|  1.37k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11419|  1.37k|      const int up = sse2_uppercase(w);
11420|  1.37k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11421|  1.37k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11422|  1.37k|      if (mask == 0) {
  ------------------
  |  Branch (11422:11): [True: 0, False: 1.37k]
  ------------------
11423|      0|        if (up != 0) {
  ------------------
  |  Branch (11423:13): [True: 0, False: 0]
  ------------------
11424|      0|          has_upper = true;
11425|      0|        }
11426|      0|        if (xs != 0) {
  ------------------
  |  Branch (11426:13): [True: 0, False: 0]
  ------------------
11427|      0|          has_x = true;
11428|      0|        }
11429|      0|        return false;
11430|      0|      }
11431|  1.37k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11432|  1.37k|      const int valid = (1 << hit) - 1;
11433|  1.37k|      if ((up & valid) != 0) {
  ------------------
  |  Branch (11433:11): [True: 0, False: 1.37k]
  ------------------
11434|      0|        has_upper = true;
11435|      0|      }
11436|  1.37k|      if ((xs & valid) != 0) {
  ------------------
  |  Branch (11436:11): [True: 1.37k, False: 0]
  ------------------
11437|  1.37k|        has_x = true;
11438|  1.37k|      }
11439|  1.37k|      end = at + static_cast<size_t>(hit);
11440|  1.37k|      return true;
11441|  1.37k|    };
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_114sse2_uppercaseEDv2_x:
11310|  1.37k|ada_really_inline int sse2_uppercase(__m128i w) noexcept {
11311|  1.37k|  return _mm_movemask_epi8(_mm_and_si128(
11312|  1.37k|      _mm_cmpgt_epi8(w, _mm_set1_epi8(static_cast<char>('A' - 1))),
11313|  1.37k|      _mm_cmplt_epi8(w, _mm_set1_epi8(static_cast<char>('Z' + 1)))));
11314|  1.37k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_117ssse3_nibble_maskEDv2_xS2_S2_:
11230|  4.39k|                                          __m128i hi_tbl) noexcept {
11231|  4.39k|  const __m128i nibble = _mm_set1_epi8(0x0F);
11232|  4.39k|  const __m128i lo = _mm_and_si128(w, nibble);
11233|  4.39k|  const __m128i hi = _mm_and_si128(_mm_srli_epi16(w, 4), nibble);
11234|  4.39k|  const __m128i hit =
11235|  4.39k|      _mm_and_si128(_mm_shuffle_epi8(lo_tbl, lo), _mm_shuffle_epi8(hi_tbl, hi));
11236|  4.39k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hit, _mm_setzero_si128())) ^ 0xFFFF;
11237|  4.39k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_117trailing_zeroes32Ej:
11202|  3.25k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
11203|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
11204|       |  unsigned long ret;
11205|       |  _BitScanForward(&ret, input_num);
11206|       |  return static_cast<int>(ret);
11207|       |#else
11208|  3.25k|  return __builtin_ctz(input_num);
11209|  3.25k|#endif
11210|  3.25k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_113scan_path_runEPKhRmmRb:
11642|  2.99k|                                   bool& maybe_dot_segment) noexcept {
11643|  2.99k|  const size_t run_start = i;
11644|  2.99k|#if ADA_PARSER_SSSE3
11645|  2.99k|  if (parser_use_ssse3() && (i + 16 <= len)) {
  ------------------
  |  Branch (11645:7): [True: 2.99k, False: 0]
  |  Branch (11645:29): [True: 933, False: 2.05k]
  ------------------
11646|    933|    const __m128i lo_tbl = nibble_load(k_path_nibbles.low);
11647|    933|    const __m128i hi_tbl = nibble_load(k_path_nibbles.high);
11648|    933|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11648:12): [True: 675, False: 258]
  ------------------
11649|    675|      const __m128i w0 =
11650|    675|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11651|    675|      const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);
11652|    675|      if (m0 != 0) {
  ------------------
  |  Branch (11652:11): [True: 675, False: 0]
  ------------------
11653|    675|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m0));
11654|    675|        note_dots_in_window(b, i, run_start, w0, (1 << hit_bit) - 1,
11655|    675|                            maybe_dot_segment);
11656|    675|        i += static_cast<size_t>(hit_bit);
11657|    675|        return;
11658|    675|      }
11659|      0|      note_dots_in_window(b, i, run_start, w0, 0xFFFF, maybe_dot_segment);
11660|      0|      const __m128i w1 =
11661|      0|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));
11662|      0|      const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);
11663|      0|      if (m1 != 0) {
  ------------------
  |  Branch (11663:11): [True: 0, False: 0]
  ------------------
11664|      0|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m1));
11665|      0|        note_dots_in_window(b, i + 16, run_start, w1, (1 << hit_bit) - 1,
11666|      0|                            maybe_dot_segment);
11667|      0|        i += 16 + static_cast<size_t>(hit_bit);
11668|      0|        return;
11669|      0|      }
11670|      0|      note_dots_in_window(b, i + 16, run_start, w1, 0xFFFF, maybe_dot_segment);
11671|      0|    }
11672|    258|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11672:12): [True: 258, False: 0]
  ------------------
11673|    258|      const __m128i w =
11674|    258|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11675|    258|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11676|    258|      if (mask != 0) {
  ------------------
  |  Branch (11676:11): [True: 258, False: 0]
  ------------------
11677|    258|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11678|    258|        note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11679|    258|                            maybe_dot_segment);
11680|    258|        i += static_cast<size_t>(hit_bit);
11681|    258|        return;
11682|    258|      }
11683|      0|      note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11684|      0|    }
11685|      0|    if (i > run_start && i < len) {
  ------------------
  |  Branch (11685:9): [True: 0, False: 0]
  |  Branch (11685:26): [True: 0, False: 0]
  ------------------
11686|      0|      const __m128i w =
11687|      0|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16));
11688|      0|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11689|      0|      if (mask != 0) {
  ------------------
  |  Branch (11689:11): [True: 0, False: 0]
  ------------------
11690|      0|        const size_t hit =
11691|      0|            len - 16 +
11692|      0|            static_cast<size_t>(trailing_zeroes32(static_cast<uint32_t>(mask)));
11693|      0|        if (hit >= i) {
  ------------------
  |  Branch (11693:13): [True: 0, False: 0]
  ------------------
11694|      0|          for (size_t j = i; j < hit; ++j) {
  ------------------
  |  Branch (11694:30): [True: 0, False: 0]
  ------------------
11695|      0|            if (b[j] == '.') {
  ------------------
  |  Branch (11695:17): [True: 0, False: 0]
  ------------------
11696|      0|              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11697|      0|            }
11698|      0|          }
11699|      0|          i = hit;
11700|      0|          return;
11701|      0|        }
11702|      0|      }
11703|      0|      for (size_t j = i; j < len; ++j) {
  ------------------
  |  Branch (11703:26): [True: 0, False: 0]
  ------------------
11704|      0|        if (b[j] == '.') {
  ------------------
  |  Branch (11704:13): [True: 0, False: 0]
  ------------------
11705|      0|          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11706|      0|        }
11707|      0|      }
11708|      0|      i = len;
11709|      0|      return;
11710|      0|    }
11711|      0|  }
11712|       |#elif ADA_SSE2
11713|       |  for (; i + 16 <= len; i += 16) {
11714|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11715|       |    const int mask = sse2_path_stop(w);
11716|       |    if (mask != 0) {
11717|       |      const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11718|       |      note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11719|       |                          maybe_dot_segment);
11720|       |      i += static_cast<size_t>(hit_bit);
11721|       |      return;
11722|       |    }
11723|       |    note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11724|       |  }
11725|       |#elif ADA_NEON
11726|       |  if (i + 16 <= len) {
11727|       |    const uint8x16_t lo_tbl = vld1q_u8(k_path_nibbles.low);
11728|       |    const uint8x16_t hi_tbl = vld1q_u8(k_path_nibbles.high);
11729|       |    for (; i + 32 <= len; i += 32) {
11730|       |      const uint8x16_t w0 = vld1q_u8(b + i);
11731|       |      const uint64_t bits0 = neon_table_stop(w0, lo_tbl, hi_tbl);
11732|       |      if (bits0 != 0) {
11733|       |        const size_t hit_off =
11734|       |            static_cast<size_t>(trailing_zeroes64(bits0)) >> 2;
11735|       |        const uint64_t valid =
11736|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11737|       |        note_dots_in_window_neon(b, i, run_start, w0, valid, maybe_dot_segment);
11738|       |        i += hit_off;
11739|       |        return;
11740|       |      }
11741|       |      note_dots_in_window_neon(b, i, run_start, w0, ~uint64_t{0},
11742|       |                               maybe_dot_segment);
11743|       |      const uint8x16_t w1 = vld1q_u8(b + i + 16);
11744|       |      const uint64_t bits1 = neon_table_stop(w1, lo_tbl, hi_tbl);
11745|       |      if (bits1 != 0) {
11746|       |        const size_t hit_off =
11747|       |            static_cast<size_t>(trailing_zeroes64(bits1)) >> 2;
11748|       |        const uint64_t valid =
11749|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11750|       |        note_dots_in_window_neon(b, i + 16, run_start, w1, valid,
11751|       |                                 maybe_dot_segment);
11752|       |        i += 16 + hit_off;
11753|       |        return;
11754|       |      }
11755|       |      note_dots_in_window_neon(b, i + 16, run_start, w1, ~uint64_t{0},
11756|       |                               maybe_dot_segment);
11757|       |    }
11758|       |    for (; i + 16 <= len; i += 16) {
11759|       |      const uint8x16_t w = vld1q_u8(b + i);
11760|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11761|       |      if (bits != 0) {
11762|       |        const size_t hit_off =
11763|       |            static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11764|       |        const uint64_t valid =
11765|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11766|       |        note_dots_in_window_neon(b, i, run_start, w, valid, maybe_dot_segment);
11767|       |        i += hit_off;
11768|       |        return;
11769|       |      }
11770|       |      note_dots_in_window_neon(b, i, run_start, w, ~uint64_t{0},
11771|       |                               maybe_dot_segment);
11772|       |    }
11773|       |    if (i > run_start && i < len) {
11774|       |      const uint8x16_t w = vld1q_u8(b + len - 16);
11775|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11776|       |      if (bits != 0) {
11777|       |        const size_t hit =
11778|       |            len - 16 + (static_cast<size_t>(trailing_zeroes64(bits)) >> 2);
11779|       |        if (hit >= i) {
11780|       |          for (size_t j = i; j < hit; ++j) {
11781|       |            if (b[j] == '.') {
11782|       |              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11783|       |            }
11784|       |          }
11785|       |          i = hit;
11786|       |          return;
11787|       |        }
11788|       |      }
11789|       |      for (size_t j = i; j < len; ++j) {
11790|       |        if (b[j] == '.') {
11791|       |          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11792|       |        }
11793|       |      }
11794|       |      i = len;
11795|       |      return;
11796|       |    }
11797|       |  }
11798|       |#endif
11799|  2.05k|  for (; i < len; ++i) {
  ------------------
  |  Branch (11799:10): [True: 2.05k, False: 0]
  ------------------
11800|  2.05k|    const uint8_t c = b[i];
11801|  2.05k|    const uint8_t cls = k_path[c];
11802|  2.05k|    if (cls != 0) {
  ------------------
  |  Branch (11802:9): [True: 2.05k, False: 0]
  ------------------
11803|  2.05k|      return;
11804|  2.05k|    }
11805|      0|    if (c == '.') {
  ------------------
  |  Branch (11805:9): [True: 0, False: 0]
  ------------------
11806|      0|      note_possible_dot_segment(b, i, run_start, maybe_dot_segment);
11807|      0|    }
11808|      0|  }
11809|  2.05k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_119note_dots_in_windowEPKhmmDv2_xiRb:
11599|    933|                                           bool& maybe_dot_segment) noexcept {
11600|    933|  if (maybe_dot_segment || valid_mask == 0) {
  ------------------
  |  Branch (11600:7): [True: 0, False: 933]
  |  Branch (11600:28): [True: 933, False: 0]
  ------------------
11601|    933|    return;
11602|    933|  }
11603|      0|  const int dots =
11604|      0|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('.'))) & valid_mask;
11605|      0|  if (dots == 0) {
  ------------------
  |  Branch (11605:7): [True: 0, False: 0]
  ------------------
11606|      0|    return;
11607|      0|  }
11608|      0|  const int slashes =
11609|      0|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) & valid_mask;
11610|      0|  if (((dots & 1) != 0 && (i == run_start || b[i - 1] == '/')) ||
  ------------------
  |  Branch (11610:8): [True: 0, False: 0]
  |  Branch (11610:28): [True: 0, False: 0]
  |  Branch (11610:46): [True: 0, False: 0]
  ------------------
11611|      0|      (dots & (slashes << 1)) != 0) {
  ------------------
  |  Branch (11611:7): [True: 0, False: 0]
  ------------------
11612|      0|    maybe_dot_segment = true;
11613|      0|  }
11614|      0|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_114scan_query_runEPKhRmm:
11952|  2.99k|                                    size_t len) noexcept {
11953|  2.99k|  ADA_SCAN_STOP_RUN(k_query_nibbles, k_query, sse2_query_stop);
  ------------------
  |  |11815|  2.99k|  do {                                                                       \
  |  |11816|  2.99k|    const size_t scan_start = i;                                             \
  |  |11817|  2.99k|    if (parser_use_ssse3() && (i + 16 <= len)) {                             \
  |  |  ------------------
  |  |  |  Branch (11817:9): [True: 2.99k, False: 0]
  |  |  |  Branch (11817:31): [True: 897, False: 2.09k]
  |  |  ------------------
  |  |11818|    897|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11819|    897|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11820|  1.25k|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11820:14): [True: 921, False: 332]
  |  |  ------------------
  |  |11821|    921|        const __m128i w0 =                                                   \
  |  |11822|    921|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11823|    921|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11824|    921|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11824:13): [True: 506, False: 415]
  |  |  ------------------
  |  |11825|    506|          i += static_cast<size_t>(                                          \
  |  |11826|    506|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11827|    506|          return;                                                            \
  |  |11828|    506|        }                                                                    \
  |  |11829|    921|        const __m128i w1 =                                                   \
  |  |11830|    415|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11831|    415|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11832|    415|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11832:13): [True: 59, False: 356]
  |  |  ------------------
  |  |11833|     59|          i += 16 + static_cast<size_t>(                                     \
  |  |11834|     59|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11835|     59|          return;                                                            \
  |  |11836|     59|        }                                                                    \
  |  |11837|    415|      }                                                                      \
  |  |11838|    897|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11838:14): [True: 268, False: 135]
  |  |  ------------------
  |  |11839|    268|        const __m128i w =                                                    \
  |  |11840|    268|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11841|    268|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11842|    268|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11842:13): [True: 197, False: 71]
  |  |  ------------------
  |  |11843|    197|          i += static_cast<size_t>(                                          \
  |  |11844|    197|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11845|    197|          return;                                                            \
  |  |11846|    197|        }                                                                    \
  |  |11847|    268|      }                                                                      \
  |  |11848|    332|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11848:11): [True: 135, False: 0]
  |  |  |  Branch (11848:29): [True: 109, False: 26]
  |  |  ------------------
  |  |11849|    109|        const __m128i w =                                                    \
  |  |11850|    109|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11851|    109|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11852|    109|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11852:13): [True: 55, False: 54]
  |  |  ------------------
  |  |11853|     55|          const size_t hit = len - 16 +                                      \
  |  |11854|     55|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11855|     55|                                 static_cast<uint32_t>(mask)));              \
  |  |11856|     55|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11856:15): [True: 55, False: 0]
  |  |  ------------------
  |  |11857|     55|            i = hit;                                                         \
  |  |11858|     55|            return;                                                          \
  |  |11859|     55|          }                                                                  \
  |  |11860|     55|        }                                                                    \
  |  |11861|    109|        i = len;                                                             \
  |  |11862|     54|        return;                                                              \
  |  |11863|    109|      }                                                                      \
  |  |11864|    135|    }                                                                        \
  |  |11865|  3.08k|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11865:12): [True: 1.76k, False: 1.32k]
  |  |  ------------------
  |  |11866|  1.76k|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11866:11): [True: 794, False: 966]
  |  |  ------------------
  |  |11867|    794|        return;                                                              \
  |  |11868|    794|      }                                                                      \
  |  |11869|  1.76k|    }                                                                        \
  |  |11870|  2.11k|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11870:12): [Folded, False: 1.32k]
  |  |  ------------------
  ------------------
11954|  2.99k|}
url_search_params.cc:_ZN3ada6parser12_GLOBAL__N_113scan_hash_runEPKhRmm:
11957|    249|                                   size_t len) noexcept {
11958|    249|  ADA_SCAN_STOP_RUN(k_hash_nibbles, k_hash, sse2_hash_stop);
  ------------------
  |  |11815|    249|  do {                                                                       \
  |  |11816|    249|    const size_t scan_start = i;                                             \
  |  |11817|    249|    if (parser_use_ssse3() && (i + 16 <= len)) {                             \
  |  |  ------------------
  |  |  |  Branch (11817:9): [True: 249, False: 0]
  |  |  |  Branch (11817:31): [True: 159, False: 90]
  |  |  ------------------
  |  |11818|    159|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11819|    159|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11820|    241|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11820:14): [True: 151, False: 90]
  |  |  ------------------
  |  |11821|    151|        const __m128i w0 =                                                   \
  |  |11822|    151|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11823|    151|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11824|    151|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11824:13): [True: 41, False: 110]
  |  |  ------------------
  |  |11825|     41|          i += static_cast<size_t>(                                          \
  |  |11826|     41|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11827|     41|          return;                                                            \
  |  |11828|     41|        }                                                                    \
  |  |11829|    151|        const __m128i w1 =                                                   \
  |  |11830|    110|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11831|    110|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11832|    110|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11832:13): [True: 28, False: 82]
  |  |  ------------------
  |  |11833|     28|          i += 16 + static_cast<size_t>(                                     \
  |  |11834|     28|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11835|     28|          return;                                                            \
  |  |11836|     28|        }                                                                    \
  |  |11837|    110|      }                                                                      \
  |  |11838|    159|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11838:14): [True: 70, False: 53]
  |  |  ------------------
  |  |11839|     70|        const __m128i w =                                                    \
  |  |11840|     70|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11841|     70|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11842|     70|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11842:13): [True: 37, False: 33]
  |  |  ------------------
  |  |11843|     37|          i += static_cast<size_t>(                                          \
  |  |11844|     37|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11845|     37|          return;                                                            \
  |  |11846|     37|        }                                                                    \
  |  |11847|     70|      }                                                                      \
  |  |11848|     90|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11848:11): [True: 53, False: 0]
  |  |  |  Branch (11848:29): [True: 43, False: 10]
  |  |  ------------------
  |  |11849|     43|        const __m128i w =                                                    \
  |  |11850|     43|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11851|     43|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11852|     43|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11852:13): [True: 32, False: 11]
  |  |  ------------------
  |  |11853|     32|          const size_t hit = len - 16 +                                      \
  |  |11854|     32|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11855|     32|                                 static_cast<uint32_t>(mask)));              \
  |  |11856|     32|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11856:15): [True: 32, False: 0]
  |  |  ------------------
  |  |11857|     32|            i = hit;                                                         \
  |  |11858|     32|            return;                                                          \
  |  |11859|     32|          }                                                                  \
  |  |11860|     32|        }                                                                    \
  |  |11861|     43|        i = len;                                                             \
  |  |11862|     11|        return;                                                              \
  |  |11863|     43|      }                                                                      \
  |  |11864|     53|    }                                                                        \
  |  |11865|    249|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11865:12): [True: 122, False: 69]
  |  |  ------------------
  |  |11866|    122|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11866:11): [True: 31, False: 91]
  |  |  ------------------
  |  |11867|     31|        return;                                                              \
  |  |11868|     31|      }                                                                      \
  |  |11869|    122|    }                                                                        \
  |  |11870|    100|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11870:12): [Folded, False: 69]
  |  |  ------------------
  ------------------
11959|    249|}
url_search_params.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
13963|    503|    ada::url_components& components, uint32_t new_difference) {
13964|    503|  components.username_end += new_difference;
13965|    503|  components.host_start += new_difference;
13966|    503|  components.host_end += new_difference;
13967|    503|  components.pathname_start += new_difference;
13968|    503|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13968:7): [True: 0, False: 503]
  ------------------
13969|      0|    components.search_start += new_difference;
13970|      0|  }
13971|    503|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13971:7): [True: 0, False: 503]
  ------------------
13972|      0|    components.hash_start += new_difference;
13973|      0|  }
13974|    503|}
_ZN3ada14url_aggregator10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14333|  1.02k|ada_really_inline void url_aggregator::parse_path(std::string_view input) {
14334|  1.02k|  ada_log("url_aggregator::parse_path ", input);
14335|  1.02k|  ADA_ASSERT_TRUE(validate());
14336|  1.02k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14337|  1.02k|  std::string tmp_buffer;
14338|  1.02k|  std::string_view internal_input;
14339|  1.02k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (14339:7): [True: 0, False: 1.02k]
  ------------------
14340|      0|    tmp_buffer = input;
14341|       |    // Optimization opportunity: Instead of copying and then pruning, we could
14342|       |    // just directly build the string from user_input.
14343|      0|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
14344|      0|    internal_input = tmp_buffer;
14345|  1.02k|  } else {
14346|  1.02k|    internal_input = input;
14347|  1.02k|  }
14348|       |
14349|       |  // If url is special, then:
14350|  1.02k|  if (is_special()) {
  ------------------
  |  Branch (14350:7): [True: 1.02k, False: 0]
  ------------------
14351|  1.02k|    if (internal_input.empty()) {
  ------------------
  |  Branch (14351:9): [True: 0, False: 1.02k]
  ------------------
14352|      0|      update_base_pathname("/");
14353|  1.02k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (14353:16): [True: 1.02k, False: 0]
  |  Branch (14353:46): [True: 0, False: 0]
  ------------------
14354|  1.02k|      consume_prepared_path(internal_input.substr(1));
14355|  1.02k|    } else {
14356|      0|      consume_prepared_path(internal_input);
14357|      0|    }
14358|  1.02k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (14358:14): [True: 0, False: 0]
  ------------------
14359|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (14359:9): [True: 0, False: 0]
  ------------------
14360|      0|      consume_prepared_path(internal_input.substr(1));
14361|      0|    } else {
14362|      0|      consume_prepared_path(internal_input);
14363|      0|    }
14364|      0|  } else {
14365|       |    // Non-special URLs with an empty host can have their paths erased
14366|       |    // Path-only URLs cannot have their paths erased
14367|      0|    if (components.host_start == components.host_end && !has_authority()) {
  ------------------
  |  Branch (14367:9): [True: 0, False: 0]
  |  Branch (14367:57): [True: 0, False: 0]
  ------------------
14368|      0|      update_base_pathname("/");
14369|      0|    }
14370|      0|  }
14371|  1.02k|  ADA_ASSERT_TRUE(validate());
14372|  1.02k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14466|    503|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
14467|    503|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
14468|    503|          " bytes]");
14469|    503|  ADA_ASSERT_TRUE(validate());
14470|    503|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14471|    503|  if (input.empty()) {
  ------------------
  |  Branch (14471:7): [True: 0, False: 503]
  ------------------
14472|      0|    return is_valid = false;
14473|      0|  }  // technically unnecessary.
14474|       |  // If input starts with U+005B ([), then:
14475|    503|  if (input[0] == '[') {
  ------------------
  |  Branch (14475:7): [True: 0, False: 503]
  ------------------
14476|       |    // If input does not end with U+005D (]), validation error, return failure.
14477|      0|    if (input.back() != ']') {
  ------------------
  |  Branch (14477:9): [True: 0, False: 0]
  ------------------
14478|      0|      return is_valid = false;
14479|      0|    }
14480|      0|    ada_log("parse_host ipv6");
14481|       |
14482|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
14483|       |    // trailing U+005D (]) removed.
14484|      0|    input.remove_prefix(1);
14485|      0|    input.remove_suffix(1);
14486|      0|    return parse_ipv6(input);
14487|      0|  }
14488|       |
14489|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
14490|       |  // input.
14491|    503|  if (!is_special()) {
  ------------------
  |  Branch (14491:7): [True: 0, False: 503]
  ------------------
14492|      0|    return parse_opaque_host(input);
14493|      0|  }
14494|       |  // Let domain be the result of running UTF-8 decode without BOM on the
14495|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
14496|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
14497|       |  // which case we do not need to call the expensive 'to_ascii' if a few
14498|       |  // conditions are met: no '%' and no 'xn-' subsequence.
14499|       |
14500|       |  // Often, the input does not contain any forbidden code points, and no upper
14501|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
14502|       |  // optimize for such a common case.
14503|       |
14504|       |  // Fast path: try to parse as pure decimal IPv4 first.
14505|    503|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
14506|    503|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (14506:7): [True: 0, False: 503]
  ------------------
14507|      0|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (14507:9): [True: 0, False: 0]
  |  Branch (14507:27): [True: 0, False: 0]
  ------------------
14508|      0|      update_base_hostname(input.substr(0, input.size() - 1));
14509|      0|    } else {
14510|      0|      update_base_hostname(input);
14511|      0|    }
14512|      0|    host_type = IPV4;
14513|      0|    is_valid = true;
14514|      0|    ada_log("parse_host fast path decimal ipv4");
14515|      0|    ADA_ASSERT_TRUE(validate());
14516|      0|    return true;
14517|      0|  }
14518|    503|  uint8_t is_forbidden_or_upper =
14519|    503|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
14520|    503|                                                             input.size());
14521|    503|  static constexpr std::string_view xn_dash{"xn-", 3};
14522|    503|  if ((is_forbidden_or_upper & 1) == 0) {
  ------------------
  |  Branch (14522:7): [True: 503, False: 0]
  ------------------
14523|    503|    if ((is_forbidden_or_upper & 2) != 0) {
  ------------------
  |  Branch (14523:9): [True: 0, False: 503]
  ------------------
14524|      0|      std::string lowered(input);
14525|      0|      unicode::to_lower_ascii(lowered.data(), lowered.size());
14526|      0|      if (lowered.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14526:11): [True: 0, False: 0]
  ------------------
14527|      0|          lowered.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14527:11): [True: 0, False: 0]
  ------------------
14528|       |        // Check IPv4 before mutating so failures leave the buffer untouched.
14529|       |        // This also avoids a double buffer rewrite for hex/octal IPv4.
14530|      0|        if (checkers::is_ipv4(lowered)) {
  ------------------
  |  Branch (14530:13): [True: 0, False: 0]
  ------------------
14531|      0|          ada_log("parse_host fast path ipv4");
14532|      0|          return parse_ipv4(lowered, false);
14533|      0|        }
14534|      0|        update_base_hostname(lowered);
14535|      0|        ada_log("parse_host fast path ", get_hostname());
14536|      0|        is_valid = true;
14537|      0|        return true;
14538|      0|      }
14539|    503|    } else if (input.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14539:16): [True: 503, False: 0]
  ------------------
14540|    503|               input.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14540:16): [True: 0, False: 0]
  ------------------
14541|       |      // Check IPv4 before mutating so failures leave the buffer untouched.
14542|    503|      if (checkers::is_ipv4(input)) {
  ------------------
  |  Branch (14542:11): [True: 0, False: 503]
  ------------------
14543|      0|        ada_log("parse_host fast path ipv4");
14544|      0|        return parse_ipv4(input, false);
14545|      0|      }
14546|    503|      update_base_hostname(input);
14547|    503|      ada_log("parse_host fast path ", get_hostname());
14548|    503|      is_valid = true;
14549|    503|      return true;
14550|    503|    }
14551|    503|  } else if (const size_t first_percent = input.find('%');
14552|      0|             first_percent != std::string_view::npos) {
  ------------------
  |  Branch (14552:14): [True: 0, False: 0]
  ------------------
14553|      0|    std::string decoded = unicode::percent_decode(input, first_percent);
14554|      0|    const uint8_t decoded_flags =
14555|      0|        unicode::contains_forbidden_domain_code_point_or_upper(decoded.data(),
14556|      0|                                                               decoded.size());
14557|      0|    if ((decoded_flags & 1) == 0) {
  ------------------
  |  Branch (14557:9): [True: 0, False: 0]
  ------------------
14558|      0|      if ((decoded_flags & 2) != 0) {
  ------------------
  |  Branch (14558:11): [True: 0, False: 0]
  ------------------
14559|      0|        unicode::to_lower_ascii(decoded.data(), decoded.size());
14560|      0|      }
14561|      0|      if (decoded.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14561:11): [True: 0, False: 0]
  ------------------
14562|      0|          decoded.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14562:11): [True: 0, False: 0]
  ------------------
14563|       |        // Check IPv4 before mutating so failures leave the buffer untouched.
14564|      0|        if (checkers::is_ipv4(decoded)) {
  ------------------
  |  Branch (14564:13): [True: 0, False: 0]
  ------------------
14565|      0|          return parse_ipv4(decoded, false);
14566|      0|        }
14567|      0|        update_base_hostname(decoded);
14568|      0|        is_valid = true;
14569|      0|        return true;
14570|      0|      }
14571|      0|    }
14572|      0|  }
14573|       |  // We have encountered at least one forbidden code point or the input contains
14574|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
14575|       |  // conversion.
14576|       |
14577|      0|  ada_log("parse_host calling to_ascii");
14578|      0|  std::optional<std::string> host = std::string(get_hostname());
14579|      0|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
14580|      0|  if (!is_valid) {
  ------------------
  |  Branch (14580:7): [True: 0, False: 0]
  ------------------
14581|      0|    ada_log("parse_host to_ascii returns false");
14582|      0|    return is_valid = false;
14583|      0|  }
14584|      0|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
14585|      0|          " bytes]");
14586|       |
14587|      0|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (14587:7): [True: 0, False: 0]
  ------------------
14588|      0|                          ada::unicode::is_forbidden_domain_code_point)) {
14589|      0|    return is_valid = false;
14590|      0|  }
14591|       |
14592|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
14593|       |  // asciiDomain.
14594|      0|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (14594:7): [True: 0, False: 0]
  ------------------
14595|      0|    ada_log("parse_host got ipv4 ", *host);
14596|      0|    return parse_ipv4(host.value(), false);
14597|      0|  }
14598|       |
14599|      0|  update_base_hostname(host.value());
14600|      0|  ADA_ASSERT_TRUE(validate());
14601|      0|  return true;
14602|      0|}
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15448|  1.53k|inline void url_aggregator::consume_prepared_path(std::string_view input) {
15449|  1.53k|  ada_log("url_aggregator::consume_prepared_path ", input);
15450|       |  /***
15451|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
15452|       |   * unfortunate. This particular function is nearly identical, except that it
15453|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
15454|       |   * very common) merely appends to the buffer. This is the same trivial path as
15455|       |   * with helpers::parse_prepared_path, except that we have the additional check
15456|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
15457|       |   * modify it, and then insert it back into the buffer.
15458|       |   */
15459|  1.53k|  uint8_t accumulator = checkers::path_signature(input);
15460|       |  // Let us first detect a trivial case.
15461|       |  // If it is special, we check that we have no dot, no %,  no \ and no
15462|       |  // character needing percent encoding. Otherwise, we check that we have no %,
15463|       |  // no dot, and no character needing percent encoding.
15464|  1.53k|  constexpr uint8_t need_encoding = 1;
15465|  1.53k|  constexpr uint8_t backslash_char = 2;
15466|  1.53k|  constexpr uint8_t dot_char = 4;
15467|  1.53k|  constexpr uint8_t percent_char = 8;
15468|  1.53k|  bool special = type != ada::scheme::NOT_SPECIAL;
15469|  1.53k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (15469:39): [True: 0, False: 1.53k]
  ------------------
15470|      0|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (15470:39): [True: 0, False: 0]
  ------------------
15471|  1.53k|  bool trivial_path =
15472|  1.53k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (15472:7): [True: 1.53k, False: 0]
  |  Branch (15472:8): [True: 1.53k, False: 0]
  ------------------
15473|  1.53k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
15474|      0|                  0)) &&
15475|  1.53k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (15475:7): [True: 1.53k, False: 0]
  ------------------
15476|  1.53k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (15476:7): [True: 0, False: 1.53k]
  |  Branch (15476:34): [True: 0, False: 0]
  ------------------
15477|       |    // '4' means that we have at least one dot, but nothing that requires
15478|       |    // percent encoding or decoding. The only part that is not trivial is
15479|       |    // that we may have single dots and double dots path segments.
15480|       |    // If we have such segments, then we either have a path that begins
15481|       |    // with '.' (easy to check), or we have the sequence './'.
15482|       |    // Note: input cannot be empty, it must at least contain one character ('.')
15483|       |    // Note: we know that '\' is not present.
15484|      0|    if (input[0] != '.') {
  ------------------
  |  Branch (15484:9): [True: 0, False: 0]
  ------------------
15485|      0|      size_t slashdot = 0;
15486|      0|      bool dot_is_file = true;
15487|      0|      for (;;) {
15488|      0|        slashdot = input.find("/.", slashdot);
15489|      0|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (15489:13): [True: 0, False: 0]
  ------------------
15490|      0|          break;
15491|      0|        } else {  // uncommon
15492|       |          // only three cases matter: /./, /.. or a final /
15493|      0|          slashdot += 2;
15494|      0|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (15494:28): [True: 0, False: 0]
  |  Branch (15494:56): [True: 0, False: 0]
  ------------------
15495|      0|                           input[slashdot] == '/');
  ------------------
  |  Branch (15495:28): [True: 0, False: 0]
  ------------------
15496|      0|        }
15497|      0|      }
15498|      0|      trivial_path = dot_is_file;
15499|      0|    }
15500|      0|  }
15501|  1.53k|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (15501:7): [True: 1.53k, False: 0]
  |  Branch (15501:23): [True: 1.53k, False: 0]
  ------------------
15502|  1.53k|    ada_log("parse_path trivial");
15503|  1.53k|    buffer += '/';
15504|  1.53k|    buffer += input;
15505|  1.53k|    return;
15506|  1.53k|  }
15507|      0|  std::string path = std::string(get_pathname());
15508|       |  // We are going to need to look a bit at the path, but let us see if we can
15509|       |  // ignore percent encoding *and* backslashes *and* percent characters.
15510|       |  // Except for the trivial case, this is likely to capture 99% of paths out
15511|       |  // there.
15512|      0|  bool fast_path =
15513|      0|      (special &&
  ------------------
  |  Branch (15513:8): [True: 0, False: 0]
  ------------------
15514|      0|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (15514:8): [True: 0, False: 0]
  ------------------
15515|      0|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (15515:7): [True: 0, False: 0]
  ------------------
15516|      0|  if (fast_path) {
  ------------------
  |  Branch (15516:7): [True: 0, False: 0]
  ------------------
15517|      0|    ada_log("parse_prepared_path fast");
15518|       |    // Here we don't need to worry about \ or percent encoding.
15519|       |    // We also do not have a file protocol. We might have dots, however,
15520|       |    // but dots must as appear as '.', and they cannot be encoded because
15521|       |    // the symbol '%' is not present.
15522|      0|    size_t previous_location = 0;  // We start at 0.
15523|      0|    do {
15524|      0|      size_t new_location = input.find('/', previous_location);
15525|       |      // std::string_view path_view = input;
15526|       |      //  We process the last segment separately:
15527|      0|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (15527:11): [True: 0, False: 0]
  ------------------
15528|      0|        std::string_view path_view = input.substr(previous_location);
15529|      0|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (15529:13): [True: 0, False: 0]
  ------------------
15530|       |          // e.g., if you receive ".." with an empty path, you go to "/".
15531|      0|          if (path.empty()) {
  ------------------
  |  Branch (15531:15): [True: 0, False: 0]
  ------------------
15532|      0|            path = '/';
15533|      0|            update_base_pathname(path);
15534|      0|            return;
15535|      0|          }
15536|       |          // Fast case where we have nothing to do:
15537|      0|          if (path.back() == '/') {
  ------------------
  |  Branch (15537:15): [True: 0, False: 0]
  ------------------
15538|      0|            update_base_pathname(path);
15539|      0|            return;
15540|      0|          }
15541|       |          // If you have the path "/joe/myfriend",
15542|       |          // then you delete 'myfriend'.
15543|      0|          path.resize(path.rfind('/') + 1);
15544|      0|          update_base_pathname(path);
15545|      0|          return;
15546|      0|        }
15547|      0|        path += '/';
15548|      0|        if (path_view != ".") {
  ------------------
  |  Branch (15548:13): [True: 0, False: 0]
  ------------------
15549|      0|          path.append(path_view);
15550|      0|        }
15551|      0|        update_base_pathname(path);
15552|      0|        return;
15553|      0|      } else {
15554|       |        // This is a non-final segment.
15555|      0|        std::string_view path_view =
15556|      0|            input.substr(previous_location, new_location - previous_location);
15557|      0|        previous_location = new_location + 1;
15558|      0|        if (path_view == "..") {
  ------------------
  |  Branch (15558:13): [True: 0, False: 0]
  ------------------
15559|      0|          size_t last_delimiter = path.rfind('/');
15560|      0|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (15560:15): [True: 0, False: 0]
  ------------------
15561|      0|            path.erase(last_delimiter);
15562|      0|          }
15563|      0|        } else if (path_view != ".") {
  ------------------
  |  Branch (15563:20): [True: 0, False: 0]
  ------------------
15564|      0|          path += '/';
15565|      0|          path.append(path_view);
15566|      0|        }
15567|      0|      }
15568|      0|    } while (true);
  ------------------
  |  Branch (15568:14): [True: 0, Folded]
  ------------------
15569|      0|  } else {
15570|      0|    ada_log("parse_path slow");
15571|       |    // we have reached the general case
15572|      0|    bool needs_percent_encoding = (accumulator & 1);
15573|      0|    std::string path_buffer_tmp;
15574|      0|    do {
15575|      0|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (15575:26): [True: 0, False: 0]
  |  Branch (15575:37): [True: 0, False: 0]
  ------------------
15576|      0|                            ? input.find_first_of("/\\")
15577|      0|                            : input.find('/');
15578|      0|      std::string_view path_view = input;
15579|      0|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (15579:11): [True: 0, False: 0]
  ------------------
15580|      0|        path_view.remove_suffix(path_view.size() - location);
15581|      0|        input.remove_prefix(location + 1);
15582|      0|      }
15583|       |      // path_buffer is either path_view or it might point at a percent encoded
15584|       |      // temporary string.
15585|      0|      std::string_view path_buffer =
15586|      0|          (needs_percent_encoding &&
  ------------------
  |  Branch (15586:12): [True: 0, False: 0]
  ------------------
15587|      0|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (15587:12): [True: 0, False: 0]
  ------------------
15588|      0|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
15589|      0|              ? path_buffer_tmp
15590|      0|              : path_view;
15591|      0|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (15591:11): [True: 0, False: 0]
  ------------------
15592|      0|        helpers::shorten_path(path, type);
15593|      0|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (15593:13): [True: 0, False: 0]
  ------------------
15594|      0|          path += '/';
15595|      0|        }
15596|      0|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (15596:18): [True: 0, False: 0]
  ------------------
15597|      0|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (15597:18): [True: 0, False: 0]
  ------------------
15598|      0|        path += '/';
15599|      0|      }
15600|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
15601|      0|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (15601:16): [True: 0, False: 0]
  ------------------
15602|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
15603|       |        // Windows drive letter, then replace the second code point in
15604|       |        // path_buffer with U+003A (:).
15605|      0|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (15605:13): [True: 0, False: 0]
  |  Branch (15605:48): [True: 0, False: 0]
  ------------------
15606|      0|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (15606:13): [True: 0, False: 0]
  ------------------
15607|      0|          path += '/';
15608|      0|          path += path_buffer[0];
15609|      0|          path += ':';
15610|      0|          path_buffer.remove_prefix(2);
15611|      0|          path.append(path_buffer);
15612|      0|        } else {
15613|       |          // Append path_buffer to url's path.
15614|      0|          path += '/';
15615|      0|          path.append(path_buffer);
15616|      0|        }
15617|      0|      }
15618|      0|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (15618:11): [True: 0, False: 0]
  ------------------
15619|      0|        update_base_pathname(path);
15620|      0|        return;
15621|      0|      }
15622|      0|    } while (true);
  ------------------
  |  Branch (15622:14): [True: 0, Folded]
  ------------------
15623|      0|  }
15624|      0|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   14|    503|ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept {
   15|       |  // The string is not empty and does not contain upper case ASCII characters.
   16|    503|  if (!last_label_may_be_a_number(view)) {
  ------------------
  |  Branch (16:7): [True: 503, False: 0]
  ------------------
   17|    503|    return false;
   18|    503|  }
   19|       |  // If the address ends with a dot, we need to prune it (special case).
   20|      0|  if (view.ends_with('.')) {
  ------------------
  |  Branch (20:7): [True: 0, False: 0]
  ------------------
   21|      0|    view.remove_suffix(1);
   22|      0|  }
   23|       |  // From the last character, find the last dot.
   24|      0|  size_t last_dot = view.rfind('.');
   25|      0|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (25:7): [True: 0, False: 0]
  ------------------
   26|       |    // We have at least one dot.
   27|      0|    view.remove_prefix(last_dot + 1);
   28|      0|  }
   29|       |  /** Optimization opportunity: we have basically identified the last number of
   30|       |     the ipv4 if we return true here. We might as well parse it and have at
   31|       |     least one number parsed when we get to parse_ipv4. */
   32|      0|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (32:7): [True: 0, False: 0]
  ------------------
   33|      0|    return true;
   34|      0|  }
   35|       |  // It could be hex (0x), but not if there is a single character.
   36|      0|  if (view.size() == 1) {
  ------------------
  |  Branch (36:7): [True: 0, False: 0]
  ------------------
   37|      0|    return false;
   38|      0|  }
   39|       |  // It must start with 0x.
   40|      0|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (40:7): [True: 0, False: 0]
  ------------------
   41|      0|    return false;
   42|      0|  }
   43|       |  // We must allow "0x".
   44|      0|  if (view.size() == 2) {
  ------------------
  |  Branch (44:7): [True: 0, False: 0]
  ------------------
   45|      0|    return true;
   46|      0|  }
   47|       |  // We have 0x followed by some characters, we need to check that they are
   48|       |  // hexadecimals.
   49|      0|  view.remove_prefix(2);
   50|      0|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   51|      0|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
13981|    503|    const std::string_view input_with_colon) {
13982|    503|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
13983|    503|  ADA_ASSERT_TRUE(validate());
13984|    503|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
13985|    503|  std::string_view input{input_with_colon};
13986|    503|  input.remove_suffix(1);
13987|    503|  auto parsed_type = ada::scheme::get_scheme_type(input);
13988|    503|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
13989|       |  /**
13990|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
13991|       |   *http, https), in which case, we can go really fast.
13992|       |   **/
13993|    503|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (13993:7): [True: 503, False: 0]
  ------------------
13994|       |    if constexpr (has_state_override) {
13995|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
13996|       |      // then return.
13997|       |      if (is_special() != is_input_special) {
13998|       |        return false;
13999|       |      }
14000|       |
14001|       |      // If url includes credentials or has a non-null port, and buffer is
14002|       |      // "file", then return.
14003|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14004|       |          parsed_type == ada::scheme::type::FILE) {
14005|       |        return false;
14006|       |      }
14007|       |
14008|       |      // If url's scheme is "file" and its host is an empty host, then return.
14009|       |      // An empty host is the empty string.
14010|       |      if (type == ada::scheme::type::FILE &&
14011|       |          components.host_start == components.host_end) {
14012|       |        return false;
14013|       |      }
14014|       |    }
14015|       |
14016|    503|    type = parsed_type;
14017|    503|    set_scheme_from_view_with_colon(input_with_colon);
14018|       |
14019|       |    if constexpr (has_state_override) {
14020|       |      // This is uncommon.
14021|       |      uint16_t urls_scheme_port = get_special_port();
14022|       |
14023|       |      if (urls_scheme_port) {
14024|       |        // If url's port is url's scheme's default port, then set url's port to
14025|       |        // null.
14026|       |        if (components.port == urls_scheme_port) {
14027|       |          clear_port();
14028|       |        }
14029|       |      }
14030|       |    }
14031|    503|  } else {  // slow path
14032|      0|    std::string _buffer(input);
14033|       |    // Next function is only valid if the input is ASCII and returns false
14034|       |    // otherwise, but it seems that we always have ascii content so we do not
14035|       |    // need to check the return value.
14036|      0|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
14037|       |
14038|       |    if constexpr (has_state_override) {
14039|       |      // The state-override validation errors below ("return" in the WHATWG URL
14040|       |      // parser) leave the URL unchanged. The setter contract is
14041|       |      // "true on success, false if the scheme is invalid" -- the fast path
14042|       |      // above already returns false here, so the slow path must agree.
14043|       |
14044|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
14045|       |      // then return. If url's scheme is not a special scheme and buffer is a
14046|       |      // special scheme, then return.
14047|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
14048|       |        return false;
14049|       |      }
14050|       |
14051|       |      // If url includes credentials or has a non-null port, and buffer is
14052|       |      // "file", then return.
14053|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14054|       |          _buffer == "file") {
14055|       |        return false;
14056|       |      }
14057|       |
14058|       |      // If url's scheme is "file" and its host is an empty host, then return.
14059|       |      // An empty host is the empty string.
14060|       |      if (type == ada::scheme::type::FILE &&
14061|       |          components.host_start == components.host_end) {
14062|       |        return false;
14063|       |      }
14064|       |    }
14065|       |
14066|      0|    set_scheme(_buffer);
14067|       |
14068|       |    if constexpr (has_state_override) {
14069|       |      // This is uncommon.
14070|       |      uint16_t urls_scheme_port = get_special_port();
14071|       |
14072|       |      if (urls_scheme_port) {
14073|       |        // If url's port is url's scheme's default port, then set url's port to
14074|       |        // null.
14075|       |        if (components.port == urls_scheme_port) {
14076|       |          clear_port();
14077|       |        }
14078|       |      }
14079|       |    }
14080|      0|  }
14081|    503|  ADA_ASSERT_TRUE(validate());
14082|    503|  return true;
14083|    503|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14107|    503|    std::string_view new_scheme_with_colon) {
14108|    503|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
14109|    503|          new_scheme_with_colon);
14110|    503|  ADA_ASSERT_TRUE(validate());
14111|    503|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
14112|    503|                  new_scheme_with_colon.back() == ':');
14113|       |  // next line could overflow but unsigned arithmetic has well-defined
14114|       |  // overflows.
14115|    503|  uint32_t new_difference =
14116|    503|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
14117|       |
14118|    503|  if (buffer.empty()) {
  ------------------
  |  Branch (14118:7): [True: 503, False: 0]
  ------------------
14119|    503|    buffer.append(new_scheme_with_colon);
14120|    503|  } else {
14121|      0|    buffer.erase(0, components.protocol_end);
14122|      0|    buffer.insert(0, new_scheme_with_colon);
14123|      0|  }
14124|    503|  components.protocol_end += new_difference;
14125|       |
14126|    503|  apply_shifted_non_scheme_offsets(components, new_difference);
14127|    503|  ADA_ASSERT_TRUE(validate());
14128|    503|}
_ZN3ada7helpers38strip_trailing_spaces_from_opaque_pathINS_14url_aggregatorEEEvRT_:
 9723|  1.41k|ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url) {
 9724|  1.41k|  ada_log("helpers::strip_trailing_spaces_from_opaque_path");
 9725|  1.41k|  if (!url.has_opaque_path) return;
  ------------------
  |  Branch (9725:7): [True: 1.41k, False: 0]
  ------------------
 9726|      0|  if (url.has_hash()) return;
  ------------------
  |  Branch (9726:7): [True: 0, False: 0]
  ------------------
 9727|      0|  if (url.has_search()) return;
  ------------------
  |  Branch (9727:7): [True: 0, False: 0]
  ------------------
 9728|       |
 9729|      0|  auto path = std::string(url.get_pathname());
 9730|      0|  while (!path.empty() && path.back() == ' ') {
  ------------------
  |  Branch (9730:10): [True: 0, False: 0]
  |  Branch (9730:27): [True: 0, False: 0]
  ------------------
 9731|      0|    path.resize(path.size() - 1);
 9732|      0|  }
 9733|      0|  url.update_base_pathname(path);
 9734|      0|}

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5868|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1085|   474k|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1086|   474k|  return !!(a[i >> 3] & (1 << (i & 7)));
 1087|   474k|}
_ZN3ada8url_baseD2Ev:
 1711|  5.98k|  virtual ~url_base() = default;
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3305|  2.99k|  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_:
 3787|  2.99k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3660|  2.99k|      : impl_base(in_place, std::forward<Args>(args)...),
 3661|  2.99k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2675|  2.99k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorC2EOS0_:
 7921|  2.99k|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZN3ada14url_aggregatorD2Ev:
 7924|  5.98k|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6922|    503|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6923|    503|  if (scheme.empty()) {
  ------------------
  |  Branch (6923:7): [True: 0, False: 503]
  ------------------
 6924|      0|    return ada::scheme::NOT_SPECIAL;
 6925|      0|  }
 6926|    503|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6927|    503|  const std::string_view target = details::is_special_list[hash_value];
 6928|    503|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6928:7): [True: 503, False: 0]
  ------------------
 6929|    503|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6929:7): [True: 503, False: 0]
  ------------------
 6930|    503|          details::scheme_keys[hash_value]) {
 6931|    503|    return ada::scheme::type(hash_value);
 6932|    503|  } else {
 6933|      0|    return ada::scheme::NOT_SPECIAL;
 6934|      0|  }
 6935|    503|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6863|    503|inline uint64_t branchless_load5(const char* p, size_t n) {
 6864|    503|  uint64_t input = (uint8_t)p[0];
 6865|    503|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6866|    503|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6867|    503|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6868|    503|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6869|    503|  return input;
 6870|    503|}
_ZN3ada14url_aggregatorC2Ev:
 7919|  2.99k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 5016|  2.99k|  url_components() = default;
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1459|    503|try_parse_ipv4_fast(std::string_view input) noexcept {
 1460|    503|  const size_t len = input.size();
 1461|       |  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
 1462|    503|  if (len < 7 || len > 16) [[unlikely]] {
  ------------------
  |  Branch (1462:7): [True: 0, False: 503]
  |  Branch (1462:18): [True: 0, False: 503]
  ------------------
 1463|      0|    return ipv4_fast_fail;
 1464|      0|  }
 1465|    503|  const char* data = input.data();
 1466|       |
 1467|       |#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
 1468|       |  return detail::try_parse_ipv4_avx512(data, len);
 1469|       |#else
 1470|    503|  return detail::parse_ipv4_decimal_scalar(data, data + len);
 1471|    503|#endif
 1472|    503|}
_ZN3ada8checkers6detail25parse_ipv4_decimal_scalarEPKcS3_:
 1300|    503|parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
 1301|    503|  uint32_t ipv4 = 0;
 1302|    503|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1302:19): [True: 503, False: 0]
  ------------------
 1303|    503|    if (p == pend) [[unlikely]] {
  ------------------
  |  Branch (1303:9): [True: 0, False: 503]
  ------------------
 1304|      0|      return ipv4_fast_fail;
 1305|      0|    }
 1306|    503|    uint32_t val;
 1307|    503|    char c = *p;
 1308|    503|    if (c >= '0' && c <= '9') [[likely]] {
  ------------------
  |  Branch (1308:9): [True: 503, False: 0]
  |  Branch (1308:21): [True: 0, False: 503]
  ------------------
 1309|      0|      val = static_cast<uint32_t>(c - '0');
 1310|      0|      ++p;
 1311|    503|    } else {
 1312|    503|      return ipv4_fast_fail;
 1313|    503|    }
 1314|      0|    if (p < pend) {
  ------------------
  |  Branch (1314:9): [True: 0, False: 0]
  ------------------
 1315|      0|      c = *p;
 1316|      0|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1316:11): [True: 0, False: 0]
  |  Branch (1316:23): [True: 0, False: 0]
  ------------------
 1317|      0|        if (val == 0) [[unlikely]] {
  ------------------
  |  Branch (1317:13): [True: 0, False: 0]
  ------------------
 1318|      0|          return ipv4_fast_fail;
 1319|      0|        }
 1320|      0|        val = val * 10u + static_cast<uint32_t>(c - '0');
 1321|      0|        ++p;
 1322|      0|        if (p < pend) {
  ------------------
  |  Branch (1322:13): [True: 0, False: 0]
  ------------------
 1323|      0|          c = *p;
 1324|      0|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1324:15): [True: 0, False: 0]
  |  Branch (1324:27): [True: 0, False: 0]
  ------------------
 1325|      0|            val = val * 10u + static_cast<uint32_t>(c - '0');
 1326|      0|            ++p;
 1327|      0|            if (val > 255u) [[unlikely]] {
  ------------------
  |  Branch (1327:17): [True: 0, False: 0]
  ------------------
 1328|      0|              return ipv4_fast_fail;
 1329|      0|            }
 1330|      0|          }
 1331|      0|        }
 1332|      0|      }
 1333|      0|    }
 1334|      0|    ipv4 = (ipv4 << 8) | val;
 1335|      0|    if (i < 3) {
  ------------------
  |  Branch (1335:9): [True: 0, False: 0]
  ------------------
 1336|      0|      if (p == pend || *p != '.') [[unlikely]] {
  ------------------
  |  Branch (1336:11): [True: 0, False: 0]
  |  Branch (1336:24): [True: 0, False: 0]
  ------------------
 1337|      0|        return ipv4_fast_fail;
 1338|      0|      }
 1339|      0|      ++p;
 1340|      0|    }
 1341|      0|  }
 1342|      0|  if (p != pend) {
  ------------------
  |  Branch (1342:7): [True: 0, False: 0]
  ------------------
 1343|      0|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1343:9): [True: 0, False: 0]
  |  Branch (1343:26): [True: 0, False: 0]
  ------------------
 1344|      0|      return ipv4;
 1345|      0|    }
 1346|      0|    return ipv4_fast_fail;
 1347|      0|  }
 1348|      0|  return ipv4;
 1349|      0|}
_ZNK3ada8url_base10is_specialEv:
 7410|  5.67k|    const noexcept {
 7411|  5.67k|  return type != ada::scheme::NOT_SPECIAL;
 7412|  5.67k|}
_ZN3ada8checkers8is_alphaEc:
 1280|    503|constexpr bool is_alpha(char x) noexcept {
 1281|    503|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1281:10): [True: 503, False: 0]
  |  Branch (1281:34): [True: 503, False: 0]
  ------------------
 1282|    503|}
_ZN3ada8checkers8to_lowerEc:
 1278|  1.00k|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZN3ada8checkers26last_label_may_be_a_numberENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1253|  3.49k|constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
 1254|  3.49k|  if (view.empty()) {
  ------------------
  |  Branch (1254:7): [True: 0, False: 3.49k]
  ------------------
 1255|      0|    return false;
 1256|      0|  }
 1257|  3.49k|  const char* start = view.data();
 1258|  3.49k|  const char* end = start + view.size();
 1259|  3.49k|  if (end[-1] == '.') {
  ------------------
  |  Branch (1259:7): [True: 0, False: 3.49k]
  ------------------
 1260|      0|    --end;
 1261|      0|    if (end == start) {
  ------------------
  |  Branch (1261:9): [True: 0, False: 0]
  ------------------
 1262|      0|      return false;
 1263|      0|    }
 1264|      0|  }
 1265|  3.49k|  if (!is_ipv4_number_char(end[-1])) {
  ------------------
  |  Branch (1265:7): [True: 3.49k, False: 0]
  ------------------
 1266|  3.49k|    return false;
 1267|  3.49k|  }
 1268|      0|  const char* label = end;
 1269|      0|  while (label != start && is_ipv4_number_char(label[-1])) {
  ------------------
  |  Branch (1269:10): [True: 0, False: 0]
  |  Branch (1269:28): [True: 0, False: 0]
  ------------------
 1270|      0|    --label;
 1271|      0|  }
 1272|      0|  if (label != start && label[-1] != '.') {
  ------------------
  |  Branch (1272:7): [True: 0, False: 0]
  |  Branch (1272:25): [True: 0, False: 0]
  ------------------
 1273|      0|    return false;
 1274|      0|  }
 1275|      0|  return label != end && is_digit(*label);
  ------------------
  |  Branch (1275:10): [True: 0, False: 0]
  |  Branch (1275:26): [True: 0, False: 0]
  ------------------
 1276|      0|}
_ZN3ada8checkers19is_ipv4_number_charEc:
 1247|  3.49k|constexpr bool is_ipv4_number_char(char x) noexcept {
 1248|  3.49k|  const unsigned char c = static_cast<unsigned char>(x);
 1249|  3.49k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
  ------------------
  |  Branch (1249:11): [True: 3.49k, False: 0]
  |  Branch (1249:23): [True: 0, False: 3.49k]
  |  Branch (1249:37): [True: 3.49k, False: 0]
  |  Branch (1249:49): [True: 0, False: 3.49k]
  ------------------
 1250|  3.49k|         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
  ------------------
  |  Branch (1250:11): [True: 3.49k, False: 0]
  |  Branch (1250:23): [True: 0, False: 3.49k]
  |  Branch (1250:36): [True: 0, False: 3.49k]
  |  Branch (1250:48): [True: 0, False: 3.49k]
  ------------------
 1251|  3.49k|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8494|    743|    uint32_t start, uint32_t end, std::string_view input) {
 8495|    743|  uint32_t current_length = end - start;
 8496|    743|  uint32_t input_size = uint32_t(input.size());
 8497|    743|  uint32_t new_difference = input_size - current_length;
 8498|       |
 8499|    743|  if (current_length == 0) {
  ------------------
  |  Branch (8499:7): [True: 503, False: 240]
  ------------------
 8500|    503|    buffer.insert(start, input);
 8501|    503|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8501:14): [True: 5, False: 235]
  ------------------
 8502|      5|    if (input_size != 0) {
  ------------------
  |  Branch (8502:9): [True: 5, False: 0]
  ------------------
 8503|      5|      std::memmove(buffer.data() + start, input.data(), input_size);
 8504|      5|    }
 8505|    235|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8505:14): [True: 54, False: 181]
  ------------------
 8506|     54|    buffer.erase(start, current_length - input_size);
 8507|     54|    buffer.replace(start, input_size, input);
 8508|    181|  } else {
 8509|    181|    buffer.replace(start, current_length, input.substr(0, current_length));
 8510|    181|    buffer.insert(start + current_length, input.substr(current_length));
 8511|    181|  }
 8512|       |
 8513|    743|  return new_difference;
 8514|    743|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8470|    333|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8471|    333|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8472|    333|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8473|    333|          " bytes] components.hash_start = ", components.hash_start);
 8474|    333|  ADA_ASSERT_TRUE(validate());
 8475|    333|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8476|    333|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8476:7): [True: 0, False: 333]
  ------------------
 8477|      0|    buffer.resize(components.hash_start);
 8478|      0|  }
 8479|    333|  components.hash_start = uint32_t(buffer.size());
 8480|    333|  buffer += "#";
 8481|    333|  bool encoding_required = unicode::percent_encode<true>(
 8482|    333|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8483|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8484|       |  // encoding was not deemed required.
 8485|    333|  if (!encoding_required) {
  ------------------
  |  Branch (8485:7): [True: 141, False: 192]
  ------------------
 8486|    141|    buffer.append(input);
 8487|    141|  }
 8488|    333|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8489|    333|          buffer, "' [", buffer.size(), " bytes]");
 8490|    333|  ADA_ASSERT_TRUE(validate());
 8491|    333|}
_ZN3ada7helpers14leading_zeroesEj:
 2000|    503|inline int leading_zeroes(uint32_t input_num) noexcept {
 2001|       |#if ADA_REGULAR_VISUAL_STUDIO
 2002|       |  unsigned long leading_zero(0);
 2003|       |  unsigned long in(input_num);
 2004|       |  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
 2005|       |#else
 2006|    503|  return __builtin_clz(input_num);
 2007|    503|#endif  // ADA_REGULAR_VISUAL_STUDIO
 2008|    503|}
_ZN3ada14url_aggregator7reserveEj:
 9160|    503|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 9161|    503|  buffer.reserve(capacity);
 9162|    503|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 9134|    503|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 9135|    503|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 9136|    503|  ADA_ASSERT_TRUE(validate());
 9137|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 9138|       |  // to insert
 9139|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 9140|    503|  if (has_authority()) {
  ------------------
  |  Branch (9140:7): [True: 0, False: 503]
  ------------------
 9141|      0|    return;
 9142|      0|  }
 9143|       |  // Performance: the common case is components.protocol_end == buffer.size()
 9144|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 9145|       |  // the insert could be fused with another insert.
 9146|    503|  buffer.insert(components.protocol_end, "//");
 9147|    503|  components.username_end += 2;
 9148|    503|  components.host_start += 2;
 9149|    503|  components.host_end += 2;
 9150|    503|  components.pathname_start += 2;
 9151|    503|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9151:7): [True: 0, False: 503]
  ------------------
 9152|      0|    components.search_start += 2;
 9153|      0|  }
 9154|    503|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9154:7): [True: 0, False: 503]
  ------------------
 9155|      0|    components.hash_start += 2;
 9156|      0|  }
 9157|    503|  ADA_ASSERT_TRUE(validate());
 9158|    503|}
_ZN3ada14url_aggregator12clear_searchEv:
 8984|  1.41k|inline void url_aggregator::clear_search() {
 8985|  1.41k|  ada_log("url_aggregator::clear_search");
 8986|  1.41k|  ADA_ASSERT_TRUE(validate());
 8987|  1.41k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8987:7): [True: 0, False: 1.41k]
  ------------------
 8988|      0|    return;
 8989|      0|  }
 8990|       |
 8991|  1.41k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8991:7): [True: 1.24k, False: 173]
  ------------------
 8992|  1.24k|    buffer.resize(components.search_start);
 8993|  1.24k|  } else {
 8994|    173|    buffer.erase(components.search_start,
 8995|    173|                 components.hash_start - components.search_start);
 8996|    173|    components.hash_start = components.search_start;
 8997|    173|  }
 8998|       |
 8999|  1.41k|  components.search_start = url_components::omitted;
 9000|       |
 9001|       |#if ADA_DEVELOPMENT_CHECKS
 9002|       |  ADA_ASSERT_EQUAL(get_search(), "",
 9003|       |                   "search should have been cleared on buffer=" + buffer +
 9004|       |                       " with " + components.to_string() + "\n" + to_diagram());
 9005|       |#endif
 9006|  1.41k|  ADA_ASSERT_TRUE(validate());
 9007|  1.41k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8601|  3.10k|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8602|  3.10k|  ada_log("url_aggregator::update_base_search ", input,
 8603|  3.10k|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8604|  3.10k|  ADA_ASSERT_TRUE(validate());
 8605|  3.10k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8606|       |
 8607|  3.10k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8607:7): [True: 2.86k, False: 240]
  ------------------
 8608|  2.86k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8608:9): [True: 1.53k, False: 1.33k]
  ------------------
 8609|  1.53k|      components.search_start = uint32_t(buffer.size());
 8610|  1.53k|      buffer += "?";
 8611|  1.53k|    } else {
 8612|  1.33k|      buffer.resize(components.search_start + 1);
 8613|  1.33k|    }
 8614|       |
 8615|  2.86k|    bool encoding_required =
 8616|  2.86k|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8617|       |    // When encoding_required is false, then buffer is left unchanged, and
 8618|       |    // percent encoding was not deemed required.
 8619|  2.86k|    if (!encoding_required) {
  ------------------
  |  Branch (8619:9): [True: 1.69k, False: 1.16k]
  ------------------
 8620|  1.69k|      buffer.append(input);
 8621|  1.69k|    }
 8622|  2.86k|  } else {
 8623|    240|    const size_t idx =
 8624|    240|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8625|    240|    std::string encoded;
 8626|    240|    std::string_view replacement = input;
 8627|    240|    if (idx != input.size()) {
  ------------------
  |  Branch (8627:9): [True: 0, False: 240]
  ------------------
 8628|      0|      encoded =
 8629|      0|          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
 8630|      0|      replacement = encoded;
 8631|      0|    }
 8632|       |
 8633|    240|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8633:9): [True: 240, False: 0]
  ------------------
 8634|    240|      const uint32_t difference = replace_and_resize(
 8635|    240|          components.search_start + 1, components.hash_start, replacement);
 8636|    240|      components.hash_start += difference;
 8637|    240|    } else {
 8638|      0|      components.search_start = components.hash_start;
 8639|      0|      buffer.insert(components.search_start, replacement.size() + 1, '?');
 8640|      0|      if (!replacement.empty()) {
  ------------------
  |  Branch (8640:11): [True: 0, False: 0]
  ------------------
 8641|      0|        std::memmove(buffer.data() + components.search_start + 1,
 8642|      0|                     replacement.data(), replacement.size());
 8643|      0|      }
 8644|      0|      components.hash_start +=
 8645|      0|          uint32_t(replacement.size() + 1);  // Do not forget `?`
 8646|      0|    }
 8647|    240|  }
 8648|       |
 8649|  3.10k|  ADA_ASSERT_TRUE(validate());
 8650|  3.10k|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8516|    503|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8517|    503|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8518|    503|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8519|    503|  ADA_ASSERT_TRUE(validate());
 8520|    503|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8521|       |
 8522|       |  // This next line is required for when parsing a URL like `foo://`
 8523|    503|  add_authority_slashes_if_needed();
 8524|       |
 8525|    503|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8526|    503|  uint32_t new_difference =
 8527|    503|      replace_and_resize(components.host_start, components.host_end, input);
 8528|       |
 8529|    503|  if (has_credentials) {
  ------------------
  |  Branch (8529:7): [True: 0, False: 503]
  ------------------
 8530|      0|    buffer.insert(components.host_start, "@");
 8531|      0|    new_difference++;
 8532|      0|  }
 8533|    503|  components.host_end += new_difference;
 8534|    503|  components.pathname_start += new_difference;
 8535|    503|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8535:7): [True: 0, False: 503]
  ------------------
 8536|      0|    components.search_start += new_difference;
 8537|      0|  }
 8538|    503|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8538:7): [True: 0, False: 503]
  ------------------
 8539|      0|    components.hash_start += new_difference;
 8540|      0|  }
 8541|    503|  ADA_ASSERT_TRUE(validate());
 8542|    503|}
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8368|    240|                                              const uint8_t character_set[]) {
 8369|    240|  const char* data = input.data();
 8370|    240|  const size_t size = input.size();
 8371|       |
 8372|       |  // Process 8 bytes at a time using unrolled loop
 8373|    240|  size_t i = 0;
 8374|  3.62k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (8374:10): [True: 3.38k, False: 240]
  ------------------
 8375|  3.38k|    unsigned char chunk[8];
 8376|  3.38k|    std::memcpy(&chunk, data + i,
 8377|  3.38k|                8);  // entices compiler to unconditionally process 8 characters
 8378|       |
 8379|       |    // Check 8 characters at once
 8380|  30.4k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (8380:24): [True: 27.0k, False: 3.38k]
  ------------------
 8381|  27.0k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (8381:11): [True: 0, False: 27.0k]
  ------------------
 8382|      0|        return i + j;
 8383|      0|      }
 8384|  27.0k|    }
 8385|  3.38k|  }
 8386|       |
 8387|       |  // Handle remaining bytes
 8388|  1.05k|  for (; i < size; i++) {
  ------------------
  |  Branch (8388:10): [True: 818, False: 240]
  ------------------
 8389|    818|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (8389:9): [True: 0, False: 818]
  ------------------
 8390|      0|      return i;
 8391|      0|    }
 8392|    818|  }
 8393|       |
 8394|    240|  return size;
 8395|    240|}
_ZNK3ada14url_aggregator13has_authorityEv:
 9125|    503|    const noexcept {
 9126|    503|  ada_log("url_aggregator::has_authority");
 9127|       |  // Performance: instead of doing this potentially expensive check, we could
 9128|       |  // have a boolean in the struct.
 9129|    503|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (9129:10): [True: 0, False: 503]
  ------------------
 9130|      0|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (9130:10): [True: 0, False: 0]
  ------------------
 9131|      0|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (9131:10): [True: 0, False: 0]
  ------------------
 9132|    503|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 4100|  2.99k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 4069|  8.97k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 4070|  8.97k|    TL_ASSERT(has_value());
  ------------------
  |  | 2122|  8.97k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4070:5): [True: 8.97k, False: 0]
  ------------------
 4071|  8.97k|    return valptr();
 4072|  8.97k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3365|  8.97k|  T* valptr() { return std::addressof(this->m_val); }
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 4099|  8.97k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2697|  2.99k|  ~expected_storage_base() {
 2698|  2.99k|    if (m_has_val) {
  ------------------
  |  Branch (2698:9): [True: 2.99k, False: 0]
  ------------------
 2699|  2.99k|      m_val.~T();
 2700|  2.99k|    }
 2701|  2.99k|  }
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1912|  1.62k|                                                       size_t pos2) {
 1913|       |#if ADA_DEVELOPMENT_CHECKS
 1914|       |  if (pos2 < pos1) {
 1915|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1916|       |              << std::endl;
 1917|       |    abort();
 1918|       |  }
 1919|       |#endif
 1920|  1.62k|  return input.substr(pos1, pos2 - pos1);
 1921|  1.62k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8557|  1.53k|    const noexcept {
 8558|  1.53k|  return buffer.size() == components.pathname_start;
 8559|  1.53k|}
_ZN3ada17url_search_paramsC2ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9604|  14.9k|  explicit url_search_params(const std::string_view input) {
 9605|  14.9k|    initialize(input);
 9606|  14.9k|  }
_ZN3ada17url_search_params10initializeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9829|  17.9k|inline void url_search_params::initialize(std::string_view input) {
 9830|  17.9k|  if (!input.empty() && input.front() == '?') {
  ------------------
  |  Branch (9830:7): [True: 6.33k, False: 11.6k]
  |  Branch (9830:25): [True: 9, False: 6.32k]
  ------------------
 9831|      9|    input.remove_prefix(1);
 9832|      9|  }
 9833|  17.9k|  if (input.empty()) {
  ------------------
  |  Branch (9833:7): [True: 11.6k, False: 6.33k]
  ------------------
 9834|  11.6k|    return;
 9835|  11.6k|  }
 9836|       |  // Refuse overlong query strings (same process-wide cap as URL parsing).
 9837|  6.33k|  if (input.size() > get_max_input_length()) {
  ------------------
  |  Branch (9837:7): [True: 0, False: 6.33k]
  ------------------
 9838|      0|    return;
 9839|      0|  }
 9840|       |
 9841|  6.33k|  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
 9842|       |
 9843|  6.33k|  auto process_key_value = [&](const std::string_view current) {
 9844|  6.33k|    const auto equal = current.find('=');
 9845|  6.33k|    if (equal == std::string_view::npos) {
 9846|  6.33k|      params.emplace_back(unicode::form_urlencoded_decode(current), "");
 9847|  6.33k|    } else {
 9848|  6.33k|      params.emplace_back(
 9849|  6.33k|          unicode::form_urlencoded_decode(current.substr(0, equal)),
 9850|  6.33k|          unicode::form_urlencoded_decode(current.substr(equal + 1)));
 9851|  6.33k|    }
 9852|  6.33k|  };
 9853|       |
 9854|  41.1k|  while (!input.empty()) {
  ------------------
  |  Branch (9854:10): [True: 40.9k, False: 195]
  ------------------
 9855|  40.9k|    const auto ampersand_index = input.find('&');
 9856|       |
 9857|  40.9k|    if (ampersand_index == std::string_view::npos) {
  ------------------
  |  Branch (9857:9): [True: 6.13k, False: 34.7k]
  ------------------
 9858|  6.13k|      if (!input.empty()) {
  ------------------
  |  Branch (9858:11): [True: 6.13k, False: 0]
  ------------------
 9859|  6.13k|        process_key_value(input);
 9860|  6.13k|      }
 9861|  6.13k|      break;
 9862|  34.7k|    } else if (ampersand_index != 0) {
  ------------------
  |  Branch (9862:16): [True: 27.7k, False: 7.01k]
  ------------------
 9863|  27.7k|      process_key_value(input.substr(0, ampersand_index));
 9864|  27.7k|    }
 9865|       |
 9866|  34.7k|    input.remove_prefix(ampersand_index + 1);
 9867|  34.7k|  }
 9868|  6.33k|}
_ZZN3ada17url_search_params10initializeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_E_clES5_:
 9843|  33.8k|  auto process_key_value = [&](const std::string_view current) {
 9844|  33.8k|    const auto equal = current.find('=');
 9845|  33.8k|    if (equal == std::string_view::npos) {
  ------------------
  |  Branch (9845:9): [True: 12.8k, False: 21.0k]
  ------------------
 9846|  12.8k|      params.emplace_back(unicode::form_urlencoded_decode(current), "");
 9847|  21.0k|    } else {
 9848|  21.0k|      params.emplace_back(
 9849|  21.0k|          unicode::form_urlencoded_decode(current.substr(0, equal)),
 9850|  21.0k|          unicode::form_urlencoded_decode(current.substr(equal + 1)));
 9851|  21.0k|    }
 9852|  33.8k|  };
_ZN3ada17url_search_paramsD2Ev:
 9612|  29.9k|  ~url_search_params() = default;
_ZNK3ada17url_search_params9to_stringEv:
 9916|  26.9k|inline std::string url_search_params::to_string() const {
 9917|  26.9k|  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
 9918|  26.9k|  std::string out{};
 9919|  85.9k|  for (size_t i = 0; i < params.size(); i++) {
  ------------------
  |  Branch (9919:22): [True: 58.9k, False: 26.9k]
  ------------------
 9920|  58.9k|    auto key = ada::unicode::percent_encode(params[i].first, character_set);
 9921|  58.9k|    auto value = ada::unicode::percent_encode(params[i].second, character_set);
 9922|       |
 9923|       |    // Performance optimization: Move this inside percent_encode.
 9924|  58.9k|    std::ranges::replace(key, ' ', '+');
 9925|  58.9k|    std::ranges::replace(value, ' ', '+');
 9926|       |
 9927|  58.9k|    if (i != 0) {
  ------------------
  |  Branch (9927:9): [True: 45.5k, False: 13.4k]
  ------------------
 9928|  45.5k|      out += "&";
 9929|  45.5k|    }
 9930|  58.9k|    out.append(key);
 9931|  58.9k|    out += "=";
 9932|  58.9k|    out.append(value);
 9933|  58.9k|  }
 9934|  26.9k|  return out;
 9935|  26.9k|}
_ZNK3ada17url_search_params4sizeEv:
 9875|  26.9k|inline size_t url_search_params::size() const noexcept { return params.size(); }
_ZN3ada17url_search_params4sortEv:
 9964|  5.98k|inline void url_search_params::sort() {
 9965|       |  // Keys are expected to be valid UTF-8, but percent_decode can produce
 9966|       |  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
 9967|  5.98k|  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
 9968|  5.98k|                                      const key_value_pair& rhs) {
 9969|  5.98k|    size_t i = 0, j = 0;
 9970|  5.98k|    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
 9971|  5.98k|    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
 9972|  5.98k|           (j < rhs.first.size() || low_surrogate2 != 0)) {
 9973|  5.98k|      uint32_t codePoint1 = 0, codePoint2 = 0;
 9974|       |
 9975|  5.98k|      if (low_surrogate1 != 0) {
 9976|  5.98k|        codePoint1 = low_surrogate1;
 9977|  5.98k|        low_surrogate1 = 0;
 9978|  5.98k|      } else {
 9979|  5.98k|        uint8_t c1 = uint8_t(lhs.first[i]);
 9980|  5.98k|        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
 9981|  5.98k|          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
 9982|  5.98k|          i += 2;
 9983|  5.98k|        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
 9984|  5.98k|          codePoint1 = ((c1 & 0x0F) << 12) |
 9985|  5.98k|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
 9986|  5.98k|                       (uint8_t(lhs.first[i + 2]) & 0x3F);
 9987|  5.98k|          i += 3;
 9988|  5.98k|        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
 9989|  5.98k|          codePoint1 = ((c1 & 0x07) << 18) |
 9990|  5.98k|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
 9991|  5.98k|                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
 9992|  5.98k|                       (uint8_t(lhs.first[i + 3]) & 0x3F);
 9993|  5.98k|          i += 4;
 9994|       |
 9995|  5.98k|          codePoint1 -= 0x10000;
 9996|  5.98k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
 9997|  5.98k|          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
 9998|  5.98k|          codePoint1 = high_surrogate;
 9999|  5.98k|        } else {
10000|       |          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10001|  5.98k|          codePoint1 = c1;
10002|  5.98k|          i++;
10003|  5.98k|        }
10004|  5.98k|      }
10005|       |
10006|  5.98k|      if (low_surrogate2 != 0) {
10007|  5.98k|        codePoint2 = low_surrogate2;
10008|  5.98k|        low_surrogate2 = 0;
10009|  5.98k|      } else {
10010|  5.98k|        uint8_t c2 = uint8_t(rhs.first[j]);
10011|  5.98k|        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
10012|  5.98k|          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
10013|  5.98k|          j += 2;
10014|  5.98k|        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
10015|  5.98k|          codePoint2 = ((c2 & 0x0F) << 12) |
10016|  5.98k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
10017|  5.98k|                       (uint8_t(rhs.first[j + 2]) & 0x3F);
10018|  5.98k|          j += 3;
10019|  5.98k|        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
10020|  5.98k|          codePoint2 = ((c2 & 0x07) << 18) |
10021|  5.98k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
10022|  5.98k|                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
10023|  5.98k|                       (uint8_t(rhs.first[j + 3]) & 0x3F);
10024|  5.98k|          j += 4;
10025|  5.98k|          codePoint2 -= 0x10000;
10026|  5.98k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
10027|  5.98k|          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
10028|  5.98k|          codePoint2 = high_surrogate;
10029|  5.98k|        } else {
10030|       |          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10031|  5.98k|          codePoint2 = c2;
10032|  5.98k|          j++;
10033|  5.98k|        }
10034|  5.98k|      }
10035|       |
10036|  5.98k|      if (codePoint1 != codePoint2) {
10037|  5.98k|        return (codePoint1 < codePoint2);
10038|  5.98k|      }
10039|  5.98k|    }
10040|  5.98k|    return (j < rhs.first.size() || low_surrogate2 != 0);
10041|  5.98k|  });
10042|  5.98k|}
_ZZN3ada17url_search_params4sortEvENKUlRKNSt3__14pairINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_EESB_E_clESB_SB_:
 9968|  40.8k|                                      const key_value_pair& rhs) {
 9969|  40.8k|    size_t i = 0, j = 0;
 9970|  40.8k|    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
 9971|  65.3k|    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
  ------------------
  |  Branch (9971:13): [True: 41.3k, False: 23.9k]
  |  Branch (9971:37): [True: 318, False: 23.6k]
  ------------------
 9972|  41.6k|           (j < rhs.first.size() || low_surrogate2 != 0)) {
  ------------------
  |  Branch (9972:13): [True: 39.4k, False: 2.22k]
  |  Branch (9972:37): [True: 324, False: 1.90k]
  ------------------
 9973|  39.7k|      uint32_t codePoint1 = 0, codePoint2 = 0;
 9974|       |
 9975|  39.7k|      if (low_surrogate1 != 0) {
  ------------------
  |  Branch (9975:11): [True: 1.06k, False: 38.7k]
  ------------------
 9976|  1.06k|        codePoint1 = low_surrogate1;
 9977|  1.06k|        low_surrogate1 = 0;
 9978|  38.7k|      } else {
 9979|  38.7k|        uint8_t c1 = uint8_t(lhs.first[i]);
 9980|  38.7k|        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
  ------------------
  |  Branch (9980:13): [True: 13.1k, False: 25.5k]
  |  Branch (9980:26): [True: 4.84k, False: 8.34k]
  |  Branch (9980:40): [True: 1.38k, False: 3.46k]
  ------------------
 9981|  1.38k|          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
 9982|  1.38k|          i += 2;
 9983|  37.3k|        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
  ------------------
  |  Branch (9983:20): [True: 8.34k, False: 28.9k]
  |  Branch (9983:33): [True: 2.20k, False: 6.14k]
  |  Branch (9983:47): [True: 1.18k, False: 1.01k]
  ------------------
 9984|  1.18k|          codePoint1 = ((c1 & 0x0F) << 12) |
 9985|  1.18k|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
 9986|  1.18k|                       (uint8_t(lhs.first[i + 2]) & 0x3F);
 9987|  1.18k|          i += 3;
 9988|  36.1k|        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
  ------------------
  |  Branch (9988:20): [True: 6.14k, False: 29.9k]
  |  Branch (9988:33): [True: 4.08k, False: 2.05k]
  |  Branch (9988:47): [True: 1.38k, False: 2.70k]
  ------------------
 9989|  1.38k|          codePoint1 = ((c1 & 0x07) << 18) |
 9990|  1.38k|                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
 9991|  1.38k|                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
 9992|  1.38k|                       (uint8_t(lhs.first[i + 3]) & 0x3F);
 9993|  1.38k|          i += 4;
 9994|       |
 9995|  1.38k|          codePoint1 -= 0x10000;
 9996|  1.38k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
 9997|  1.38k|          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
 9998|  1.38k|          codePoint1 = high_surrogate;
 9999|  34.7k|        } else {
10000|       |          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10001|  34.7k|          codePoint1 = c1;
10002|  34.7k|          i++;
10003|  34.7k|        }
10004|  38.7k|      }
10005|       |
10006|  39.7k|      if (low_surrogate2 != 0) {
  ------------------
  |  Branch (10006:11): [True: 1.06k, False: 38.7k]
  ------------------
10007|  1.06k|        codePoint2 = low_surrogate2;
10008|  1.06k|        low_surrogate2 = 0;
10009|  38.7k|      } else {
10010|  38.7k|        uint8_t c2 = uint8_t(rhs.first[j]);
10011|  38.7k|        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
  ------------------
  |  Branch (10011:13): [True: 15.2k, False: 23.5k]
  |  Branch (10011:26): [True: 6.06k, False: 9.14k]
  |  Branch (10011:40): [True: 1.93k, False: 4.12k]
  ------------------
10012|  1.93k|          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
10013|  1.93k|          j += 2;
10014|  36.7k|        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
  ------------------
  |  Branch (10014:20): [True: 9.14k, False: 27.6k]
  |  Branch (10014:33): [True: 2.43k, False: 6.71k]
  |  Branch (10014:47): [True: 1.21k, False: 1.21k]
  ------------------
10015|  1.21k|          codePoint2 = ((c2 & 0x0F) << 12) |
10016|  1.21k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
10017|  1.21k|                       (uint8_t(rhs.first[j + 2]) & 0x3F);
10018|  1.21k|          j += 3;
10019|  35.5k|        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
  ------------------
  |  Branch (10019:20): [True: 6.71k, False: 28.8k]
  |  Branch (10019:33): [True: 4.19k, False: 2.51k]
  |  Branch (10019:47): [True: 1.51k, False: 2.68k]
  ------------------
10020|  1.51k|          codePoint2 = ((c2 & 0x07) << 18) |
10021|  1.51k|                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
10022|  1.51k|                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
10023|  1.51k|                       (uint8_t(rhs.first[j + 3]) & 0x3F);
10024|  1.51k|          j += 4;
10025|  1.51k|          codePoint2 -= 0x10000;
10026|  1.51k|          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
10027|  1.51k|          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
10028|  1.51k|          codePoint2 = high_surrogate;
10029|  34.0k|        } else {
10030|       |          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10031|  34.0k|          codePoint2 = c2;
10032|  34.0k|          j++;
10033|  34.0k|        }
10034|  38.7k|      }
10035|       |
10036|  39.7k|      if (codePoint1 != codePoint2) {
  ------------------
  |  Branch (10036:11): [True: 15.2k, False: 24.5k]
  ------------------
10037|  15.2k|        return (codePoint1 < codePoint2);
10038|  15.2k|      }
10039|  39.7k|    }
10040|  25.5k|    return (j < rhs.first.size() || low_surrogate2 != 0);
  ------------------
  |  Branch (10040:13): [True: 5.31k, False: 20.2k]
  |  Branch (10040:37): [True: 12, False: 20.2k]
  ------------------
10041|  40.8k|  });
_ZN3ada17url_search_params5resetENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9824|  2.99k|inline void url_search_params::reset(std::string_view input) {
 9825|  2.99k|  params.clear();
 9826|  2.99k|  initialize(input);
 9827|  2.99k|}
_ZN3ada17url_search_params6appendENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9871|  16.7k|                                      const std::string_view value) {
 9872|  16.7k|  params.emplace_back(key, value);
 9873|  16.7k|}
_ZN3ada17url_search_params3setENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9938|  2.99k|                                   const std::string_view value) {
 9939|  2.99k|  const auto find = [&key](const auto& param) { return param.first == key; };
 9940|       |
 9941|  2.99k|  auto it = std::ranges::find_if(params, find);
 9942|       |
 9943|  2.99k|  if (it == params.end()) {
  ------------------
  |  Branch (9943:7): [True: 0, False: 2.99k]
  ------------------
 9944|      0|    params.emplace_back(key, value);
 9945|  2.99k|  } else {
 9946|  2.99k|    it->second = value;
 9947|  2.99k|    params.erase(std::remove_if(std::next(it), params.end(), find),
 9948|  2.99k|                 params.end());
 9949|  2.99k|  }
 9950|  2.99k|}
_ZZN3ada17url_search_params3setENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_ENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9939|  5.98k|  const auto find = [&key](const auto& param) { return param.first == key; };
_ZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9952|  4.28k|inline void url_search_params::remove(const std::string_view key) {
 9953|  4.28k|  std::erase_if(params,
 9954|  4.28k|                [&key](const auto& param) { return param.first == key; });
 9955|  4.28k|}
_ZZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9954|  9.51k|                [&key](const auto& param) { return param.first == key; });
_ZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9958|  4.28k|                                      const std::string_view value) {
 9959|  4.28k|  std::erase_if(params, [&key, &value](const auto& param) {
 9960|  4.28k|    return param.first == key && param.second == value;
 9961|  4.28k|  });
 9962|  4.28k|}
_ZZN3ada17url_search_params6removeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_ENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9959|  3.46k|  std::erase_if(params, [&key, &value](const auto& param) {
 9960|  3.46k|    return param.first == key && param.second == value;
  ------------------
  |  Branch (9960:12): [True: 0, False: 3.46k]
  |  Branch (9960:34): [True: 0, False: 0]
  ------------------
 9961|  3.46k|  });
_ZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9902|  23.1k|inline bool url_search_params::has(const std::string_view key) noexcept {
 9903|  23.1k|  auto entry = std::ranges::find_if(
 9904|  23.1k|      params, [&key](const auto& param) { return param.first == key; });
 9905|  23.1k|  return entry != params.end();
 9906|  23.1k|}
_ZZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9904|   277k|      params, [&key](const auto& param) { return param.first == key; });
_ZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9909|  13.0k|                                   std::string_view value) noexcept {
 9910|  13.0k|  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
 9911|  13.0k|    return param.first == key && param.second == value;
 9912|  13.0k|  });
 9913|  13.0k|  return entry != params.end();
 9914|  13.0k|}
_ZZN3ada17url_search_params3hasENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_ENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9910|   142k|  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
 9911|   142k|    return param.first == key && param.second == value;
  ------------------
  |  Branch (9911:12): [True: 15.2k, False: 127k]
  |  Branch (9911:34): [True: 11.3k, False: 3.86k]
  ------------------
 9912|   142k|  });
_ZN3ada17url_search_params3getENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9878|  19.0k|    const std::string_view key) {
 9879|  19.0k|  auto entry = std::ranges::find_if(
 9880|  19.0k|      params, [&key](const auto& param) { return param.first == key; });
 9881|       |
 9882|  19.0k|  if (entry == params.end()) {
  ------------------
  |  Branch (9882:7): [True: 5.67k, False: 13.3k]
  ------------------
 9883|  5.67k|    return std::nullopt;
 9884|  5.67k|  }
 9885|       |
 9886|  13.3k|  return entry->second;
 9887|  19.0k|}
_ZZN3ada17url_search_params3getENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlRKT_E_clINS1_4pairINS1_12basic_stringIcS4_NS1_9allocatorIcEEEESF_EEEEDaS8_:
 9880|   148k|      params, [&key](const auto& param) { return param.first == key; });
_ZN3ada17url_search_params7get_allENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9890|  5.98k|    const std::string_view key) {
 9891|  5.98k|  std::vector<std::string> out{};
 9892|       |
 9893|  17.0k|  for (auto& param : params) {
  ------------------
  |  Branch (9893:20): [True: 17.0k, False: 5.98k]
  ------------------
 9894|  17.0k|    if (param.first == key) {
  ------------------
  |  Branch (9894:9): [True: 5.93k, False: 11.0k]
  ------------------
 9895|  5.93k|      out.emplace_back(param.second);
 9896|  5.93k|    }
 9897|  17.0k|  }
 9898|       |
 9899|  5.98k|  return out;
 9900|  5.98k|}
_ZN3ada17url_search_params8get_keysEv:
10044|  2.99k|inline url_search_params_keys_iter url_search_params::get_keys() {
10045|  2.99k|  return url_search_params_keys_iter(*this);
10046|  2.99k|}
_ZN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE0EEC2ERNS_17url_search_paramsE:
 9784|  2.99k|  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
_ZN3ada17url_search_params10get_valuesEv:
10051|  2.99k|inline url_search_params_values_iter url_search_params::get_values() {
10052|  2.99k|  return url_search_params_values_iter(*this);
10053|  2.99k|}
_ZN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE1EEC2ERNS_17url_search_paramsE:
 9784|  2.99k|  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
_ZN3ada17url_search_params11get_entriesEv:
10058|  2.99k|inline url_search_params_entries_iter url_search_params::get_entries() {
10059|  2.99k|  return url_search_params_entries_iter(*this);
10060|  2.99k|}
_ZN3ada22url_search_params_iterINSt3__14pairINS1_17basic_string_viewIcNS1_11char_traitsIcEEEES6_EELNS_27url_search_params_iter_typeE2EEC2ERNS_17url_search_paramsE:
 9784|  2.99k|  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:
10068|  9.84k|inline std::optional<std::string_view> url_search_params_keys_iter::next() {
10069|  9.84k|  if (!has_next()) {
  ------------------
  |  Branch (10069:7): [True: 0, False: 9.84k]
  ------------------
10070|      0|    return std::nullopt;
10071|      0|  }
10072|  9.84k|  return params.params[pos++].first;
10073|  9.84k|}
_ZN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE1EE4nextEv:
10076|  9.84k|inline std::optional<std::string_view> url_search_params_values_iter::next() {
10077|  9.84k|  if (!has_next()) {
  ------------------
  |  Branch (10077:7): [True: 0, False: 9.84k]
  ------------------
10078|      0|    return std::nullopt;
10079|      0|  }
10080|  9.84k|  return params.params[pos++].second;
10081|  9.84k|}
_ZN3ada22url_search_params_iterINSt3__14pairINS1_17basic_string_viewIcNS1_11char_traitsIcEEEES6_EELNS_27url_search_params_iter_typeE2EE4nextEv:
10085|  9.84k|url_search_params_entries_iter::next() {
10086|  9.84k|  if (!has_next()) {
  ------------------
  |  Branch (10086:7): [True: 0, False: 9.84k]
  ------------------
10087|      0|    return std::nullopt;
10088|      0|  }
10089|  9.84k|  return params.params[pos++];
10090|  9.84k|}
_ZNK3ada17url_search_params5beginEv:
 9721|  11.9k|  inline auto begin() const { return params.begin(); }
_ZNK3ada17url_search_params3endEv:
 9722|  11.9k|  inline auto end() const { return params.end(); }
_ZNK3ada17url_search_params5frontEv:
 9723|  1.15k|  inline auto front() const { return params.front(); }
_ZNK3ada17url_search_params4backEv:
 9724|  1.15k|  inline auto back() const { return params.back(); }
_ZNK3ada17url_search_paramsixEm:
 9725|  1.15k|  inline auto operator[](size_t index) const { return params[index]; }
_ZN3ada17url_search_paramsC2Ev:
 9597|  8.97k|  url_search_params() = default;
_ZN3ada17url_search_paramsC2ERKS0_:
 9608|  2.99k|  url_search_params(const url_search_params& u) = default;
_ZN3ada17url_search_paramsaSERKS0_:
 9611|  2.99k|  url_search_params& operator=(const url_search_params& u) = default;
_ZN3ada17url_search_paramsC2EOS0_:
 9609|  2.99k|  url_search_params(url_search_params&& u) noexcept = default;
_ZN3ada17url_search_paramsaSEOS0_:
 9610|  2.99k|  url_search_params& operator=(url_search_params&& u) noexcept = default;
_ZNK3ada14url_aggregator8validateEv:
 9316|  2.99k|[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
 9317|  2.99k|  if (!is_valid) {
  ------------------
  |  Branch (9317:7): [True: 0, False: 2.99k]
  ------------------
 9318|      0|    return true;
 9319|      0|  }
 9320|  2.99k|  if (!components.check_offset_consistency()) {
  ------------------
  |  Branch (9320:7): [True: 0, False: 2.99k]
  ------------------
 9321|      0|    ada_log("url_aggregator::validate inconsistent components \n",
 9322|      0|            to_diagram());
 9323|      0|    return false;
 9324|      0|  }
 9325|       |  // We have a credible components struct, but let us investivate more
 9326|       |  // carefully:
 9327|       |  /**
 9328|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 9329|       |   *       |     |    |          | ^^^^|       |   |
 9330|       |   *       |     |    |          | |   |       |   `----- hash_start
 9331|       |   *       |     |    |          | |   |       `--------- search_start
 9332|       |   *       |     |    |          | |   `----------------- pathname_start
 9333|       |   *       |     |    |          | `--------------------- port
 9334|       |   *       |     |    |          `----------------------- host_end
 9335|       |   *       |     |    `---------------------------------- host_start
 9336|       |   *       |     `--------------------------------------- username_end
 9337|       |   *       `--------------------------------------------- protocol_end
 9338|       |   */
 9339|  2.99k|  if (components.protocol_end == url_components::omitted) {
  ------------------
  |  Branch (9339:7): [True: 0, False: 2.99k]
  ------------------
 9340|      0|    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
 9341|      0|    return false;
 9342|      0|  }
 9343|  2.99k|  if (components.username_end == url_components::omitted) {
  ------------------
  |  Branch (9343:7): [True: 0, False: 2.99k]
  ------------------
 9344|      0|    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
 9345|      0|    return false;
 9346|      0|  }
 9347|  2.99k|  if (components.host_start == url_components::omitted) {
  ------------------
  |  Branch (9347:7): [True: 0, False: 2.99k]
  ------------------
 9348|      0|    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
 9349|      0|    return false;
 9350|      0|  }
 9351|  2.99k|  if (components.host_end == url_components::omitted) {
  ------------------
  |  Branch (9351:7): [True: 0, False: 2.99k]
  ------------------
 9352|      0|    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
 9353|      0|    return false;
 9354|      0|  }
 9355|  2.99k|  if (components.pathname_start == url_components::omitted) {
  ------------------
  |  Branch (9355:7): [True: 0, False: 2.99k]
  ------------------
 9356|      0|    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
 9357|      0|    return false;
 9358|      0|  }
 9359|       |
 9360|  2.99k|  if (components.protocol_end > buffer.size()) {
  ------------------
  |  Branch (9360:7): [True: 0, False: 2.99k]
  ------------------
 9361|      0|    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
 9362|      0|    return false;
 9363|      0|  }
 9364|  2.99k|  if (components.username_end > buffer.size()) {
  ------------------
  |  Branch (9364:7): [True: 0, False: 2.99k]
  ------------------
 9365|      0|    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
 9366|      0|    return false;
 9367|      0|  }
 9368|  2.99k|  if (components.host_start > buffer.size()) {
  ------------------
  |  Branch (9368:7): [True: 0, False: 2.99k]
  ------------------
 9369|      0|    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
 9370|      0|    return false;
 9371|      0|  }
 9372|  2.99k|  if (components.host_end > buffer.size()) {
  ------------------
  |  Branch (9372:7): [True: 0, False: 2.99k]
  ------------------
 9373|      0|    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
 9374|      0|    return false;
 9375|      0|  }
 9376|  2.99k|  if (components.pathname_start > buffer.size()) {
  ------------------
  |  Branch (9376:7): [True: 0, False: 2.99k]
  ------------------
 9377|      0|    ada_log("url_aggregator::validate pathname_start overflow \n",
 9378|      0|            to_diagram());
 9379|      0|    return false;
 9380|      0|  }
 9381|       |
 9382|  2.99k|  if (components.protocol_end > 0) {
  ------------------
  |  Branch (9382:7): [True: 2.99k, False: 0]
  ------------------
 9383|  2.99k|    if (buffer[components.protocol_end - 1] != ':') {
  ------------------
  |  Branch (9383:9): [True: 0, False: 2.99k]
  ------------------
 9384|      0|      ada_log(
 9385|      0|          "url_aggregator::validate missing : at the end of the protocol \n",
 9386|      0|          to_diagram());
 9387|      0|      return false;
 9388|      0|    }
 9389|  2.99k|  }
 9390|       |
 9391|  2.99k|  if (components.username_end != buffer.size() &&
  ------------------
  |  Branch (9391:7): [True: 2.99k, False: 0]
  ------------------
 9392|  2.99k|      components.username_end > components.protocol_end + 2) {
  ------------------
  |  Branch (9392:7): [True: 0, False: 2.99k]
  ------------------
 9393|      0|    if (buffer[components.username_end] != ':' &&
  ------------------
  |  Branch (9393:9): [True: 0, False: 0]
  ------------------
 9394|      0|        buffer[components.username_end] != '@') {
  ------------------
  |  Branch (9394:9): [True: 0, False: 0]
  ------------------
 9395|      0|      ada_log(
 9396|      0|          "url_aggregator::validate missing : or @ at the end of the username "
 9397|      0|          "\n",
 9398|      0|          to_diagram());
 9399|      0|      return false;
 9400|      0|    }
 9401|      0|  }
 9402|       |
 9403|  2.99k|  if (components.host_start != buffer.size()) {
  ------------------
  |  Branch (9403:7): [True: 2.99k, False: 0]
  ------------------
 9404|  2.99k|    if (components.host_start > components.username_end) {
  ------------------
  |  Branch (9404:9): [True: 0, False: 2.99k]
  ------------------
 9405|      0|      if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (9405:11): [True: 0, False: 0]
  ------------------
 9406|      0|        ada_log(
 9407|      0|            "url_aggregator::validate missing @ at the end of the password \n",
 9408|      0|            to_diagram());
 9409|      0|        return false;
 9410|      0|      }
 9411|  2.99k|    } else if (components.host_start == components.username_end &&
  ------------------
  |  Branch (9411:16): [True: 2.99k, False: 0]
  ------------------
 9412|  2.99k|               components.host_end > components.host_start) {
  ------------------
  |  Branch (9412:16): [True: 2.99k, False: 0]
  ------------------
 9413|  2.99k|      if (components.host_start == components.protocol_end + 2) {
  ------------------
  |  Branch (9413:11): [True: 2.99k, False: 0]
  ------------------
 9414|  2.99k|        if (buffer[components.protocol_end] != '/' ||
  ------------------
  |  Branch (9414:13): [True: 0, False: 2.99k]
  ------------------
 9415|  2.99k|            buffer[components.protocol_end + 1] != '/') {
  ------------------
  |  Branch (9415:13): [True: 0, False: 2.99k]
  ------------------
 9416|      0|          ada_log(
 9417|      0|              "url_aggregator::validate missing // between protocol and host "
 9418|      0|              "\n",
 9419|      0|              to_diagram());
 9420|      0|          return false;
 9421|      0|        }
 9422|  2.99k|      } else {
 9423|      0|        if (components.host_start > components.protocol_end &&
  ------------------
  |  Branch (9423:13): [True: 0, False: 0]
  ------------------
 9424|      0|            buffer[components.host_start] != '@') {
  ------------------
  |  Branch (9424:13): [True: 0, False: 0]
  ------------------
 9425|      0|          ada_log(
 9426|      0|              "url_aggregator::validate missing @ at the end of the username "
 9427|      0|              "\n",
 9428|      0|              to_diagram());
 9429|      0|          return false;
 9430|      0|        }
 9431|      0|      }
 9432|  2.99k|    } else {
 9433|      0|      if (components.host_end != components.host_start) {
  ------------------
  |  Branch (9433:11): [True: 0, False: 0]
  ------------------
 9434|      0|        ada_log("url_aggregator::validate expected omitted host \n",
 9435|      0|                to_diagram());
 9436|      0|        return false;
 9437|      0|      }
 9438|      0|    }
 9439|  2.99k|  }
 9440|  2.99k|  if (components.host_end != buffer.size() &&
  ------------------
  |  Branch (9440:7): [True: 2.99k, False: 0]
  ------------------
 9441|  2.99k|      components.pathname_start > components.host_end) {
  ------------------
  |  Branch (9441:7): [True: 0, False: 2.99k]
  ------------------
 9442|      0|    if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (9442:9): [True: 0, False: 0]
  ------------------
 9443|      0|        buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9443:9): [True: 0, False: 0]
  ------------------
 9444|      0|        buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (9444:9): [True: 0, False: 0]
  ------------------
 9445|      0|      if (components.pathname_start + 1 >= buffer.size() ||
  ------------------
  |  Branch (9445:11): [True: 0, False: 0]
  ------------------
 9446|      0|          buffer[components.pathname_start] != '/' ||
  ------------------
  |  Branch (9446:11): [True: 0, False: 0]
  ------------------
 9447|      0|          buffer[components.pathname_start + 1] != '/') {
  ------------------
  |  Branch (9447:11): [True: 0, False: 0]
  ------------------
 9448|      0|        ada_log(
 9449|      0|            "url_aggregator::validate expected the path to begin with // \n",
 9450|      0|            to_diagram());
 9451|      0|        return false;
 9452|      0|      }
 9453|      0|    } else if (buffer[components.host_end] != ':') {
  ------------------
  |  Branch (9453:16): [True: 0, False: 0]
  ------------------
 9454|      0|      ada_log("url_aggregator::validate missing : at the port \n",
 9455|      0|              to_diagram());
 9456|      0|      return false;
 9457|      0|    }
 9458|      0|  }
 9459|  2.99k|  if (components.pathname_start != buffer.size() &&
  ------------------
  |  Branch (9459:7): [True: 2.99k, False: 0]
  ------------------
 9460|  2.99k|      components.pathname_start < components.search_start &&
  ------------------
  |  Branch (9460:7): [True: 2.99k, False: 0]
  ------------------
 9461|  2.99k|      components.pathname_start < components.hash_start && !has_opaque_path) {
  ------------------
  |  Branch (9461:7): [True: 2.99k, False: 0]
  |  Branch (9461:60): [True: 2.99k, False: 0]
  ------------------
 9462|  2.99k|    if (buffer[components.pathname_start] != '/') {
  ------------------
  |  Branch (9462:9): [True: 0, False: 2.99k]
  ------------------
 9463|      0|      ada_log("url_aggregator::validate missing / at the path \n",
 9464|      0|              to_diagram());
 9465|      0|      return false;
 9466|      0|    }
 9467|  2.99k|  }
 9468|  2.99k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9468:7): [True: 1.57k, False: 1.41k]
  ------------------
 9469|  1.57k|    if (buffer[components.search_start] != '?') {
  ------------------
  |  Branch (9469:9): [True: 0, False: 1.57k]
  ------------------
 9470|      0|      ada_log("url_aggregator::validate missing ? at the search \n",
 9471|      0|              to_diagram());
 9472|      0|      return false;
 9473|      0|    }
 9474|  1.57k|  }
 9475|  2.99k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9475:7): [True: 413, False: 2.57k]
  ------------------
 9476|    413|    if (buffer[components.hash_start] != '#') {
  ------------------
  |  Branch (9476:9): [True: 0, False: 413]
  ------------------
 9477|      0|      ada_log("url_aggregator::validate missing # at the hash \n",
 9478|      0|              to_diagram());
 9479|      0|      return false;
 9480|      0|    }
 9481|    413|  }
 9482|       |
 9483|  2.99k|  return true;
 9484|  2.99k|}
_ZNK3ada14url_components24check_offset_consistencyEv:
 7800|  2.99k|    const noexcept {
 7801|       |  /**
 7802|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 7803|       |   *       |     |    |          | ^^^^|       |   |
 7804|       |   *       |     |    |          | |   |       |   `----- hash_start
 7805|       |   *       |     |    |          | |   |       `--------- search_start
 7806|       |   *       |     |    |          | |   `----------------- pathname_start
 7807|       |   *       |     |    |          | `--------------------- port
 7808|       |   *       |     |    |          `----------------------- host_end
 7809|       |   *       |     |    `---------------------------------- host_start
 7810|       |   *       |     `--------------------------------------- username_end
 7811|       |   *       `--------------------------------------------- protocol_end
 7812|       |   */
 7813|       |  // These conditions can be made more strict.
 7814|  2.99k|  if (protocol_end == url_components::omitted) {
  ------------------
  |  Branch (7814:7): [True: 0, False: 2.99k]
  ------------------
 7815|      0|    return false;
 7816|      0|  }
 7817|  2.99k|  uint32_t index = protocol_end;
 7818|       |
 7819|  2.99k|  if (username_end == url_components::omitted) {
  ------------------
  |  Branch (7819:7): [True: 0, False: 2.99k]
  ------------------
 7820|      0|    return false;
 7821|      0|  }
 7822|  2.99k|  if (username_end < index) {
  ------------------
  |  Branch (7822:7): [True: 0, False: 2.99k]
  ------------------
 7823|      0|    return false;
 7824|      0|  }
 7825|  2.99k|  index = username_end;
 7826|       |
 7827|  2.99k|  if (host_start == url_components::omitted) {
  ------------------
  |  Branch (7827:7): [True: 0, False: 2.99k]
  ------------------
 7828|      0|    return false;
 7829|      0|  }
 7830|  2.99k|  if (host_start < index) {
  ------------------
  |  Branch (7830:7): [True: 0, False: 2.99k]
  ------------------
 7831|      0|    return false;
 7832|      0|  }
 7833|  2.99k|  index = host_start;
 7834|       |
 7835|  2.99k|  if (port != url_components::omitted) {
  ------------------
  |  Branch (7835:7): [True: 0, False: 2.99k]
  ------------------
 7836|      0|    if (port > 0xffff) {
  ------------------
  |  Branch (7836:9): [True: 0, False: 0]
  ------------------
 7837|      0|      return false;
 7838|      0|    }
 7839|      0|    uint32_t port_length = helpers::fast_digit_count(port) + 1;
 7840|      0|    if (index + port_length < index) {
  ------------------
  |  Branch (7840:9): [True: 0, False: 0]
  ------------------
 7841|      0|      return false;
 7842|      0|    }
 7843|      0|    index += port_length;
 7844|      0|  }
 7845|       |
 7846|  2.99k|  if (pathname_start == url_components::omitted) {
  ------------------
  |  Branch (7846:7): [True: 0, False: 2.99k]
  ------------------
 7847|      0|    return false;
 7848|      0|  }
 7849|  2.99k|  if (pathname_start < index) {
  ------------------
  |  Branch (7849:7): [True: 0, False: 2.99k]
  ------------------
 7850|      0|    return false;
 7851|      0|  }
 7852|  2.99k|  index = pathname_start;
 7853|       |
 7854|  2.99k|  if (search_start != url_components::omitted) {
  ------------------
  |  Branch (7854:7): [True: 1.57k, False: 1.41k]
  ------------------
 7855|  1.57k|    if (search_start < index) {
  ------------------
  |  Branch (7855:9): [True: 0, False: 1.57k]
  ------------------
 7856|      0|      return false;
 7857|      0|    }
 7858|  1.57k|    index = search_start;
 7859|  1.57k|  }
 7860|       |
 7861|  2.99k|  if (hash_start != url_components::omitted) {
  ------------------
  |  Branch (7861:7): [True: 413, False: 2.57k]
  ------------------
 7862|    413|    if (hash_start < index) {
  ------------------
  |  Branch (7862:9): [True: 0, False: 413]
  ------------------
 7863|      0|      return false;
 7864|      0|    }
 7865|    413|  }
 7866|       |
 7867|  2.99k|  return true;
 7868|  2.99k|}
_ZNK3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE0EE8has_nextEv:
10063|  22.6k|inline bool url_search_params_iter<T, Type>::has_next() const {
10064|  22.6k|  return pos < params.params.size();
10065|  22.6k|}
_ZNK3ada22url_search_params_iterINSt3__117basic_string_viewIcNS1_11char_traitsIcEEEELNS_27url_search_params_iter_typeE1EE8has_nextEv:
10063|  22.6k|inline bool url_search_params_iter<T, Type>::has_next() const {
10064|  22.6k|  return pos < params.params.size();
10065|  22.6k|}
_ZNK3ada22url_search_params_iterINSt3__14pairINS1_17basic_string_viewIcNS1_11char_traitsIcEEEES6_EELNS_27url_search_params_iter_typeE2EE8has_nextEv:
10063|  22.6k|inline bool url_search_params_iter<T, Type>::has_next() const {
10064|  22.6k|  return pos < params.params.size();
10065|  22.6k|}

LLVMFuzzerTestOneInput:
    9|  2.99k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   10|  2.99k|  FuzzedDataProvider fdp(data, size);
   11|  2.99k|  std::string source = fdp.ConsumeRandomLengthString(256);
   12|  2.99k|  std::string base_source = fdp.ConsumeRandomLengthString(256);
   13|  2.99k|  std::string key3 = fdp.ConsumeRandomLengthString(64);
   14|  2.99k|  std::string value3 = fdp.ConsumeRandomLengthString(64);
   15|       |
   16|       |  /**
   17|       |   * ada::url_search_params
   18|       |   */
   19|  2.99k|  volatile size_t length = 0;
   20|       |
   21|  2.99k|  auto base_source_view =
   22|  2.99k|      std::string_view(base_source.data(), base_source.length());
   23|       |
   24|       |  // Test constructor with initial value
   25|  2.99k|  auto initialized = ada::url_search_params(base_source_view);
   26|  2.99k|  length += initialized.size();
   27|  2.99k|  initialized.to_string();
   28|       |
   29|       |  // Test get() and get_all() on initialized params
   30|  2.99k|  {
   31|  2.99k|    auto val = initialized.get(source);
   32|  2.99k|    if (val.has_value()) {
  ------------------
  |  Branch (32:9): [True: 277, False: 2.71k]
  ------------------
   33|    277|      length += val->size();
   34|    277|    }
   35|  2.99k|    auto all_vals = initialized.get_all(source);
   36|  2.99k|    for (const auto& v : all_vals) {
  ------------------
  |  Branch (36:24): [True: 2.47k, False: 2.99k]
  ------------------
   37|  2.47k|      length += v.size();
   38|  2.47k|    }
   39|  2.99k|  }
   40|       |
   41|       |  // Test sort() on initialized params
   42|  2.99k|  initialized.sort();
   43|       |
   44|       |  // Test C++ range-for iteration; also verify has(k) and has(k,v) consistency.
   45|  10.0k|  for (const auto& pair : initialized) {
  ------------------
  |  Branch (45:25): [True: 10.0k, False: 2.99k]
  ------------------
   46|  10.0k|    length += pair.first.size();
   47|  10.0k|    length += pair.second.size();
   48|       |
   49|       |    // Every key seen in iteration must be reported by has(key).
   50|  10.0k|    if (!initialized.has(pair.first)) {
  ------------------
  |  Branch (50:9): [True: 0, False: 10.0k]
  ------------------
   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.0k|    if (initialized.has(pair.first, pair.second) &&
  ------------------
  |  Branch (59:9): [True: 10.0k, False: 0]
  ------------------
   60|  10.0k|        !initialized.has(pair.first)) {
  ------------------
  |  Branch (60:9): [True: 0, False: 10.0k]
  ------------------
   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.0k|    auto got = initialized.get(pair.first);
   70|  10.0k|    if (!got.has_value()) {
  ------------------
  |  Branch (70:9): [True: 0, False: 10.0k]
  ------------------
   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.0k|  }
   78|       |
   79|       |  // Test index-based access
   80|  2.99k|  if (initialized.size() > 0) {
  ------------------
  |  Branch (80:7): [True: 1.15k, False: 1.83k]
  ------------------
   81|  1.15k|    auto front = initialized.front();
   82|  1.15k|    length += front.first.size();
   83|  1.15k|    auto back = initialized.back();
   84|  1.15k|    length += back.first.size();
   85|  1.15k|    auto first = initialized[0];
   86|  1.15k|    length += first.first.size();
   87|  1.15k|  }
   88|       |
   89|       |  // Test default-constructed params with various mutations
   90|  2.99k|  auto search_params = ada::url_search_params();
   91|  2.99k|  search_params.append(source, base_source);
   92|  2.99k|  search_params.append(key3, value3);
   93|  2.99k|  search_params.set(source, base_source);
   94|  2.99k|  search_params.to_string();
   95|       |
   96|       |  // Test size()
   97|  2.99k|  length += search_params.size();
   98|       |
   99|       |  // Test has() and has(key, value) overloads
  100|  2.99k|  volatile bool has_key = search_params.has(base_source);
  101|  2.99k|  if (has_key) {
  ------------------
  |  Branch (101:7): [True: 1.76k, False: 1.22k]
  ------------------
  102|  1.76k|    search_params.append(base_source, source);
  103|  1.76k|  }
  104|       |
  105|       |  // Test get() - returns first matching value
  106|  2.99k|  {
  107|  2.99k|    auto val = search_params.get(source);
  108|  2.99k|    if (val.has_value()) {
  ------------------
  |  Branch (108:9): [True: 2.99k, False: 0]
  ------------------
  109|  2.99k|      length += val->size();
  110|  2.99k|    }
  111|  2.99k|  }
  112|       |
  113|       |  // Test get_all() - returns all matching values
  114|  2.99k|  {
  115|  2.99k|    auto all_vals = search_params.get_all(source);
  116|  2.99k|    length += all_vals.size();
  117|  3.46k|    for (const auto& v : all_vals) {
  ------------------
  |  Branch (117:24): [True: 3.46k, False: 2.99k]
  ------------------
  118|  3.46k|      length += v.size();
  119|  3.46k|    }
  120|  2.99k|  }
  121|       |
  122|       |  // Test remove(key) and remove(key, value) overloads
  123|  2.99k|  search_params.remove(source);
  124|  2.99k|  search_params.remove(source, base_source);
  125|       |
  126|       |  // Test has(key, value) after remove
  127|  2.99k|  if (search_params.has(base_source, source)) {
  ------------------
  |  Branch (127:7): [True: 1.29k, False: 1.69k]
  ------------------
  128|  1.29k|    search_params.remove(base_source);
  129|  1.29k|    search_params.remove(base_source, source);
  130|  1.29k|  }
  131|       |
  132|       |  // Append more pairs for iteration testing
  133|  2.99k|  search_params.append(key3, value3);
  134|  2.99k|  search_params.append(source, key3);
  135|  2.99k|  search_params.append(base_source, value3);
  136|       |
  137|       |  // Test sort()
  138|  2.99k|  search_params.sort();
  139|       |
  140|       |  // Test serialization after sort
  141|  2.99k|  std::string serialized = search_params.to_string();
  142|  2.99k|  length += serialized.size();
  143|       |
  144|       |  // Test JavaScript-style iterator: keys
  145|  2.99k|  auto keys = search_params.get_keys();
  146|  12.8k|  while (keys.has_next()) {
  ------------------
  |  Branch (146:10): [True: 9.84k, False: 2.99k]
  ------------------
  147|  9.84k|    auto k = keys.next();
  148|  9.84k|    if (k.has_value()) {
  ------------------
  |  Branch (148:9): [True: 9.84k, False: 0]
  ------------------
  149|  9.84k|      length += k->size();
  150|  9.84k|    }
  151|  9.84k|  }
  152|       |
  153|       |  // Test JavaScript-style iterator: values
  154|  2.99k|  auto values = search_params.get_values();
  155|  12.8k|  while (values.has_next()) {
  ------------------
  |  Branch (155:10): [True: 9.84k, False: 2.99k]
  ------------------
  156|  9.84k|    auto v = values.next();
  157|  9.84k|    if (v.has_value()) {
  ------------------
  |  Branch (157:9): [True: 9.84k, False: 0]
  ------------------
  158|  9.84k|      length += v->size();
  159|  9.84k|    }
  160|  9.84k|  }
  161|       |
  162|       |  // Test JavaScript-style iterator: entries
  163|  2.99k|  auto entries = search_params.get_entries();
  164|  12.8k|  while (entries.has_next()) {
  ------------------
  |  Branch (164:10): [True: 9.84k, False: 2.99k]
  ------------------
  165|  9.84k|    auto e = entries.next();
  166|  9.84k|    if (e.has_value()) {
  ------------------
  |  Branch (166:9): [True: 9.84k, False: 0]
  ------------------
  167|  9.84k|      length += e->first.size();
  168|  9.84k|      length += e->second.size();
  169|  9.84k|    }
  170|  9.84k|  }
  171|       |
  172|       |  // Test C++ range-for on the mutated params
  173|  9.84k|  for (const auto& pair : search_params) {
  ------------------
  |  Branch (173:25): [True: 9.84k, False: 2.99k]
  ------------------
  174|  9.84k|    length += pair.first.size();
  175|  9.84k|    length += pair.second.size();
  176|  9.84k|  }
  177|       |
  178|       |  // Test reset() - private method used by C API
  179|  2.99k|  std::string resetted_value = fdp.ConsumeRandomLengthString(256);
  180|  2.99k|  search_params.reset(resetted_value);
  181|  2.99k|  length += search_params.size();
  182|       |
  183|       |  // Test that reset() followed by iteration doesn't crash
  184|  2.99k|  for (const auto& pair : search_params) {
  ------------------
  |  Branch (184:25): [True: 2.84k, False: 2.99k]
  ------------------
  185|  2.84k|    length += pair.first.size();
  186|  2.84k|    length += pair.second.size();
  187|  2.84k|  }
  188|       |
  189|       |  // Test get() after reset
  190|  2.99k|  {
  191|  2.99k|    auto val = search_params.get(source);
  192|  2.99k|    if (val.has_value()) {
  ------------------
  |  Branch (192:9): [True: 31, False: 2.95k]
  ------------------
  193|     31|      length += val->size();
  194|     31|    }
  195|  2.99k|  }
  196|       |
  197|       |  // Test copy constructor and copy assignment
  198|  2.99k|  ada::url_search_params copied = search_params;
  199|  2.99k|  length += copied.size();
  200|  2.99k|  ada::url_search_params assigned;
  201|  2.99k|  assigned = search_params;
  202|  2.99k|  length += assigned.size();
  203|       |
  204|       |  // Test move constructor and move assignment
  205|  2.99k|  ada::url_search_params move_constructed = std::move(copied);
  206|  2.99k|  length += move_constructed.size();
  207|  2.99k|  ada::url_search_params move_assigned;
  208|  2.99k|  move_assigned = std::move(assigned);
  209|  2.99k|  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.99k|  {
  225|  2.99k|    std::string s1 = initialized.to_string();
  226|  2.99k|    ada::url_search_params reparsed(s1);
  227|  2.99k|    std::string s2 = reparsed.to_string();
  228|  2.99k|    if (s1 != s2) {
  ------------------
  |  Branch (228:9): [True: 0, False: 2.99k]
  ------------------
  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.99k|  }
  236|       |
  237|       |  // Mutated params idempotency.
  238|  2.99k|  {
  239|  2.99k|    std::string s1 = move_assigned.to_string();
  240|  2.99k|    ada::url_search_params reparsed(s1);
  241|  2.99k|    std::string s2 = reparsed.to_string();
  242|  2.99k|    if (s1 != s2) {
  ------------------
  |  Branch (242:9): [True: 0, False: 2.99k]
  ------------------
  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.99k|  }
  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.99k|  {
  260|  2.99k|    std::string url_str = "https://example.com/?" + source;
  261|  2.99k|    auto parsed_url = ada::parse<ada::url_aggregator>(url_str);
  262|  2.99k|    if (parsed_url) {
  ------------------
  |  Branch (262:9): [True: 2.99k, False: 0]
  ------------------
  263|  2.99k|      std::string search_raw = std::string(parsed_url->get_search());
  264|  2.99k|      std::string_view search_view = search_raw;
  265|  2.99k|      if (!search_view.empty() && search_view[0] == '?') {
  ------------------
  |  Branch (265:11): [True: 1.62k, False: 1.36k]
  |  Branch (265:35): [True: 1.62k, False: 0]
  ------------------
  266|  1.62k|        search_view = search_view.substr(1);
  267|  1.62k|      }
  268|       |
  269|  2.99k|      ada::url_search_params sp_from_url(search_view);
  270|       |
  271|       |      // Size and iteration must not crash.
  272|  2.99k|      length += sp_from_url.size();
  273|  4.00k|      for (const auto& pair : sp_from_url) {
  ------------------
  |  Branch (273:29): [True: 4.00k, False: 2.99k]
  ------------------
  274|  4.00k|        length += pair.first.size();
  275|  4.00k|        length += pair.second.size();
  276|  4.00k|      }
  277|       |
  278|       |      // Idempotency check.
  279|  2.99k|      std::string t1 = sp_from_url.to_string();
  280|  2.99k|      ada::url_search_params sp2(t1);
  281|  2.99k|      std::string t2 = sp2.to_string();
  282|  2.99k|      if (t1 != t2) {
  ------------------
  |  Branch (282:11): [True: 0, False: 2.99k]
  ------------------
  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.99k|      parsed_url->set_search(t1);
  292|  2.99k|      volatile bool v = parsed_url->validate();
  293|  2.99k|      (void)v;
  294|  2.99k|    }
  295|  2.99k|  }
  296|       |
  297|  2.99k|  return 0;
  298|  2.99k|}

