_ZN3ada4idna13utf8_to_utf32EPKcmPDi:
  151|  18.4k|size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
  152|  18.4k|  const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
  153|  18.4k|  size_t pos = 0;
  154|  18.4k|  const char32_t* start{utf32_output};
  155|   204k|  while (pos < len) {
  ------------------
  |  Branch (155:10): [True: 192k, False: 12.2k]
  ------------------
  156|       |    // try to convert the next block of 16 ASCII bytes
  157|   192k|    if (pos + 16 <= len) {  // if it is safe to read 16 more
  ------------------
  |  Branch (157:9): [True: 120k, False: 71.9k]
  ------------------
  158|       |                            // bytes, check that they are ascii
  159|   120k|      uint64_t v1;
  160|   120k|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  161|   120k|      uint64_t v2;
  162|   120k|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  163|   120k|      uint64_t v{v1 | v2};
  164|   120k|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (164:11): [True: 3.74k, False: 116k]
  ------------------
  165|  3.74k|        size_t final_pos = pos + 16;
  166|  63.6k|        while (pos < final_pos) {
  ------------------
  |  Branch (166:16): [True: 59.9k, False: 3.74k]
  ------------------
  167|  59.9k|          *utf32_output++ = char32_t(buf[pos]);
  168|  59.9k|          pos++;
  169|  59.9k|        }
  170|  3.74k|        continue;
  171|  3.74k|      }
  172|   120k|    }
  173|   188k|    uint8_t leading_byte = data[pos];  // leading byte
  174|   188k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (174:9): [True: 95.7k, False: 93.1k]
  ------------------
  175|       |      // converting one ASCII byte !!!
  176|  95.7k|      *utf32_output++ = char32_t(leading_byte);
  177|  95.7k|      pos++;
  178|  95.7k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (178:16): [True: 13.1k, False: 80.0k]
  ------------------
  179|       |      // We have a two-byte UTF-8
  180|  13.1k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (180:11): [True: 466, False: 12.6k]
  ------------------
  181|    466|        return 0;
  182|    466|      }  // minimal bound checking
  183|  12.6k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (183:11): [True: 695, False: 11.9k]
  ------------------
  184|    695|        return 0;
  185|    695|      }
  186|       |      // range check
  187|  11.9k|      uint32_t code_point =
  188|  11.9k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  189|  11.9k|      if (code_point < 0x80 || 0x7ff < code_point) {
  ------------------
  |  Branch (189:11): [True: 71, False: 11.9k]
  |  Branch (189:32): [True: 0, False: 11.9k]
  ------------------
  190|     71|        return 0;
  191|     71|      }
  192|  11.9k|      *utf32_output++ = char32_t(code_point);
  193|  11.9k|      pos += 2;
  194|  80.0k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (194:16): [True: 73.9k, False: 6.10k]
  ------------------
  195|       |      // We have a three-byte UTF-8
  196|  73.9k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (196:11): [True: 267, False: 73.6k]
  ------------------
  197|    267|        return 0;
  198|    267|      }  // minimal bound checking
  199|       |
  200|  73.6k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (200:11): [True: 303, False: 73.3k]
  ------------------
  201|    303|        return 0;
  202|    303|      }
  203|  73.3k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (203:11): [True: 81, False: 73.2k]
  ------------------
  204|     81|        return 0;
  205|     81|      }
  206|       |      // range check
  207|  73.2k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  208|  73.2k|                            (data[pos + 1] & 0b00111111) << 6 |
  209|  73.2k|                            (data[pos + 2] & 0b00111111);
  210|  73.2k|      if (code_point < 0x800 || 0xffff < code_point ||
  ------------------
  |  Branch (210:11): [True: 45, False: 73.2k]
  |  Branch (210:33): [True: 0, False: 73.2k]
  ------------------
  211|  73.2k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (211:12): [True: 4.85k, False: 68.3k]
  |  Branch (211:35): [True: 23, False: 4.82k]
  ------------------
  212|     68|        return 0;
  213|     68|      }
  214|  73.2k|      *utf32_output++ = char32_t(code_point);
  215|  73.2k|      pos += 3;
  216|  73.2k|    } else if ((leading_byte & 0b11111000) == 0b11110000) {  // 0b11110000
  ------------------
  |  Branch (216:16): [True: 2.49k, False: 3.61k]
  ------------------
  217|       |      // we have a 4-byte UTF-8 word.
  218|  2.49k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (218:11): [True: 251, False: 2.24k]
  ------------------
  219|    251|        return 0;
  220|    251|      }  // minimal bound checking
  221|  2.24k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (221:11): [True: 203, False: 2.03k]
  ------------------
  222|    203|        return 0;
  223|    203|      }
  224|  2.03k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (224:11): [True: 74, False: 1.96k]
  ------------------
  225|     74|        return 0;
  226|     74|      }
  227|  1.96k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (227:11): [True: 52, False: 1.91k]
  ------------------
  228|     52|        return 0;
  229|     52|      }
  230|       |
  231|       |      // range check
  232|  1.91k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  233|  1.91k|                            (data[pos + 1] & 0b00111111) << 12 |
  234|  1.91k|                            (data[pos + 2] & 0b00111111) << 6 |
  235|  1.91k|                            (data[pos + 3] & 0b00111111);
  236|  1.91k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (236:11): [True: 36, False: 1.87k]
  |  Branch (236:35): [True: 33, False: 1.84k]
  ------------------
  237|     69|        return 0;
  238|     69|      }
  239|  1.84k|      *utf32_output++ = char32_t(code_point);
  240|  1.84k|      pos += 4;
  241|  3.61k|    } else {
  242|  3.61k|      return 0;
  243|  3.61k|    }
  244|   188k|  }
  245|  12.2k|  return utf32_output - start;
  246|  18.4k|}
_ZN3ada4idna22utf32_length_from_utf8EPKcm:
  261|  18.4k|size_t utf32_length_from_utf8(const char* buf, size_t len) {
  262|  18.4k|  const int8_t* p = reinterpret_cast<const int8_t*>(buf);
  263|  18.4k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  264|       |    // -65 is 0b10111111, anything larger in two-complement's
  265|       |    // should start a new code point.
  266|  18.4k|    return c > -65;
  267|  18.4k|  });
  268|  18.4k|}
_ZN3ada4idna9ascii_mapEPcm:
 2837|  29.7k|void ascii_map(char* input, size_t length) {
 2838|  29.7k|  auto broadcast = [](uint8_t v) -> uint64_t {
 2839|  29.7k|    return 0x101010101010101ull * v;
 2840|  29.7k|  };
 2841|  29.7k|  uint64_t broadcast_80 = broadcast(0x80);
 2842|  29.7k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 2843|  29.7k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 2844|  29.7k|  size_t i = 0;
 2845|       |
 2846|  52.6k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (2846:10): [True: 22.8k, False: 29.7k]
  ------------------
 2847|  22.8k|    uint64_t word{};
 2848|  22.8k|    std::memcpy(&word, input + i, sizeof(word));
 2849|  22.8k|    word ^=
 2850|  22.8k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 2851|  22.8k|    std::memcpy(input + i, &word, sizeof(word));
 2852|  22.8k|  }
 2853|  29.7k|  if (i < length) {
  ------------------
  |  Branch (2853:7): [True: 28.0k, False: 1.73k]
  ------------------
 2854|  28.0k|    uint64_t word{};
 2855|  28.0k|    std::memcpy(&word, input + i, length - i);
 2856|  28.0k|    word ^=
 2857|  28.0k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 2858|  28.0k|    std::memcpy(input + i, &word, length - i);
 2859|  28.0k|  }
 2860|  29.7k|}
_ZN3ada4idna3mapENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIDiS4_NS1_9allocatorIDiEEEE:
 2866|  23.3k|bool map(std::u32string_view input, std::u32string& out) {
 2867|       |  //  [Map](https://www.unicode.org/reports/tr46/#ProcessingStepMap).
 2868|       |  //  For each code point in the domain_name string, look up the status
 2869|       |  //  value in Section 5, [IDNA Mapping
 2870|       |  //  Table](https://www.unicode.org/reports/tr46/#IDNA_Mapping_Table),
 2871|       |  //  and take the following actions:
 2872|       |  //    * disallowed: Leave the code point unchanged in the string, and
 2873|       |  //    record that there was an error.
 2874|       |  //    * ignored: Remove the code point from the string.
 2875|       |  //    * mapped: Replace the code point in the string by the value for
 2876|       |  //    the mapping in Section 5, [IDNA Mapping
 2877|       |  //    Table](https://www.unicode.org/reports/tr46/#IDNA_Mapping_Table).
 2878|       |  //    * valid: Leave the code point unchanged in the string.
 2879|  23.3k|  out.clear();
 2880|  23.3k|  out.reserve(input.size());
 2881|   273k|  for (char32_t x : input) {
  ------------------
  |  Branch (2881:19): [True: 273k, False: 22.5k]
  ------------------
 2882|   273k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 2883|   273k|    if (status == IDNA_DISALLOWED) {
  ------------------
  |  Branch (2883:9): [True: 827, False: 273k]
  ------------------
 2884|    827|      return false;
 2885|    827|    }
 2886|   273k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (2886:9): [True: 184k, False: 88.3k]
  ------------------
 2887|   184k|      out.push_back(x);
 2888|   184k|      continue;
 2889|   184k|    }
 2890|       |    // IDNA_IGNORED (status==0) falls through: idna_utf8_mappings[0] == 0x00
 2891|       |    // (null terminator), so the decode loop below produces nothing.
 2892|       |
 2893|       |    // Mapped (or ignored): decode null-terminated UTF-8 from the mapping table.
 2894|  88.3k|    const uint8_t* ptr = idna_utf8_mappings + status;
 2895|   522k|    while (*ptr != 0) {
  ------------------
  |  Branch (2895:12): [True: 433k, False: 88.3k]
  ------------------
 2896|   433k|      out.push_back(utf8_next(ptr));
 2897|   433k|    }
 2898|  88.3k|  }
 2899|  22.5k|  return true;
 2900|  23.3k|}
_ZN3ada4idna28compute_decomposition_lengthENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 7828|  22.2k|    const std::u32string_view input) noexcept {
 7829|  22.2k|  bool decomposition_needed{false};
 7830|  22.2k|  size_t additional_elements{0};
 7831|   535k|  for (char32_t current_character : input) {
  ------------------
  |  Branch (7831:35): [True: 535k, False: 22.2k]
  ------------------
 7832|   535k|    size_t decomposition_length{0};
 7833|       |
 7834|   535k|    if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (7834:9): [True: 21.7k, False: 513k]
  ------------------
 7835|  21.7k|        current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (7835:9): [True: 17.7k, False: 3.95k]
  ------------------
 7836|  17.7k|      decomposition_length = 2;
 7837|  17.7k|      if ((current_character - hangul_sbase) % hangul_tcount) {
  ------------------
  |  Branch (7837:11): [True: 5.77k, False: 12.0k]
  ------------------
 7838|  5.77k|        decomposition_length = 3;
 7839|  5.77k|      }
 7840|   517k|    } else if (current_character < 0x110000) {
  ------------------
  |  Branch (7840:16): [True: 517k, False: 0]
  ------------------
 7841|   517k|      const uint8_t di = decomposition_index[current_character >> 8];
 7842|   517k|      const uint16_t* const decomposition =
 7843|   517k|          decomposition_block[di] + (current_character % 256);
 7844|   517k|      decomposition_length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 7845|   517k|      if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (7845:11): [True: 5.77k, False: 512k]
  |  Branch (7845:41): [True: 0, False: 5.77k]
  ------------------
 7846|      0|        decomposition_length = 0;
 7847|      0|      }
 7848|   517k|    }
 7849|   535k|    if (decomposition_length != 0) {
  ------------------
  |  Branch (7849:9): [True: 23.5k, False: 512k]
  ------------------
 7850|  23.5k|      decomposition_needed = true;
 7851|  23.5k|      additional_elements += decomposition_length - 1;
 7852|  23.5k|    }
 7853|   535k|  }
 7854|  22.2k|  return {decomposition_needed, additional_elements};
 7855|  22.2k|}
_ZN3ada4idna9decomposeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEEm:
 7857|  6.20k|void decompose(std::u32string& input, size_t additional_elements) {
 7858|  6.20k|  input.resize(input.size() + additional_elements);
 7859|  6.20k|  for (size_t descending_idx = input.size(),
 7860|  6.20k|              input_count = descending_idx - additional_elements;
 7861|   241k|       input_count--;) {
  ------------------
  |  Branch (7861:8): [True: 235k, False: 6.20k]
  ------------------
 7862|   235k|    if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (7862:9): [True: 18.4k, False: 217k]
  ------------------
 7863|  18.4k|        input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (7863:9): [True: 17.7k, False: 640]
  ------------------
 7864|       |      // Hangul decomposition.
 7865|  17.7k|      char32_t s_index = input[input_count] - hangul_sbase;
 7866|  17.7k|      if (s_index % hangul_tcount != 0) {
  ------------------
  |  Branch (7866:11): [True: 5.77k, False: 12.0k]
  ------------------
 7867|  5.77k|        input[--descending_idx] = hangul_tbase + s_index % hangul_tcount;
 7868|  5.77k|      }
 7869|  17.7k|      input[--descending_idx] =
 7870|  17.7k|          hangul_vbase + (s_index % hangul_ncount) / hangul_tcount;
 7871|  17.7k|      input[--descending_idx] = hangul_lbase + s_index / hangul_ncount;
 7872|   217k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (7872:16): [True: 217k, False: 0]
  ------------------
 7873|       |      // Check decomposition_data.
 7874|   217k|      const uint16_t* decomposition =
 7875|   217k|          decomposition_block[decomposition_index[input[input_count] >> 8]] +
 7876|   217k|          (input[input_count] % 256);
 7877|   217k|      uint16_t decomposition_length =
 7878|   217k|          (decomposition[1] >> 2) - (decomposition[0] >> 2);
 7879|   217k|      if (decomposition_length > 0 && (decomposition[0] & 1)) {
  ------------------
  |  Branch (7879:11): [True: 5.77k, False: 211k]
  |  Branch (7879:39): [True: 0, False: 5.77k]
  ------------------
 7880|      0|        decomposition_length = 0;
 7881|      0|      }
 7882|   217k|      if (decomposition_length > 0) {
  ------------------
  |  Branch (7882:11): [True: 5.77k, False: 211k]
  ------------------
 7883|       |        // Non-recursive decomposition.
 7884|  19.3k|        while (decomposition_length-- > 0) {
  ------------------
  |  Branch (7884:16): [True: 13.5k, False: 5.77k]
  ------------------
 7885|  13.5k|          input[--descending_idx] = decomposition_data[(decomposition[0] >> 2) +
 7886|  13.5k|                                                       decomposition_length];
 7887|  13.5k|        }
 7888|   211k|      } else {
 7889|       |        // No decomposition.
 7890|   211k|        input[--descending_idx] = input[input_count];
 7891|   211k|      }
 7892|   217k|    } else {
 7893|       |      // Non-Unicode character.
 7894|      0|      input[--descending_idx] = input[input_count];
 7895|      0|    }
 7896|   235k|  }
 7897|  6.20k|}
_ZN3ada4idna7get_cccEDi:
 7899|  1.06M|uint8_t get_ccc(char32_t c) noexcept {
 7900|  1.06M|  return c < 0x110000 ? canonical_combining_class_block
  ------------------
  |  Branch (7900:10): [True: 1.06M, False: 0]
  ------------------
 7901|  1.06M|                            [canonical_combining_class_index[c >> 8]][c % 256]
 7902|  1.06M|                      : 0;
 7903|  1.06M|}
_ZN3ada4idna10sort_marksERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 7905|  22.2k|void sort_marks(std::u32string& input) {
 7906|   567k|  for (size_t idx = 1; idx < input.size(); idx++) {
  ------------------
  |  Branch (7906:24): [True: 544k, False: 22.2k]
  ------------------
 7907|   544k|    uint8_t ccc = get_ccc(input[idx]);
 7908|   544k|    if (ccc == 0) {
  ------------------
  |  Branch (7908:9): [True: 528k, False: 15.7k]
  ------------------
 7909|   528k|      continue;
 7910|   528k|    }  // Skip non-combining characters.
 7911|  15.7k|    auto current_character = input[idx];
 7912|  15.7k|    size_t back_idx = idx;
 7913|  17.4k|    while (back_idx != 0 && get_ccc(input[back_idx - 1]) > ccc) {
  ------------------
  |  Branch (7913:12): [True: 17.3k, False: 177]
  |  Branch (7913:29): [True: 1.71k, False: 15.5k]
  ------------------
 7914|  1.71k|      input[back_idx] = input[back_idx - 1];
 7915|  1.71k|      back_idx--;
 7916|  1.71k|    }
 7917|  15.7k|    input[back_idx] = current_character;
 7918|  15.7k|  }
 7919|  22.2k|}
_ZN3ada4idna13decompose_nfcERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 7921|  22.2k|void decompose_nfc(std::u32string& input) {
 7922|       |  /**
 7923|       |   * Decompose the domain_name string to Unicode Normalization Form C.
 7924|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepDecompose
 7925|       |   */
 7926|  22.2k|  auto [decomposition_needed, additional_elements] =
 7927|  22.2k|      compute_decomposition_length(input);
 7928|  22.2k|  if (decomposition_needed) {
  ------------------
  |  Branch (7928:7): [True: 6.20k, False: 16.0k]
  ------------------
 7929|  6.20k|    decompose(input, additional_elements);
 7930|  6.20k|  }
 7931|  22.2k|  sort_marks(input);
 7932|  22.2k|}
_ZN3ada4idna7composeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 7934|  22.2k|void compose(std::u32string& input) {
 7935|       |  /**
 7936|       |   * Compose the domain_name string to Unicode Normalization Form C.
 7937|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepCompose
 7938|       |   */
 7939|  22.2k|  size_t input_count{0};
 7940|  22.2k|  size_t composition_count{0};
 7941|   549k|  for (; input_count < input.size(); input_count++, composition_count++) {
  ------------------
  |  Branch (7941:10): [True: 527k, False: 22.2k]
  ------------------
 7942|   527k|    input[composition_count] = input[input_count];
 7943|   527k|    if (input[input_count] >= hangul_lbase &&
  ------------------
  |  Branch (7943:9): [True: 289k, False: 238k]
  ------------------
 7944|   289k|        input[input_count] < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (7944:9): [True: 20.0k, False: 269k]
  ------------------
 7945|  20.0k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (7945:11): [True: 19.8k, False: 282]
  ------------------
 7946|  19.8k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (7946:11): [True: 18.1k, False: 1.68k]
  ------------------
 7947|  18.1k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (7947:11): [True: 17.7k, False: 331]
  ------------------
 7948|  17.7k|        input[composition_count] =
 7949|  17.7k|            hangul_sbase +
 7950|  17.7k|            ((input[input_count] - hangul_lbase) * hangul_vcount +
 7951|  17.7k|             input[input_count + 1] - hangul_vbase) *
 7952|  17.7k|                hangul_tcount;
 7953|  17.7k|        input_count++;
 7954|  17.7k|        if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (7954:13): [True: 17.4k, False: 381]
  ------------------
 7955|  17.4k|            input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (7955:13): [True: 6.15k, False: 11.2k]
  ------------------
 7956|  6.15k|            input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (7956:13): [True: 5.77k, False: 376]
  ------------------
 7957|  5.77k|          input[composition_count] += input[++input_count] - hangul_tbase;
 7958|  5.77k|        }
 7959|  17.7k|      }
 7960|   507k|    } else if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (7960:16): [True: 2.52k, False: 505k]
  ------------------
 7961|  2.52k|               input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (7961:16): [True: 0, False: 2.52k]
  ------------------
 7962|      0|      if ((input[input_count] - hangul_sbase) % hangul_tcount &&
  ------------------
  |  Branch (7962:11): [True: 0, False: 0]
  ------------------
 7963|      0|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (7963:11): [True: 0, False: 0]
  ------------------
 7964|      0|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (7964:11): [True: 0, False: 0]
  ------------------
 7965|      0|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (7965:11): [True: 0, False: 0]
  ------------------
 7966|      0|        input[composition_count] += input[++input_count] - hangul_tbase;
 7967|      0|      }
 7968|   507k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (7968:16): [True: 507k, False: 0]
  ------------------
 7969|   507k|      const uint16_t* composition =
 7970|   507k|          &composition_block[composition_index[input[input_count] >> 8]]
 7971|   507k|                            [input[input_count] % 256];
 7972|   507k|      size_t initial_composition_count = composition_count;
 7973|   523k|      for (int32_t previous_ccc = -1; input_count + 1 < input.size();
  ------------------
  |  Branch (7973:39): [True: 502k, False: 21.0k]
  ------------------
 7974|   507k|           input_count++) {
 7975|   502k|        uint8_t ccc = get_ccc(input[input_count + 1]);
 7976|       |
 7977|   502k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (7977:13): [True: 100k, False: 401k]
  |  Branch (7977:49): [True: 99.2k, False: 1.02k]
  ------------------
 7978|       |          // Try finding a composition.
 7979|  99.2k|          int left = composition[0];
 7980|  99.2k|          int right = composition[1];
 7981|   241k|          while (left + 2 < right) {
  ------------------
  |  Branch (7981:18): [True: 142k, False: 99.2k]
  ------------------
 7982|       |            // mean without overflow
 7983|   142k|            int middle = left + (((right - left) >> 1) & ~1);
 7984|   142k|            if (composition_data[middle] <= input[input_count + 1]) {
  ------------------
  |  Branch (7984:17): [True: 15.5k, False: 127k]
  ------------------
 7985|  15.5k|              left = middle;
 7986|  15.5k|            }
 7987|   142k|            if (composition_data[middle] >= input[input_count + 1]) {
  ------------------
  |  Branch (7987:17): [True: 131k, False: 11.6k]
  ------------------
 7988|   131k|              right = middle;
 7989|   131k|            }
 7990|   142k|          }
 7991|  99.2k|          if (composition_data[left] == input[input_count + 1]) {
  ------------------
  |  Branch (7991:15): [True: 7.87k, False: 91.3k]
  ------------------
 7992|  7.87k|            input[initial_composition_count] = composition_data[left + 1];
 7993|  7.87k|            composition =
 7994|  7.87k|                &composition_block
 7995|  7.87k|                    [composition_index[composition_data[left + 1] >> 8]]
 7996|  7.87k|                    [composition_data[left + 1] % 256];
 7997|  7.87k|            continue;
 7998|  7.87k|          }
 7999|  99.2k|        }
 8000|       |
 8001|   494k|        if (ccc == 0) {
  ------------------
  |  Branch (8001:13): [True: 486k, False: 7.90k]
  ------------------
 8002|   486k|          break;
 8003|   486k|        }  // Not a combining character.
 8004|  7.90k|        previous_ccc = ccc;
 8005|  7.90k|        input[++composition_count] = input[input_count + 1];
 8006|  7.90k|      }
 8007|   507k|    }
 8008|   527k|  }
 8009|       |
 8010|  22.2k|  if (composition_count < input_count) {
  ------------------
  |  Branch (8010:7): [True: 6.25k, False: 15.9k]
  ------------------
 8011|  6.25k|    input.resize(composition_count);
 8012|  6.25k|  }
 8013|  22.2k|}
_ZN3ada4idna9normalizeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 8015|  22.2k|void normalize(std::u32string& input) {
 8016|       |  /**
 8017|       |   * Normalize the domain_name string to Unicode Normalization Form C.
 8018|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepNormalize
 8019|       |   */
 8020|  22.2k|  decompose_nfc(input);
 8021|  22.2k|  compose(input);
 8022|  22.2k|}
_ZN3ada4idna17punycode_to_utf32ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIDiNS3_IDiEENS1_9allocatorIDiEEEE:
 8065|  11.9k|bool punycode_to_utf32(std::string_view input, std::u32string &out) {
 8066|  11.9k|  int32_t written_out{0};
 8067|  11.9k|  out.reserve(out.size() + input.size());
 8068|  11.9k|  uint32_t n = initial_n;
 8069|  11.9k|  int32_t i = 0;
 8070|  11.9k|  int32_t bias = initial_bias;
 8071|       |  // grab ascii content
 8072|  11.9k|  size_t end_of_ascii = input.find_last_of('-');
 8073|  11.9k|  if (end_of_ascii != std::string_view::npos) {
  ------------------
  |  Branch (8073:7): [True: 2.73k, False: 9.24k]
  ------------------
 8074|  9.06k|    for (uint8_t c : input.substr(0, end_of_ascii)) {
  ------------------
  |  Branch (8074:20): [True: 9.06k, False: 2.73k]
  ------------------
 8075|  9.06k|      if (c >= 0x80) {
  ------------------
  |  Branch (8075:11): [True: 0, False: 9.06k]
  ------------------
 8076|      0|        return false;
 8077|      0|      }
 8078|  9.06k|      out.push_back(c);
 8079|  9.06k|      written_out++;
 8080|  9.06k|    }
 8081|  2.73k|    input.remove_prefix(end_of_ascii + 1);
 8082|  2.73k|  }
 8083|  66.9k|  while (!input.empty()) {
  ------------------
  |  Branch (8083:10): [True: 55.4k, False: 11.4k]
  ------------------
 8084|  55.4k|    int32_t oldi = i;
 8085|  55.4k|    int32_t w = 1;
 8086|   122k|    for (int32_t k = base;; k += base) {
 8087|   122k|      if (input.empty()) {
  ------------------
  |  Branch (8087:11): [True: 169, False: 122k]
  ------------------
 8088|    169|        return false;
 8089|    169|      }
 8090|   122k|      uint8_t code_point = input.front();
 8091|   122k|      input.remove_prefix(1);
 8092|   122k|      int32_t digit = char_to_digit_value(code_point);
 8093|   122k|      if (digit < 0) {
  ------------------
  |  Branch (8093:11): [True: 182, False: 121k]
  ------------------
 8094|    182|        return false;
 8095|    182|      }
 8096|   121k|      if (digit > (0x7fffffff - i) / w) {
  ------------------
  |  Branch (8096:11): [True: 48, False: 121k]
  ------------------
 8097|     48|        return false;
 8098|     48|      }
 8099|   121k|      i = i + digit * w;
 8100|   121k|      int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (8100:19): [True: 35.5k, False: 86.4k]
  |  Branch (8100:38): [True: 67.6k, False: 18.7k]
  ------------------
 8101|   121k|      if (digit < t) {
  ------------------
  |  Branch (8101:11): [True: 55.0k, False: 66.8k]
  ------------------
 8102|  55.0k|        break;
 8103|  55.0k|      }
 8104|  66.8k|      if (w > 0x7fffffff / (base - t)) {
  ------------------
  |  Branch (8104:11): [True: 0, False: 66.8k]
  ------------------
 8105|      0|        return false;
 8106|      0|      }
 8107|  66.8k|      w = w * (base - t);
 8108|  66.8k|    }
 8109|  55.0k|    bias = adapt(i - oldi, written_out + 1, oldi == 0);
 8110|  55.0k|    if (i / (written_out + 1) > int32_t(0x7fffffff - n)) {
  ------------------
  |  Branch (8110:9): [True: 81, False: 54.9k]
  ------------------
 8111|     81|      return false;
 8112|     81|    }
 8113|  54.9k|    n = n + i / (written_out + 1);
 8114|  54.9k|    i = i % (written_out + 1);
 8115|  54.9k|    if (n < 0x80) {
  ------------------
  |  Branch (8115:9): [True: 0, False: 54.9k]
  ------------------
 8116|      0|      return false;
 8117|      0|    }
 8118|  54.9k|    out.insert(out.begin() + i, n);
 8119|  54.9k|    written_out++;
 8120|  54.9k|    ++i;
 8121|  54.9k|  }
 8122|       |  // See https://github.com/whatwg/url/issues/803
 8123|       |  // Reject labels whose decoded form begins with "xn--" (double-encoded ACE).
 8124|  11.4k|  if (out.size() >= 4 && out[0] == U'x' && out[1] == U'n' && out[2] == U'-' &&
  ------------------
  |  Branch (8124:7): [True: 6.70k, False: 4.79k]
  |  Branch (8124:26): [True: 761, False: 5.94k]
  |  Branch (8124:44): [True: 346, False: 415]
  |  Branch (8124:62): [True: 222, False: 124]
  ------------------
 8125|    222|      out[3] == U'-') {
  ------------------
  |  Branch (8125:7): [True: 27, False: 195]
  ------------------
 8126|     27|    return false;
 8127|     27|  }
 8128|  11.4k|  return true;
 8129|  11.4k|}
_ZN3ada4idna17utf32_to_punycodeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIcNS3_IcEENS1_9allocatorIcEEEE:
 8209|  42.7k|bool utf32_to_punycode(std::u32string_view input, std::string &out) {
 8210|  42.7k|  out.reserve(input.size() + out.size());
 8211|  42.7k|  uint32_t n = initial_n;
 8212|  42.7k|  int32_t d = 0;
 8213|  42.7k|  int32_t bias = initial_bias;
 8214|  42.7k|  size_t h = 0;
 8215|       |  // first push the ascii content
 8216|   342k|  for (uint32_t c : input) {
  ------------------
  |  Branch (8216:19): [True: 342k, False: 42.7k]
  ------------------
 8217|   342k|    if (c < 0x80) {
  ------------------
  |  Branch (8217:9): [True: 41.3k, False: 301k]
  ------------------
 8218|  41.3k|      ++h;
 8219|  41.3k|      out.push_back(char(c));
 8220|  41.3k|    }
 8221|   342k|    if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) {
  ------------------
  |  Branch (8221:9): [True: 0, False: 342k]
  |  Branch (8221:26): [True: 2.70k, False: 339k]
  |  Branch (8221:41): [True: 0, False: 2.70k]
  ------------------
 8222|      0|      return false;
 8223|      0|    }
 8224|   342k|  }
 8225|  42.7k|  size_t b = h;
 8226|  42.7k|  if (b > 0) {
  ------------------
  |  Branch (8226:7): [True: 12.0k, False: 30.6k]
  ------------------
 8227|  12.0k|    out.push_back('-');
 8228|  12.0k|  }
 8229|   270k|  while (h < input.size()) {
  ------------------
  |  Branch (8229:10): [True: 227k, False: 42.7k]
  ------------------
 8230|   227k|    uint32_t m = 0x10FFFF;
 8231|  2.94M|    for (auto code_point : input) {
  ------------------
  |  Branch (8231:26): [True: 2.94M, False: 227k]
  ------------------
 8232|  2.94M|      if (code_point >= n && code_point < m) m = code_point;
  ------------------
  |  Branch (8232:11): [True: 1.44M, False: 1.50M]
  |  Branch (8232:30): [True: 449k, False: 993k]
  ------------------
 8233|  2.94M|    }
 8234|       |
 8235|   227k|    if ((m - n) > (0x7fffffff - d) / (h + 1)) {
  ------------------
  |  Branch (8235:9): [True: 0, False: 227k]
  ------------------
 8236|      0|      return false;
 8237|      0|    }
 8238|   227k|    d = d + int32_t((m - n) * (h + 1));
 8239|   227k|    n = m;
 8240|  2.94M|    for (auto c : input) {
  ------------------
  |  Branch (8240:17): [True: 2.94M, False: 227k]
  ------------------
 8241|  2.94M|      if (c < n) {
  ------------------
  |  Branch (8241:11): [True: 1.50M, False: 1.44M]
  ------------------
 8242|  1.50M|        if (d == 0x7fffffff) {
  ------------------
  |  Branch (8242:13): [True: 0, False: 1.50M]
  ------------------
 8243|      0|          return false;
 8244|      0|        }
 8245|  1.50M|        ++d;
 8246|  1.50M|      }
 8247|  2.94M|      if (c == n) {
  ------------------
  |  Branch (8247:11): [True: 301k, False: 2.64M]
  ------------------
 8248|   301k|        int32_t q = d;
 8249|   817k|        for (int32_t k = base;; k += base) {
 8250|   817k|          int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (8250:23): [True: 213k, False: 603k]
  |  Branch (8250:42): [True: 454k, False: 149k]
  ------------------
 8251|       |
 8252|   817k|          if (q < t) {
  ------------------
  |  Branch (8252:15): [True: 301k, False: 515k]
  ------------------
 8253|   301k|            break;
 8254|   301k|          }
 8255|   515k|          out.push_back(digit_to_char(t + ((q - t) % (base - t))));
 8256|   515k|          q = (q - t) / (base - t);
 8257|   515k|        }
 8258|   301k|        out.push_back(digit_to_char(q));
 8259|   301k|        bias = adapt(d, int32_t(h + 1), h == b);
 8260|   301k|        d = 0;
 8261|   301k|        ++h;
 8262|   301k|      }
 8263|  2.94M|    }
 8264|   227k|    ++d;
 8265|   227k|    ++n;
 8266|   227k|  }
 8267|  42.7k|  return true;
 8268|  42.7k|}
_ZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9077|  54.5k|bool is_label_valid(const std::u32string_view label) {
 9078|  54.5k|  if (label.empty()) {
  ------------------
  |  Branch (9078:7): [True: 0, False: 54.5k]
  ------------------
 9079|      0|    return true;
 9080|      0|  }
 9081|       |
 9082|       |  ///////////////
 9083|       |  // We have a normalization step which ensures that we are in NFC.
 9084|       |  // If we receive punycode, we normalize and check that the normalized
 9085|       |  // version matches the original.
 9086|       |  // --------------------------------------
 9087|       |  // The label must be in Unicode Normalization Form NFC.
 9088|       |
 9089|       |  // Current URL standard indicatest that CheckHyphens is set to false.
 9090|       |  // ---------------------------------------
 9091|       |  // If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character
 9092|       |  // in both the third and fourth positions. If CheckHyphens, the label must
 9093|       |  // neither begin nor end with a U+002D HYPHEN-MINUS character.
 9094|       |
 9095|       |  // This is not necessary because we segment the
 9096|       |  // labels by '.'.
 9097|       |  // ---------------------------------------
 9098|       |  // The label must not contain a U+002E ( . ) FULL STOP.
 9099|       |  // if (label.find('.') != std::string_view::npos) return false;
 9100|       |
 9101|       |  // The label must not begin with a combining mark, that is:
 9102|       |  // General_Category=Mark.
 9103|  54.5k|  constexpr static uint32_t combining[] = {
 9104|  54.5k|      0x300,   0x301,   0x302,   0x303,   0x304,   0x305,   0x306,   0x307,
 9105|  54.5k|      0x308,   0x309,   0x30a,   0x30b,   0x30c,   0x30d,   0x30e,   0x30f,
 9106|  54.5k|      0x310,   0x311,   0x312,   0x313,   0x314,   0x315,   0x316,   0x317,
 9107|  54.5k|      0x318,   0x319,   0x31a,   0x31b,   0x31c,   0x31d,   0x31e,   0x31f,
 9108|  54.5k|      0x320,   0x321,   0x322,   0x323,   0x324,   0x325,   0x326,   0x327,
 9109|  54.5k|      0x328,   0x329,   0x32a,   0x32b,   0x32c,   0x32d,   0x32e,   0x32f,
 9110|  54.5k|      0x330,   0x331,   0x332,   0x333,   0x334,   0x335,   0x336,   0x337,
 9111|  54.5k|      0x338,   0x339,   0x33a,   0x33b,   0x33c,   0x33d,   0x33e,   0x33f,
 9112|  54.5k|      0x340,   0x341,   0x342,   0x343,   0x344,   0x345,   0x346,   0x347,
 9113|  54.5k|      0x348,   0x349,   0x34a,   0x34b,   0x34c,   0x34d,   0x34e,   0x34f,
 9114|  54.5k|      0x350,   0x351,   0x352,   0x353,   0x354,   0x355,   0x356,   0x357,
 9115|  54.5k|      0x358,   0x359,   0x35a,   0x35b,   0x35c,   0x35d,   0x35e,   0x35f,
 9116|  54.5k|      0x360,   0x361,   0x362,   0x363,   0x364,   0x365,   0x366,   0x367,
 9117|  54.5k|      0x368,   0x369,   0x36a,   0x36b,   0x36c,   0x36d,   0x36e,   0x36f,
 9118|  54.5k|      0x483,   0x484,   0x485,   0x486,   0x487,   0x488,   0x489,   0x591,
 9119|  54.5k|      0x592,   0x593,   0x594,   0x595,   0x596,   0x597,   0x598,   0x599,
 9120|  54.5k|      0x59a,   0x59b,   0x59c,   0x59d,   0x59e,   0x59f,   0x5a0,   0x5a1,
 9121|  54.5k|      0x5a2,   0x5a3,   0x5a4,   0x5a5,   0x5a6,   0x5a7,   0x5a8,   0x5a9,
 9122|  54.5k|      0x5aa,   0x5ab,   0x5ac,   0x5ad,   0x5ae,   0x5af,   0x5b0,   0x5b1,
 9123|  54.5k|      0x5b2,   0x5b3,   0x5b4,   0x5b5,   0x5b6,   0x5b7,   0x5b8,   0x5b9,
 9124|  54.5k|      0x5ba,   0x5bb,   0x5bc,   0x5bd,   0x5bf,   0x5c1,   0x5c2,   0x5c4,
 9125|  54.5k|      0x5c5,   0x5c7,   0x610,   0x611,   0x612,   0x613,   0x614,   0x615,
 9126|  54.5k|      0x616,   0x617,   0x618,   0x619,   0x61a,   0x64b,   0x64c,   0x64d,
 9127|  54.5k|      0x64e,   0x64f,   0x650,   0x651,   0x652,   0x653,   0x654,   0x655,
 9128|  54.5k|      0x656,   0x657,   0x658,   0x659,   0x65a,   0x65b,   0x65c,   0x65d,
 9129|  54.5k|      0x65e,   0x65f,   0x670,   0x6d6,   0x6d7,   0x6d8,   0x6d9,   0x6da,
 9130|  54.5k|      0x6db,   0x6dc,   0x6df,   0x6e0,   0x6e1,   0x6e2,   0x6e3,   0x6e4,
 9131|  54.5k|      0x6e7,   0x6e8,   0x6ea,   0x6eb,   0x6ec,   0x6ed,   0x711,   0x730,
 9132|  54.5k|      0x731,   0x732,   0x733,   0x734,   0x735,   0x736,   0x737,   0x738,
 9133|  54.5k|      0x739,   0x73a,   0x73b,   0x73c,   0x73d,   0x73e,   0x73f,   0x740,
 9134|  54.5k|      0x741,   0x742,   0x743,   0x744,   0x745,   0x746,   0x747,   0x748,
 9135|  54.5k|      0x749,   0x74a,   0x7a6,   0x7a7,   0x7a8,   0x7a9,   0x7aa,   0x7ab,
 9136|  54.5k|      0x7ac,   0x7ad,   0x7ae,   0x7af,   0x7b0,   0x7eb,   0x7ec,   0x7ed,
 9137|  54.5k|      0x7ee,   0x7ef,   0x7f0,   0x7f1,   0x7f2,   0x7f3,   0x7fd,   0x816,
 9138|  54.5k|      0x817,   0x818,   0x819,   0x81b,   0x81c,   0x81d,   0x81e,   0x81f,
 9139|  54.5k|      0x820,   0x821,   0x822,   0x823,   0x825,   0x826,   0x827,   0x829,
 9140|  54.5k|      0x82a,   0x82b,   0x82c,   0x82d,   0x859,   0x85a,   0x85b,   0x8d3,
 9141|  54.5k|      0x8d4,   0x8d5,   0x8d6,   0x8d7,   0x8d8,   0x8d9,   0x8da,   0x8db,
 9142|  54.5k|      0x8dc,   0x8dd,   0x8de,   0x8df,   0x8e0,   0x8e1,   0x8e3,   0x8e4,
 9143|  54.5k|      0x8e5,   0x8e6,   0x8e7,   0x8e8,   0x8e9,   0x8ea,   0x8eb,   0x8ec,
 9144|  54.5k|      0x8ed,   0x8ee,   0x8ef,   0x8f0,   0x8f1,   0x8f2,   0x8f3,   0x8f4,
 9145|  54.5k|      0x8f5,   0x8f6,   0x8f7,   0x8f8,   0x8f9,   0x8fa,   0x8fb,   0x8fc,
 9146|  54.5k|      0x8fd,   0x8fe,   0x8ff,   0x900,   0x901,   0x902,   0x903,   0x93a,
 9147|  54.5k|      0x93b,   0x93c,   0x93e,   0x93f,   0x940,   0x941,   0x942,   0x943,
 9148|  54.5k|      0x944,   0x945,   0x946,   0x947,   0x948,   0x949,   0x94a,   0x94b,
 9149|  54.5k|      0x94c,   0x94d,   0x94e,   0x94f,   0x951,   0x952,   0x953,   0x954,
 9150|  54.5k|      0x955,   0x956,   0x957,   0x962,   0x963,   0x981,   0x982,   0x983,
 9151|  54.5k|      0x9bc,   0x9be,   0x9bf,   0x9c0,   0x9c1,   0x9c2,   0x9c3,   0x9c4,
 9152|  54.5k|      0x9c7,   0x9c8,   0x9cb,   0x9cc,   0x9cd,   0x9d7,   0x9e2,   0x9e3,
 9153|  54.5k|      0x9fe,   0xa01,   0xa02,   0xa03,   0xa3c,   0xa3e,   0xa3f,   0xa40,
 9154|  54.5k|      0xa41,   0xa42,   0xa47,   0xa48,   0xa4b,   0xa4c,   0xa4d,   0xa51,
 9155|  54.5k|      0xa70,   0xa71,   0xa75,   0xa81,   0xa82,   0xa83,   0xabc,   0xabe,
 9156|  54.5k|      0xabf,   0xac0,   0xac1,   0xac2,   0xac3,   0xac4,   0xac5,   0xac7,
 9157|  54.5k|      0xac8,   0xac9,   0xacb,   0xacc,   0xacd,   0xae2,   0xae3,   0xafa,
 9158|  54.5k|      0xafb,   0xafc,   0xafd,   0xafe,   0xaff,   0xb01,   0xb02,   0xb03,
 9159|  54.5k|      0xb3c,   0xb3e,   0xb3f,   0xb40,   0xb41,   0xb42,   0xb43,   0xb44,
 9160|  54.5k|      0xb47,   0xb48,   0xb4b,   0xb4c,   0xb4d,   0xb55,   0xb56,   0xb57,
 9161|  54.5k|      0xb62,   0xb63,   0xb82,   0xbbe,   0xbbf,   0xbc0,   0xbc1,   0xbc2,
 9162|  54.5k|      0xbc6,   0xbc7,   0xbc8,   0xbca,   0xbcb,   0xbcc,   0xbcd,   0xbd7,
 9163|  54.5k|      0xc00,   0xc01,   0xc02,   0xc03,   0xc04,   0xc3e,   0xc3f,   0xc40,
 9164|  54.5k|      0xc41,   0xc42,   0xc43,   0xc44,   0xc46,   0xc47,   0xc48,   0xc4a,
 9165|  54.5k|      0xc4b,   0xc4c,   0xc4d,   0xc55,   0xc56,   0xc62,   0xc63,   0xc81,
 9166|  54.5k|      0xc82,   0xc83,   0xcbc,   0xcbe,   0xcbf,   0xcc0,   0xcc1,   0xcc2,
 9167|  54.5k|      0xcc3,   0xcc4,   0xcc6,   0xcc7,   0xcc8,   0xcca,   0xccb,   0xccc,
 9168|  54.5k|      0xccd,   0xcd5,   0xcd6,   0xce2,   0xce3,   0xd00,   0xd01,   0xd02,
 9169|  54.5k|      0xd03,   0xd3b,   0xd3c,   0xd3e,   0xd3f,   0xd40,   0xd41,   0xd42,
 9170|  54.5k|      0xd43,   0xd44,   0xd46,   0xd47,   0xd48,   0xd4a,   0xd4b,   0xd4c,
 9171|  54.5k|      0xd4d,   0xd57,   0xd62,   0xd63,   0xd81,   0xd82,   0xd83,   0xdca,
 9172|  54.5k|      0xdcf,   0xdd0,   0xdd1,   0xdd2,   0xdd3,   0xdd4,   0xdd6,   0xdd8,
 9173|  54.5k|      0xdd9,   0xdda,   0xddb,   0xddc,   0xddd,   0xdde,   0xddf,   0xdf2,
 9174|  54.5k|      0xdf3,   0xe31,   0xe34,   0xe35,   0xe36,   0xe37,   0xe38,   0xe39,
 9175|  54.5k|      0xe3a,   0xe47,   0xe48,   0xe49,   0xe4a,   0xe4b,   0xe4c,   0xe4d,
 9176|  54.5k|      0xe4e,   0xeb1,   0xeb4,   0xeb5,   0xeb6,   0xeb7,   0xeb8,   0xeb9,
 9177|  54.5k|      0xeba,   0xebb,   0xebc,   0xec8,   0xec9,   0xeca,   0xecb,   0xecc,
 9178|  54.5k|      0xecd,   0xf18,   0xf19,   0xf35,   0xf37,   0xf39,   0xf3e,   0xf3f,
 9179|  54.5k|      0xf71,   0xf72,   0xf73,   0xf74,   0xf75,   0xf76,   0xf77,   0xf78,
 9180|  54.5k|      0xf79,   0xf7a,   0xf7b,   0xf7c,   0xf7d,   0xf7e,   0xf7f,   0xf80,
 9181|  54.5k|      0xf81,   0xf82,   0xf83,   0xf84,   0xf86,   0xf87,   0xf8d,   0xf8e,
 9182|  54.5k|      0xf8f,   0xf90,   0xf91,   0xf92,   0xf93,   0xf94,   0xf95,   0xf96,
 9183|  54.5k|      0xf97,   0xf99,   0xf9a,   0xf9b,   0xf9c,   0xf9d,   0xf9e,   0xf9f,
 9184|  54.5k|      0xfa0,   0xfa1,   0xfa2,   0xfa3,   0xfa4,   0xfa5,   0xfa6,   0xfa7,
 9185|  54.5k|      0xfa8,   0xfa9,   0xfaa,   0xfab,   0xfac,   0xfad,   0xfae,   0xfaf,
 9186|  54.5k|      0xfb0,   0xfb1,   0xfb2,   0xfb3,   0xfb4,   0xfb5,   0xfb6,   0xfb7,
 9187|  54.5k|      0xfb8,   0xfb9,   0xfba,   0xfbb,   0xfbc,   0xfc6,   0x102b,  0x102c,
 9188|  54.5k|      0x102d,  0x102e,  0x102f,  0x1030,  0x1031,  0x1032,  0x1033,  0x1034,
 9189|  54.5k|      0x1035,  0x1036,  0x1037,  0x1038,  0x1039,  0x103a,  0x103b,  0x103c,
 9190|  54.5k|      0x103d,  0x103e,  0x1056,  0x1057,  0x1058,  0x1059,  0x105e,  0x105f,
 9191|  54.5k|      0x1060,  0x1062,  0x1063,  0x1064,  0x1067,  0x1068,  0x1069,  0x106a,
 9192|  54.5k|      0x106b,  0x106c,  0x106d,  0x1071,  0x1072,  0x1073,  0x1074,  0x1082,
 9193|  54.5k|      0x1083,  0x1084,  0x1085,  0x1086,  0x1087,  0x1088,  0x1089,  0x108a,
 9194|  54.5k|      0x108b,  0x108c,  0x108d,  0x108f,  0x109a,  0x109b,  0x109c,  0x109d,
 9195|  54.5k|      0x135d,  0x135e,  0x135f,  0x1712,  0x1713,  0x1714,  0x1732,  0x1733,
 9196|  54.5k|      0x1734,  0x1752,  0x1753,  0x1772,  0x1773,  0x17b4,  0x17b5,  0x17b6,
 9197|  54.5k|      0x17b7,  0x17b8,  0x17b9,  0x17ba,  0x17bb,  0x17bc,  0x17bd,  0x17be,
 9198|  54.5k|      0x17bf,  0x17c0,  0x17c1,  0x17c2,  0x17c3,  0x17c4,  0x17c5,  0x17c6,
 9199|  54.5k|      0x17c7,  0x17c8,  0x17c9,  0x17ca,  0x17cb,  0x17cc,  0x17cd,  0x17ce,
 9200|  54.5k|      0x17cf,  0x17d0,  0x17d1,  0x17d2,  0x17d3,  0x17dd,  0x180b,  0x180c,
 9201|  54.5k|      0x180d,  0x1885,  0x1886,  0x18a9,  0x1920,  0x1921,  0x1922,  0x1923,
 9202|  54.5k|      0x1924,  0x1925,  0x1926,  0x1927,  0x1928,  0x1929,  0x192a,  0x192b,
 9203|  54.5k|      0x1930,  0x1931,  0x1932,  0x1933,  0x1934,  0x1935,  0x1936,  0x1937,
 9204|  54.5k|      0x1938,  0x1939,  0x193a,  0x193b,  0x1a17,  0x1a18,  0x1a19,  0x1a1a,
 9205|  54.5k|      0x1a1b,  0x1a55,  0x1a56,  0x1a57,  0x1a58,  0x1a59,  0x1a5a,  0x1a5b,
 9206|  54.5k|      0x1a5c,  0x1a5d,  0x1a5e,  0x1a60,  0x1a61,  0x1a62,  0x1a63,  0x1a64,
 9207|  54.5k|      0x1a65,  0x1a66,  0x1a67,  0x1a68,  0x1a69,  0x1a6a,  0x1a6b,  0x1a6c,
 9208|  54.5k|      0x1a6d,  0x1a6e,  0x1a6f,  0x1a70,  0x1a71,  0x1a72,  0x1a73,  0x1a74,
 9209|  54.5k|      0x1a75,  0x1a76,  0x1a77,  0x1a78,  0x1a79,  0x1a7a,  0x1a7b,  0x1a7c,
 9210|  54.5k|      0x1a7f,  0x1ab0,  0x1ab1,  0x1ab2,  0x1ab3,  0x1ab4,  0x1ab5,  0x1ab6,
 9211|  54.5k|      0x1ab7,  0x1ab8,  0x1ab9,  0x1aba,  0x1abb,  0x1abc,  0x1abd,  0x1abe,
 9212|  54.5k|      0x1abf,  0x1ac0,  0x1b00,  0x1b01,  0x1b02,  0x1b03,  0x1b04,  0x1b34,
 9213|  54.5k|      0x1b35,  0x1b36,  0x1b37,  0x1b38,  0x1b39,  0x1b3a,  0x1b3b,  0x1b3c,
 9214|  54.5k|      0x1b3d,  0x1b3e,  0x1b3f,  0x1b40,  0x1b41,  0x1b42,  0x1b43,  0x1b44,
 9215|  54.5k|      0x1b6b,  0x1b6c,  0x1b6d,  0x1b6e,  0x1b6f,  0x1b70,  0x1b71,  0x1b72,
 9216|  54.5k|      0x1b73,  0x1b80,  0x1b81,  0x1b82,  0x1ba1,  0x1ba2,  0x1ba3,  0x1ba4,
 9217|  54.5k|      0x1ba5,  0x1ba6,  0x1ba7,  0x1ba8,  0x1ba9,  0x1baa,  0x1bab,  0x1bac,
 9218|  54.5k|      0x1bad,  0x1be6,  0x1be7,  0x1be8,  0x1be9,  0x1bea,  0x1beb,  0x1bec,
 9219|  54.5k|      0x1bed,  0x1bee,  0x1bef,  0x1bf0,  0x1bf1,  0x1bf2,  0x1bf3,  0x1c24,
 9220|  54.5k|      0x1c25,  0x1c26,  0x1c27,  0x1c28,  0x1c29,  0x1c2a,  0x1c2b,  0x1c2c,
 9221|  54.5k|      0x1c2d,  0x1c2e,  0x1c2f,  0x1c30,  0x1c31,  0x1c32,  0x1c33,  0x1c34,
 9222|  54.5k|      0x1c35,  0x1c36,  0x1c37,  0x1cd0,  0x1cd1,  0x1cd2,  0x1cd4,  0x1cd5,
 9223|  54.5k|      0x1cd6,  0x1cd7,  0x1cd8,  0x1cd9,  0x1cda,  0x1cdb,  0x1cdc,  0x1cdd,
 9224|  54.5k|      0x1cde,  0x1cdf,  0x1ce0,  0x1ce1,  0x1ce2,  0x1ce3,  0x1ce4,  0x1ce5,
 9225|  54.5k|      0x1ce6,  0x1ce7,  0x1ce8,  0x1ced,  0x1cf4,  0x1cf7,  0x1cf8,  0x1cf9,
 9226|  54.5k|      0x1dc0,  0x1dc1,  0x1dc2,  0x1dc3,  0x1dc4,  0x1dc5,  0x1dc6,  0x1dc7,
 9227|  54.5k|      0x1dc8,  0x1dc9,  0x1dca,  0x1dcb,  0x1dcc,  0x1dcd,  0x1dce,  0x1dcf,
 9228|  54.5k|      0x1dd0,  0x1dd1,  0x1dd2,  0x1dd3,  0x1dd4,  0x1dd5,  0x1dd6,  0x1dd7,
 9229|  54.5k|      0x1dd8,  0x1dd9,  0x1dda,  0x1ddb,  0x1ddc,  0x1ddd,  0x1dde,  0x1ddf,
 9230|  54.5k|      0x1de0,  0x1de1,  0x1de2,  0x1de3,  0x1de4,  0x1de5,  0x1de6,  0x1de7,
 9231|  54.5k|      0x1de8,  0x1de9,  0x1dea,  0x1deb,  0x1dec,  0x1ded,  0x1dee,  0x1def,
 9232|  54.5k|      0x1df0,  0x1df1,  0x1df2,  0x1df3,  0x1df4,  0x1df5,  0x1df6,  0x1df7,
 9233|  54.5k|      0x1df8,  0x1df9,  0x1dfb,  0x1dfc,  0x1dfd,  0x1dfe,  0x1dff,  0x20d0,
 9234|  54.5k|      0x20d1,  0x20d2,  0x20d3,  0x20d4,  0x20d5,  0x20d6,  0x20d7,  0x20d8,
 9235|  54.5k|      0x20d9,  0x20da,  0x20db,  0x20dc,  0x20dd,  0x20de,  0x20df,  0x20e0,
 9236|  54.5k|      0x20e1,  0x20e2,  0x20e3,  0x20e4,  0x20e5,  0x20e6,  0x20e7,  0x20e8,
 9237|  54.5k|      0x20e9,  0x20ea,  0x20eb,  0x20ec,  0x20ed,  0x20ee,  0x20ef,  0x20f0,
 9238|  54.5k|      0x2cef,  0x2cf0,  0x2cf1,  0x2d7f,  0x2de0,  0x2de1,  0x2de2,  0x2de3,
 9239|  54.5k|      0x2de4,  0x2de5,  0x2de6,  0x2de7,  0x2de8,  0x2de9,  0x2dea,  0x2deb,
 9240|  54.5k|      0x2dec,  0x2ded,  0x2dee,  0x2def,  0x2df0,  0x2df1,  0x2df2,  0x2df3,
 9241|  54.5k|      0x2df4,  0x2df5,  0x2df6,  0x2df7,  0x2df8,  0x2df9,  0x2dfa,  0x2dfb,
 9242|  54.5k|      0x2dfc,  0x2dfd,  0x2dfe,  0x2dff,  0x302a,  0x302b,  0x302c,  0x302d,
 9243|  54.5k|      0x302e,  0x302f,  0x3099,  0x309a,  0xa66f,  0xa670,  0xa671,  0xa672,
 9244|  54.5k|      0xa674,  0xa675,  0xa676,  0xa677,  0xa678,  0xa679,  0xa67a,  0xa67b,
 9245|  54.5k|      0xa67c,  0xa67d,  0xa69e,  0xa69f,  0xa6f0,  0xa6f1,  0xa802,  0xa806,
 9246|  54.5k|      0xa80b,  0xa823,  0xa824,  0xa825,  0xa826,  0xa827,  0xa82c,  0xa880,
 9247|  54.5k|      0xa881,  0xa8b4,  0xa8b5,  0xa8b6,  0xa8b7,  0xa8b8,  0xa8b9,  0xa8ba,
 9248|  54.5k|      0xa8bb,  0xa8bc,  0xa8bd,  0xa8be,  0xa8bf,  0xa8c0,  0xa8c1,  0xa8c2,
 9249|  54.5k|      0xa8c3,  0xa8c4,  0xa8c5,  0xa8e0,  0xa8e1,  0xa8e2,  0xa8e3,  0xa8e4,
 9250|  54.5k|      0xa8e5,  0xa8e6,  0xa8e7,  0xa8e8,  0xa8e9,  0xa8ea,  0xa8eb,  0xa8ec,
 9251|  54.5k|      0xa8ed,  0xa8ee,  0xa8ef,  0xa8f0,  0xa8f1,  0xa8ff,  0xa926,  0xa927,
 9252|  54.5k|      0xa928,  0xa929,  0xa92a,  0xa92b,  0xa92c,  0xa92d,  0xa947,  0xa948,
 9253|  54.5k|      0xa949,  0xa94a,  0xa94b,  0xa94c,  0xa94d,  0xa94e,  0xa94f,  0xa950,
 9254|  54.5k|      0xa951,  0xa952,  0xa953,  0xa980,  0xa981,  0xa982,  0xa983,  0xa9b3,
 9255|  54.5k|      0xa9b4,  0xa9b5,  0xa9b6,  0xa9b7,  0xa9b8,  0xa9b9,  0xa9ba,  0xa9bb,
 9256|  54.5k|      0xa9bc,  0xa9bd,  0xa9be,  0xa9bf,  0xa9c0,  0xa9e5,  0xaa29,  0xaa2a,
 9257|  54.5k|      0xaa2b,  0xaa2c,  0xaa2d,  0xaa2e,  0xaa2f,  0xaa30,  0xaa31,  0xaa32,
 9258|  54.5k|      0xaa33,  0xaa34,  0xaa35,  0xaa36,  0xaa43,  0xaa4c,  0xaa4d,  0xaa7b,
 9259|  54.5k|      0xaa7c,  0xaa7d,  0xaab0,  0xaab2,  0xaab3,  0xaab4,  0xaab7,  0xaab8,
 9260|  54.5k|      0xaabe,  0xaabf,  0xaac1,  0xaaeb,  0xaaec,  0xaaed,  0xaaee,  0xaaef,
 9261|  54.5k|      0xaaf5,  0xaaf6,  0xabe3,  0xabe4,  0xabe5,  0xabe6,  0xabe7,  0xabe8,
 9262|  54.5k|      0xabe9,  0xabea,  0xabec,  0xabed,  0xfb1e,  0xfe00,  0xfe01,  0xfe02,
 9263|  54.5k|      0xfe03,  0xfe04,  0xfe05,  0xfe06,  0xfe07,  0xfe08,  0xfe09,  0xfe0a,
 9264|  54.5k|      0xfe0b,  0xfe0c,  0xfe0d,  0xfe0e,  0xfe0f,  0xfe20,  0xfe21,  0xfe22,
 9265|  54.5k|      0xfe23,  0xfe24,  0xfe25,  0xfe26,  0xfe27,  0xfe28,  0xfe29,  0xfe2a,
 9266|  54.5k|      0xfe2b,  0xfe2c,  0xfe2d,  0xfe2e,  0xfe2f,  0x101fd, 0x102e0, 0x10376,
 9267|  54.5k|      0x10377, 0x10378, 0x10379, 0x1037a, 0x10a01, 0x10a02, 0x10a03, 0x10a05,
 9268|  54.5k|      0x10a06, 0x10a0c, 0x10a0d, 0x10a0e, 0x10a0f, 0x10a38, 0x10a39, 0x10a3a,
 9269|  54.5k|      0x10a3f, 0x10ae5, 0x10ae6, 0x10d24, 0x10d25, 0x10d26, 0x10d27, 0x10eab,
 9270|  54.5k|      0x10eac, 0x10f46, 0x10f47, 0x10f48, 0x10f49, 0x10f4a, 0x10f4b, 0x10f4c,
 9271|  54.5k|      0x10f4d, 0x10f4e, 0x10f4f, 0x10f50, 0x11000, 0x11001, 0x11002, 0x11038,
 9272|  54.5k|      0x11039, 0x1103a, 0x1103b, 0x1103c, 0x1103d, 0x1103e, 0x1103f, 0x11040,
 9273|  54.5k|      0x11041, 0x11042, 0x11043, 0x11044, 0x11045, 0x11046, 0x1107f, 0x11080,
 9274|  54.5k|      0x11081, 0x11082, 0x110b0, 0x110b1, 0x110b2, 0x110b3, 0x110b4, 0x110b5,
 9275|  54.5k|      0x110b6, 0x110b7, 0x110b8, 0x110b9, 0x110ba, 0x11100, 0x11101, 0x11102,
 9276|  54.5k|      0x11127, 0x11128, 0x11129, 0x1112a, 0x1112b, 0x1112c, 0x1112d, 0x1112e,
 9277|  54.5k|      0x1112f, 0x11130, 0x11131, 0x11132, 0x11133, 0x11134, 0x11145, 0x11146,
 9278|  54.5k|      0x11173, 0x11180, 0x11181, 0x11182, 0x111b3, 0x111b4, 0x111b5, 0x111b6,
 9279|  54.5k|      0x111b7, 0x111b8, 0x111b9, 0x111ba, 0x111bb, 0x111bc, 0x111bd, 0x111be,
 9280|  54.5k|      0x111bf, 0x111c0, 0x111c9, 0x111ca, 0x111cb, 0x111cc, 0x111ce, 0x111cf,
 9281|  54.5k|      0x1122c, 0x1122d, 0x1122e, 0x1122f, 0x11230, 0x11231, 0x11232, 0x11233,
 9282|  54.5k|      0x11234, 0x11235, 0x11236, 0x11237, 0x1123e, 0x112df, 0x112e0, 0x112e1,
 9283|  54.5k|      0x112e2, 0x112e3, 0x112e4, 0x112e5, 0x112e6, 0x112e7, 0x112e8, 0x112e9,
 9284|  54.5k|      0x112ea, 0x11300, 0x11301, 0x11302, 0x11303, 0x1133b, 0x1133c, 0x1133e,
 9285|  54.5k|      0x1133f, 0x11340, 0x11341, 0x11342, 0x11343, 0x11344, 0x11347, 0x11348,
 9286|  54.5k|      0x1134b, 0x1134c, 0x1134d, 0x11357, 0x11362, 0x11363, 0x11366, 0x11367,
 9287|  54.5k|      0x11368, 0x11369, 0x1136a, 0x1136b, 0x1136c, 0x11370, 0x11371, 0x11372,
 9288|  54.5k|      0x11373, 0x11374, 0x11435, 0x11436, 0x11437, 0x11438, 0x11439, 0x1143a,
 9289|  54.5k|      0x1143b, 0x1143c, 0x1143d, 0x1143e, 0x1143f, 0x11440, 0x11441, 0x11442,
 9290|  54.5k|      0x11443, 0x11444, 0x11445, 0x11446, 0x1145e, 0x114b0, 0x114b1, 0x114b2,
 9291|  54.5k|      0x114b3, 0x114b4, 0x114b5, 0x114b6, 0x114b7, 0x114b8, 0x114b9, 0x114ba,
 9292|  54.5k|      0x114bb, 0x114bc, 0x114bd, 0x114be, 0x114bf, 0x114c0, 0x114c1, 0x114c2,
 9293|  54.5k|      0x114c3, 0x115af, 0x115b0, 0x115b1, 0x115b2, 0x115b3, 0x115b4, 0x115b5,
 9294|  54.5k|      0x115b8, 0x115b9, 0x115ba, 0x115bb, 0x115bc, 0x115bd, 0x115be, 0x115bf,
 9295|  54.5k|      0x115c0, 0x115dc, 0x115dd, 0x11630, 0x11631, 0x11632, 0x11633, 0x11634,
 9296|  54.5k|      0x11635, 0x11636, 0x11637, 0x11638, 0x11639, 0x1163a, 0x1163b, 0x1163c,
 9297|  54.5k|      0x1163d, 0x1163e, 0x1163f, 0x11640, 0x116ab, 0x116ac, 0x116ad, 0x116ae,
 9298|  54.5k|      0x116af, 0x116b0, 0x116b1, 0x116b2, 0x116b3, 0x116b4, 0x116b5, 0x116b6,
 9299|  54.5k|      0x116b7, 0x1171d, 0x1171e, 0x1171f, 0x11720, 0x11721, 0x11722, 0x11723,
 9300|  54.5k|      0x11724, 0x11725, 0x11726, 0x11727, 0x11728, 0x11729, 0x1172a, 0x1172b,
 9301|  54.5k|      0x1182c, 0x1182d, 0x1182e, 0x1182f, 0x11830, 0x11831, 0x11832, 0x11833,
 9302|  54.5k|      0x11834, 0x11835, 0x11836, 0x11837, 0x11838, 0x11839, 0x1183a, 0x11930,
 9303|  54.5k|      0x11931, 0x11932, 0x11933, 0x11934, 0x11935, 0x11937, 0x11938, 0x1193b,
 9304|  54.5k|      0x1193c, 0x1193d, 0x1193e, 0x11940, 0x11942, 0x11943, 0x119d1, 0x119d2,
 9305|  54.5k|      0x119d3, 0x119d4, 0x119d5, 0x119d6, 0x119d7, 0x119da, 0x119db, 0x119dc,
 9306|  54.5k|      0x119dd, 0x119de, 0x119df, 0x119e0, 0x119e4, 0x11a01, 0x11a02, 0x11a03,
 9307|  54.5k|      0x11a04, 0x11a05, 0x11a06, 0x11a07, 0x11a08, 0x11a09, 0x11a0a, 0x11a33,
 9308|  54.5k|      0x11a34, 0x11a35, 0x11a36, 0x11a37, 0x11a38, 0x11a39, 0x11a3b, 0x11a3c,
 9309|  54.5k|      0x11a3d, 0x11a3e, 0x11a47, 0x11a51, 0x11a52, 0x11a53, 0x11a54, 0x11a55,
 9310|  54.5k|      0x11a56, 0x11a57, 0x11a58, 0x11a59, 0x11a5a, 0x11a5b, 0x11a8a, 0x11a8b,
 9311|  54.5k|      0x11a8c, 0x11a8d, 0x11a8e, 0x11a8f, 0x11a90, 0x11a91, 0x11a92, 0x11a93,
 9312|  54.5k|      0x11a94, 0x11a95, 0x11a96, 0x11a97, 0x11a98, 0x11a99, 0x11c2f, 0x11c30,
 9313|  54.5k|      0x11c31, 0x11c32, 0x11c33, 0x11c34, 0x11c35, 0x11c36, 0x11c38, 0x11c39,
 9314|  54.5k|      0x11c3a, 0x11c3b, 0x11c3c, 0x11c3d, 0x11c3e, 0x11c3f, 0x11c92, 0x11c93,
 9315|  54.5k|      0x11c94, 0x11c95, 0x11c96, 0x11c97, 0x11c98, 0x11c99, 0x11c9a, 0x11c9b,
 9316|  54.5k|      0x11c9c, 0x11c9d, 0x11c9e, 0x11c9f, 0x11ca0, 0x11ca1, 0x11ca2, 0x11ca3,
 9317|  54.5k|      0x11ca4, 0x11ca5, 0x11ca6, 0x11ca7, 0x11ca9, 0x11caa, 0x11cab, 0x11cac,
 9318|  54.5k|      0x11cad, 0x11cae, 0x11caf, 0x11cb0, 0x11cb1, 0x11cb2, 0x11cb3, 0x11cb4,
 9319|  54.5k|      0x11cb5, 0x11cb6, 0x11d31, 0x11d32, 0x11d33, 0x11d34, 0x11d35, 0x11d36,
 9320|  54.5k|      0x11d3a, 0x11d3c, 0x11d3d, 0x11d3f, 0x11d40, 0x11d41, 0x11d42, 0x11d43,
 9321|  54.5k|      0x11d44, 0x11d45, 0x11d47, 0x11d8a, 0x11d8b, 0x11d8c, 0x11d8d, 0x11d8e,
 9322|  54.5k|      0x11d90, 0x11d91, 0x11d93, 0x11d94, 0x11d95, 0x11d96, 0x11d97, 0x11ef3,
 9323|  54.5k|      0x11ef4, 0x11ef5, 0x11ef6, 0x16af0, 0x16af1, 0x16af2, 0x16af3, 0x16af4,
 9324|  54.5k|      0x16b30, 0x16b31, 0x16b32, 0x16b33, 0x16b34, 0x16b35, 0x16b36, 0x16f4f,
 9325|  54.5k|      0x16f51, 0x16f52, 0x16f53, 0x16f54, 0x16f55, 0x16f56, 0x16f57, 0x16f58,
 9326|  54.5k|      0x16f59, 0x16f5a, 0x16f5b, 0x16f5c, 0x16f5d, 0x16f5e, 0x16f5f, 0x16f60,
 9327|  54.5k|      0x16f61, 0x16f62, 0x16f63, 0x16f64, 0x16f65, 0x16f66, 0x16f67, 0x16f68,
 9328|  54.5k|      0x16f69, 0x16f6a, 0x16f6b, 0x16f6c, 0x16f6d, 0x16f6e, 0x16f6f, 0x16f70,
 9329|  54.5k|      0x16f71, 0x16f72, 0x16f73, 0x16f74, 0x16f75, 0x16f76, 0x16f77, 0x16f78,
 9330|  54.5k|      0x16f79, 0x16f7a, 0x16f7b, 0x16f7c, 0x16f7d, 0x16f7e, 0x16f7f, 0x16f80,
 9331|  54.5k|      0x16f81, 0x16f82, 0x16f83, 0x16f84, 0x16f85, 0x16f86, 0x16f87, 0x16f8f,
 9332|  54.5k|      0x16f90, 0x16f91, 0x16f92, 0x16fe4, 0x16ff0, 0x16ff1, 0x1bc9d, 0x1bc9e,
 9333|  54.5k|      0x1d165, 0x1d166, 0x1d167, 0x1d168, 0x1d169, 0x1d16d, 0x1d16e, 0x1d16f,
 9334|  54.5k|      0x1d170, 0x1d171, 0x1d172, 0x1d17b, 0x1d17c, 0x1d17d, 0x1d17e, 0x1d17f,
 9335|  54.5k|      0x1d180, 0x1d181, 0x1d182, 0x1d185, 0x1d186, 0x1d187, 0x1d188, 0x1d189,
 9336|  54.5k|      0x1d18a, 0x1d18b, 0x1d1aa, 0x1d1ab, 0x1d1ac, 0x1d1ad, 0x1d242, 0x1d243,
 9337|  54.5k|      0x1d244, 0x1da00, 0x1da01, 0x1da02, 0x1da03, 0x1da04, 0x1da05, 0x1da06,
 9338|  54.5k|      0x1da07, 0x1da08, 0x1da09, 0x1da0a, 0x1da0b, 0x1da0c, 0x1da0d, 0x1da0e,
 9339|  54.5k|      0x1da0f, 0x1da10, 0x1da11, 0x1da12, 0x1da13, 0x1da14, 0x1da15, 0x1da16,
 9340|  54.5k|      0x1da17, 0x1da18, 0x1da19, 0x1da1a, 0x1da1b, 0x1da1c, 0x1da1d, 0x1da1e,
 9341|  54.5k|      0x1da1f, 0x1da20, 0x1da21, 0x1da22, 0x1da23, 0x1da24, 0x1da25, 0x1da26,
 9342|  54.5k|      0x1da27, 0x1da28, 0x1da29, 0x1da2a, 0x1da2b, 0x1da2c, 0x1da2d, 0x1da2e,
 9343|  54.5k|      0x1da2f, 0x1da30, 0x1da31, 0x1da32, 0x1da33, 0x1da34, 0x1da35, 0x1da36,
 9344|  54.5k|      0x1da3b, 0x1da3c, 0x1da3d, 0x1da3e, 0x1da3f, 0x1da40, 0x1da41, 0x1da42,
 9345|  54.5k|      0x1da43, 0x1da44, 0x1da45, 0x1da46, 0x1da47, 0x1da48, 0x1da49, 0x1da4a,
 9346|  54.5k|      0x1da4b, 0x1da4c, 0x1da4d, 0x1da4e, 0x1da4f, 0x1da50, 0x1da51, 0x1da52,
 9347|  54.5k|      0x1da53, 0x1da54, 0x1da55, 0x1da56, 0x1da57, 0x1da58, 0x1da59, 0x1da5a,
 9348|  54.5k|      0x1da5b, 0x1da5c, 0x1da5d, 0x1da5e, 0x1da5f, 0x1da60, 0x1da61, 0x1da62,
 9349|  54.5k|      0x1da63, 0x1da64, 0x1da65, 0x1da66, 0x1da67, 0x1da68, 0x1da69, 0x1da6a,
 9350|  54.5k|      0x1da6b, 0x1da6c, 0x1da75, 0x1da84, 0x1da9b, 0x1da9c, 0x1da9d, 0x1da9e,
 9351|  54.5k|      0x1da9f, 0x1daa1, 0x1daa2, 0x1daa3, 0x1daa4, 0x1daa5, 0x1daa6, 0x1daa7,
 9352|  54.5k|      0x1daa8, 0x1daa9, 0x1daaa, 0x1daab, 0x1daac, 0x1daad, 0x1daae, 0x1daaf,
 9353|  54.5k|      0x1e000, 0x1e001, 0x1e002, 0x1e003, 0x1e004, 0x1e005, 0x1e006, 0x1e008,
 9354|  54.5k|      0x1e009, 0x1e00a, 0x1e00b, 0x1e00c, 0x1e00d, 0x1e00e, 0x1e00f, 0x1e010,
 9355|  54.5k|      0x1e011, 0x1e012, 0x1e013, 0x1e014, 0x1e015, 0x1e016, 0x1e017, 0x1e018,
 9356|  54.5k|      0x1e01b, 0x1e01c, 0x1e01d, 0x1e01e, 0x1e01f, 0x1e020, 0x1e021, 0x1e023,
 9357|  54.5k|      0x1e024, 0x1e026, 0x1e027, 0x1e028, 0x1e029, 0x1e02a, 0x1e130, 0x1e131,
 9358|  54.5k|      0x1e132, 0x1e133, 0x1e134, 0x1e135, 0x1e136, 0x1e2ec, 0x1e2ed, 0x1e2ee,
 9359|  54.5k|      0x1e2ef, 0x1e8d0, 0x1e8d1, 0x1e8d2, 0x1e8d3, 0x1e8d4, 0x1e8d5, 0x1e8d6,
 9360|  54.5k|      0x1e944, 0x1e945, 0x1e946, 0x1e947, 0x1e948, 0x1e949, 0x1e94a, 0xe0100,
 9361|  54.5k|      0xe0101, 0xe0102, 0xe0103, 0xe0104, 0xe0105, 0xe0106, 0xe0107, 0xe0108,
 9362|  54.5k|      0xe0109, 0xe010a, 0xe010b, 0xe010c, 0xe010d, 0xe010e, 0xe010f, 0xe0110,
 9363|  54.5k|      0xe0111, 0xe0112, 0xe0113, 0xe0114, 0xe0115, 0xe0116, 0xe0117, 0xe0118,
 9364|  54.5k|      0xe0119, 0xe011a, 0xe011b, 0xe011c, 0xe011d, 0xe011e, 0xe011f, 0xe0120,
 9365|  54.5k|      0xe0121, 0xe0122, 0xe0123, 0xe0124, 0xe0125, 0xe0126, 0xe0127, 0xe0128,
 9366|  54.5k|      0xe0129, 0xe012a, 0xe012b, 0xe012c, 0xe012d, 0xe012e, 0xe012f, 0xe0130,
 9367|  54.5k|      0xe0131, 0xe0132, 0xe0133, 0xe0134, 0xe0135, 0xe0136, 0xe0137, 0xe0138,
 9368|  54.5k|      0xe0139, 0xe013a, 0xe013b, 0xe013c, 0xe013d, 0xe013e, 0xe013f, 0xe0140,
 9369|  54.5k|      0xe0141, 0xe0142, 0xe0143, 0xe0144, 0xe0145, 0xe0146, 0xe0147, 0xe0148,
 9370|  54.5k|      0xe0149, 0xe014a, 0xe014b, 0xe014c, 0xe014d, 0xe014e, 0xe014f, 0xe0150,
 9371|  54.5k|      0xe0151, 0xe0152, 0xe0153, 0xe0154, 0xe0155, 0xe0156, 0xe0157, 0xe0158,
 9372|  54.5k|      0xe0159, 0xe015a, 0xe015b, 0xe015c, 0xe015d, 0xe015e, 0xe015f, 0xe0160,
 9373|  54.5k|      0xe0161, 0xe0162, 0xe0163, 0xe0164, 0xe0165, 0xe0166, 0xe0167, 0xe0168,
 9374|  54.5k|      0xe0169, 0xe016a, 0xe016b, 0xe016c, 0xe016d, 0xe016e, 0xe016f, 0xe0170,
 9375|  54.5k|      0xe0171, 0xe0172, 0xe0173, 0xe0174, 0xe0175, 0xe0176, 0xe0177, 0xe0178,
 9376|  54.5k|      0xe0179, 0xe017a, 0xe017b, 0xe017c, 0xe017d, 0xe017e, 0xe017f, 0xe0180,
 9377|  54.5k|      0xe0181, 0xe0182, 0xe0183, 0xe0184, 0xe0185, 0xe0186, 0xe0187, 0xe0188,
 9378|  54.5k|      0xe0189, 0xe018a, 0xe018b, 0xe018c, 0xe018d, 0xe018e, 0xe018f, 0xe0190,
 9379|  54.5k|      0xe0191, 0xe0192, 0xe0193, 0xe0194, 0xe0195, 0xe0196, 0xe0197, 0xe0198,
 9380|  54.5k|      0xe0199, 0xe019a, 0xe019b, 0xe019c, 0xe019d, 0xe019e, 0xe019f, 0xe01a0,
 9381|  54.5k|      0xe01a1, 0xe01a2, 0xe01a3, 0xe01a4, 0xe01a5, 0xe01a6, 0xe01a7, 0xe01a8,
 9382|  54.5k|      0xe01a9, 0xe01aa, 0xe01ab, 0xe01ac, 0xe01ad, 0xe01ae, 0xe01af, 0xe01b0,
 9383|  54.5k|      0xe01b1, 0xe01b2, 0xe01b3, 0xe01b4, 0xe01b5, 0xe01b6, 0xe01b7, 0xe01b8,
 9384|  54.5k|      0xe01b9, 0xe01ba, 0xe01bb, 0xe01bc, 0xe01bd, 0xe01be, 0xe01bf, 0xe01c0,
 9385|  54.5k|      0xe01c1, 0xe01c2, 0xe01c3, 0xe01c4, 0xe01c5, 0xe01c6, 0xe01c7, 0xe01c8,
 9386|  54.5k|      0xe01c9, 0xe01ca, 0xe01cb, 0xe01cc, 0xe01cd, 0xe01ce, 0xe01cf, 0xe01d0,
 9387|  54.5k|      0xe01d1, 0xe01d2, 0xe01d3, 0xe01d4, 0xe01d5, 0xe01d6, 0xe01d7, 0xe01d8,
 9388|  54.5k|      0xe01d9, 0xe01da, 0xe01db, 0xe01dc, 0xe01dd, 0xe01de, 0xe01df, 0xe01e0,
 9389|  54.5k|      0xe01e1, 0xe01e2, 0xe01e3, 0xe01e4, 0xe01e5, 0xe01e6, 0xe01e7, 0xe01e8,
 9390|  54.5k|      0xe01e9, 0xe01ea, 0xe01eb, 0xe01ec, 0xe01ed, 0xe01ee, 0xe01ef};
 9391|  54.5k|  if (std::binary_search(std::begin(combining), std::end(combining),
  ------------------
  |  Branch (9391:7): [True: 184, False: 54.4k]
  ------------------
 9392|  54.5k|                         label.front())) {
 9393|    184|    return false;
 9394|    184|  }
 9395|       |  // We verify this next step as part of the mapping:
 9396|       |  // ---------------------------------------------
 9397|       |  // Each code point in the label must only have certain status values
 9398|       |  // according to Section 5, IDNA Mapping Table:
 9399|       |  // - For Transitional Processing, each value must be valid.
 9400|       |  // - For Nontransitional Processing, each value must be either valid or
 9401|       |  // deviation.
 9402|       |
 9403|       |  // If CheckJoiners, the label must satisfy the ContextJ rules from Appendix
 9404|       |  // A, in The Unicode Code Points and Internationalized Domain Names for
 9405|       |  // Applications (IDNA) [IDNA2008].
 9406|  54.4k|  constexpr static uint32_t virama[] = {
 9407|  54.4k|      0x094D,  0x09CD,  0x0A4D,  0x0ACD,  0x0B4D,  0x0BCD,  0x0C4D,  0x0CCD,
 9408|  54.4k|      0x0D3B,  0x0D3C,  0x0D4D,  0x0DCA,  0x0E3A,  0x0EBA,  0x0F84,  0x1039,
 9409|  54.4k|      0x103A,  0x1714,  0x1734,  0x17D2,  0x1A60,  0x1B44,  0x1BAA,  0x1BAB,
 9410|  54.4k|      0x1BF2,  0x1BF3,  0x2D7F,  0xA806,  0xA82C,  0xA8C4,  0xA953,  0xA9C0,
 9411|  54.4k|      0xAAF6,  0xABED,  0x10A3F, 0x11046, 0x1107F, 0x110B9, 0x11133, 0x11134,
 9412|  54.4k|      0x111C0, 0x11235, 0x112EA, 0x1134D, 0x11442, 0x114C2, 0x115BF, 0x1163F,
 9413|  54.4k|      0x116B6, 0x1172B, 0x11839, 0x1193D, 0x1193E, 0x119E0, 0x11A34, 0x11A47,
 9414|  54.4k|      0x11A99, 0x11C3F, 0x11D44, 0x11D45, 0x11D97};
 9415|  54.4k|  constexpr static uint32_t R[] = {
 9416|  54.4k|      0x622, 0x623, 0x624, 0x625, 0x627, 0x629, 0x62f, 0x630, 0x631,
 9417|  54.4k|      0x632, 0x648, 0x671, 0x672, 0x673, 0x675, 0x676, 0x677, 0x688,
 9418|  54.4k|      0x689, 0x68a, 0x68b, 0x68c, 0x68d, 0x68e, 0x68f, 0x690, 0x691,
 9419|  54.4k|      0x692, 0x693, 0x694, 0x695, 0x696, 0x697, 0x698, 0x699, 0x6c0,
 9420|  54.4k|      0x6c3, 0x6c4, 0x6c5, 0x6c6, 0x6c7, 0x6c8, 0x6c9, 0x6ca, 0x6cb,
 9421|  54.4k|      0x6cd, 0x6cf, 0x6d2, 0x6d3, 0x6d5, 0x6ee, 0x6ef, 0x710, 0x715,
 9422|  54.4k|      0x716, 0x717, 0x718, 0x719, 0x71e, 0x728, 0x72a, 0x72c, 0x72f,
 9423|  54.4k|      0x74d, 0x759, 0x75a, 0x75b, 0x854, 0x8aa, 0x8ab, 0x8ac};
 9424|  54.4k|  constexpr static uint32_t L[] = {0xa872};
 9425|  54.4k|  constexpr static uint32_t D[] = {
 9426|  54.4k|      0x620,  0x626,  0x628,  0x62a,  0x62b,  0x62c,  0x62d,  0x62e,  0x633,
 9427|  54.4k|      0x634,  0x635,  0x636,  0x637,  0x638,  0x639,  0x63a,  0x63b,  0x63c,
 9428|  54.4k|      0x63d,  0x63e,  0x63f,  0x641,  0x642,  0x643,  0x644,  0x645,  0x646,
 9429|  54.4k|      0x647,  0x649,  0x64a,  0x66e,  0x66f,  0x678,  0x679,  0x67a,  0x67b,
 9430|  54.4k|      0x67c,  0x67d,  0x67e,  0x67f,  0x680,  0x681,  0x682,  0x683,  0x684,
 9431|  54.4k|      0x685,  0x686,  0x687,  0x69a,  0x69b,  0x69c,  0x69d,  0x69e,  0x69f,
 9432|  54.4k|      0x6a0,  0x6a1,  0x6a2,  0x6a3,  0x6a4,  0x6a5,  0x6a6,  0x6a7,  0x6a8,
 9433|  54.4k|      0x6a9,  0x6aa,  0x6ab,  0x6ac,  0x6ad,  0x6ae,  0x6af,  0x6b0,  0x6b1,
 9434|  54.4k|      0x6b2,  0x6b3,  0x6b4,  0x6b5,  0x6b6,  0x6b7,  0x6b8,  0x6b9,  0x6ba,
 9435|  54.4k|      0x6bb,  0x6bc,  0x6bd,  0x6be,  0x6bf,  0x6c1,  0x6c2,  0x6cc,  0x6ce,
 9436|  54.4k|      0x6d0,  0x6d1,  0x6fa,  0x6fb,  0x6fc,  0x6ff,  0x712,  0x713,  0x714,
 9437|  54.4k|      0x71a,  0x71b,  0x71c,  0x71d,  0x71f,  0x720,  0x721,  0x722,  0x723,
 9438|  54.4k|      0x724,  0x725,  0x726,  0x727,  0x729,  0x72b,  0x72d,  0x72e,  0x74e,
 9439|  54.4k|      0x74f,  0x750,  0x751,  0x752,  0x753,  0x754,  0x755,  0x756,  0x757,
 9440|  54.4k|      0x758,  0x75c,  0x75d,  0x75e,  0x75f,  0x760,  0x761,  0x762,  0x763,
 9441|  54.4k|      0x764,  0x765,  0x766,  0x850,  0x851,  0x852,  0x853,  0x855,  0x8a0,
 9442|  54.4k|      0x8a2,  0x8a3,  0x8a4,  0x8a5,  0x8a6,  0x8a7,  0x8a8,  0x8a9,  0x1807,
 9443|  54.4k|      0x1820, 0x1821, 0x1822, 0x1823, 0x1824, 0x1825, 0x1826, 0x1827, 0x1828,
 9444|  54.4k|      0x1829, 0x182a, 0x182b, 0x182c, 0x182d, 0x182e, 0x182f, 0x1830, 0x1831,
 9445|  54.4k|      0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837, 0x1838, 0x1839, 0x183a,
 9446|  54.4k|      0x183b, 0x183c, 0x183d, 0x183e, 0x183f, 0x1840, 0x1841, 0x1842, 0x1843,
 9447|  54.4k|      0x1844, 0x1845, 0x1846, 0x1847, 0x1848, 0x1849, 0x184a, 0x184b, 0x184c,
 9448|  54.4k|      0x184d, 0x184e, 0x184f, 0x1850, 0x1851, 0x1852, 0x1853, 0x1854, 0x1855,
 9449|  54.4k|      0x1856, 0x1857, 0x1858, 0x1859, 0x185a, 0x185b, 0x185c, 0x185d, 0x185e,
 9450|  54.4k|      0x185f, 0x1860, 0x1861, 0x1862, 0x1863, 0x1864, 0x1865, 0x1866, 0x1867,
 9451|  54.4k|      0x1868, 0x1869, 0x186a, 0x186b, 0x186c, 0x186d, 0x186e, 0x186f, 0x1870,
 9452|  54.4k|      0x1871, 0x1872, 0x1873, 0x1874, 0x1875, 0x1876, 0x1877, 0x1887, 0x1888,
 9453|  54.4k|      0x1889, 0x188a, 0x188b, 0x188c, 0x188d, 0x188e, 0x188f, 0x1890, 0x1891,
 9454|  54.4k|      0x1892, 0x1893, 0x1894, 0x1895, 0x1896, 0x1897, 0x1898, 0x1899, 0x189a,
 9455|  54.4k|      0x189b, 0x189c, 0x189d, 0x189e, 0x189f, 0x18a0, 0x18a1, 0x18a2, 0x18a3,
 9456|  54.4k|      0x18a4, 0x18a5, 0x18a6, 0x18a7, 0x18a8, 0x18aa, 0xa840, 0xa841, 0xa842,
 9457|  54.4k|      0xa843, 0xa844, 0xa845, 0xa846, 0xa847, 0xa848, 0xa849, 0xa84a, 0xa84b,
 9458|  54.4k|      0xa84c, 0xa84d, 0xa84e, 0xa84f, 0xa850, 0xa851, 0xa852, 0xa853, 0xa854,
 9459|  54.4k|      0xa855, 0xa856, 0xa857, 0xa858, 0xa859, 0xa85a, 0xa85b, 0xa85c, 0xa85d,
 9460|  54.4k|      0xa85e, 0xa85f, 0xa860, 0xa861, 0xa862, 0xa863, 0xa864, 0xa865, 0xa866,
 9461|  54.4k|      0xa867, 0xa868, 0xa869, 0xa86a, 0xa86b, 0xa86c, 0xa86d, 0xa86e, 0xa86f,
 9462|  54.4k|      0xa870, 0xa871};
 9463|       |
 9464|   446k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (9464:22): [True: 394k, False: 52.1k]
  ------------------
 9465|   394k|    uint32_t c = label[i];
 9466|   394k|    if (c == 0x200c) {
  ------------------
  |  Branch (9466:9): [True: 1.53k, False: 392k]
  ------------------
 9467|  1.53k|      if (i > 0) {
  ------------------
  |  Branch (9467:11): [True: 1.51k, False: 18]
  ------------------
 9468|  1.51k|        if (std::binary_search(std::begin(virama), std::end(virama),
  ------------------
  |  Branch (9468:13): [True: 202, False: 1.31k]
  ------------------
 9469|  1.51k|                               label[i - 1])) {
 9470|    202|          return true;
 9471|    202|        }
 9472|  1.51k|      }
 9473|  1.32k|      if ((i == 0) || (i + 1 >= label.size())) {
  ------------------
  |  Branch (9473:11): [True: 18, False: 1.31k]
  |  Branch (9473:23): [True: 93, False: 1.21k]
  ------------------
 9474|    111|        return false;
 9475|    111|      }
 9476|       |      // we go backward looking for L or D
 9477|  1.21k|      auto is_l_or_d = [](uint32_t code) {
 9478|  1.21k|        return std::binary_search(std::begin(L), std::end(L), code) ||
 9479|  1.21k|               std::binary_search(std::begin(D), std::end(D), code);
 9480|  1.21k|      };
 9481|  1.21k|      auto is_r_or_d = [](uint32_t code) {
 9482|  1.21k|        return std::binary_search(std::begin(R), std::end(R), code) ||
 9483|  1.21k|               std::binary_search(std::begin(D), std::end(D), code);
 9484|  1.21k|      };
 9485|  1.21k|      std::u32string_view before = label.substr(0, i);
 9486|  1.21k|      std::u32string_view after = label.substr(i + 1);
 9487|  1.21k|      return (std::find_if(before.begin(), before.end(), is_l_or_d) !=
  ------------------
  |  Branch (9487:14): [True: 1.04k, False: 173]
  ------------------
 9488|  1.21k|              before.end()) &&
 9489|  1.04k|             (std::find_if(after.begin(), after.end(), is_r_or_d) !=
  ------------------
  |  Branch (9489:14): [True: 800, False: 245]
  ------------------
 9490|  1.04k|              after.end());
 9491|   392k|    } else if (c == 0x200d) {
  ------------------
  |  Branch (9491:16): [True: 691, False: 391k]
  ------------------
 9492|    691|      if (i > 0) {
  ------------------
  |  Branch (9492:11): [True: 673, False: 18]
  ------------------
 9493|    673|        if (std::binary_search(std::begin(virama), std::end(virama),
  ------------------
  |  Branch (9493:13): [True: 554, False: 119]
  ------------------
 9494|    673|                               label[i - 1])) {
 9495|    554|          return true;
 9496|    554|        }
 9497|    673|      }
 9498|    137|      return false;
 9499|    691|    }
 9500|   394k|  }
 9501|       |
 9502|       |  // If CheckBidi, and if the domain name is a  Bidi domain name, then the label
 9503|       |  // must satisfy all six of the numbered conditions in [IDNA2008] RFC 5893,
 9504|       |  // Section 2.
 9505|       |
 9506|       |  // The following rule, consisting of six conditions, applies to labels
 9507|       |  // in Bidi domain names.  The requirements that this rule satisfies are
 9508|       |  // described in Section 3.  All of the conditions must be satisfied for
 9509|       |  // the rule to be satisfied.
 9510|       |  //
 9511|       |  //  1.  The first character must be a character with Bidi property L, R,
 9512|       |  //     or AL.  If it has the R or AL property, it is an RTL label; if it
 9513|       |  //     has the L property, it is an LTR label.
 9514|       |  //
 9515|       |  //  2.  In an RTL label, only characters with the Bidi properties R, AL,
 9516|       |  //      AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
 9517|       |  //
 9518|       |  //   3.  In an RTL label, the end of the label must be a character with
 9519|       |  //       Bidi property R, AL, EN, or AN, followed by zero or more
 9520|       |  //       characters with Bidi property NSM.
 9521|       |  //
 9522|       |  //   4.  In an RTL label, if an EN is present, no AN may be present, and
 9523|       |  //       vice versa.
 9524|       |  //
 9525|       |  //  5.  In an LTR label, only characters with the Bidi properties L, EN,
 9526|       |  //       ES, CS, ET, ON, BN, or NSM are allowed.
 9527|       |  //
 9528|       |  //   6.  In an LTR label, the end of the label must be a character with
 9529|       |  //       Bidi property L or EN, followed by zero or more characters with
 9530|       |  //       Bidi property NSM.
 9531|       |
 9532|  52.1k|  size_t last_non_nsm_char = find_last_not_of_nsm(label);
 9533|  52.1k|  if (last_non_nsm_char == std::u32string_view::npos) {
  ------------------
  |  Branch (9533:7): [True: 0, False: 52.1k]
  ------------------
 9534|      0|    return false;
 9535|      0|  }
 9536|       |
 9537|       |  // A "Bidi domain name" is a domain name that contains at least one RTL label.
 9538|       |  // The following rule, consisting of six conditions, applies to labels in Bidi
 9539|       |  // domain names.
 9540|  52.1k|  if (is_rtl_label(label)) {
  ------------------
  |  Branch (9540:7): [True: 3.20k, False: 48.9k]
  ------------------
 9541|       |    // The first character must be a character with Bidi property L, R,
 9542|       |    // or AL. If it has the R or AL property, it is an RTL label; if it
 9543|       |    // has the L property, it is an LTR label.
 9544|       |
 9545|  3.20k|    if (find_direction(label[0]) == direction::L) {
  ------------------
  |  Branch (9545:9): [True: 487, False: 2.71k]
  ------------------
 9546|       |      // Eval as LTR
 9547|       |
 9548|       |      // In an LTR label, only characters with the Bidi properties L, EN,
 9549|       |      // ES, CS, ET, ON, BN, or NSM are allowed.
 9550|  4.34k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (9550:26): [True: 4.34k, False: 0]
  ------------------
 9551|  4.34k|        const direction d = find_direction(label[i]);
 9552|  4.34k|        if (!(d == direction::L || d == direction::EN || d == direction::ES ||
  ------------------
  |  Branch (9552:15): [True: 1.16k, False: 3.17k]
  |  Branch (9552:36): [True: 313, False: 2.86k]
  |  Branch (9552:58): [True: 263, False: 2.59k]
  ------------------
 9553|  2.59k|              d == direction::CS || d == direction::ET || d == direction::ON ||
  ------------------
  |  Branch (9553:15): [True: 246, False: 2.35k]
  |  Branch (9553:37): [True: 287, False: 2.06k]
  |  Branch (9553:59): [True: 502, False: 1.56k]
  ------------------
 9554|  1.56k|              d == direction::BN || d == direction::NSM)) {
  ------------------
  |  Branch (9554:15): [True: 882, False: 681]
  |  Branch (9554:37): [True: 194, False: 487]
  ------------------
 9555|    487|          return false;
 9556|    487|        }
 9557|  4.34k|      }
 9558|       |
 9559|      0|      const direction last_dir = find_direction(label[last_non_nsm_char]);
 9560|      0|      if (!(last_dir == direction::L || last_dir == direction::EN)) {
  ------------------
  |  Branch (9560:13): [True: 0, False: 0]
  |  Branch (9560:41): [True: 0, False: 0]
  ------------------
 9561|      0|        return false;
 9562|      0|      }
 9563|       |
 9564|      0|      return true;
 9565|       |
 9566|  2.71k|    } else {
 9567|       |      // Eval as RTL
 9568|       |
 9569|  2.71k|      bool has_an = false;
 9570|  2.71k|      bool has_en = false;
 9571|  12.8k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (9571:26): [True: 10.5k, False: 2.26k]
  ------------------
 9572|  10.5k|        const direction d = find_direction(label[i]);
 9573|       |
 9574|       |        // In an RTL label, if an EN is present, no AN may be present, and vice
 9575|       |        // versa.
 9576|  10.5k|        if ((d == direction::EN && ((has_en = true) && has_an)) ||
  ------------------
  |  Branch (9576:14): [True: 1.13k, False: 9.46k]
  |  Branch (9576:37): [True: 1.13k, False: 0]
  |  Branch (9576:56): [True: 22, False: 1.10k]
  ------------------
 9577|  10.5k|            (d == direction::AN && ((has_an = true) && has_en))) {
  ------------------
  |  Branch (9577:14): [True: 1.08k, False: 9.49k]
  |  Branch (9577:37): [True: 1.08k, False: 0]
  |  Branch (9577:56): [True: 31, False: 1.05k]
  ------------------
 9578|     53|          return false;
 9579|     53|        }
 9580|       |
 9581|  10.5k|        if (!(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (9581:15): [True: 832, False: 9.70k]
  |  Branch (9581:36): [True: 2.14k, False: 7.56k]
  |  Branch (9581:58): [True: 1.05k, False: 6.50k]
  ------------------
 9582|  6.50k|              d == direction::EN || d == direction::ES || d == direction::CS ||
  ------------------
  |  Branch (9582:15): [True: 1.10k, False: 5.40k]
  |  Branch (9582:37): [True: 233, False: 5.16k]
  |  Branch (9582:59): [True: 231, False: 4.93k]
  ------------------
 9583|  4.93k|              d == direction::ET || d == direction::ON || d == direction::BN ||
  ------------------
  |  Branch (9583:15): [True: 275, False: 4.66k]
  |  Branch (9583:37): [True: 1.30k, False: 3.35k]
  |  Branch (9583:59): [True: 735, False: 2.62k]
  ------------------
 9584|  2.62k|              d == direction::NSM)) {
  ------------------
  |  Branch (9584:15): [True: 2.34k, False: 278]
  ------------------
 9585|    278|          return false;
 9586|    278|        }
 9587|       |
 9588|  10.2k|        if (i == last_non_nsm_char &&
  ------------------
  |  Branch (9588:13): [True: 2.38k, False: 7.88k]
  ------------------
 9589|  2.38k|            !(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (9589:15): [True: 488, False: 1.89k]
  |  Branch (9589:36): [True: 985, False: 909]
  |  Branch (9589:58): [True: 452, False: 457]
  ------------------
 9590|    457|              d == direction::EN)) {
  ------------------
  |  Branch (9590:15): [True: 342, False: 115]
  ------------------
 9591|    115|          return false;
 9592|    115|        }
 9593|  10.2k|      }
 9594|       |
 9595|  2.26k|      return true;
 9596|  2.71k|    }
 9597|  3.20k|  }
 9598|       |
 9599|  48.9k|  return true;
 9600|  52.1k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9726|  36.4k|std::string to_ascii(std::string_view ut8_string) {
 9727|  36.4k|  if (is_ascii(ut8_string)) {
  ------------------
  |  Branch (9727:7): [True: 18.0k, False: 18.4k]
  ------------------
 9728|  18.0k|    return from_ascii_to_ascii(ut8_string);
 9729|  18.0k|  }
 9730|  18.4k|  static const std::string error = "";
 9731|       |  // We convert to UTF-32
 9732|       |
 9733|       |#ifdef ADA_USE_SIMDUTF
 9734|       |  size_t utf32_length =
 9735|       |      simdutf::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 9736|       |  std::u32string utf32(utf32_length, '\0');
 9737|       |  size_t actual_utf32_length = simdutf::convert_utf8_to_utf32(
 9738|       |      ut8_string.data(), ut8_string.size(), utf32.data());
 9739|       |#else
 9740|  18.4k|  size_t utf32_length =
 9741|  18.4k|      ada::idna::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 9742|  18.4k|  std::u32string utf32(utf32_length, '\0');
 9743|  18.4k|  size_t actual_utf32_length = ada::idna::utf8_to_utf32(
 9744|  18.4k|      ut8_string.data(), ut8_string.size(), utf32.data());
 9745|  18.4k|#endif
 9746|  18.4k|  if (actual_utf32_length == 0) {
  ------------------
  |  Branch (9746:7): [True: 6.21k, False: 12.2k]
  ------------------
 9747|  6.21k|    return error;
 9748|  6.21k|  }
 9749|       |  // mapping: use the two-argument overload to avoid an extra heap allocation
 9750|       |  // that the single-argument overload (which returns by value) would incur.
 9751|  12.2k|  std::u32string tmp_buffer;
 9752|  12.2k|  std::u32string post_map;
 9753|  12.2k|  if (!ada::idna::map(utf32, tmp_buffer)) {
  ------------------
  |  Branch (9753:7): [True: 300, False: 11.9k]
  ------------------
 9754|    300|    return error;
 9755|    300|  }
 9756|  11.9k|  utf32 = std::move(tmp_buffer);
 9757|  11.9k|  normalize(utf32);
 9758|  11.9k|  std::string out;
 9759|  11.9k|  out.reserve(ut8_string.size());
 9760|  11.9k|  size_t label_start = 0;
 9761|       |
 9762|  64.6k|  while (label_start != utf32.size()) {
  ------------------
  |  Branch (9762:10): [True: 54.9k, False: 9.64k]
  ------------------
 9763|  54.9k|    size_t loc_dot = utf32.find('.', label_start);
 9764|  54.9k|    bool is_last_label = (loc_dot == std::string_view::npos);
 9765|  54.9k|    size_t label_size =
 9766|  54.9k|        is_last_label ? utf32.size() - label_start : loc_dot - label_start;
  ------------------
  |  Branch (9766:9): [True: 10.6k, False: 44.3k]
  ------------------
 9767|  54.9k|    size_t label_size_with_dot = is_last_label ? label_size : label_size + 1;
  ------------------
  |  Branch (9767:34): [True: 10.6k, False: 44.3k]
  ------------------
 9768|  54.9k|    std::u32string_view label_view(utf32.data() + label_start, label_size);
 9769|  54.9k|    label_start += label_size_with_dot;
 9770|  54.9k|    if (label_size == 0) {
  ------------------
  |  Branch (9770:9): [True: 2.53k, False: 52.4k]
  ------------------
 9771|       |      // empty label? Nothing to do.
 9772|  52.4k|    } else if (label_view.starts_with(U"xn--")) {
  ------------------
  |  Branch (9772:16): [True: 2.86k, False: 49.5k]
  ------------------
 9773|       |      // we do not need to check, e.g., Xn-- because mapping goes to lower case
 9774|       |      // Validate first, then bulk-copy with a single resize to avoid per-char
 9775|       |      // capacity checks in operator+=.
 9776|  38.5k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (9776:23): [True: 38.5k, False: 2.80k]
  ------------------
 9777|  38.5k|        if (c >= 0x80) {
  ------------------
  |  Branch (9777:13): [True: 62, False: 38.4k]
  ------------------
 9778|     62|          return error;
 9779|     62|        }
 9780|  38.5k|      }
 9781|  2.80k|      size_t label_out_start = out.size();
 9782|  2.80k|      out.resize(label_out_start + label_size);
 9783|  2.80k|      char* dest = out.data() + label_out_start;
 9784|  38.2k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (9784:23): [True: 38.2k, False: 2.80k]
  ------------------
 9785|  38.2k|        *dest++ = static_cast<char>(c);
 9786|  38.2k|      }
 9787|  2.80k|      std::string_view puny_segment_ascii(out.data() + label_out_start + 4,
 9788|  2.80k|                                          label_size - 4);
 9789|  2.80k|      tmp_buffer.clear();
 9790|  2.80k|      bool is_ok = ada::idna::punycode_to_utf32(puny_segment_ascii, tmp_buffer);
 9791|  2.80k|      if (!is_ok) {
  ------------------
  |  Branch (9791:11): [True: 154, False: 2.64k]
  ------------------
 9792|    154|        return error;
 9793|    154|      }
 9794|       |      // If the input is just ASCII, it should not have been encoded
 9795|       |      // as punycode.
 9796|       |      // https://github.com/whatwg/url/issues/760
 9797|  2.64k|      if (is_ascii(tmp_buffer)) {
  ------------------
  |  Branch (9797:11): [True: 110, False: 2.53k]
  ------------------
 9798|    110|        return error;
 9799|    110|      }
 9800|  2.53k|      if (!ada::idna::map(tmp_buffer, post_map)) {
  ------------------
  |  Branch (9800:11): [True: 57, False: 2.48k]
  ------------------
 9801|     57|        return error;
 9802|     57|      }
 9803|  2.48k|      if (tmp_buffer != post_map) {
  ------------------
  |  Branch (9803:11): [True: 127, False: 2.35k]
  ------------------
 9804|    127|        return error;
 9805|    127|      }
 9806|  2.35k|      normalize(post_map);
 9807|  2.35k|      if (post_map != tmp_buffer) {
  ------------------
  |  Branch (9807:11): [True: 51, False: 2.30k]
  ------------------
 9808|     51|        return error;
 9809|     51|      }
 9810|  2.30k|      if (post_map.empty()) {
  ------------------
  |  Branch (9810:11): [True: 0, False: 2.30k]
  ------------------
 9811|      0|        return error;
 9812|      0|      }
 9813|  2.30k|      if (!is_label_valid(post_map)) {
  ------------------
  |  Branch (9813:11): [True: 29, False: 2.27k]
  ------------------
 9814|     29|        return error;
 9815|     29|      }
 9816|  49.5k|    } else {
 9817|       |      // The fast path here is an ascii label.
 9818|  49.5k|      if (is_ascii(label_view)) {
  ------------------
  |  Branch (9818:11): [True: 5.15k, False: 44.4k]
  ------------------
 9819|       |        // no validation needed; bulk-copy with single resize.
 9820|  5.15k|        size_t old_size = out.size();
 9821|  5.15k|        out.resize(old_size + label_size);
 9822|  5.15k|        char* dest = out.data() + old_size;
 9823|  28.4k|        for (char32_t c : label_view) {
  ------------------
  |  Branch (9823:25): [True: 28.4k, False: 5.15k]
  ------------------
 9824|  28.4k|          *dest++ = static_cast<char>(c);
 9825|  28.4k|        }
 9826|  44.4k|      } else {
 9827|       |        // slow path.
 9828|       |        // first check validity.
 9829|  44.4k|        if (!is_label_valid(label_view)) {
  ------------------
  |  Branch (9829:13): [True: 1.69k, False: 42.7k]
  ------------------
 9830|  1.69k|          return error;
 9831|  1.69k|        }
 9832|       |        // It is valid! So now we must encode it as punycode...
 9833|  42.7k|        out.append("xn--");
 9834|  42.7k|        bool is_ok = ada::idna::utf32_to_punycode(label_view, out);
 9835|  42.7k|        if (!is_ok) {
  ------------------
  |  Branch (9835:13): [True: 0, False: 42.7k]
  ------------------
 9836|      0|          return error;
 9837|      0|        }
 9838|  42.7k|      }
 9839|  49.5k|    }
 9840|  52.6k|    if (!is_last_label) {
  ------------------
  |  Branch (9840:9): [True: 44.1k, False: 8.52k]
  ------------------
 9841|  44.1k|      out.push_back('.');
 9842|  44.1k|    }
 9843|  52.6k|  }
 9844|  9.64k|  return out;
 9845|  11.9k|}
_ZN3ada7unicode14percent_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
10998|  84.8k|std::string percent_decode(const std::string_view input, size_t first_percent) {
10999|       |  // next line is for safety only, we expect users to avoid calling
11000|       |  // percent_decode when first_percent is outside the range.
11001|  84.8k|  if (first_percent == std::string_view::npos) {
  ------------------
  |  Branch (11001:7): [True: 40.2k, False: 44.6k]
  ------------------
11002|  40.2k|    return std::string(input);
11003|  40.2k|  }
11004|  44.6k|  std::string dest;
11005|  44.6k|  dest.reserve(input.length());
11006|  44.6k|  dest.append(input.substr(0, first_percent));
11007|  44.6k|  const char* pointer = input.data() + first_percent;
11008|  44.6k|  const char* end = input.data() + input.size();
11009|       |  // Optimization opportunity: if the following code gets
11010|       |  // called often, it can be optimized quite a bit.
11011|  1.01M|  while (pointer < end) {
  ------------------
  |  Branch (11011:10): [True: 966k, False: 44.6k]
  ------------------
11012|   966k|    const char ch = pointer[0];
11013|   966k|    size_t remaining = end - pointer - 1;
11014|   966k|    if (ch != '%' || remaining < 2 ||
  ------------------
  |  Branch (11014:9): [True: 263k, False: 702k]
  |  Branch (11014:22): [True: 1.12k, False: 701k]
  ------------------
11015|   701k|        (  // ch == '%' && // It is unnecessary to check that ch == '%'.
11016|   701k|            (!is_ascii_hex_digit(pointer[1]) ||
  ------------------
  |  Branch (11016:14): [True: 2.05k, False: 699k]
  ------------------
11017|   699k|             !is_ascii_hex_digit(pointer[2])))) {
  ------------------
  |  Branch (11017:14): [True: 780, False: 698k]
  ------------------
11018|   267k|      dest += ch;
11019|   267k|      pointer++;
11020|   698k|    } else {
11021|   698k|      unsigned a = convert_hex_to_binary(pointer[1]);
11022|   698k|      unsigned b = convert_hex_to_binary(pointer[2]);
11023|   698k|      char c = static_cast<char>(a * 16 + b);
11024|   698k|      dest += c;
11025|   698k|      pointer += 3;
11026|   698k|    }
11027|   966k|  }
11028|  44.6k|  return dest;
11029|  84.8k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
11032|   333k|                           const uint8_t character_set[]) {
11033|   333k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11034|   333k|    return character_sets::bit_at(character_set, c);
11035|   333k|  });
11036|       |  // Optimization: Don't iterate if percent encode is not required
11037|   333k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11037:7): [True: 219k, False: 113k]
  ------------------
11038|   219k|    return std::string(input);
11039|   219k|  }
11040|       |
11041|   113k|  std::string result;
11042|   113k|  result.reserve(input.length());  // in the worst case, percent encoding might
11043|       |                                   // produce 3 characters.
11044|   113k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
11045|       |
11046|  2.77M|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11046:10): [True: 2.66M, False: 113k]
  ------------------
11047|  2.66M|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11047:9): [True: 2.03M, False: 632k]
  ------------------
11048|  2.03M|      result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11049|  2.03M|    } else {
11050|   632k|      result += *pointer;
11051|   632k|    }
11052|  2.66M|  }
11053|       |
11054|   113k|  return result;
11055|   333k|}
_ZN3ada7unicode8to_asciiERNSt3__18optionalINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEEENS1_17basic_string_viewIcS5_EEm:
11093|  36.4k|              size_t first_percent) {
11094|  36.4k|  std::string percent_decoded_buffer;
11095|  36.4k|  std::string_view input = plain;
11096|  36.4k|  if (first_percent != std::string_view::npos) {
  ------------------
  |  Branch (11096:7): [True: 5.10k, False: 31.3k]
  ------------------
11097|  5.10k|    percent_decoded_buffer = unicode::percent_decode(plain, first_percent);
11098|  5.10k|    input = percent_decoded_buffer;
11099|  5.10k|  }
11100|       |  // input is a non-empty UTF-8 string, must be percent decoded
11101|  36.4k|  std::string idna_ascii = ada::idna::to_ascii(input);
11102|  36.4k|  if (idna_ascii.empty() || contains_forbidden_domain_code_point(
  ------------------
  |  Branch (11102:7): [True: 10.2k, False: 26.2k]
  |  Branch (11102:29): [True: 7.81k, False: 18.4k]
  ------------------
11103|  26.2k|                                idna_ascii.data(), idna_ascii.size())) {
11104|  18.0k|    return false;
11105|  18.0k|  }
11106|  18.4k|  out = std::move(idna_ascii);
11107|  18.4k|  return true;
11108|  36.4k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhm:
11111|  23.5k|                           const uint8_t character_set[], size_t index) {
11112|  23.5k|  std::string out;
11113|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
11114|  23.5k|  out.append(input.data(), index);
11115|  23.5k|  auto pointer = input.begin() + index;
11116|   622k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11116:10): [True: 599k, False: 23.5k]
  ------------------
11117|   599k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11117:9): [True: 448k, False: 151k]
  ------------------
11118|   448k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11119|   448k|    } else {
11120|   151k|      out += *pointer;
11121|   151k|    }
11122|   599k|  }
11123|  23.5k|  return out;
11124|  23.5k|}
_ZN3ada11serializers36find_longest_sequence_of_ipv6_piecesERKNSt3__15arrayItLm8EEERmS6_:
11137|  1.35k|    size_t& compress_length) noexcept {
11138|  4.75k|  for (size_t i = 0; i < 8; i++) {
  ------------------
  |  Branch (11138:22): [True: 3.93k, False: 821]
  ------------------
11139|  3.93k|    if (address[i] == 0) {
  ------------------
  |  Branch (11139:9): [True: 1.49k, False: 2.44k]
  ------------------
11140|  1.49k|      size_t next = i + 1;
11141|  7.72k|      while (next != 8 && address[next] == 0) ++next;
  ------------------
  |  Branch (11141:14): [True: 7.06k, False: 659]
  |  Branch (11141:27): [True: 6.23k, False: 837]
  ------------------
11142|  1.49k|      const size_t count = next - i;
11143|  1.49k|      if (compress_length < count) {
  ------------------
  |  Branch (11143:11): [True: 1.27k, False: 217]
  ------------------
11144|  1.27k|        compress_length = count;
11145|  1.27k|        compress = i;
11146|  1.27k|        if (next == 8) break;
  ------------------
  |  Branch (11146:13): [True: 529, False: 750]
  ------------------
11147|    750|        i = next;
11148|    750|      }
11149|  1.49k|    }
11150|  3.93k|  }
11151|  1.35k|}
_ZN3ada11serializers4ipv6ERKNSt3__15arrayItLm8EEE:
11153|  1.35k|std::string ipv6(const std::array<uint16_t, 8>& address) {
11154|  1.35k|  size_t compress_length = 0;  // The length of a long sequence of zeros.
11155|  1.35k|  size_t compress = 0;         // The start of a long sequence of zeros.
11156|  1.35k|  find_longest_sequence_of_ipv6_pieces(address, compress, compress_length);
11157|       |
11158|  1.35k|  if (compress_length <= 1) {
  ------------------
  |  Branch (11158:7): [True: 237, False: 1.11k]
  ------------------
11159|       |    // Optimization opportunity: Find a faster way then snprintf for imploding
11160|       |    // and return here.
11161|    237|    compress = compress_length = 8;
11162|    237|  }
11163|       |
11164|  1.35k|  std::string output(4 * 8 + 7 + 2, '\0');
11165|  1.35k|  size_t piece_index = 0;
11166|  1.35k|  char* point = output.data();
11167|  1.35k|  char* point_end = output.data() + output.size();
11168|  1.35k|  *point++ = '[';
11169|  4.10k|  while (true) {
  ------------------
  |  Branch (11169:10): [True: 4.10k, Folded]
  ------------------
11170|  4.10k|    if (piece_index == compress) {
  ------------------
  |  Branch (11170:9): [True: 1.11k, False: 2.99k]
  ------------------
11171|  1.11k|      *point++ = ':';
11172|       |      // If we skip a value initially, we need to write '::', otherwise
11173|       |      // a single ':' will do since it follows a previous ':'.
11174|  1.11k|      if (piece_index == 0) {
  ------------------
  |  Branch (11174:11): [True: 668, False: 445]
  ------------------
11175|    668|        *point++ = ':';
11176|    668|      }
11177|  1.11k|      piece_index += compress_length;
11178|  1.11k|      if (piece_index == 8) {
  ------------------
  |  Branch (11178:11): [True: 529, False: 584]
  ------------------
11179|    529|        break;
11180|    529|      }
11181|  1.11k|    }
11182|  3.57k|    point = std::to_chars(point, point_end, address[piece_index], 16).ptr;
11183|  3.57k|    piece_index++;
11184|  3.57k|    if (piece_index == 8) {
  ------------------
  |  Branch (11184:9): [True: 821, False: 2.75k]
  ------------------
11185|    821|      break;
11186|    821|    }
11187|  2.75k|    *point++ = ':';
11188|  2.75k|  }
11189|  1.35k|  *point++ = ']';
11190|  1.35k|  output.resize(point - output.data());
11191|  1.35k|  return output;
11192|  1.35k|}
_ZN3ada11serializers4ipv4Em:
11194|  8.76k|std::string ipv4(const uint64_t address) {
11195|  8.76k|  std::string output(15, '\0');
11196|  8.76k|  char* point = output.data();
11197|  8.76k|  char* point_end = output.data() + output.size();
11198|  8.76k|  point = std::to_chars(point, point_end, uint8_t(address >> 24)).ptr;
11199|  35.0k|  for (int i = 2; i >= 0; i--) {
  ------------------
  |  Branch (11199:19): [True: 26.3k, False: 8.76k]
  ------------------
11200|  26.3k|    *point++ = '.';
11201|  26.3k|    point = std::to_chars(point, point_end, uint8_t(address >> (i * 8))).ptr;
11202|  26.3k|  }
11203|  8.76k|  output.resize(point - output.data());
11204|  8.76k|  return output;
11205|  8.76k|}
_ZN3ada5parseINS_3urlEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
11477|  74.0k|    std::string_view input, const result_type* base_url) {
11478|  74.0k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
11479|  74.0k|  if (!u.is_valid) {
  ------------------
  |  Branch (11479:7): [True: 24.7k, False: 49.3k]
  ------------------
11480|  24.7k|    return tl::unexpected(errors::type_error);
11481|  24.7k|  }
11482|  49.3k|  return u;
11483|  74.0k|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
11477|   132k|    std::string_view input, const result_type* base_url) {
11478|   132k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
11479|   132k|  if (!u.is_valid) {
  ------------------
  |  Branch (11479:7): [True: 27.8k, False: 104k]
  ------------------
11480|  27.8k|    return tl::unexpected(errors::type_error);
11481|  27.8k|  }
11482|   104k|  return u;
11483|   132k|}
_ZN3ada20get_max_input_lengthEv:
11226|   432k|uint32_t get_max_input_length() {
11227|   432k|  return max_input_length_.load(std::memory_order_relaxed);
11228|   432k|}
_ZN3ada14href_from_fileENSt3__117basic_string_viewIcNS0_11char_traitsIcEEEE:
11490|  15.3k|std::string href_from_file(std::string_view input) {
11491|       |  // This is going to be much faster than constructing a URL.
11492|  15.3k|  std::string tmp_buffer;
11493|  15.3k|  std::string_view internal_input;
11494|  15.3k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (11494:7): [True: 410, False: 14.9k]
  ------------------
11495|    410|    tmp_buffer = input;
11496|    410|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
11497|    410|    internal_input = tmp_buffer;
11498|  14.9k|  } else {
11499|  14.9k|    internal_input = input;
11500|  14.9k|  }
11501|  15.3k|  std::string path;
11502|  15.3k|  if (internal_input.empty()) {
  ------------------
  |  Branch (11502:7): [True: 1.29k, False: 14.0k]
  ------------------
11503|  1.29k|    path = "/";
11504|  14.0k|  } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (11504:14): [True: 1.57k, False: 12.5k]
  |  Branch (11504:44): [True: 37, False: 12.4k]
  ------------------
11505|  1.60k|    helpers::parse_prepared_path(internal_input.substr(1),
11506|  1.60k|                                 ada::scheme::type::FILE, path);
11507|  12.4k|  } else {
11508|  12.4k|    helpers::parse_prepared_path(internal_input, ada::scheme::type::FILE, path);
11509|  12.4k|  }
11510|  15.3k|  return "file://" + path;
11511|  15.3k|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12385|   206k|void trim_c0_whitespace(std::string_view& input) noexcept {
12386|   215k|  while (!input.empty() &&
  ------------------
  |  Branch (12386:10): [True: 187k, False: 28.5k]
  ------------------
12387|   187k|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (12387:10): [True: 9.50k, False: 177k]
  ------------------
12388|  9.50k|    input.remove_prefix(1);
12389|  9.50k|  }
12390|   220k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (12390:10): [True: 191k, False: 28.5k]
  |  Branch (12390:28): [True: 14.2k, False: 177k]
  ------------------
12391|  14.2k|    input.remove_suffix(1);
12392|  14.2k|  }
12393|   206k|}
_ZN3ada3url17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12645|    818|bool url::parse_opaque_host(std::string_view input) {
12646|    818|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
12647|    818|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (12647:7): [True: 96, False: 722]
  ------------------
12648|     96|    return is_valid = false;
12649|     96|  }
12650|       |
12651|       |  // Return the result of running UTF-8 percent-encode on input using the C0
12652|       |  // control percent-encode set.
12653|    722|  host = ada::unicode::percent_encode(
12654|    722|      input, ada::character_sets::C0_CONTROL_PERCENT_ENCODE);
12655|    722|  return true;
12656|    818|}
_ZN3ada3url10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12658|  5.60k|bool url::parse_ipv4(std::string_view input) {
12659|  5.60k|  ada_log("parse_ipv4 ", input, " [", input.size(), " bytes]");
12660|  5.60k|  if (input.back() == '.') {
  ------------------
  |  Branch (12660:7): [True: 508, False: 5.09k]
  ------------------
12661|    508|    input.remove_suffix(1);
12662|    508|  }
12663|  5.60k|  size_t digit_count{0};
12664|  5.60k|  int pure_decimal_count = 0;  // entries that are decimal
12665|  5.60k|  std::string_view original_input =
12666|  5.60k|      input;  // we might use this if pure_decimal_count == 4.
12667|  5.60k|  uint64_t ipv4{0};
12668|       |  // we could unroll for better performance?
12669|  7.11k|  for (; (digit_count < 4) && !(input.empty()); digit_count++) {
  ------------------
  |  Branch (12669:10): [True: 7.02k, False: 90]
  |  Branch (12669:31): [True: 7.02k, False: 0]
  ------------------
12670|  7.02k|    uint32_t
12671|  7.02k|        segment_result{};  // If any number exceeds 32 bits, we have an error.
12672|  7.02k|    bool is_hex = checkers::has_hex_prefix(input);
12673|  7.02k|    if (is_hex && ((input.length() == 2) ||
  ------------------
  |  Branch (12673:9): [True: 1.74k, False: 5.28k]
  |  Branch (12673:20): [True: 269, False: 1.47k]
  ------------------
12674|  1.47k|                   ((input.length() > 2) && (input[2] == '.')))) {
  ------------------
  |  Branch (12674:21): [True: 1.47k, False: 0]
  |  Branch (12674:45): [True: 46, False: 1.42k]
  ------------------
12675|       |      // special case
12676|    315|      segment_result = 0;
12677|    315|      input.remove_prefix(2);
12678|  6.70k|    } else {
12679|  6.70k|      std::from_chars_result r{};
12680|  6.70k|      if (is_hex) {
  ------------------
  |  Branch (12680:11): [True: 1.42k, False: 5.28k]
  ------------------
12681|  1.42k|        r = std::from_chars(input.data() + 2, input.data() + input.size(),
12682|  1.42k|                            segment_result, 16);
12683|  5.28k|      } else if ((input.length() >= 2) && input[0] == '0' &&
  ------------------
  |  Branch (12683:18): [True: 3.78k, False: 1.49k]
  |  Branch (12683:43): [True: 1.47k, False: 2.31k]
  ------------------
12684|  1.47k|                 checkers::is_digit(input[1])) {
  ------------------
  |  Branch (12684:18): [True: 1.04k, False: 435]
  ------------------
12685|  1.04k|        r = std::from_chars(input.data() + 1, input.data() + input.size(),
12686|  1.04k|                            segment_result, 8);
12687|  4.24k|      } else {
12688|  4.24k|        pure_decimal_count++;
12689|  4.24k|        r = std::from_chars(input.data(), input.data() + input.size(),
12690|  4.24k|                            segment_result, 10);
12691|  4.24k|      }
12692|  6.70k|      if (r.ec != std::errc()) {
  ------------------
  |  Branch (12692:11): [True: 1.26k, False: 5.44k]
  ------------------
12693|  1.26k|        return is_valid = false;
12694|  1.26k|      }
12695|  5.44k|      input.remove_prefix(r.ptr - input.data());
12696|  5.44k|    }
12697|  5.75k|    if (input.empty()) {
  ------------------
  |  Branch (12697:9): [True: 3.91k, False: 1.83k]
  ------------------
12698|       |      // We have the last value.
12699|       |      // At this stage, ipv4 contains digit_count*8 bits.
12700|       |      // So we have 32-digit_count*8 bits left.
12701|  3.91k|      if (segment_result >= (uint64_t(1) << (32 - digit_count * 8))) {
  ------------------
  |  Branch (12701:11): [True: 57, False: 3.86k]
  ------------------
12702|     57|        return is_valid = false;
12703|     57|      }
12704|  3.86k|      ipv4 <<= (32 - digit_count * 8);
12705|  3.86k|      ipv4 |= segment_result;
12706|  3.86k|      goto final;
12707|  3.91k|    } else {
12708|       |      // There is more, so that the value must no be larger than 255
12709|       |      // and we must have a '.'.
12710|  1.83k|      if ((segment_result > 255) || (input[0] != '.')) {
  ------------------
  |  Branch (12710:11): [True: 182, False: 1.65k]
  |  Branch (12710:37): [True: 151, False: 1.50k]
  ------------------
12711|    333|        return is_valid = false;
12712|    333|      }
12713|  1.50k|      ipv4 <<= 8;
12714|  1.50k|      ipv4 |= segment_result;
12715|  1.50k|      input.remove_prefix(1);  // remove '.'
12716|  1.50k|    }
12717|  5.75k|  }
12718|     90|  if ((digit_count != 4) || (!input.empty())) {
  ------------------
  |  Branch (12718:7): [True: 0, False: 90]
  |  Branch (12718:29): [True: 90, False: 0]
  ------------------
12719|     90|    return is_valid = false;
12720|     90|  }
12721|  3.86k|final:
12722|       |  // We could also check r.ptr to see where the parsing ended.
12723|  3.86k|  if (pure_decimal_count == 4) {
  ------------------
  |  Branch (12723:7): [True: 15, False: 3.84k]
  ------------------
12724|     15|    host = original_input;  // The original input was already all decimal and we
12725|       |                            // validated it.
12726|  3.84k|  } else {
12727|  3.84k|    host = ada::serializers::ipv4(ipv4);  // We have to reserialize the address.
12728|  3.84k|  }
12729|  3.86k|  host_type = IPV4;
12730|  3.86k|  return true;
12731|     90|}
_ZN3ada3url10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12733|  1.17k|bool url::parse_ipv6(std::string_view input) {
12734|  1.17k|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
12735|       |
12736|  1.17k|  if (input.empty()) {
  ------------------
  |  Branch (12736:7): [True: 60, False: 1.11k]
  ------------------
12737|     60|    return is_valid = false;
12738|     60|  }
12739|       |  // Let address be a new IPv6 address whose IPv6 pieces are all 0.
12740|  1.11k|  std::array<uint16_t, 8> address{};
12741|       |
12742|       |  // Let pieceIndex be 0.
12743|  1.11k|  int piece_index = 0;
12744|       |
12745|       |  // Let compress be null.
12746|  1.11k|  std::optional<int> compress{};
12747|       |
12748|       |  // Let pointer be a pointer for input.
12749|  1.11k|  std::string_view::iterator pointer = input.begin();
12750|       |
12751|       |  // If c is U+003A (:), then:
12752|  1.11k|  if (input[0] == ':') {
  ------------------
  |  Branch (12752:7): [True: 385, False: 733]
  ------------------
12753|       |    // If remaining does not start with U+003A (:), validation error, return
12754|       |    // failure.
12755|    385|    if (input.size() == 1 || input[1] != ':') {
  ------------------
  |  Branch (12755:9): [True: 13, False: 372]
  |  Branch (12755:30): [True: 40, False: 332]
  ------------------
12756|     53|      ada_log("parse_ipv6 starts with : but the rest does not start with :");
12757|     53|      return is_valid = false;
12758|     53|    }
12759|       |
12760|       |    // Increase pointer by 2.
12761|    332|    pointer += 2;
12762|       |
12763|       |    // Increase pieceIndex by 1 and then set compress to pieceIndex.
12764|    332|    compress = ++piece_index;
12765|    332|  }
12766|       |
12767|       |  // While c is not the EOF code point:
12768|  3.10k|  while (pointer != input.end()) {
  ------------------
  |  Branch (12768:10): [True: 2.54k, False: 559]
  ------------------
12769|       |    // If pieceIndex is 8, validation error, return failure.
12770|  2.54k|    if (piece_index == 8) {
  ------------------
  |  Branch (12770:9): [True: 18, False: 2.52k]
  ------------------
12771|     18|      ada_log("parse_ipv6 piece_index == 8");
12772|     18|      return is_valid = false;
12773|     18|    }
12774|       |
12775|       |    // If c is U+003A (:), then:
12776|  2.52k|    if (*pointer == ':') {
  ------------------
  |  Branch (12776:9): [True: 266, False: 2.26k]
  ------------------
12777|       |      // If compress is non-null, validation error, return failure.
12778|    266|      if (compress.has_value()) {
  ------------------
  |  Branch (12778:11): [True: 20, False: 246]
  ------------------
12779|     20|        ada_log("parse_ipv6 compress is non-null");
12780|     20|        return is_valid = false;
12781|     20|      }
12782|       |
12783|       |      // Increase pointer and pieceIndex by 1, set compress to pieceIndex, and
12784|       |      // then continue.
12785|    246|      pointer++;
12786|    246|      compress = ++piece_index;
12787|    246|      continue;
12788|    266|    }
12789|       |
12790|       |    // Let value and length be 0.
12791|  2.26k|    uint16_t value = 0, length = 0;
12792|       |
12793|       |    // While length is less than 4 and c is an ASCII hex digit,
12794|       |    // set value to value times 0x10 + c interpreted as hexadecimal number, and
12795|       |    // increase pointer and length by 1.
12796|  5.59k|    while (length < 4 && pointer != input.end() &&
  ------------------
  |  Branch (12796:12): [True: 5.39k, False: 199]
  |  Branch (12796:26): [True: 5.06k, False: 327]
  ------------------
12797|  5.06k|           unicode::is_ascii_hex_digit(*pointer)) {
  ------------------
  |  Branch (12797:12): [True: 3.33k, False: 1.73k]
  ------------------
12798|       |      // https://stackoverflow.com/questions/39060852/why-does-the-addition-of-two-shorts-return-an-int
12799|  3.33k|      value = uint16_t(value * 0x10 + unicode::convert_hex_to_binary(*pointer));
12800|  3.33k|      pointer++;
12801|  3.33k|      length++;
12802|  3.33k|    }
12803|       |
12804|       |    // If c is U+002E (.), then:
12805|  2.26k|    if (pointer != input.end() && *pointer == '.') {
  ------------------
  |  Branch (12805:9): [True: 1.89k, False: 369]
  |  Branch (12805:35): [True: 269, False: 1.62k]
  ------------------
12806|       |      // If length is 0, validation error, return failure.
12807|    269|      if (length == 0) {
  ------------------
  |  Branch (12807:11): [True: 14, False: 255]
  ------------------
12808|     14|        ada_log("parse_ipv6 length is 0");
12809|     14|        return is_valid = false;
12810|     14|      }
12811|       |
12812|       |      // Decrease pointer by length.
12813|    255|      pointer -= length;
12814|       |
12815|       |      // If pieceIndex is greater than 6, validation error, return failure.
12816|    255|      if (piece_index > 6) {
  ------------------
  |  Branch (12816:11): [True: 12, False: 243]
  ------------------
12817|     12|        ada_log("parse_ipv6 piece_index > 6");
12818|     12|        return is_valid = false;
12819|     12|      }
12820|       |
12821|       |      // Let numbersSeen be 0.
12822|    243|      int numbers_seen = 0;
12823|       |
12824|       |      // While c is not the EOF code point:
12825|    774|      while (pointer != input.end()) {
  ------------------
  |  Branch (12825:14): [True: 695, False: 79]
  ------------------
12826|       |        // Let ipv4Piece be null.
12827|    695|        std::optional<uint16_t> ipv4_piece{};
12828|       |
12829|       |        // If numbersSeen is greater than 0, then:
12830|    695|        if (numbers_seen > 0) {
  ------------------
  |  Branch (12830:13): [True: 452, False: 243]
  ------------------
12831|       |          // If c is a U+002E (.) and numbersSeen is less than 4, then increase
12832|       |          // pointer by 1.
12833|    452|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (12833:15): [True: 422, False: 30]
  |  Branch (12833:34): [True: 410, False: 12]
  ------------------
12834|    410|            pointer++;
12835|    410|          }
12836|       |          // Otherwise, validation error, return failure.
12837|     42|          else {
12838|     42|            ada_log("parse_ipv6 Otherwise, validation error, return failure");
12839|     42|            return is_valid = false;
12840|     42|          }
12841|    452|        }
12842|       |
12843|       |        // If c is not an ASCII digit, validation error, return failure.
12844|    653|        if (pointer == input.end() || !checkers::is_digit(*pointer)) {
  ------------------
  |  Branch (12844:13): [True: 24, False: 629]
  |  Branch (12844:39): [True: 45, False: 584]
  ------------------
12845|     69|          ada_log(
12846|     69|              "parse_ipv6 If c is not an ASCII digit, validation error, return "
12847|     69|              "failure");
12848|     69|          return is_valid = false;
12849|     69|        }
12850|       |
12851|       |        // While c is an ASCII digit:
12852|  1.38k|        while (pointer != input.end() && checkers::is_digit(*pointer)) {
  ------------------
  |  Branch (12852:16): [True: 1.30k, False: 79]
  |  Branch (12852:42): [True: 851, False: 452]
  ------------------
12853|       |          // Let number be c interpreted as decimal number.
12854|    851|          int number = *pointer - '0';
12855|       |
12856|       |          // If ipv4Piece is null, then set ipv4Piece to number.
12857|    851|          if (!ipv4_piece.has_value()) {
  ------------------
  |  Branch (12857:15): [True: 584, False: 267]
  ------------------
12858|    584|            ipv4_piece = number;
12859|    584|          }
12860|       |          // Otherwise, if ipv4Piece is 0, validation error, return failure.
12861|    267|          else if (ipv4_piece == 0) {
  ------------------
  |  Branch (12861:20): [True: 26, False: 241]
  ------------------
12862|     26|            ada_log("parse_ipv6 if ipv4Piece is 0, validation error");
12863|     26|            return is_valid = false;
12864|     26|          }
12865|       |          // Otherwise, set ipv4Piece to ipv4Piece times 10 + number.
12866|    241|          else {
12867|    241|            ipv4_piece = *ipv4_piece * 10 + number;
12868|    241|          }
12869|       |
12870|       |          // If ipv4Piece is greater than 255, validation error, return failure.
12871|    825|          if (ipv4_piece > 255) {
  ------------------
  |  Branch (12871:15): [True: 27, False: 798]
  ------------------
12872|     27|            ada_log("parse_ipv6 ipv4_piece > 255");
12873|     27|            return is_valid = false;
12874|     27|          }
12875|       |
12876|       |          // Increase pointer by 1.
12877|    798|          pointer++;
12878|    798|        }
12879|       |
12880|       |        // Set address[pieceIndex] to address[pieceIndex] times 0x100 +
12881|       |        // ipv4Piece.
12882|       |        // https://stackoverflow.com/questions/39060852/why-does-the-addition-of-two-shorts-return-an-int
12883|    531|        address[piece_index] =
12884|    531|            uint16_t(address[piece_index] * 0x100 + *ipv4_piece);
12885|       |
12886|       |        // Increase numbersSeen by 1.
12887|    531|        numbers_seen++;
12888|       |
12889|       |        // If numbersSeen is 2 or 4, then increase pieceIndex by 1.
12890|    531|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (12890:13): [True: 157, False: 374]
  |  Branch (12890:34): [True: 69, False: 305]
  ------------------
12891|    226|          piece_index++;
12892|    226|        }
12893|    531|      }
12894|       |
12895|       |      // If numbersSeen is not 4, validation error, return failure.
12896|     79|      if (numbers_seen != 4) {
  ------------------
  |  Branch (12896:11): [True: 23, False: 56]
  ------------------
12897|     23|        return is_valid = false;
12898|     23|      }
12899|       |
12900|       |      // Break.
12901|     56|      break;
12902|     79|    }
12903|       |    // Otherwise, if c is U+003A (:):
12904|  1.99k|    else if ((pointer != input.end()) && (*pointer == ':')) {
  ------------------
  |  Branch (12904:14): [True: 1.62k, False: 369]
  |  Branch (12904:42): [True: 1.44k, False: 182]
  ------------------
12905|       |      // Increase pointer by 1.
12906|  1.44k|      pointer++;
12907|       |
12908|       |      // If c is the EOF code point, validation error, return failure.
12909|  1.44k|      if (pointer == input.end()) {
  ------------------
  |  Branch (12909:11): [True: 17, False: 1.42k]
  ------------------
12910|     17|        ada_log(
12911|     17|            "parse_ipv6 If c is the EOF code point, validation error, return "
12912|     17|            "failure");
12913|     17|        return is_valid = false;
12914|     17|      }
12915|  1.44k|    }
12916|       |    // Otherwise, if c is not the EOF code point, validation error, return
12917|       |    // failure.
12918|    551|    else if (pointer != input.end()) {
  ------------------
  |  Branch (12918:14): [True: 182, False: 369]
  ------------------
12919|    182|      ada_log(
12920|    182|          "parse_ipv6 Otherwise, if c is not the EOF code point, validation "
12921|    182|          "error, return failure");
12922|    182|      return is_valid = false;
12923|    182|    }
12924|       |
12925|       |    // Set address[pieceIndex] to value.
12926|  1.79k|    address[piece_index] = value;
12927|       |
12928|       |    // Increase pieceIndex by 1.
12929|  1.79k|    piece_index++;
12930|  1.79k|  }
12931|       |
12932|       |  // If compress is non-null, then:
12933|    615|  if (compress.has_value()) {
  ------------------
  |  Branch (12933:7): [True: 493, False: 122]
  ------------------
12934|       |    // Let swaps be pieceIndex - compress.
12935|    493|    int swaps = piece_index - *compress;
12936|       |
12937|       |    // Set pieceIndex to 7.
12938|    493|    piece_index = 7;
12939|       |
12940|       |    // While pieceIndex is not 0 and swaps is greater than 0,
12941|       |    // swap address[pieceIndex] with address[compress + swaps - 1], and then
12942|       |    // decrease both pieceIndex and swaps by 1.
12943|  1.25k|    while (piece_index != 0 && swaps > 0) {
  ------------------
  |  Branch (12943:12): [True: 1.21k, False: 39]
  |  Branch (12943:32): [True: 764, False: 454]
  ------------------
12944|    764|      std::swap(address[piece_index], address[*compress + swaps - 1]);
12945|    764|      piece_index--;
12946|    764|      swaps--;
12947|    764|    }
12948|    493|  }
12949|       |  // Otherwise, if compress is null and pieceIndex is not 8, validation error,
12950|       |  // return failure.
12951|    122|  else if (piece_index != 8) {
  ------------------
  |  Branch (12951:12): [True: 75, False: 47]
  ------------------
12952|     75|    ada_log(
12953|     75|        "parse_ipv6 if compress is null and pieceIndex is not 8, validation "
12954|     75|        "error, return failure");
12955|     75|    return is_valid = false;
12956|     75|  }
12957|    540|  host = ada::serializers::ipv6(address);
12958|    540|  ada_log("parse_ipv6 ", *host);
12959|    540|  host_type = IPV6;
12960|    540|  return true;
12961|    615|}
_ZNK3ada3url9to_stringEv:
13188|  23.1k|[[nodiscard]] std::string url::to_string() const {
13189|  23.1k|  if (!is_valid) {
  ------------------
  |  Branch (13189:7): [True: 3.49k, False: 19.6k]
  ------------------
13190|  3.49k|    return "null";
13191|  3.49k|  }
13192|  19.6k|  std::string answer;
13193|  19.6k|  auto back = std::back_insert_iterator(answer);
13194|  19.6k|  answer.append("{\n");
13195|  19.6k|  answer.append("\t\"protocol\":\"");
13196|  19.6k|  helpers::encode_json(get_protocol(), back);
13197|  19.6k|  answer.append("\",\n");
13198|  19.6k|  if (has_credentials()) {
  ------------------
  |  Branch (13198:7): [True: 11.4k, False: 8.19k]
  ------------------
13199|  11.4k|    answer.append("\t\"username\":\"");
13200|  11.4k|    helpers::encode_json(username, back);
13201|  11.4k|    answer.append("\",\n");
13202|  11.4k|    answer.append("\t\"password\":\"");
13203|  11.4k|    helpers::encode_json(password, back);
13204|  11.4k|    answer.append("\",\n");
13205|  11.4k|  }
13206|  19.6k|  if (host.has_value()) {
  ------------------
  |  Branch (13206:7): [True: 17.2k, False: 2.42k]
  ------------------
13207|  17.2k|    answer.append("\t\"host\":\"");
13208|  17.2k|    helpers::encode_json(host.value(), back);
13209|  17.2k|    answer.append("\",\n");
13210|  17.2k|  }
13211|  19.6k|  if (port.has_value()) {
  ------------------
  |  Branch (13211:7): [True: 2.06k, False: 17.5k]
  ------------------
13212|  2.06k|    answer.append("\t\"port\":\"");
13213|  2.06k|    answer.append(std::to_string(port.value()));
13214|  2.06k|    answer.append("\",\n");
13215|  2.06k|  }
13216|  19.6k|  answer.append("\t\"path\":\"");
13217|  19.6k|  helpers::encode_json(path, back);
13218|  19.6k|  answer.append("\",\n");
13219|  19.6k|  answer.append("\t\"opaque path\":");
13220|  19.6k|  answer.append((has_opaque_path ? "true" : "false"));
  ------------------
  |  Branch (13220:18): [True: 1.75k, False: 17.8k]
  ------------------
13221|  19.6k|  if (has_search()) {
  ------------------
  |  Branch (13221:7): [True: 11.3k, False: 8.26k]
  ------------------
13222|  11.3k|    answer.append(",\n");
13223|  11.3k|    answer.append("\t\"query\":\"");
13224|       |    // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
13225|  11.3k|    helpers::encode_json(query.value(), back);
13226|  11.3k|    answer.append("\"");
13227|  11.3k|  }
13228|  19.6k|  if (hash.has_value()) {
  ------------------
  |  Branch (13228:7): [True: 11.3k, False: 8.27k]
  ------------------
13229|  11.3k|    answer.append(",\n");
13230|  11.3k|    answer.append("\t\"hash\":\"");
13231|  11.3k|    helpers::encode_json(hash.value(), back);
13232|  11.3k|    answer.append("\"");
13233|  11.3k|  }
13234|  19.6k|  answer.append("\n}");
13235|  19.6k|  return answer;
13236|  23.1k|}
_ZNK3ada3url16has_valid_domainEv:
13238|  23.1k|[[nodiscard]] bool url::has_valid_domain() const noexcept {
13239|  23.1k|  if (!host.has_value()) {
  ------------------
  |  Branch (13239:7): [True: 2.42k, False: 20.6k]
  ------------------
13240|  2.42k|    return false;
13241|  2.42k|  }
13242|  20.6k|  return checkers::verify_dns_length(*host);
13243|  23.1k|}
_ZNK3ada3url10get_originEv:
13245|  30.8k|[[nodiscard]] std::string url::get_origin() const {
13246|  30.8k|  if (is_special()) {
  ------------------
  |  Branch (13246:7): [True: 24.9k, False: 5.85k]
  ------------------
13247|       |    // Return a new opaque origin.
13248|  24.9k|    if (type == scheme::FILE) {
  ------------------
  |  Branch (13248:9): [True: 4.06k, False: 20.9k]
  ------------------
13249|  4.06k|      return "null";
13250|  4.06k|    }
13251|  20.9k|    return ada::helpers::concat(get_protocol(), "//", get_host());
13252|  24.9k|  }
13253|       |
13254|  5.85k|  if (non_special_scheme == "blob") {
  ------------------
  |  Branch (13254:7): [True: 1.51k, False: 4.34k]
  ------------------
13255|  1.51k|    if (!path.empty()) {
  ------------------
  |  Branch (13255:9): [True: 1.48k, False: 24]
  ------------------
13256|  1.48k|      auto result = ada::parse<ada::url>(path);
13257|  1.48k|      if (result &&
  ------------------
  |  Branch (13257:11): [True: 380, False: 1.10k]
  ------------------
13258|    380|          (result->type == scheme::HTTP || result->type == scheme::HTTPS)) {
  ------------------
  |  Branch (13258:12): [True: 36, False: 344]
  |  Branch (13258:44): [True: 10, False: 334]
  ------------------
13259|       |        // If pathURL's scheme is not "http" and not "https", then return a
13260|       |        // new opaque origin.
13261|     46|        return ada::helpers::concat(result->get_protocol(), "//",
13262|     46|                                    result->get_host());
13263|     46|      }
13264|  1.48k|    }
13265|  1.51k|  }
13266|       |
13267|       |  // Return a new opaque origin.
13268|  5.81k|  return "null";
13269|  5.85k|}
_ZNK3ada3url12get_protocolEv:
13271|   181k|[[nodiscard]] std::string url::get_protocol() const {
13272|   181k|  if (is_special()) {
  ------------------
  |  Branch (13272:7): [True: 150k, False: 31.3k]
  ------------------
13273|   150k|    return helpers::concat(ada::scheme::details::is_special_list[type], ":");
13274|   150k|  }
13275|       |  // We only move the 'scheme' if it is non-special.
13276|  31.3k|  return helpers::concat(non_special_scheme, ":");
13277|   181k|}
_ZNK3ada3url8get_hostEv:
13279|  48.7k|[[nodiscard]] std::string url::get_host() const {
13280|       |  // If url's host is null, then return the empty string.
13281|       |  // If url's port is null, return url's host, serialized.
13282|       |  // Return url's host, serialized, followed by U+003A (:) and url's port,
13283|       |  // serialized.
13284|  48.7k|  if (!host.has_value()) {
  ------------------
  |  Branch (13284:7): [True: 3.92k, False: 44.8k]
  ------------------
13285|  3.92k|    return "";
13286|  3.92k|  }
13287|  44.8k|  if (port.has_value()) {
  ------------------
  |  Branch (13287:7): [True: 4.60k, False: 40.2k]
  ------------------
13288|  4.60k|    return host.value() + ":" + get_port();
13289|  4.60k|  }
13290|  40.2k|  return host.value();
13291|  44.8k|}
_ZNK3ada3url12get_hostnameEv:
13293|  27.7k|[[nodiscard]] std::string url::get_hostname() const {
13294|  27.7k|  return host.value_or("");
13295|  27.7k|}
_ZNK3ada3url10get_searchEv:
13297|  57.4k|[[nodiscard]] std::string url::get_search() const {
13298|       |  // If this's URL's query is either null or the empty string, then return the
13299|       |  // empty string. Return U+003F (?), followed by this's URL's query.
13300|  57.4k|  return (!query.has_value() || (query->empty())) ? "" : "?" + query.value();
  ------------------
  |  Branch (13300:11): [True: 12.5k, False: 44.8k]
  |  Branch (13300:33): [True: 1.35k, False: 43.5k]
  ------------------
13301|  57.4k|}
_ZNK3ada3url12get_usernameEv:
13303|  27.7k|[[nodiscard]] const std::string& url::get_username() const noexcept {
13304|  27.7k|  return username;
13305|  27.7k|}
_ZNK3ada3url12get_passwordEv:
13307|  57.3k|[[nodiscard]] const std::string& url::get_password() const noexcept {
13308|  57.3k|  return password;
13309|  57.3k|}
_ZNK3ada3url8get_portEv:
13311|  64.4k|[[nodiscard]] std::string url::get_port() const {
13312|  64.4k|  return port.has_value() ? std::to_string(port.value()) : "";
  ------------------
  |  Branch (13312:10): [True: 38.9k, False: 25.4k]
  ------------------
13313|  64.4k|}
_ZNK3ada3url8get_hashEv:
13315|  27.7k|[[nodiscard]] std::string url::get_hash() const {
13316|       |  // If this's URL's fragment is either null or the empty string, then return
13317|       |  // the empty string. Return U+0023 (#), followed by this's URL's fragment.
13318|  27.7k|  return (!hash.has_value() || (hash->empty())) ? "" : "#" + hash.value();
  ------------------
  |  Branch (13318:11): [True: 12.6k, False: 15.1k]
  |  Branch (13318:32): [True: 651, False: 14.4k]
  ------------------
13319|  27.7k|}
_ZN3ada3url8set_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13445|  18.9k|bool url::set_host(const std::string_view input) {
13446|  18.9k|  return set_host_or_hostname<false>(input);
13447|  18.9k|}
_ZN3ada3url12set_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13449|  18.1k|bool url::set_hostname(const std::string_view input) {
13450|  18.1k|  return set_host_or_hostname<true>(input);
13451|  18.1k|}
_ZN3ada3url12set_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13453|  16.9k|bool url::set_username(const std::string_view input) {
13454|  16.9k|  if (cannot_have_credentials_or_port()) {
  ------------------
  |  Branch (13454:7): [True: 935, False: 16.0k]
  ------------------
13455|    935|    return false;
13456|    935|  }
13457|  16.0k|  auto previous_username = std::move(username);
13458|  16.0k|  username = ada::unicode::percent_encode(
13459|  16.0k|      input, character_sets::USERINFO_PERCENT_ENCODE);
13460|  16.0k|  if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13460:7): [True: 0, False: 16.0k]
  ------------------
13461|      0|    username = std::move(previous_username);
13462|      0|    return false;
13463|      0|  }
13464|  16.0k|  return true;
13465|  16.0k|}
_ZN3ada3url12set_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13467|  17.0k|bool url::set_password(const std::string_view input) {
13468|  17.0k|  if (cannot_have_credentials_or_port()) {
  ------------------
  |  Branch (13468:7): [True: 931, False: 16.1k]
  ------------------
13469|    931|    return false;
13470|    931|  }
13471|  16.1k|  auto previous_password = std::move(password);
13472|  16.1k|  password = ada::unicode::percent_encode(
13473|  16.1k|      input, character_sets::USERINFO_PERCENT_ENCODE);
13474|  16.1k|  if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13474:7): [True: 0, False: 16.1k]
  ------------------
13475|      0|    password = std::move(previous_password);
13476|      0|    return false;
13477|      0|  }
13478|  16.1k|  return true;
13479|  16.1k|}
_ZN3ada3url8set_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13481|  22.0k|bool url::set_port(const std::string_view input) {
13482|  22.0k|  if (cannot_have_credentials_or_port()) {
  ------------------
  |  Branch (13482:7): [True: 928, False: 21.1k]
  ------------------
13483|    928|    return false;
13484|    928|  }
13485|       |
13486|  21.1k|  if (input.empty()) {
  ------------------
  |  Branch (13486:7): [True: 2.27k, False: 18.8k]
  ------------------
13487|  2.27k|    port = std::nullopt;
13488|  2.27k|    return true;
13489|  2.27k|  }
13490|       |
13491|  18.8k|  std::string trimmed(input);
13492|  18.8k|  helpers::remove_ascii_tab_or_newline(trimmed);
13493|       |
13494|  18.8k|  if (trimmed.empty()) {
  ------------------
  |  Branch (13494:7): [True: 118, False: 18.7k]
  ------------------
13495|    118|    return true;
13496|    118|  }
13497|       |
13498|       |  // Input should not start with a non-digit character.
13499|  18.7k|  if (!ada::unicode::is_ascii_digit(trimmed.front())) {
  ------------------
  |  Branch (13499:7): [True: 15.7k, False: 2.99k]
  ------------------
13500|  15.7k|    return false;
13501|  15.7k|  }
13502|       |
13503|       |  // Find the first non-digit character to determine the length of digits
13504|  2.99k|  auto first_non_digit =
13505|  2.99k|      std::ranges::find_if_not(trimmed, ada::unicode::is_ascii_digit);
13506|  2.99k|  std::string_view digits_to_parse =
13507|  2.99k|      std::string_view(trimmed.data(), first_non_digit - trimmed.begin());
13508|       |
13509|       |  // Revert changes if parse_port fails.
13510|  2.99k|  std::optional<uint16_t> previous_port = port;
13511|  2.99k|  parse_port(digits_to_parse);
13512|  2.99k|  if (is_valid) {
  ------------------
  |  Branch (13512:7): [True: 2.32k, False: 664]
  ------------------
13513|  2.32k|    if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13513:9): [True: 0, False: 2.32k]
  ------------------
13514|      0|      port = std::move(previous_port);
13515|      0|      return false;
13516|      0|    }
13517|  2.32k|    return true;
13518|  2.32k|  }
13519|    664|  port = std::move(previous_port);
13520|    664|  is_valid = true;
13521|    664|  return false;
13522|  2.99k|}
_ZN3ada3url8set_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13524|  15.8k|void url::set_hash(const std::string_view input) {
13525|  15.8k|  if (input.empty()) {
  ------------------
  |  Branch (13525:7): [True: 1.36k, False: 14.4k]
  ------------------
13526|  1.36k|    hash = std::nullopt;
13527|  1.36k|    helpers::strip_trailing_spaces_from_opaque_path(*this);
13528|  1.36k|    return;
13529|  1.36k|  }
13530|       |
13531|  14.4k|  std::string new_value;
13532|  14.4k|  new_value = input[0] == '#' ? input.substr(1) : input;
  ------------------
  |  Branch (13532:15): [True: 210, False: 14.2k]
  ------------------
13533|  14.4k|  helpers::remove_ascii_tab_or_newline(new_value);
13534|  14.4k|  auto previous_hash = std::move(hash);
13535|  14.4k|  hash = unicode::percent_encode(new_value,
13536|  14.4k|                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
13537|  14.4k|  if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13537:7): [True: 0, False: 14.4k]
  ------------------
13538|      0|    hash = std::move(previous_hash);
13539|      0|  }
13540|  14.4k|}
_ZN3ada3url10set_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13542|  16.0k|void url::set_search(const std::string_view input) {
13543|  16.0k|  if (input.empty()) {
  ------------------
  |  Branch (13543:7): [True: 1.46k, False: 14.5k]
  ------------------
13544|  1.46k|    query = std::nullopt;
13545|  1.46k|    helpers::strip_trailing_spaces_from_opaque_path(*this);
13546|  1.46k|    return;
13547|  1.46k|  }
13548|       |
13549|  14.5k|  std::string new_value;
13550|  14.5k|  new_value = input[0] == '?' ? input.substr(1) : input;
  ------------------
  |  Branch (13550:15): [True: 106, False: 14.4k]
  ------------------
13551|  14.5k|  helpers::remove_ascii_tab_or_newline(new_value);
13552|       |
13553|  14.5k|  auto query_percent_encode_set =
13554|  14.5k|      is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13554:7): [True: 14.5k, False: 0]
  ------------------
13555|  14.5k|                   : ada::character_sets::QUERY_PERCENT_ENCODE;
13556|       |
13557|  14.5k|  auto previous_query = std::move(query);
13558|  14.5k|  query = ada::unicode::percent_encode(new_value, query_percent_encode_set);
13559|  14.5k|  if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13559:7): [True: 0, False: 14.5k]
  ------------------
13560|      0|    query = std::move(previous_query);
13561|      0|  }
13562|  14.5k|}
_ZN3ada3url12set_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13564|  17.2k|bool url::set_pathname(const std::string_view input) {
13565|  17.2k|  if (has_opaque_path) {
  ------------------
  |  Branch (13565:7): [True: 0, False: 17.2k]
  ------------------
13566|      0|    return false;
13567|      0|  }
13568|  17.2k|  auto previous_path = std::move(path);
13569|  17.2k|  path.clear();
13570|  17.2k|  parse_path(input);
13571|  17.2k|  if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13571:7): [True: 0, False: 17.2k]
  ------------------
13572|      0|    path = std::move(previous_path);
13573|      0|    return false;
13574|      0|  }
13575|  17.2k|  return true;
13576|  17.2k|}
_ZN3ada3url12set_protocolENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13578|  37.0k|bool url::set_protocol(const std::string_view input) {
13579|  37.0k|  std::string view(input);
13580|  37.0k|  helpers::remove_ascii_tab_or_newline(view);
13581|  37.0k|  if (view.empty()) {
  ------------------
  |  Branch (13581:7): [True: 19.9k, False: 17.0k]
  ------------------
13582|  19.9k|    return true;
13583|  19.9k|  }
13584|       |
13585|       |  // Schemes should start with alpha values.
13586|  17.0k|  if (!checkers::is_alpha(view[0])) {
  ------------------
  |  Branch (13586:7): [True: 7.71k, False: 9.37k]
  ------------------
13587|  7.71k|    return false;
13588|  7.71k|  }
13589|       |
13590|  9.37k|  view.append(":");
13591|       |
13592|  9.37k|  std::string::iterator pointer =
13593|  9.37k|      std::ranges::find_if_not(view, unicode::is_alnum_plus);
13594|       |
13595|  9.37k|  if (pointer != view.end() && *pointer == ':') {
  ------------------
  |  Branch (13595:7): [True: 9.37k, False: 0]
  |  Branch (13595:7): [True: 8.32k, False: 1.05k]
  |  Branch (13595:32): [True: 8.32k, False: 1.05k]
  ------------------
13596|  8.32k|    url saved_url(*this);
13597|  8.32k|    bool result = parse_scheme<true>(
13598|  8.32k|        std::string_view(view.data(), pointer - view.begin()));
13599|  8.32k|    if (result && get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13599:9): [True: 5.09k, False: 3.22k]
  |  Branch (13599:19): [True: 0, False: 5.09k]
  ------------------
13600|      0|      *this = std::move(saved_url);
13601|      0|      return false;
13602|      0|    }
13603|  8.32k|    return result;
13604|  8.32k|  }
13605|  1.05k|  return false;
13606|  9.37k|}
_ZN3ada3url8set_hrefENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13608|  4.63k|bool url::set_href(const std::string_view input) {
13609|  4.63k|  ada::result<ada::url> out = ada::parse<ada::url>(input);
13610|       |
13611|  4.63k|  if (out) {
  ------------------
  |  Branch (13611:7): [True: 4.63k, False: 0]
  ------------------
13612|       |    // The parser enforces get_max_input_length() on both the input and the
13613|       |    // normalized result. This is a defense-in-depth check.
13614|  4.63k|    if (out->get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13614:9): [True: 0, False: 4.63k]
  ------------------
13615|      0|      return false;
13616|      0|    }
13617|  4.63k|    *this = *out;
13618|  4.63k|  }
13619|       |
13620|  4.63k|  return out.has_value();
13621|  4.63k|}
_ZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
13635|  74.0k|                           const result_type* base_url) {
13636|       |  // We can specialize the implementation per type.
13637|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
13638|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
13639|       |  // something else } is free (at runtime). This means that ada::url_aggregator
13640|       |  // and ada::url **do not have to support the exact same API**.
13641|  74.0k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
13642|  74.0k|  constexpr bool result_type_is_ada_url_aggregator =
13643|  74.0k|      std::is_same_v<url_aggregator, result_type>;
13644|  74.0k|  static_assert(result_type_is_ada_url ||
13645|  74.0k|                result_type_is_ada_url_aggregator);  // We don't support
13646|       |                                                     // anything else for now.
13647|       |
13648|  74.0k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
13649|  74.0k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
13650|  74.0k|          ")");
13651|       |
13652|  74.0k|  state state = state::SCHEME_START;
13653|  74.0k|  result_type url{};
13654|       |
13655|  74.0k|  const uint32_t max_input_length = ada::get_max_input_length();
13656|       |
13657|       |  // We refuse to parse URL strings that exceed the maximum input length.
13658|       |  // By default, this is 4GB but can be configured via
13659|       |  // ada::set_max_input_length().
13660|  74.0k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (13660:7): [True: 0, False: 74.0k]
  ------------------
13661|      0|    url.is_valid = false;
13662|      0|  }
13663|       |  // Going forward, user_input.size() is in [0,
13664|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
13665|       |  // base, or the optional_url was invalid, we must return.
13666|  74.0k|  if (base_url != nullptr) {
  ------------------
  |  Branch (13666:7): [True: 6.39k, False: 67.6k]
  ------------------
13667|  6.39k|    url.is_valid &= base_url->is_valid;
13668|  6.39k|  }
13669|  74.0k|  if (!url.is_valid) {
  ------------------
  |  Branch (13669:7): [True: 0, False: 74.0k]
  ------------------
13670|      0|    return url;
13671|      0|  }
13672|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
13673|       |    // Most of the time, we just need user_input.size().
13674|       |    // In some instances, we may need a bit more.
13675|       |    ///////////////////////////
13676|       |    // This is *very* important. This line should *not* be removed
13677|       |    // hastily. There are principled reasons why reserve is important
13678|       |    // for performance. If you have a benchmark with small inputs,
13679|       |    // it may not matter, but in other instances, it could.
13680|       |    ////
13681|       |    // This rounds up to the next power of two.
13682|       |    // We know that user_input.size() is in [0,
13683|       |    // std::numeric_limits<uint32_t>::max).
13684|       |    uint32_t reserve_capacity =
13685|       |        (0xFFFFFFFF >>
13686|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
13687|       |        1;
13688|       |    url.reserve(reserve_capacity);
13689|       |  }
13690|  74.0k|  std::string tmp_buffer;
13691|  74.0k|  std::string_view url_data;
13692|  74.0k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (13692:7): [True: 760, False: 73.2k]
  ------------------
13693|    760|    tmp_buffer = user_input;
13694|       |    // Optimization opportunity: Instead of copying and then pruning, we could
13695|       |    // just directly build the string from user_input.
13696|    760|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
13697|    760|    url_data = tmp_buffer;
13698|  73.2k|  } else [[likely]] {
13699|  73.2k|    url_data = user_input;
13700|  73.2k|  }
13701|       |
13702|       |  // Leading and trailing control characters are uncommon and easy to deal with
13703|       |  // (no performance concern).
13704|  74.0k|  helpers::trim_c0_whitespace(url_data);
13705|       |
13706|       |  // Optimization opportunity. Most websites do not have fragment.
13707|  74.0k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
13708|       |  // We add it last so that an implementation like ada::url_aggregator
13709|       |  // can append it last to its internal buffer, thus improving performance.
13710|       |
13711|       |  // Here url_data no longer has its fragment.
13712|       |  // We are going to access the data from url_data (it is immutable).
13713|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
13714|       |  // The input_position variable should range from 0 to input_size.
13715|       |  // It is illegal to access url_data at input_size.
13716|  74.0k|  size_t input_position = 0;
13717|  74.0k|  const size_t input_size = url_data.size();
13718|       |  // Keep running the following state machine by switching on state.
13719|       |  // If after a run pointer points to the EOF code point, go to the next step.
13720|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
13721|       |  // We never decrement input_position.
13722|   378k|  while (input_position <= input_size) {
  ------------------
  |  Branch (13722:10): [True: 366k, False: 11.5k]
  ------------------
13723|   366k|    ada_log("In parsing at ", input_position, " out of ", input_size,
13724|   366k|            " in state ", ada::to_string(state));
13725|   366k|    switch (state) {
13726|  74.0k|      case state::SCHEME_START: {
  ------------------
  |  Branch (13726:7): [True: 74.0k, False: 292k]
  ------------------
13727|  74.0k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13728|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13729|       |        // state to scheme state.
13730|  74.0k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13730:13): [True: 61.5k, False: 12.4k]
  ------------------
13731|  61.5k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13731:13): [True: 53.2k, False: 8.27k]
  ------------------
13732|  53.2k|          state = state::SCHEME;
13733|  53.2k|          input_position++;
13734|  53.2k|        } else {
13735|       |          // Otherwise, if state override is not given, set state to no scheme
13736|       |          // state and decrease pointer by 1.
13737|  20.7k|          state = state::NO_SCHEME;
13738|  20.7k|        }
13739|  74.0k|        break;
13740|      0|      }
13741|  53.2k|      case state::SCHEME: {
  ------------------
  |  Branch (13741:7): [True: 53.2k, False: 313k]
  ------------------
13742|  53.2k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13743|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13744|       |        // append c, lowercased, to buffer.
13745|   232k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13745:16): [True: 230k, False: 1.45k]
  ------------------
13746|   230k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13746:16): [True: 179k, False: 51.8k]
  ------------------
13747|   179k|          input_position++;
13748|   179k|        }
13749|       |        // Otherwise, if c is U+003A (:), then:
13750|  53.2k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13750:13): [True: 51.8k, False: 1.45k]
  ------------------
13751|  51.8k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13751:13): [True: 50.6k, False: 1.20k]
  ------------------
13752|  50.6k|          ada_log("SCHEME the scheme should be ",
13753|  50.6k|                  url_data.substr(0, input_position));
13754|  50.6k|          if constexpr (result_type_is_ada_url) {
13755|  50.6k|            if (!url.parse_scheme(url_data.substr(0, input_position))) {
  ------------------
  |  Branch (13755:17): [True: 0, False: 50.6k]
  ------------------
13756|      0|              return url;
13757|      0|            }
13758|       |          } else {
13759|       |            // we pass the colon along instead of painfully adding it back.
13760|       |            if (!url.parse_scheme_with_colon(
13761|       |                    url_data.substr(0, input_position + 1))) {
13762|       |              return url;
13763|       |            }
13764|       |          }
13765|  50.6k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13766|       |
13767|       |          // If url's scheme is "file", then:
13768|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13769|  50.6k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13769:15): [True: 3.04k, False: 47.5k]
  ------------------
13770|       |            // Set state to file state.
13771|  3.04k|            state = state::FILE;
13772|  3.04k|          }
13773|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13774|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13775|       |          // != nullptr is false.
13776|  47.5k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13776:20): [True: 39.5k, False: 8.08k]
  |  Branch (13776:40): [True: 2.49k, False: 37.0k]
  ------------------
13777|  2.49k|                   base_url->type == url.type) {
  ------------------
  |  Branch (13777:20): [True: 111, False: 2.38k]
  ------------------
13778|       |            // Set state to special relative or authority state.
13779|    111|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13780|    111|          }
13781|       |          // Otherwise, if url is special, set state to special authority
13782|       |          // slashes state.
13783|  47.4k|          else if (url.is_special()) {
  ------------------
  |  Branch (13783:20): [True: 39.3k, False: 8.08k]
  ------------------
13784|  39.3k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13785|  39.3k|          }
13786|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13787|       |          // path or authority state and increase pointer by 1.
13788|  8.08k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13788:20): [True: 5.51k, False: 2.56k]
  ------------------
13789|  5.51k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13789:20): [True: 2.75k, False: 2.75k]
  ------------------
13790|  2.75k|            state = state::PATH_OR_AUTHORITY;
13791|  2.75k|            input_position++;
13792|  2.75k|          }
13793|       |          // Otherwise, set url's path to the empty string and set state to
13794|       |          // opaque path state.
13795|  5.32k|          else {
13796|  5.32k|            state = state::OPAQUE_PATH;
13797|  5.32k|          }
13798|  50.6k|        }
13799|       |        // Otherwise, if state override is not given, set buffer to the empty
13800|       |        // string, state to no scheme state, and start over (from the first code
13801|       |        // point in input).
13802|  2.65k|        else {
13803|  2.65k|          state = state::NO_SCHEME;
13804|  2.65k|          input_position = 0;
13805|  2.65k|          break;
13806|  2.65k|        }
13807|  50.6k|        input_position++;
13808|  50.6k|        break;
13809|  53.2k|      }
13810|  23.4k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13810:7): [True: 23.4k, False: 343k]
  ------------------
13811|  23.4k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13812|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13813|       |        // validation error, return failure.
13814|  23.4k|        if (base_url == nullptr ||
  ------------------
  |  Branch (13814:13): [True: 21.3k, False: 2.11k]
  ------------------
13815|  21.6k|            (base_url->has_opaque_path && !fragment.has_value())) {
  ------------------
  |  Branch (13815:14): [True: 671, False: 1.44k]
  |  Branch (13815:43): [True: 362, False: 309]
  ------------------
13816|  21.6k|          ada_log("NO_SCHEME validation error");
13817|  21.6k|          url.is_valid = false;
13818|  21.6k|          return url;
13819|  21.6k|        }
13820|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13821|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13822|       |        // query to base's query, and set state to fragment state.
13823|  1.75k|        else if (base_url->has_opaque_path && fragment.has_value() &&
  ------------------
  |  Branch (13823:18): [True: 309, False: 1.44k]
  |  Branch (13823:47): [True: 309, False: 0]
  ------------------
13824|    309|                 input_position == input_size) {
  ------------------
  |  Branch (13824:18): [True: 149, False: 160]
  ------------------
13825|    149|          ada_log("NO_SCHEME opaque base with fragment");
13826|    149|          url.copy_scheme(*base_url);
13827|    149|          url.has_opaque_path = base_url->has_opaque_path;
13828|       |
13829|    149|          if constexpr (result_type_is_ada_url) {
13830|    149|            url.path = base_url->path;
13831|    149|            url.query = base_url->query;
13832|       |          } else {
13833|       |            url.update_base_pathname(base_url->get_pathname());
13834|       |            if (base_url->has_search()) {
13835|       |              // get_search() returns "" for an empty query string (URL ends
13836|       |              // with '?'). update_base_search("") would incorrectly clear the
13837|       |              // query, so pass "?" to preserve the empty query distinction.
13838|       |              auto s = base_url->get_search();
13839|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13840|       |            }
13841|       |          }
13842|    149|          url.update_unencoded_base_hash(*fragment);
13843|    149|          return url;
13844|    149|        }
13845|       |        // Otherwise, if base's scheme is not "file", set state to relative
13846|       |        // state and decrease pointer by 1.
13847|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13848|  1.60k|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13848:18): [True: 962, False: 639]
  ------------------
13849|    962|          ada_log("NO_SCHEME non-file relative path");
13850|    962|          state = state::RELATIVE_SCHEME;
13851|    962|        }
13852|       |        // Otherwise, set state to file state and decrease pointer by 1.
13853|    639|        else {
13854|    639|          ada_log("NO_SCHEME file base type");
13855|    639|          state = state::FILE;
13856|    639|        }
13857|  1.60k|        break;
13858|  23.4k|      }
13859|  40.9k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13859:7): [True: 40.9k, False: 325k]
  ------------------
13860|  40.9k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13861|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13862|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13863|       |        // worry about AUTHORITY.
13864|       |        // TODO: Instead of just collecting a bool, collect the location of the
13865|       |        // '@' and do something useful with it.
13866|       |        // TODO: We could do various processing early on, using a single pass
13867|       |        // over the string to collect information about it, e.g., telling us
13868|       |        // whether there is a @ and if so, where (or how many).
13869|       |
13870|       |        // Check if url data contains an @.
13871|  40.9k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13871:13): [True: 23.5k, False: 17.3k]
  ------------------
13872|  23.5k|          state = state::HOST;
13873|  23.5k|          break;
13874|  23.5k|        }
13875|  17.3k|        bool at_sign_seen{false};
13876|  17.3k|        bool password_token_seen{false};
13877|       |        /**
13878|       |         * We expect something of the sort...
13879|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13880|       |         * --------^
13881|       |         */
13882|  46.5k|        do {
13883|  46.5k|          std::string_view view = url_data.substr(input_position);
13884|       |          // The delimiters are @, /, ? \\.
13885|  46.5k|          size_t location =
13886|  46.5k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13886:15): [True: 44.5k, False: 2.03k]
  ------------------
13887|  46.5k|                               : helpers::find_authority_delimiter(view);
13888|  46.5k|          std::string_view authority_view = view.substr(0, location);
13889|  46.5k|          size_t end_of_authority = input_position + authority_view.size();
13890|       |          // If c is U+0040 (@), then:
13891|  46.5k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13891:15): [True: 45.1k, False: 1.46k]
  ------------------
13892|  45.1k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13892:15): [True: 29.2k, False: 15.8k]
  ------------------
13893|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13894|  29.2k|            if (at_sign_seen) {
  ------------------
  |  Branch (13894:17): [True: 12.0k, False: 17.1k]
  ------------------
13895|  12.0k|              if (password_token_seen) {
  ------------------
  |  Branch (13895:19): [True: 4.91k, False: 7.15k]
  ------------------
13896|  4.91k|                if constexpr (result_type_is_ada_url) {
13897|  4.91k|                  url.password += "%40";
13898|       |                } else {
13899|       |                  url.append_base_password("%40");
13900|       |                }
13901|  7.15k|              } else {
13902|  7.15k|                if constexpr (result_type_is_ada_url) {
13903|  7.15k|                  url.username += "%40";
13904|       |                } else {
13905|       |                  url.append_base_username("%40");
13906|       |                }
13907|  7.15k|              }
13908|  12.0k|            }
13909|       |
13910|  29.2k|            at_sign_seen = true;
13911|       |
13912|  29.2k|            if (!password_token_seen) {
  ------------------
  |  Branch (13912:17): [True: 24.3k, False: 4.91k]
  ------------------
13913|  24.3k|              size_t password_token_location = authority_view.find(':');
13914|  24.3k|              password_token_seen =
13915|  24.3k|                  password_token_location != std::string_view::npos;
13916|       |
13917|  24.3k|              if constexpr (store_values) {
13918|  24.3k|                if (!password_token_seen) {
  ------------------
  |  Branch (13918:21): [True: 8.14k, False: 16.2k]
  ------------------
13919|  8.14k|                  if constexpr (result_type_is_ada_url) {
13920|  8.14k|                    url.username += unicode::percent_encode(
13921|  8.14k|                        authority_view,
13922|  8.14k|                        character_sets::USERINFO_PERCENT_ENCODE);
13923|       |                  } else {
13924|       |                    url.append_base_username(unicode::percent_encode(
13925|       |                        authority_view,
13926|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13927|       |                  }
13928|  16.2k|                } else {
13929|  16.2k|                  if constexpr (result_type_is_ada_url) {
13930|  16.2k|                    url.username += unicode::percent_encode(
13931|  16.2k|                        authority_view.substr(0, password_token_location),
13932|  16.2k|                        character_sets::USERINFO_PERCENT_ENCODE);
13933|  16.2k|                    url.password += unicode::percent_encode(
13934|  16.2k|                        authority_view.substr(password_token_location + 1),
13935|  16.2k|                        character_sets::USERINFO_PERCENT_ENCODE);
13936|       |                  } else {
13937|       |                    url.append_base_username(unicode::percent_encode(
13938|       |                        authority_view.substr(0, password_token_location),
13939|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13940|       |                    url.append_base_password(unicode::percent_encode(
13941|       |                        authority_view.substr(password_token_location + 1),
13942|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13943|       |                  }
13944|  16.2k|                }
13945|  24.3k|              }
13946|  24.3k|            } else if constexpr (store_values) {
13947|  4.91k|              if constexpr (result_type_is_ada_url) {
13948|  4.91k|                url.password += unicode::percent_encode(
13949|  4.91k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13950|       |              } else {
13951|       |                url.append_base_password(unicode::percent_encode(
13952|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13953|       |              }
13954|  4.91k|            }
13955|  29.2k|          }
13956|       |          // Otherwise, if one of the following is true:
13957|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13958|       |          // - url is special and c is U+005C (\)
13959|  17.3k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13959:20): [True: 1.46k, False: 15.8k]
  ------------------
13960|  15.8k|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13960:20): [True: 15.6k, False: 201]
  ------------------
13961|    201|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13961:20): [True: 171, False: 30]
  ------------------
13962|  17.3k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13962:21): [True: 30, False: 0]
  |  Branch (13962:41): [True: 30, False: 0]
  ------------------
13963|       |            // If atSignSeen is true and authority_view is the empty string,
13964|       |            // validation error, return failure.
13965|  17.3k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13965:17): [True: 17.1k, False: 127]
  |  Branch (13965:33): [True: 138, False: 17.0k]
  ------------------
13966|    138|              url.is_valid = false;
13967|    138|              return url;
13968|    138|            }
13969|  17.1k|            state = state::HOST;
13970|  17.1k|            break;
13971|  17.3k|          }
13972|  29.2k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13972:15): [True: 0, False: 29.2k]
  ------------------
13973|      0|            if constexpr (store_values) {
13974|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13974:19): [True: 0, False: 0]
  ------------------
13975|      0|                url.update_unencoded_base_hash(*fragment);
13976|      0|              }
13977|      0|            }
13978|      0|            return url;
13979|      0|          }
13980|  29.2k|          input_position = end_of_authority + 1;
13981|  29.2k|        } while (true);
  ------------------
  |  Branch (13981:18): [True: 29.2k, Folded]
  ------------------
13982|       |
13983|  17.1k|        break;
13984|  17.3k|      }
13985|  17.1k|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13985:7): [True: 111, False: 366k]
  ------------------
13986|    111|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13987|    111|                helpers::substring(url_data, input_position));
13988|       |
13989|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13990|       |        // then set state to special authority ignore slashes state and increase
13991|       |        // pointer by 1.
13992|    111|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13992:13): [True: 5, False: 106]
  ------------------
13993|      5|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13994|      5|          input_position += 2;
13995|    106|        } else {
13996|       |          // Otherwise, validation error, set state to relative state and
13997|       |          // decrease pointer by 1.
13998|    106|          state = state::RELATIVE_SCHEME;
13999|    106|        }
14000|       |
14001|    111|        break;
14002|  17.3k|      }
14003|  2.75k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (14003:7): [True: 2.75k, False: 363k]
  ------------------
14004|  2.75k|        ada_log("PATH_OR_AUTHORITY ",
14005|  2.75k|                helpers::substring(url_data, input_position));
14006|       |
14007|       |        // If c is U+002F (/), then set state to authority state.
14008|  2.75k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14008:13): [True: 2.60k, False: 150]
  ------------------
14009|  2.60k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14009:13): [True: 1.45k, False: 1.15k]
  ------------------
14010|  1.45k|          state = state::AUTHORITY;
14011|  1.45k|          input_position++;
14012|  1.45k|        } else {
14013|       |          // Otherwise, set state to path state, and decrease pointer by 1.
14014|  1.30k|          state = state::PATH;
14015|  1.30k|        }
14016|       |
14017|  2.75k|        break;
14018|  17.3k|      }
14019|  1.06k|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (14019:7): [True: 1.06k, False: 365k]
  ------------------
14020|  1.06k|        ada_log("RELATIVE_SCHEME ",
14021|  1.06k|                helpers::substring(url_data, input_position));
14022|       |
14023|       |        // Set url's scheme to base's scheme.
14024|  1.06k|        url.copy_scheme(*base_url);
14025|       |
14026|       |        // If c is U+002F (/), then set state to relative slash state.
14027|  1.06k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14027:13): [True: 709, False: 359]
  ------------------
14028|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
14029|    709|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14029:13): [True: 120, False: 589]
  ------------------
14030|    120|          ada_log(
14031|    120|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
14032|    120|              "slash state");
14033|    120|          state = state::RELATIVE_SLASH;
14034|    948|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14034:20): [True: 424, False: 524]
  |  Branch (14034:40): [True: 276, False: 148]
  ------------------
14035|    276|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14035:20): [True: 9, False: 267]
  ------------------
14036|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
14037|       |          // set state to relative slash state.
14038|      9|          ada_log(
14039|      9|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
14040|      9|              "error, set state to relative slash state");
14041|      9|          state = state::RELATIVE_SLASH;
14042|    939|        } else {
14043|    939|          ada_log("RELATIVE_SCHEME otherwise");
14044|       |          // Set url's username to base's username, url's password to base's
14045|       |          // password, url's host to base's host, url's port to base's port,
14046|       |          // url's path to a clone of base's path, and url's query to base's
14047|       |          // query.
14048|    939|          if constexpr (result_type_is_ada_url) {
14049|    939|            url.username = base_url->username;
14050|    939|            url.password = base_url->password;
14051|    939|            url.host = base_url->host;
14052|    939|            url.port = base_url->port;
14053|       |            // cloning the base path includes cloning the has_opaque_path flag
14054|    939|            url.has_opaque_path = base_url->has_opaque_path;
14055|    939|            url.path = base_url->path;
14056|    939|            url.query = base_url->query;
14057|       |          } else {
14058|       |            url.update_base_authority(base_url->get_href(),
14059|       |                                      base_url->get_components());
14060|       |            url.update_host_to_base_host(base_url->get_hostname());
14061|       |            url.update_base_port(base_url->retrieve_base_port());
14062|       |            // cloning the base path includes cloning the has_opaque_path flag
14063|       |            url.has_opaque_path = base_url->has_opaque_path;
14064|       |            url.update_base_pathname(base_url->get_pathname());
14065|       |            if (base_url->has_search()) {
14066|       |              // get_search() returns "" for an empty query string (URL ends
14067|       |              // with '?'). update_base_search("") would incorrectly clear the
14068|       |              // query, so pass "?" to preserve the empty query distinction.
14069|       |              auto s = base_url->get_search();
14070|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
14071|       |            }
14072|       |          }
14073|       |
14074|    939|          url.has_opaque_path = base_url->has_opaque_path;
14075|       |
14076|       |          // If c is U+003F (?), then set url's query to the empty string, and
14077|       |          // state to query state.
14078|    939|          if ((input_position != input_size) &&
  ------------------
  |  Branch (14078:15): [True: 580, False: 359]
  ------------------
14079|    580|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14079:15): [True: 39, False: 541]
  ------------------
14080|     39|            state = state::QUERY;
14081|     39|          }
14082|       |          // Otherwise, if c is not the EOF code point:
14083|    900|          else if (input_position != input_size) {
  ------------------
  |  Branch (14083:20): [True: 541, False: 359]
  ------------------
14084|       |            // Set url's query to null.
14085|    541|            url.clear_search();
14086|    541|            if constexpr (result_type_is_ada_url) {
14087|       |              // Shorten url's path.
14088|    541|              helpers::shorten_path(url.path, url.type);
14089|       |            } else {
14090|       |              std::string_view path = url.get_pathname();
14091|       |              if (helpers::shorten_path(path, url.type)) {
14092|       |                url.update_base_pathname(std::move(std::string(path)));
14093|       |              }
14094|       |            }
14095|       |            // Set state to path state and decrease pointer by 1.
14096|    541|            state = state::PATH;
14097|    541|            break;
14098|    541|          }
14099|    939|        }
14100|    527|        input_position++;
14101|    527|        break;
14102|  1.06k|      }
14103|    129|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (14103:7): [True: 129, False: 366k]
  ------------------
14104|    129|        ada_log("RELATIVE_SLASH ",
14105|    129|                helpers::substring(url_data, input_position));
14106|       |
14107|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
14108|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
14109|    129|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14109:13): [True: 60, False: 69]
  |  Branch (14109:33): [True: 34, False: 26]
  ------------------
14110|     34|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14110:14): [True: 8, False: 26]
  ------------------
14111|     26|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14111:14): [True: 4, False: 22]
  ------------------
14112|       |          // Set state to special authority ignore slashes state.
14113|     12|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
14114|     12|        }
14115|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
14116|    117|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14116:18): [True: 76, False: 41]
  ------------------
14117|     76|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14117:18): [True: 38, False: 38]
  ------------------
14118|     38|          state = state::AUTHORITY;
14119|     38|        }
14120|       |        // Otherwise, set
14121|       |        // - url's username to base's username,
14122|       |        // - url's password to base's password,
14123|       |        // - url's host to base's host,
14124|       |        // - url's port to base's port,
14125|       |        // - state to path state, and then, decrease pointer by 1.
14126|     79|        else {
14127|     79|          if constexpr (result_type_is_ada_url) {
14128|     79|            url.username = base_url->username;
14129|     79|            url.password = base_url->password;
14130|     79|            url.host = base_url->host;
14131|     79|            url.port = base_url->port;
14132|       |          } else {
14133|       |            url.update_base_authority(base_url->get_href(),
14134|       |                                      base_url->get_components());
14135|       |            url.update_host_to_base_host(base_url->get_hostname());
14136|       |            url.update_base_port(base_url->retrieve_base_port());
14137|       |          }
14138|     79|          state = state::PATH;
14139|     79|          break;
14140|     79|        }
14141|       |
14142|     50|        input_position++;
14143|     50|        break;
14144|    129|      }
14145|  39.3k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (14145:7): [True: 39.3k, False: 327k]
  ------------------
14146|  39.3k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
14147|  39.3k|                helpers::substring(url_data, input_position));
14148|       |
14149|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
14150|       |        // then set state to special authority ignore slashes state and increase
14151|       |        // pointer by 1.
14152|  39.3k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (14152:13): [True: 30.8k, False: 8.52k]
  ------------------
14153|  30.8k|          input_position += 2;
14154|  30.8k|        }
14155|       |
14156|  39.3k|        [[fallthrough]];
14157|  39.3k|      }
14158|  39.4k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (14158:7): [True: 17, False: 366k]
  ------------------
14159|  39.4k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
14160|  39.4k|                helpers::substring(url_data, input_position));
14161|       |
14162|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
14163|       |        // authority state and decrease pointer by 1.
14164|  41.0k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (14164:16): [True: 40.7k, False: 273]
  ------------------
14165|  40.7k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (14165:17): [True: 555, False: 40.2k]
  ------------------
14166|  40.2k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (14166:17): [True: 1.08k, False: 39.1k]
  ------------------
14167|  1.63k|          input_position++;
14168|  1.63k|        }
14169|  39.4k|        state = state::AUTHORITY;
14170|       |
14171|  39.4k|        break;
14172|  39.3k|      }
14173|  16.6k|      case state::QUERY: {
  ------------------
  |  Branch (14173:7): [True: 16.6k, False: 349k]
  ------------------
14174|  16.6k|        ada_log("QUERY ", helpers::substring(url_data, input_position));
14175|  16.6k|        if constexpr (store_values) {
14176|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
14177|       |          // if url is special; otherwise the query percent-encode set.
14178|  16.6k|          const uint8_t* query_percent_encode_set =
14179|  16.6k|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (14179:15): [True: 15.9k, False: 666]
  ------------------
14180|  16.6k|                               : character_sets::QUERY_PERCENT_ENCODE;
14181|       |
14182|       |          // Percent-encode after encoding, with encoding, buffer, and
14183|       |          // queryPercentEncodeSet, and append the result to url's query.
14184|  16.6k|          url.update_base_search(url_data.substr(input_position),
14185|  16.6k|                                 query_percent_encode_set);
14186|  16.6k|          ada_log("QUERY update_base_search completed ");
14187|  16.6k|          if (fragment.has_value()) {
  ------------------
  |  Branch (14187:15): [True: 15.6k, False: 957]
  ------------------
14188|  15.6k|            url.update_unencoded_base_hash(*fragment);
14189|  15.6k|          }
14190|  16.6k|        }
14191|  16.6k|        return url;
14192|  39.3k|      }
14193|  40.7k|      case state::HOST: {
  ------------------
  |  Branch (14193:7): [True: 40.7k, False: 325k]
  ------------------
14194|  40.7k|        ada_log("HOST ", helpers::substring(url_data, input_position));
14195|       |
14196|  40.7k|        std::string_view host_view = url_data.substr(input_position);
14197|  40.7k|        auto [location, found_colon] =
14198|  40.7k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
14199|  40.7k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (14199:26): [True: 40.7k, False: 0]
  ------------------
14200|  40.7k|                             ? input_position + location
14201|  40.7k|                             : input_size;
14202|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
14203|       |        // Note: the 'found_colon' value is true if and only if a colon was
14204|       |        // encountered while not inside brackets.
14205|  40.7k|        if (found_colon) {
  ------------------
  |  Branch (14205:13): [True: 17.5k, False: 23.2k]
  ------------------
14206|       |          // If buffer is the empty string, validation error, return failure.
14207|       |          // Let host be the result of host parsing buffer with url is not
14208|       |          // special.
14209|  17.5k|          ada_log("HOST parsing ", host_view);
14210|  17.5k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14210:15): [True: 336, False: 17.1k]
  ------------------
14211|    336|            return url;
14212|    336|          }
14213|  17.1k|          ada_log("HOST parsing results in ", url.get_hostname());
14214|       |          // Set url's host to host, buffer to the empty string, and state to
14215|       |          // port state.
14216|  17.1k|          state = state::PORT;
14217|  17.1k|          input_position++;
14218|  17.1k|        }
14219|       |        // Otherwise, if one of the following is true:
14220|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
14221|       |        // - url is special and c is U+005C (\)
14222|       |        // The get_host_delimiter_location function either brings us to
14223|       |        // the colon outside of the bracket, or to one of those characters.
14224|  23.2k|        else {
14225|       |          // If url is special and host_view is the empty string, validation
14226|       |          // error, return failure.
14227|  23.2k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (14227:15): [True: 465, False: 22.7k]
  |  Branch (14227:36): [True: 280, False: 185]
  ------------------
14228|    280|            url.is_valid = false;
14229|    280|            return url;
14230|    280|          }
14231|  22.9k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
14232|       |          // Let host be the result of host parsing host_view with url is not
14233|       |          // special.
14234|  22.9k|          if (host_view.empty()) {
  ------------------
  |  Branch (14234:15): [True: 185, False: 22.7k]
  ------------------
14235|    185|            url.update_base_hostname("");
14236|  22.7k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14236:22): [True: 1.80k, False: 20.9k]
  ------------------
14237|  1.80k|            return url;
14238|  1.80k|          }
14239|  21.1k|          ada_log("HOST parsing results in ", url.get_hostname(),
14240|  21.1k|                  " href=", url.get_href());
14241|       |
14242|       |          // Set url's host to host, and state to path start state.
14243|  21.1k|          state = state::PATH_START;
14244|  21.1k|        }
14245|       |
14246|  38.3k|        break;
14247|  40.7k|      }
14248|  38.3k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (14248:7): [True: 5.32k, False: 361k]
  ------------------
14249|  5.32k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
14250|       |        // Opaque path, query, and fragment are structurally always valid:
14251|       |        // the parser would just percent-encode whatever is there. When we
14252|       |        // are not storing values (can_parse), we can return immediately.
14253|       |        // We must set has_opaque_path = true before returning so that when
14254|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
14255|       |        // correctly rejects relative inputs against an opaque-path base
14256|       |        // (e.g. can_parse("", &"W:") must return false).
14257|       |        if constexpr (!store_values) {
14258|       |          url.has_opaque_path = true;
14259|       |          return url;
14260|       |        }
14261|  5.32k|        std::string_view view = url_data.substr(input_position);
14262|       |        // If c is U+003F (?), then set url's query to the empty string and
14263|       |        // state to query state.
14264|  5.32k|        size_t location = view.find('?');
14265|  5.32k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (14265:13): [True: 411, False: 4.91k]
  ------------------
14266|    411|          view.remove_suffix(view.size() - location);
14267|    411|          state = state::QUERY;
14268|    411|          input_position += location + 1;
14269|  4.91k|        } else {
14270|  4.91k|          input_position = input_size + 1;
14271|  4.91k|        }
14272|  5.32k|        url.has_opaque_path = true;
14273|       |
14274|       |        // This is a really unlikely scenario in real world. We should not seek
14275|       |        // to optimize it.
14276|  5.32k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (14276:13): [True: 157, False: 5.16k]
  ------------------
14277|    157|          std::string modified_view =
14278|    157|              std::string(view.substr(0, view.size() - 1)) + "%20";
14279|    157|          url.update_base_pathname(unicode::percent_encode(
14280|    157|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14281|  5.16k|        } else {
14282|  5.16k|          url.update_base_pathname(unicode::percent_encode(
14283|  5.16k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14284|  5.16k|        }
14285|  5.32k|        break;
14286|  40.7k|      }
14287|  17.1k|      case state::PORT: {
  ------------------
  |  Branch (14287:7): [True: 17.1k, False: 349k]
  ------------------
14288|  17.1k|        ada_log("PORT ", helpers::substring(url_data, input_position));
14289|  17.1k|        std::string_view port_view = url_data.substr(input_position);
14290|  17.1k|        input_position += url.parse_port(port_view, true);
14291|  17.1k|        if (!url.is_valid) {
  ------------------
  |  Branch (14291:13): [True: 238, False: 16.9k]
  ------------------
14292|    238|          return url;
14293|    238|        }
14294|  16.9k|        state = state::PATH_START;
14295|  16.9k|        [[fallthrough]];
14296|  16.9k|      }
14297|  39.4k|      case state::PATH_START: {
  ------------------
  |  Branch (14297:7): [True: 22.5k, False: 343k]
  ------------------
14298|  39.4k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
14299|       |        // Path, query, and fragment are structurally always valid: the
14300|       |        // parser would just percent-encode whatever is there. When we are
14301|       |        // not storing values (can_parse), we can return immediately since
14302|       |        // no subsequent state can invalidate the URL.
14303|       |        if constexpr (!store_values) {
14304|       |          return url;
14305|       |        }
14306|       |
14307|       |        // If url is special, then:
14308|  39.4k|        if (url.is_special()) {
  ------------------
  |  Branch (14308:13): [True: 38.4k, False: 1.06k]
  ------------------
14309|       |          // Set state to path state.
14310|  38.4k|          state = state::PATH;
14311|       |
14312|       |          // Optimization: Avoiding going into PATH state improves the
14313|       |          // performance of urls ending with /.
14314|  38.4k|          if (input_position == input_size) {
  ------------------
  |  Branch (14314:15): [True: 21.0k, False: 17.4k]
  ------------------
14315|  21.0k|            if constexpr (store_values) {
14316|  21.0k|              url.update_base_pathname("/");
14317|  21.0k|              if (fragment.has_value()) {
  ------------------
  |  Branch (14317:19): [True: 173, False: 20.8k]
  ------------------
14318|    173|                url.update_unencoded_base_hash(*fragment);
14319|    173|              }
14320|  21.0k|            }
14321|  21.0k|            return url;
14322|  21.0k|          }
14323|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
14324|       |          // by 1. We know that (input_position == input_size) is impossible
14325|       |          // here, because of the previous if-check.
14326|  17.4k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (14326:15): [True: 471, False: 16.9k]
  ------------------
14327|    471|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (14327:15): [True: 375, False: 96]
  ------------------
14328|    375|            break;
14329|    375|          }
14330|  17.4k|        }
14331|       |        // Otherwise, if state override is not given and c is U+003F (?),
14332|       |        // set url's query to the empty string and state to query state.
14333|  1.06k|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14333:18): [True: 329, False: 737]
  ------------------
14334|    329|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14334:18): [True: 70, False: 259]
  ------------------
14335|     70|          state = state::QUERY;
14336|     70|        }
14337|       |        // Otherwise, if c is not the EOF code point:
14338|    996|        else if (input_position != input_size) {
  ------------------
  |  Branch (14338:18): [True: 259, False: 737]
  ------------------
14339|       |          // Set state to path state.
14340|    259|          state = state::PATH;
14341|       |
14342|       |          // If c is not U+002F (/), then decrease pointer by 1.
14343|    259|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (14343:15): [True: 0, False: 259]
  ------------------
14344|      0|            break;
14345|      0|          }
14346|    259|        }
14347|       |
14348|  18.1k|        input_position++;
14349|  18.1k|        break;
14350|  39.4k|      }
14351|  21.4k|      case state::PATH: {
  ------------------
  |  Branch (14351:7): [True: 21.4k, False: 345k]
  ------------------
14352|  21.4k|        ada_log("PATH ", helpers::substring(url_data, input_position));
14353|       |        // Path, query, and fragment are structurally always valid: the
14354|       |        // parser would just percent-encode whatever is there. When we are
14355|       |        // not storing values (can_parse), we can return immediately since
14356|       |        // no subsequent state can invalidate the URL.
14357|       |        if constexpr (!store_values) {
14358|       |          return url;
14359|       |        }
14360|  21.4k|        std::string_view view = url_data.substr(input_position);
14361|       |
14362|       |        // Most time, we do not need percent encoding.
14363|       |        // Furthermore, we can immediately locate the '?'.
14364|  21.4k|        size_t locofquestionmark = view.find('?');
14365|  21.4k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (14365:13): [True: 16.1k, False: 5.33k]
  ------------------
14366|  16.1k|          state = state::QUERY;
14367|  16.1k|          view.remove_suffix(view.size() - locofquestionmark);
14368|  16.1k|          input_position += locofquestionmark + 1;
14369|  16.1k|        } else {
14370|  5.33k|          input_position = input_size + 1;
14371|  5.33k|        }
14372|  21.4k|        if constexpr (store_values) {
14373|  21.4k|          if constexpr (result_type_is_ada_url) {
14374|  21.4k|            helpers::parse_prepared_path(view, url.type, url.path);
14375|       |          } else {
14376|       |            url.consume_prepared_path(view);
14377|       |            ADA_ASSERT_TRUE(url.validate());
14378|       |          }
14379|  21.4k|        }
14380|  21.4k|        break;
14381|  39.4k|      }
14382|  2.14k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (14382:7): [True: 2.14k, False: 364k]
  ------------------
14383|  2.14k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
14384|       |
14385|       |        // If c is U+002F (/) or U+005C (\), then:
14386|  2.14k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14386:13): [True: 2.04k, False: 105]
  ------------------
14387|  2.04k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14387:14): [True: 1.63k, False: 405]
  ------------------
14388|  1.65k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14388:14): [True: 16, False: 389]
  ------------------
14389|  1.65k|          ada_log("FILE_SLASH c is U+002F or U+005C");
14390|       |          // Set state to file host state.
14391|  1.65k|          state = state::FILE_HOST;
14392|  1.65k|          input_position++;
14393|  1.65k|        } else {
14394|    494|          ada_log("FILE_SLASH otherwise");
14395|       |          // If base is non-null and base's scheme is "file", then:
14396|       |          // Note: it is unsafe to do base_url->scheme unless you know that
14397|       |          // base_url_has_value() is true.
14398|    494|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14398:15): [True: 232, False: 262]
  |  Branch (14398:38): [True: 188, False: 44]
  ------------------
14399|       |            // Set url's host to base's host.
14400|    188|            if constexpr (result_type_is_ada_url) {
14401|    188|              url.host = base_url->host;
14402|       |            } else {
14403|       |              url.update_host_to_base_host(base_url->get_host());
14404|       |            }
14405|       |            // If the code point substring from pointer to the end of input does
14406|       |            // not start with a Windows drive letter and base's path[0] is a
14407|       |            // normalized Windows drive letter, then append base's path[0] to
14408|       |            // url's path.
14409|    188|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (14409:17): [True: 188, False: 0]
  ------------------
14410|    188|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (14410:19): [True: 156, False: 32]
  ------------------
14411|    188|                      url_data.substr(input_position))) {
14412|    156|                std::string_view first_base_url_path =
14413|    156|                    base_url->get_pathname().substr(1);
14414|    156|                size_t loc = first_base_url_path.find('/');
14415|    156|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (14415:21): [True: 35, False: 121]
  ------------------
14416|     35|                  helpers::resize(first_base_url_path, loc);
14417|     35|                }
14418|    156|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (14418:21): [True: 25, False: 131]
  ------------------
14419|    156|                        first_base_url_path)) {
14420|     25|                  if constexpr (result_type_is_ada_url) {
14421|     25|                    url.path += '/';
14422|     25|                    url.path += first_base_url_path;
14423|       |                  } else {
14424|       |                    url.append_base_pathname(
14425|       |                        helpers::concat("/", first_base_url_path));
14426|       |                  }
14427|     25|                }
14428|    156|              }
14429|    188|            }
14430|    188|          }
14431|       |
14432|       |          // Set state to path state, and decrease pointer by 1.
14433|    494|          state = state::PATH;
14434|    494|        }
14435|       |
14436|  2.14k|        break;
14437|  39.4k|      }
14438|  1.65k|      case state::FILE_HOST: {
  ------------------
  |  Branch (14438:7): [True: 1.65k, False: 364k]
  ------------------
14439|  1.65k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
14440|  1.65k|        std::string_view view = url_data.substr(input_position);
14441|       |
14442|  1.65k|        size_t location = view.find_first_of("/\\?");
14443|  1.65k|        std::string_view file_host_buffer = view.substr(
14444|  1.65k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (14444:16): [True: 267, False: 1.38k]
  ------------------
14445|       |
14446|  1.65k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (14446:13): [True: 28, False: 1.62k]
  ------------------
14447|     28|          state = state::PATH;
14448|  1.62k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (14448:20): [True: 93, False: 1.53k]
  ------------------
14449|       |          // Set url's host to the empty string.
14450|     93|          if constexpr (result_type_is_ada_url) {
14451|     93|            url.host = "";
14452|       |          } else {
14453|       |            url.update_base_hostname("");
14454|       |          }
14455|       |          // Set state to path start state.
14456|     93|          state = state::PATH_START;
14457|  1.53k|        } else {
14458|  1.53k|          size_t consumed_bytes = file_host_buffer.size();
14459|  1.53k|          input_position += consumed_bytes;
14460|       |          // Let host be the result of host parsing buffer with url is not
14461|       |          // special.
14462|  1.53k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (14462:15): [True: 235, False: 1.29k]
  ------------------
14463|    235|            return url;
14464|    235|          }
14465|       |
14466|  1.29k|          if constexpr (result_type_is_ada_url) {
14467|       |            // If host is "localhost", then set host to the empty string.
14468|  1.29k|            if (url.host.has_value() && url.host.value() == "localhost") {
  ------------------
  |  Branch (14468:17): [True: 1.29k, False: 0]
  |  Branch (14468:41): [True: 21, False: 1.27k]
  ------------------
14469|     21|              url.host = "";
14470|     21|            }
14471|       |          } else {
14472|       |            if (url.get_hostname() == "localhost") {
14473|       |              url.update_base_hostname("");
14474|       |            }
14475|       |          }
14476|       |
14477|       |          // Set buffer to the empty string and state to path start state.
14478|  1.29k|          state = state::PATH_START;
14479|  1.29k|        }
14480|       |
14481|  1.41k|        break;
14482|  1.65k|      }
14483|  3.68k|      case state::FILE: {
  ------------------
  |  Branch (14483:7): [True: 3.68k, False: 362k]
  ------------------
14484|  3.68k|        ada_log("FILE ", helpers::substring(url_data, input_position));
14485|  3.68k|        std::string_view file_view = url_data.substr(input_position);
14486|       |
14487|  3.68k|        url.set_protocol_as_file();
14488|  3.68k|        if constexpr (result_type_is_ada_url) {
14489|       |          // Set url's host to the empty string.
14490|  3.68k|          url.host = "";
14491|       |        } else {
14492|       |          url.update_base_hostname("");
14493|       |        }
14494|       |        // If c is U+002F (/) or U+005C (\), then:
14495|  3.68k|        if (input_position != input_size &&
  ------------------
  |  Branch (14495:13): [True: 3.33k, False: 345]
  ------------------
14496|  3.33k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14496:14): [True: 2.12k, False: 1.21k]
  ------------------
14497|  2.14k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14497:14): [True: 24, False: 1.19k]
  ------------------
14498|  2.14k|          ada_log("FILE c is U+002F or U+005C");
14499|       |          // Set state to file slash state.
14500|  2.14k|          state = state::FILE_SLASH;
14501|  2.14k|        }
14502|       |        // Otherwise, if base is non-null and base's scheme is "file":
14503|  1.53k|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14503:18): [True: 576, False: 960]
  |  Branch (14503:41): [True: 422, False: 154]
  ------------------
14504|       |          // Set url's host to base's host, url's path to a clone of base's
14505|       |          // path, and url's query to base's query.
14506|    422|          ada_log("FILE base non-null");
14507|    422|          if constexpr (result_type_is_ada_url) {
14508|    422|            url.host = base_url->host;
14509|    422|            url.path = base_url->path;
14510|    422|            url.query = base_url->query;
14511|       |          } else {
14512|       |            url.update_host_to_base_host(base_url->get_hostname());
14513|       |            url.update_base_pathname(base_url->get_pathname());
14514|       |            if (base_url->has_search()) {
14515|       |              // get_search() returns "" for an empty query string (URL ends
14516|       |              // with '?'). update_base_search("") would incorrectly clear the
14517|       |              // query, so pass "?" to preserve the empty query distinction.
14518|       |              auto s = base_url->get_search();
14519|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
14520|       |            }
14521|       |          }
14522|    422|          url.has_opaque_path = base_url->has_opaque_path;
14523|       |
14524|       |          // If c is U+003F (?), then set url's query to the empty string and
14525|       |          // state to query state.
14526|    422|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (14526:15): [True: 239, False: 183]
  |  Branch (14526:47): [True: 23, False: 216]
  ------------------
14527|     23|            state = state::QUERY;
14528|     23|          }
14529|       |          // Otherwise, if c is not the EOF code point:
14530|    399|          else if (input_position != input_size) {
  ------------------
  |  Branch (14530:20): [True: 216, False: 183]
  ------------------
14531|       |            // Set url's query to null.
14532|    216|            url.clear_search();
14533|       |            // If the code point substring from pointer to the end of input does
14534|       |            // not start with a Windows drive letter, then shorten url's path.
14535|    216|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (14535:17): [True: 175, False: 41]
  ------------------
14536|    175|              if constexpr (result_type_is_ada_url) {
14537|    175|                helpers::shorten_path(url.path, url.type);
14538|       |              } else {
14539|       |                std::string_view path = url.get_pathname();
14540|       |                if (helpers::shorten_path(path, url.type)) {
14541|       |                  url.update_base_pathname(std::move(std::string(path)));
14542|       |                }
14543|       |              }
14544|    175|            }
14545|       |            // Otherwise:
14546|     41|            else {
14547|       |              // Set url's path to an empty list.
14548|     41|              url.clear_pathname();
14549|     41|              url.has_opaque_path = true;
14550|     41|            }
14551|       |
14552|       |            // Set state to path state and decrease pointer by 1.
14553|    216|            state = state::PATH;
14554|    216|            break;
14555|    216|          }
14556|    422|        }
14557|       |        // Otherwise, set state to path state, and decrease pointer by 1.
14558|  1.11k|        else {
14559|  1.11k|          ada_log("FILE go to path");
14560|  1.11k|          state = state::PATH;
14561|  1.11k|          break;
14562|  1.11k|        }
14563|       |
14564|  2.35k|        input_position++;
14565|  2.35k|        break;
14566|  3.68k|      }
14567|      0|      default:
  ------------------
  |  Branch (14567:7): [True: 0, False: 366k]
  ------------------
14568|      0|        unreachable();
14569|   366k|    }
14570|   366k|  }
14571|  11.5k|  if constexpr (store_values) {
14572|  11.5k|    if (fragment.has_value()) {
  ------------------
  |  Branch (14572:9): [True: 630, False: 10.9k]
  ------------------
14573|    630|      url.update_unencoded_base_hash(*fragment);
14574|    630|    }
14575|  11.5k|  }
14576|       |  // Check the resulting (normalized) URL size against the maximum input length.
14577|       |  // Normalization (percent-encoding, IDNA, etc.) can expand the URL beyond the
14578|       |  // original input size.
14579|  11.5k|  if constexpr (store_values) {
14580|  11.5k|    if (url.is_valid) {
  ------------------
  |  Branch (14580:9): [True: 11.5k, False: 0]
  ------------------
14581|       |      if constexpr (result_type_is_ada_url_aggregator) {
14582|       |        if (url.buffer.size() > max_input_length) {
14583|       |          url.is_valid = false;
14584|       |        }
14585|  11.5k|      } else {
14586|  11.5k|        if (url.get_href_size() > max_input_length) {
  ------------------
  |  Branch (14586:13): [True: 0, False: 11.5k]
  ------------------
14587|      0|          url.is_valid = false;
14588|      0|        }
14589|  11.5k|      }
14590|  11.5k|    }
14591|  11.5k|  }
14592|  11.5k|  return url;
14593|  74.0k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
13635|   132k|                           const result_type* base_url) {
13636|       |  // We can specialize the implementation per type.
13637|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
13638|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
13639|       |  // something else } is free (at runtime). This means that ada::url_aggregator
13640|       |  // and ada::url **do not have to support the exact same API**.
13641|   132k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
13642|   132k|  constexpr bool result_type_is_ada_url_aggregator =
13643|   132k|      std::is_same_v<url_aggregator, result_type>;
13644|   132k|  static_assert(result_type_is_ada_url ||
13645|   132k|                result_type_is_ada_url_aggregator);  // We don't support
13646|       |                                                     // anything else for now.
13647|       |
13648|   132k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
13649|   132k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
13650|   132k|          ")");
13651|       |
13652|   132k|  state state = state::SCHEME_START;
13653|   132k|  result_type url{};
13654|       |
13655|   132k|  const uint32_t max_input_length = ada::get_max_input_length();
13656|       |
13657|       |  // We refuse to parse URL strings that exceed the maximum input length.
13658|       |  // By default, this is 4GB but can be configured via
13659|       |  // ada::set_max_input_length().
13660|   132k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (13660:7): [True: 0, False: 132k]
  ------------------
13661|      0|    url.is_valid = false;
13662|      0|  }
13663|       |  // Going forward, user_input.size() is in [0,
13664|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
13665|       |  // base, or the optional_url was invalid, we must return.
13666|   132k|  if (base_url != nullptr) {
  ------------------
  |  Branch (13666:7): [True: 28.6k, False: 103k]
  ------------------
13667|  28.6k|    url.is_valid &= base_url->is_valid;
13668|  28.6k|  }
13669|   132k|  if (!url.is_valid) {
  ------------------
  |  Branch (13669:7): [True: 0, False: 132k]
  ------------------
13670|      0|    return url;
13671|      0|  }
13672|   132k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
13673|       |    // Most of the time, we just need user_input.size().
13674|       |    // In some instances, we may need a bit more.
13675|       |    ///////////////////////////
13676|       |    // This is *very* important. This line should *not* be removed
13677|       |    // hastily. There are principled reasons why reserve is important
13678|       |    // for performance. If you have a benchmark with small inputs,
13679|       |    // it may not matter, but in other instances, it could.
13680|       |    ////
13681|       |    // This rounds up to the next power of two.
13682|       |    // We know that user_input.size() is in [0,
13683|       |    // std::numeric_limits<uint32_t>::max).
13684|   132k|    uint32_t reserve_capacity =
13685|   132k|        (0xFFFFFFFF >>
13686|   132k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
13687|   132k|        1;
13688|   132k|    url.reserve(reserve_capacity);
13689|   132k|  }
13690|   132k|  std::string tmp_buffer;
13691|   132k|  std::string_view url_data;
13692|   132k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (13692:7): [True: 1.68k, False: 130k]
  ------------------
13693|  1.68k|    tmp_buffer = user_input;
13694|       |    // Optimization opportunity: Instead of copying and then pruning, we could
13695|       |    // just directly build the string from user_input.
13696|  1.68k|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
13697|  1.68k|    url_data = tmp_buffer;
13698|   130k|  } else [[likely]] {
13699|   130k|    url_data = user_input;
13700|   130k|  }
13701|       |
13702|       |  // Leading and trailing control characters are uncommon and easy to deal with
13703|       |  // (no performance concern).
13704|   132k|  helpers::trim_c0_whitespace(url_data);
13705|       |
13706|       |  // Optimization opportunity. Most websites do not have fragment.
13707|   132k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
13708|       |  // We add it last so that an implementation like ada::url_aggregator
13709|       |  // can append it last to its internal buffer, thus improving performance.
13710|       |
13711|       |  // Here url_data no longer has its fragment.
13712|       |  // We are going to access the data from url_data (it is immutable).
13713|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
13714|       |  // The input_position variable should range from 0 to input_size.
13715|       |  // It is illegal to access url_data at input_size.
13716|   132k|  size_t input_position = 0;
13717|   132k|  const size_t input_size = url_data.size();
13718|       |  // Keep running the following state machine by switching on state.
13719|       |  // If after a run pointer points to the EOF code point, go to the next step.
13720|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
13721|       |  // We never decrement input_position.
13722|   772k|  while (input_position <= input_size) {
  ------------------
  |  Branch (13722:10): [True: 741k, False: 31.4k]
  ------------------
13723|   741k|    ada_log("In parsing at ", input_position, " out of ", input_size,
13724|   741k|            " in state ", ada::to_string(state));
13725|   741k|    switch (state) {
13726|   132k|      case state::SCHEME_START: {
  ------------------
  |  Branch (13726:7): [True: 132k, False: 609k]
  ------------------
13727|   132k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13728|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13729|       |        // state to scheme state.
13730|   132k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13730:13): [True: 114k, False: 17.1k]
  ------------------
13731|   114k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13731:13): [True: 99.5k, False: 15.4k]
  ------------------
13732|  99.5k|          state = state::SCHEME;
13733|  99.5k|          input_position++;
13734|  99.5k|        } else {
13735|       |          // Otherwise, if state override is not given, set state to no scheme
13736|       |          // state and decrease pointer by 1.
13737|  32.5k|          state = state::NO_SCHEME;
13738|  32.5k|        }
13739|   132k|        break;
13740|      0|      }
13741|  99.5k|      case state::SCHEME: {
  ------------------
  |  Branch (13741:7): [True: 99.5k, False: 641k]
  ------------------
13742|  99.5k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13743|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13744|       |        // append c, lowercased, to buffer.
13745|   446k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13745:16): [True: 444k, False: 2.58k]
  ------------------
13746|   444k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13746:16): [True: 347k, False: 96.9k]
  ------------------
13747|   347k|          input_position++;
13748|   347k|        }
13749|       |        // Otherwise, if c is U+003A (:), then:
13750|  99.5k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13750:13): [True: 96.9k, False: 2.58k]
  ------------------
13751|  96.9k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13751:13): [True: 94.7k, False: 2.22k]
  ------------------
13752|  94.7k|          ada_log("SCHEME the scheme should be ",
13753|  94.7k|                  url_data.substr(0, input_position));
13754|       |          if constexpr (result_type_is_ada_url) {
13755|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
13756|       |              return url;
13757|       |            }
13758|  94.7k|          } else {
13759|       |            // we pass the colon along instead of painfully adding it back.
13760|  94.7k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (13760:17): [True: 0, False: 94.7k]
  ------------------
13761|  94.7k|                    url_data.substr(0, input_position + 1))) {
13762|      0|              return url;
13763|      0|            }
13764|  94.7k|          }
13765|  94.7k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13766|       |
13767|       |          // If url's scheme is "file", then:
13768|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13769|  94.7k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13769:15): [True: 5.21k, False: 89.5k]
  ------------------
13770|       |            // Set state to file state.
13771|  5.21k|            state = state::FILE;
13772|  5.21k|          }
13773|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13774|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13775|       |          // != nullptr is false.
13776|  89.5k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13776:20): [True: 76.9k, False: 12.6k]
  |  Branch (13776:40): [True: 6.87k, False: 70.0k]
  ------------------
13777|  6.87k|                   base_url->type == url.type) {
  ------------------
  |  Branch (13777:20): [True: 262, False: 6.61k]
  ------------------
13778|       |            // Set state to special relative or authority state.
13779|    262|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13780|    262|          }
13781|       |          // Otherwise, if url is special, set state to special authority
13782|       |          // slashes state.
13783|  89.2k|          else if (url.is_special()) {
  ------------------
  |  Branch (13783:20): [True: 76.6k, False: 12.6k]
  ------------------
13784|  76.6k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13785|  76.6k|          }
13786|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13787|       |          // path or authority state and increase pointer by 1.
13788|  12.6k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13788:20): [True: 9.37k, False: 3.26k]
  ------------------
13789|  9.37k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13789:20): [True: 4.59k, False: 4.78k]
  ------------------
13790|  4.59k|            state = state::PATH_OR_AUTHORITY;
13791|  4.59k|            input_position++;
13792|  4.59k|          }
13793|       |          // Otherwise, set url's path to the empty string and set state to
13794|       |          // opaque path state.
13795|  8.04k|          else {
13796|  8.04k|            state = state::OPAQUE_PATH;
13797|  8.04k|          }
13798|  94.7k|        }
13799|       |        // Otherwise, if state override is not given, set buffer to the empty
13800|       |        // string, state to no scheme state, and start over (from the first code
13801|       |        // point in input).
13802|  4.81k|        else {
13803|  4.81k|          state = state::NO_SCHEME;
13804|  4.81k|          input_position = 0;
13805|  4.81k|          break;
13806|  4.81k|        }
13807|  94.7k|        input_position++;
13808|  94.7k|        break;
13809|  99.5k|      }
13810|  37.3k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13810:7): [True: 37.3k, False: 704k]
  ------------------
13811|  37.3k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13812|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13813|       |        // validation error, return failure.
13814|  37.3k|        if (base_url == nullptr ||
  ------------------
  |  Branch (13814:13): [True: 21.3k, False: 16.0k]
  ------------------
13815|  22.4k|            (base_url->has_opaque_path && !fragment.has_value())) {
  ------------------
  |  Branch (13815:14): [True: 1.69k, False: 14.3k]
  |  Branch (13815:43): [True: 1.12k, False: 573]
  ------------------
13816|  22.4k|          ada_log("NO_SCHEME validation error");
13817|  22.4k|          url.is_valid = false;
13818|  22.4k|          return url;
13819|  22.4k|        }
13820|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13821|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13822|       |        // query to base's query, and set state to fragment state.
13823|  14.9k|        else if (base_url->has_opaque_path && fragment.has_value() &&
  ------------------
  |  Branch (13823:18): [True: 573, False: 14.3k]
  |  Branch (13823:47): [True: 573, False: 0]
  ------------------
13824|    573|                 input_position == input_size) {
  ------------------
  |  Branch (13824:18): [True: 276, False: 297]
  ------------------
13825|    276|          ada_log("NO_SCHEME opaque base with fragment");
13826|    276|          url.copy_scheme(*base_url);
13827|    276|          url.has_opaque_path = base_url->has_opaque_path;
13828|       |
13829|       |          if constexpr (result_type_is_ada_url) {
13830|       |            url.path = base_url->path;
13831|       |            url.query = base_url->query;
13832|    276|          } else {
13833|    276|            url.update_base_pathname(base_url->get_pathname());
13834|    276|            if (base_url->has_search()) {
  ------------------
  |  Branch (13834:17): [True: 131, False: 145]
  ------------------
13835|       |              // get_search() returns "" for an empty query string (URL ends
13836|       |              // with '?'). update_base_search("") would incorrectly clear the
13837|       |              // query, so pass "?" to preserve the empty query distinction.
13838|    131|              auto s = base_url->get_search();
13839|    131|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13839:38): [True: 41, False: 90]
  ------------------
13840|    131|            }
13841|    276|          }
13842|    276|          url.update_unencoded_base_hash(*fragment);
13843|    276|          return url;
13844|    276|        }
13845|       |        // Otherwise, if base's scheme is not "file", set state to relative
13846|       |        // state and decrease pointer by 1.
13847|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13848|  14.6k|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13848:18): [True: 12.8k, False: 1.76k]
  ------------------
13849|  12.8k|          ada_log("NO_SCHEME non-file relative path");
13850|  12.8k|          state = state::RELATIVE_SCHEME;
13851|  12.8k|        }
13852|       |        // Otherwise, set state to file state and decrease pointer by 1.
13853|  1.76k|        else {
13854|  1.76k|          ada_log("NO_SCHEME file base type");
13855|  1.76k|          state = state::FILE;
13856|  1.76k|        }
13857|  14.6k|        break;
13858|  37.3k|      }
13859|  79.9k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13859:7): [True: 79.9k, False: 661k]
  ------------------
13860|  79.9k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13861|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13862|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13863|       |        // worry about AUTHORITY.
13864|       |        // TODO: Instead of just collecting a bool, collect the location of the
13865|       |        // '@' and do something useful with it.
13866|       |        // TODO: We could do various processing early on, using a single pass
13867|       |        // over the string to collect information about it, e.g., telling us
13868|       |        // whether there is a @ and if so, where (or how many).
13869|       |
13870|       |        // Check if url data contains an @.
13871|  79.9k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13871:13): [True: 60.2k, False: 19.7k]
  ------------------
13872|  60.2k|          state = state::HOST;
13873|  60.2k|          break;
13874|  60.2k|        }
13875|  19.7k|        bool at_sign_seen{false};
13876|  19.7k|        bool password_token_seen{false};
13877|       |        /**
13878|       |         * We expect something of the sort...
13879|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13880|       |         * --------^
13881|       |         */
13882|  56.5k|        do {
13883|  56.5k|          std::string_view view = url_data.substr(input_position);
13884|       |          // The delimiters are @, /, ? \\.
13885|  56.5k|          size_t location =
13886|  56.5k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13886:15): [True: 53.4k, False: 3.15k]
  ------------------
13887|  56.5k|                               : helpers::find_authority_delimiter(view);
13888|  56.5k|          std::string_view authority_view = view.substr(0, location);
13889|  56.5k|          size_t end_of_authority = input_position + authority_view.size();
13890|       |          // If c is U+0040 (@), then:
13891|  56.5k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13891:15): [True: 54.2k, False: 2.28k]
  ------------------
13892|  54.2k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13892:15): [True: 36.8k, False: 17.4k]
  ------------------
13893|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13894|  36.8k|            if (at_sign_seen) {
  ------------------
  |  Branch (13894:17): [True: 18.2k, False: 18.6k]
  ------------------
13895|  18.2k|              if (password_token_seen) {
  ------------------
  |  Branch (13895:19): [True: 7.40k, False: 10.8k]
  ------------------
13896|       |                if constexpr (result_type_is_ada_url) {
13897|       |                  url.password += "%40";
13898|  7.40k|                } else {
13899|  7.40k|                  url.append_base_password("%40");
13900|  7.40k|                }
13901|  10.8k|              } else {
13902|       |                if constexpr (result_type_is_ada_url) {
13903|       |                  url.username += "%40";
13904|  10.8k|                } else {
13905|  10.8k|                  url.append_base_username("%40");
13906|  10.8k|                }
13907|  10.8k|              }
13908|  18.2k|            }
13909|       |
13910|  36.8k|            at_sign_seen = true;
13911|       |
13912|  36.8k|            if (!password_token_seen) {
  ------------------
  |  Branch (13912:17): [True: 29.4k, False: 7.40k]
  ------------------
13913|  29.4k|              size_t password_token_location = authority_view.find(':');
13914|  29.4k|              password_token_seen =
13915|  29.4k|                  password_token_location != std::string_view::npos;
13916|       |
13917|  29.4k|              if constexpr (store_values) {
13918|  29.4k|                if (!password_token_seen) {
  ------------------
  |  Branch (13918:21): [True: 12.6k, False: 16.8k]
  ------------------
13919|       |                  if constexpr (result_type_is_ada_url) {
13920|       |                    url.username += unicode::percent_encode(
13921|       |                        authority_view,
13922|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13923|  12.6k|                  } else {
13924|  12.6k|                    url.append_base_username(unicode::percent_encode(
13925|  12.6k|                        authority_view,
13926|  12.6k|                        character_sets::USERINFO_PERCENT_ENCODE));
13927|  12.6k|                  }
13928|  16.8k|                } else {
13929|       |                  if constexpr (result_type_is_ada_url) {
13930|       |                    url.username += unicode::percent_encode(
13931|       |                        authority_view.substr(0, password_token_location),
13932|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13933|       |                    url.password += unicode::percent_encode(
13934|       |                        authority_view.substr(password_token_location + 1),
13935|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13936|  16.8k|                  } else {
13937|  16.8k|                    url.append_base_username(unicode::percent_encode(
13938|  16.8k|                        authority_view.substr(0, password_token_location),
13939|  16.8k|                        character_sets::USERINFO_PERCENT_ENCODE));
13940|  16.8k|                    url.append_base_password(unicode::percent_encode(
13941|  16.8k|                        authority_view.substr(password_token_location + 1),
13942|  16.8k|                        character_sets::USERINFO_PERCENT_ENCODE));
13943|  16.8k|                  }
13944|  16.8k|                }
13945|  29.4k|              }
13946|  29.4k|            } else if constexpr (store_values) {
13947|       |              if constexpr (result_type_is_ada_url) {
13948|       |                url.password += unicode::percent_encode(
13949|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13950|  7.40k|              } else {
13951|  7.40k|                url.append_base_password(unicode::percent_encode(
13952|  7.40k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13953|  7.40k|              }
13954|  7.40k|            }
13955|  36.8k|          }
13956|       |          // Otherwise, if one of the following is true:
13957|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13958|       |          // - url is special and c is U+005C (\)
13959|  19.7k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13959:20): [True: 2.28k, False: 17.4k]
  ------------------
13960|  17.4k|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13960:20): [True: 17.1k, False: 320]
  ------------------
13961|    320|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13961:20): [True: 273, False: 47]
  ------------------
13962|  19.7k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13962:21): [True: 47, False: 0]
  |  Branch (13962:41): [True: 47, False: 0]
  ------------------
13963|       |            // If atSignSeen is true and authority_view is the empty string,
13964|       |            // validation error, return failure.
13965|  19.7k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13965:17): [True: 18.6k, False: 1.09k]
  |  Branch (13965:33): [True: 251, False: 18.3k]
  ------------------
13966|    251|              url.is_valid = false;
13967|    251|              return url;
13968|    251|            }
13969|  19.4k|            state = state::HOST;
13970|  19.4k|            break;
13971|  19.7k|          }
13972|  36.8k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13972:15): [True: 0, False: 36.8k]
  ------------------
13973|      0|            if constexpr (store_values) {
13974|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13974:19): [True: 0, False: 0]
  ------------------
13975|      0|                url.update_unencoded_base_hash(*fragment);
13976|      0|              }
13977|      0|            }
13978|      0|            return url;
13979|      0|          }
13980|  36.8k|          input_position = end_of_authority + 1;
13981|  36.8k|        } while (true);
  ------------------
  |  Branch (13981:18): [True: 36.8k, Folded]
  ------------------
13982|       |
13983|  19.4k|        break;
13984|  19.7k|      }
13985|  19.4k|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13985:7): [True: 262, False: 741k]
  ------------------
13986|    262|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13987|    262|                helpers::substring(url_data, input_position));
13988|       |
13989|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13990|       |        // then set state to special authority ignore slashes state and increase
13991|       |        // pointer by 1.
13992|    262|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13992:13): [True: 13, False: 249]
  ------------------
13993|     13|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13994|     13|          input_position += 2;
13995|    249|        } else {
13996|       |          // Otherwise, validation error, set state to relative state and
13997|       |          // decrease pointer by 1.
13998|    249|          state = state::RELATIVE_SCHEME;
13999|    249|        }
14000|       |
14001|    262|        break;
14002|  19.7k|      }
14003|  4.59k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (14003:7): [True: 4.59k, False: 736k]
  ------------------
14004|  4.59k|        ada_log("PATH_OR_AUTHORITY ",
14005|  4.59k|                helpers::substring(url_data, input_position));
14006|       |
14007|       |        // If c is U+002F (/), then set state to authority state.
14008|  4.59k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14008:13): [True: 4.36k, False: 228]
  ------------------
14009|  4.36k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14009:13): [True: 2.40k, False: 1.96k]
  ------------------
14010|  2.40k|          state = state::AUTHORITY;
14011|  2.40k|          input_position++;
14012|  2.40k|        } else {
14013|       |          // Otherwise, set state to path state, and decrease pointer by 1.
14014|  2.18k|          state = state::PATH;
14015|  2.18k|        }
14016|       |
14017|  4.59k|        break;
14018|  19.7k|      }
14019|  13.1k|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (14019:7): [True: 13.1k, False: 728k]
  ------------------
14020|  13.1k|        ada_log("RELATIVE_SCHEME ",
14021|  13.1k|                helpers::substring(url_data, input_position));
14022|       |
14023|       |        // Set url's scheme to base's scheme.
14024|  13.1k|        url.copy_scheme(*base_url);
14025|       |
14026|       |        // If c is U+002F (/), then set state to relative slash state.
14027|  13.1k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14027:13): [True: 9.49k, False: 3.61k]
  ------------------
14028|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
14029|  9.49k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14029:13): [True: 1.85k, False: 7.64k]
  ------------------
14030|  1.85k|          ada_log(
14031|  1.85k|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
14032|  1.85k|              "slash state");
14033|  1.85k|          state = state::RELATIVE_SLASH;
14034|  11.2k|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14034:20): [True: 9.86k, False: 1.39k]
  |  Branch (14034:40): [True: 7.02k, False: 2.84k]
  ------------------
14035|  7.02k|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14035:20): [True: 63, False: 6.96k]
  ------------------
14036|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
14037|       |          // set state to relative slash state.
14038|     63|          ada_log(
14039|     63|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
14040|     63|              "error, set state to relative slash state");
14041|     63|          state = state::RELATIVE_SLASH;
14042|  11.1k|        } else {
14043|  11.1k|          ada_log("RELATIVE_SCHEME otherwise");
14044|       |          // Set url's username to base's username, url's password to base's
14045|       |          // password, url's host to base's host, url's port to base's port,
14046|       |          // url's path to a clone of base's path, and url's query to base's
14047|       |          // query.
14048|       |          if constexpr (result_type_is_ada_url) {
14049|       |            url.username = base_url->username;
14050|       |            url.password = base_url->password;
14051|       |            url.host = base_url->host;
14052|       |            url.port = base_url->port;
14053|       |            // cloning the base path includes cloning the has_opaque_path flag
14054|       |            url.has_opaque_path = base_url->has_opaque_path;
14055|       |            url.path = base_url->path;
14056|       |            url.query = base_url->query;
14057|  11.1k|          } else {
14058|  11.1k|            url.update_base_authority(base_url->get_href(),
14059|  11.1k|                                      base_url->get_components());
14060|  11.1k|            url.update_host_to_base_host(base_url->get_hostname());
14061|  11.1k|            url.update_base_port(base_url->retrieve_base_port());
14062|       |            // cloning the base path includes cloning the has_opaque_path flag
14063|  11.1k|            url.has_opaque_path = base_url->has_opaque_path;
14064|  11.1k|            url.update_base_pathname(base_url->get_pathname());
14065|  11.1k|            if (base_url->has_search()) {
  ------------------
  |  Branch (14065:17): [True: 8.33k, False: 2.86k]
  ------------------
14066|       |              // get_search() returns "" for an empty query string (URL ends
14067|       |              // with '?'). update_base_search("") would incorrectly clear the
14068|       |              // query, so pass "?" to preserve the empty query distinction.
14069|  8.33k|              auto s = base_url->get_search();
14070|  8.33k|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (14070:38): [True: 171, False: 8.16k]
  ------------------
14071|  8.33k|            }
14072|  11.1k|          }
14073|       |
14074|  11.1k|          url.has_opaque_path = base_url->has_opaque_path;
14075|       |
14076|       |          // If c is U+003F (?), then set url's query to the empty string, and
14077|       |          // state to query state.
14078|  11.1k|          if ((input_position != input_size) &&
  ------------------
  |  Branch (14078:15): [True: 7.57k, False: 3.61k]
  ------------------
14079|  7.57k|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14079:15): [True: 159, False: 7.41k]
  ------------------
14080|    159|            state = state::QUERY;
14081|    159|          }
14082|       |          // Otherwise, if c is not the EOF code point:
14083|  11.0k|          else if (input_position != input_size) {
  ------------------
  |  Branch (14083:20): [True: 7.41k, False: 3.61k]
  ------------------
14084|       |            // Set url's query to null.
14085|  7.41k|            url.clear_search();
14086|       |            if constexpr (result_type_is_ada_url) {
14087|       |              // Shorten url's path.
14088|       |              helpers::shorten_path(url.path, url.type);
14089|  7.41k|            } else {
14090|  7.41k|              std::string_view path = url.get_pathname();
14091|  7.41k|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (14091:19): [True: 7.19k, False: 228]
  ------------------
14092|  7.19k|                url.update_base_pathname(std::move(std::string(path)));
14093|  7.19k|              }
14094|  7.41k|            }
14095|       |            // Set state to path state and decrease pointer by 1.
14096|  7.41k|            state = state::PATH;
14097|  7.41k|            break;
14098|  7.41k|          }
14099|  11.1k|        }
14100|  5.69k|        input_position++;
14101|  5.69k|        break;
14102|  13.1k|      }
14103|  1.92k|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (14103:7): [True: 1.92k, False: 739k]
  ------------------
14104|  1.92k|        ada_log("RELATIVE_SLASH ",
14105|  1.92k|                helpers::substring(url_data, input_position));
14106|       |
14107|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
14108|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
14109|  1.92k|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14109:13): [True: 1.78k, False: 132]
  |  Branch (14109:33): [True: 1.61k, False: 173]
  ------------------
14110|  1.61k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14110:14): [True: 791, False: 824]
  ------------------
14111|    825|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14111:14): [True: 34, False: 790]
  ------------------
14112|       |          // Set state to special authority ignore slashes state.
14113|    825|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
14114|    825|        }
14115|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
14116|  1.09k|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14116:18): [True: 893, False: 202]
  ------------------
14117|    893|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14117:18): [True: 71, False: 822]
  ------------------
14118|     71|          state = state::AUTHORITY;
14119|     71|        }
14120|       |        // Otherwise, set
14121|       |        // - url's username to base's username,
14122|       |        // - url's password to base's password,
14123|       |        // - url's host to base's host,
14124|       |        // - url's port to base's port,
14125|       |        // - state to path state, and then, decrease pointer by 1.
14126|  1.02k|        else {
14127|       |          if constexpr (result_type_is_ada_url) {
14128|       |            url.username = base_url->username;
14129|       |            url.password = base_url->password;
14130|       |            url.host = base_url->host;
14131|       |            url.port = base_url->port;
14132|  1.02k|          } else {
14133|  1.02k|            url.update_base_authority(base_url->get_href(),
14134|  1.02k|                                      base_url->get_components());
14135|  1.02k|            url.update_host_to_base_host(base_url->get_hostname());
14136|  1.02k|            url.update_base_port(base_url->retrieve_base_port());
14137|  1.02k|          }
14138|  1.02k|          state = state::PATH;
14139|  1.02k|          break;
14140|  1.02k|        }
14141|       |
14142|    896|        input_position++;
14143|    896|        break;
14144|  1.92k|      }
14145|  76.6k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (14145:7): [True: 76.6k, False: 664k]
  ------------------
14146|  76.6k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
14147|  76.6k|                helpers::substring(url_data, input_position));
14148|       |
14149|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
14150|       |        // then set state to special authority ignore slashes state and increase
14151|       |        // pointer by 1.
14152|  76.6k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (14152:13): [True: 63.7k, False: 12.9k]
  ------------------
14153|  63.7k|          input_position += 2;
14154|  63.7k|        }
14155|       |
14156|  76.6k|        [[fallthrough]];
14157|  76.6k|      }
14158|  77.4k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (14158:7): [True: 838, False: 740k]
  ------------------
14159|  77.4k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
14160|  77.4k|                helpers::substring(url_data, input_position));
14161|       |
14162|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
14163|       |        // authority state and decrease pointer by 1.
14164|  81.6k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (14164:16): [True: 81.1k, False: 526]
  ------------------
14165|  81.1k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (14165:17): [True: 2.28k, False: 78.8k]
  ------------------
14166|  78.8k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (14166:17): [True: 1.89k, False: 76.9k]
  ------------------
14167|  4.17k|          input_position++;
14168|  4.17k|        }
14169|  77.4k|        state = state::AUTHORITY;
14170|       |
14171|  77.4k|        break;
14172|  76.6k|      }
14173|  48.6k|      case state::QUERY: {
  ------------------
  |  Branch (14173:7): [True: 48.6k, False: 692k]
  ------------------
14174|  48.6k|        ada_log("QUERY ", helpers::substring(url_data, input_position));
14175|  48.6k|        if constexpr (store_values) {
14176|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
14177|       |          // if url is special; otherwise the query percent-encode set.
14178|  48.6k|          const uint8_t* query_percent_encode_set =
14179|  48.6k|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (14179:15): [True: 47.4k, False: 1.16k]
  ------------------
14180|  48.6k|                               : character_sets::QUERY_PERCENT_ENCODE;
14181|       |
14182|       |          // Percent-encode after encoding, with encoding, buffer, and
14183|       |          // queryPercentEncodeSet, and append the result to url's query.
14184|  48.6k|          url.update_base_search(url_data.substr(input_position),
14185|  48.6k|                                 query_percent_encode_set);
14186|  48.6k|          ada_log("QUERY update_base_search completed ");
14187|  48.6k|          if (fragment.has_value()) {
  ------------------
  |  Branch (14187:15): [True: 32.1k, False: 16.5k]
  ------------------
14188|  32.1k|            url.update_unencoded_base_hash(*fragment);
14189|  32.1k|          }
14190|  48.6k|        }
14191|  48.6k|        return url;
14192|  76.6k|      }
14193|  79.7k|      case state::HOST: {
  ------------------
  |  Branch (14193:7): [True: 79.7k, False: 661k]
  ------------------
14194|  79.7k|        ada_log("HOST ", helpers::substring(url_data, input_position));
14195|       |
14196|  79.7k|        std::string_view host_view = url_data.substr(input_position);
14197|  79.7k|        auto [location, found_colon] =
14198|  79.7k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
14199|  79.7k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (14199:26): [True: 79.7k, False: 0]
  ------------------
14200|  79.7k|                             ? input_position + location
14201|  79.7k|                             : input_size;
14202|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
14203|       |        // Note: the 'found_colon' value is true if and only if a colon was
14204|       |        // encountered while not inside brackets.
14205|  79.7k|        if (found_colon) {
  ------------------
  |  Branch (14205:13): [True: 19.0k, False: 60.6k]
  ------------------
14206|       |          // If buffer is the empty string, validation error, return failure.
14207|       |          // Let host be the result of host parsing buffer with url is not
14208|       |          // special.
14209|  19.0k|          ada_log("HOST parsing ", host_view);
14210|  19.0k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14210:15): [True: 583, False: 18.4k]
  ------------------
14211|    583|            return url;
14212|    583|          }
14213|  18.4k|          ada_log("HOST parsing results in ", url.get_hostname());
14214|       |          // Set url's host to host, buffer to the empty string, and state to
14215|       |          // port state.
14216|  18.4k|          state = state::PORT;
14217|  18.4k|          input_position++;
14218|  18.4k|        }
14219|       |        // Otherwise, if one of the following is true:
14220|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
14221|       |        // - url is special and c is U+005C (\)
14222|       |        // The get_host_delimiter_location function either brings us to
14223|       |        // the colon outside of the bracket, or to one of those characters.
14224|  60.6k|        else {
14225|       |          // If url is special and host_view is the empty string, validation
14226|       |          // error, return failure.
14227|  60.6k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (14227:15): [True: 851, False: 59.8k]
  |  Branch (14227:36): [True: 542, False: 309]
  ------------------
14228|    542|            url.is_valid = false;
14229|    542|            return url;
14230|    542|          }
14231|  60.1k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
14232|       |          // Let host be the result of host parsing host_view with url is not
14233|       |          // special.
14234|  60.1k|          if (host_view.empty()) {
  ------------------
  |  Branch (14234:15): [True: 309, False: 59.8k]
  ------------------
14235|    309|            url.update_base_hostname("");
14236|  59.8k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14236:22): [True: 3.13k, False: 56.6k]
  ------------------
14237|  3.13k|            return url;
14238|  3.13k|          }
14239|  56.9k|          ada_log("HOST parsing results in ", url.get_hostname(),
14240|  56.9k|                  " href=", url.get_href());
14241|       |
14242|       |          // Set url's host to host, and state to path start state.
14243|  56.9k|          state = state::PATH_START;
14244|  56.9k|        }
14245|       |
14246|  75.4k|        break;
14247|  79.7k|      }
14248|  75.4k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (14248:7): [True: 8.04k, False: 733k]
  ------------------
14249|  8.04k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
14250|       |        // Opaque path, query, and fragment are structurally always valid:
14251|       |        // the parser would just percent-encode whatever is there. When we
14252|       |        // are not storing values (can_parse), we can return immediately.
14253|       |        // We must set has_opaque_path = true before returning so that when
14254|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
14255|       |        // correctly rejects relative inputs against an opaque-path base
14256|       |        // (e.g. can_parse("", &"W:") must return false).
14257|       |        if constexpr (!store_values) {
14258|       |          url.has_opaque_path = true;
14259|       |          return url;
14260|       |        }
14261|  8.04k|        std::string_view view = url_data.substr(input_position);
14262|       |        // If c is U+003F (?), then set url's query to the empty string and
14263|       |        // state to query state.
14264|  8.04k|        size_t location = view.find('?');
14265|  8.04k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (14265:13): [True: 708, False: 7.33k]
  ------------------
14266|    708|          view.remove_suffix(view.size() - location);
14267|    708|          state = state::QUERY;
14268|    708|          input_position += location + 1;
14269|  7.33k|        } else {
14270|  7.33k|          input_position = input_size + 1;
14271|  7.33k|        }
14272|  8.04k|        url.has_opaque_path = true;
14273|       |
14274|       |        // This is a really unlikely scenario in real world. We should not seek
14275|       |        // to optimize it.
14276|  8.04k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (14276:13): [True: 242, False: 7.79k]
  ------------------
14277|    242|          std::string modified_view =
14278|    242|              std::string(view.substr(0, view.size() - 1)) + "%20";
14279|    242|          url.update_base_pathname(unicode::percent_encode(
14280|    242|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14281|  7.79k|        } else {
14282|  7.79k|          url.update_base_pathname(unicode::percent_encode(
14283|  7.79k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14284|  7.79k|        }
14285|  8.04k|        break;
14286|  79.7k|      }
14287|  18.4k|      case state::PORT: {
  ------------------
  |  Branch (14287:7): [True: 18.4k, False: 723k]
  ------------------
14288|  18.4k|        ada_log("PORT ", helpers::substring(url_data, input_position));
14289|  18.4k|        std::string_view port_view = url_data.substr(input_position);
14290|  18.4k|        input_position += url.parse_port(port_view, true);
14291|  18.4k|        if (!url.is_valid) {
  ------------------
  |  Branch (14291:13): [True: 449, False: 18.0k]
  ------------------
14292|    449|          return url;
14293|    449|        }
14294|  18.0k|        state = state::PATH_START;
14295|  18.0k|        [[fallthrough]];
14296|  18.0k|      }
14297|  77.9k|      case state::PATH_START: {
  ------------------
  |  Branch (14297:7): [True: 59.8k, False: 681k]
  ------------------
14298|  77.9k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
14299|       |        // Path, query, and fragment are structurally always valid: the
14300|       |        // parser would just percent-encode whatever is there. When we are
14301|       |        // not storing values (can_parse), we can return immediately since
14302|       |        // no subsequent state can invalidate the URL.
14303|       |        if constexpr (!store_values) {
14304|       |          return url;
14305|       |        }
14306|       |
14307|       |        // If url is special, then:
14308|  77.9k|        if (url.is_special()) {
  ------------------
  |  Branch (14308:13): [True: 76.0k, False: 1.83k]
  ------------------
14309|       |          // Set state to path state.
14310|  76.0k|          state = state::PATH;
14311|       |
14312|       |          // Optimization: Avoiding going into PATH state improves the
14313|       |          // performance of urls ending with /.
14314|  76.0k|          if (input_position == input_size) {
  ------------------
  |  Branch (14314:15): [True: 24.0k, False: 52.0k]
  ------------------
14315|  24.0k|            if constexpr (store_values) {
14316|  24.0k|              url.update_base_pathname("/");
14317|  24.0k|              if (fragment.has_value()) {
  ------------------
  |  Branch (14317:19): [True: 267, False: 23.7k]
  ------------------
14318|    267|                url.update_unencoded_base_hash(*fragment);
14319|    267|              }
14320|  24.0k|            }
14321|  24.0k|            return url;
14322|  24.0k|          }
14323|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
14324|       |          // by 1. We know that (input_position == input_size) is impossible
14325|       |          // here, because of the previous if-check.
14326|  52.0k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (14326:15): [True: 711, False: 51.3k]
  ------------------
14327|    711|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (14327:15): [True: 565, False: 146]
  ------------------
14328|    565|            break;
14329|    565|          }
14330|  52.0k|        }
14331|       |        // Otherwise, if state override is not given and c is U+003F (?),
14332|       |        // set url's query to the empty string and state to query state.
14333|  1.83k|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14333:18): [True: 558, False: 1.27k]
  ------------------
14334|    558|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14334:18): [True: 125, False: 433]
  ------------------
14335|    125|          state = state::QUERY;
14336|    125|        }
14337|       |        // Otherwise, if c is not the EOF code point:
14338|  1.70k|        else if (input_position != input_size) {
  ------------------
  |  Branch (14338:18): [True: 433, False: 1.27k]
  ------------------
14339|       |          // Set state to path state.
14340|    433|          state = state::PATH;
14341|       |
14342|       |          // If c is not U+002F (/), then decrease pointer by 1.
14343|    433|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (14343:15): [True: 0, False: 433]
  ------------------
14344|      0|            break;
14345|      0|          }
14346|    433|        }
14347|       |
14348|  53.3k|        input_position++;
14349|  53.3k|        break;
14350|  77.9k|      }
14351|  65.9k|      case state::PATH: {
  ------------------
  |  Branch (14351:7): [True: 65.9k, False: 675k]
  ------------------
14352|  65.9k|        ada_log("PATH ", helpers::substring(url_data, input_position));
14353|       |        // Path, query, and fragment are structurally always valid: the
14354|       |        // parser would just percent-encode whatever is there. When we are
14355|       |        // not storing values (can_parse), we can return immediately since
14356|       |        // no subsequent state can invalidate the URL.
14357|       |        if constexpr (!store_values) {
14358|       |          return url;
14359|       |        }
14360|  65.9k|        std::string_view view = url_data.substr(input_position);
14361|       |
14362|       |        // Most time, we do not need percent encoding.
14363|       |        // Furthermore, we can immediately locate the '?'.
14364|  65.9k|        size_t locofquestionmark = view.find('?');
14365|  65.9k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (14365:13): [True: 47.6k, False: 18.3k]
  ------------------
14366|  47.6k|          state = state::QUERY;
14367|  47.6k|          view.remove_suffix(view.size() - locofquestionmark);
14368|  47.6k|          input_position += locofquestionmark + 1;
14369|  47.6k|        } else {
14370|  18.3k|          input_position = input_size + 1;
14371|  18.3k|        }
14372|  65.9k|        if constexpr (store_values) {
14373|       |          if constexpr (result_type_is_ada_url) {
14374|       |            helpers::parse_prepared_path(view, url.type, url.path);
14375|  65.9k|          } else {
14376|  65.9k|            url.consume_prepared_path(view);
14377|  65.9k|            ADA_ASSERT_TRUE(url.validate());
14378|  65.9k|          }
14379|  65.9k|        }
14380|  65.9k|        break;
14381|  77.9k|      }
14382|  4.13k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (14382:7): [True: 4.13k, False: 737k]
  ------------------
14383|  4.13k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
14384|       |
14385|       |        // If c is U+002F (/) or U+005C (\), then:
14386|  4.13k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14386:13): [True: 3.95k, False: 183]
  ------------------
14387|  3.95k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14387:14): [True: 3.30k, False: 652]
  ------------------
14388|  3.33k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14388:14): [True: 26, False: 626]
  ------------------
14389|  3.33k|          ada_log("FILE_SLASH c is U+002F or U+005C");
14390|       |          // Set state to file host state.
14391|  3.33k|          state = state::FILE_HOST;
14392|  3.33k|          input_position++;
14393|  3.33k|        } else {
14394|    809|          ada_log("FILE_SLASH otherwise");
14395|       |          // If base is non-null and base's scheme is "file", then:
14396|       |          // Note: it is unsafe to do base_url->scheme unless you know that
14397|       |          // base_url_has_value() is true.
14398|    809|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14398:15): [True: 544, False: 265]
  |  Branch (14398:38): [True: 361, False: 183]
  ------------------
14399|       |            // Set url's host to base's host.
14400|       |            if constexpr (result_type_is_ada_url) {
14401|       |              url.host = base_url->host;
14402|    361|            } else {
14403|    361|              url.update_host_to_base_host(base_url->get_host());
14404|    361|            }
14405|       |            // If the code point substring from pointer to the end of input does
14406|       |            // not start with a Windows drive letter and base's path[0] is a
14407|       |            // normalized Windows drive letter, then append base's path[0] to
14408|       |            // url's path.
14409|    361|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (14409:17): [True: 361, False: 0]
  ------------------
14410|    361|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (14410:19): [True: 298, False: 63]
  ------------------
14411|    361|                      url_data.substr(input_position))) {
14412|    298|                std::string_view first_base_url_path =
14413|    298|                    base_url->get_pathname().substr(1);
14414|    298|                size_t loc = first_base_url_path.find('/');
14415|    298|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (14415:21): [True: 72, False: 226]
  ------------------
14416|     72|                  helpers::resize(first_base_url_path, loc);
14417|     72|                }
14418|    298|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (14418:21): [True: 49, False: 249]
  ------------------
14419|    298|                        first_base_url_path)) {
14420|       |                  if constexpr (result_type_is_ada_url) {
14421|       |                    url.path += '/';
14422|       |                    url.path += first_base_url_path;
14423|     49|                  } else {
14424|     49|                    url.append_base_pathname(
14425|     49|                        helpers::concat("/", first_base_url_path));
14426|     49|                  }
14427|     49|                }
14428|    298|              }
14429|    361|            }
14430|    361|          }
14431|       |
14432|       |          // Set state to path state, and decrease pointer by 1.
14433|    809|          state = state::PATH;
14434|    809|        }
14435|       |
14436|  4.13k|        break;
14437|  77.9k|      }
14438|  3.33k|      case state::FILE_HOST: {
  ------------------
  |  Branch (14438:7): [True: 3.33k, False: 738k]
  ------------------
14439|  3.33k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
14440|  3.33k|        std::string_view view = url_data.substr(input_position);
14441|       |
14442|  3.33k|        size_t location = view.find_first_of("/\\?");
14443|  3.33k|        std::string_view file_host_buffer = view.substr(
14444|  3.33k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (14444:16): [True: 1.22k, False: 2.10k]
  ------------------
14445|       |
14446|  3.33k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (14446:13): [True: 43, False: 3.28k]
  ------------------
14447|     43|          state = state::PATH;
14448|  3.28k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (14448:20): [True: 581, False: 2.70k]
  ------------------
14449|       |          // Set url's host to the empty string.
14450|       |          if constexpr (result_type_is_ada_url) {
14451|       |            url.host = "";
14452|    581|          } else {
14453|    581|            url.update_base_hostname("");
14454|    581|          }
14455|       |          // Set state to path start state.
14456|    581|          state = state::PATH_START;
14457|  2.70k|        } else {
14458|  2.70k|          size_t consumed_bytes = file_host_buffer.size();
14459|  2.70k|          input_position += consumed_bytes;
14460|       |          // Let host be the result of host parsing buffer with url is not
14461|       |          // special.
14462|  2.70k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (14462:15): [True: 392, False: 2.31k]
  ------------------
14463|    392|            return url;
14464|    392|          }
14465|       |
14466|       |          if constexpr (result_type_is_ada_url) {
14467|       |            // If host is "localhost", then set host to the empty string.
14468|       |            if (url.host.has_value() && url.host.value() == "localhost") {
14469|       |              url.host = "";
14470|       |            }
14471|  2.31k|          } else {
14472|  2.31k|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (14472:17): [True: 32, False: 2.28k]
  ------------------
14473|     32|              url.update_base_hostname("");
14474|     32|            }
14475|  2.31k|          }
14476|       |
14477|       |          // Set buffer to the empty string and state to path start state.
14478|  2.31k|          state = state::PATH_START;
14479|  2.31k|        }
14480|       |
14481|  2.93k|        break;
14482|  3.33k|      }
14483|  6.98k|      case state::FILE: {
  ------------------
  |  Branch (14483:7): [True: 6.98k, False: 734k]
  ------------------
14484|  6.98k|        ada_log("FILE ", helpers::substring(url_data, input_position));
14485|  6.98k|        std::string_view file_view = url_data.substr(input_position);
14486|       |
14487|  6.98k|        url.set_protocol_as_file();
14488|       |        if constexpr (result_type_is_ada_url) {
14489|       |          // Set url's host to the empty string.
14490|       |          url.host = "";
14491|  6.98k|        } else {
14492|  6.98k|          url.update_base_hostname("");
14493|  6.98k|        }
14494|       |        // If c is U+002F (/) or U+005C (\), then:
14495|  6.98k|        if (input_position != input_size &&
  ------------------
  |  Branch (14495:13): [True: 5.94k, False: 1.03k]
  ------------------
14496|  5.94k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14496:14): [True: 4.09k, False: 1.84k]
  ------------------
14497|  4.13k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14497:14): [True: 43, False: 1.80k]
  ------------------
14498|  4.13k|          ada_log("FILE c is U+002F or U+005C");
14499|       |          // Set state to file slash state.
14500|  4.13k|          state = state::FILE_SLASH;
14501|  4.13k|        }
14502|       |        // Otherwise, if base is non-null and base's scheme is "file":
14503|  2.84k|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14503:18): [True: 1.87k, False: 969]
  |  Branch (14503:41): [True: 1.33k, False: 537]
  ------------------
14504|       |          // Set url's host to base's host, url's path to a clone of base's
14505|       |          // path, and url's query to base's query.
14506|  1.33k|          ada_log("FILE base non-null");
14507|       |          if constexpr (result_type_is_ada_url) {
14508|       |            url.host = base_url->host;
14509|       |            url.path = base_url->path;
14510|       |            url.query = base_url->query;
14511|  1.33k|          } else {
14512|  1.33k|            url.update_host_to_base_host(base_url->get_hostname());
14513|  1.33k|            url.update_base_pathname(base_url->get_pathname());
14514|  1.33k|            if (base_url->has_search()) {
  ------------------
  |  Branch (14514:17): [True: 156, False: 1.17k]
  ------------------
14515|       |              // get_search() returns "" for an empty query string (URL ends
14516|       |              // with '?'). update_base_search("") would incorrectly clear the
14517|       |              // query, so pass "?" to preserve the empty query distinction.
14518|    156|              auto s = base_url->get_search();
14519|    156|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (14519:38): [True: 70, False: 86]
  ------------------
14520|    156|            }
14521|  1.33k|          }
14522|  1.33k|          url.has_opaque_path = base_url->has_opaque_path;
14523|       |
14524|       |          // If c is U+003F (?), then set url's query to the empty string and
14525|       |          // state to query state.
14526|  1.33k|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (14526:15): [True: 474, False: 861]
  |  Branch (14526:47): [True: 41, False: 433]
  ------------------
14527|     41|            state = state::QUERY;
14528|     41|          }
14529|       |          // Otherwise, if c is not the EOF code point:
14530|  1.29k|          else if (input_position != input_size) {
  ------------------
  |  Branch (14530:20): [True: 433, False: 861]
  ------------------
14531|       |            // Set url's query to null.
14532|    433|            url.clear_search();
14533|       |            // If the code point substring from pointer to the end of input does
14534|       |            // not start with a Windows drive letter, then shorten url's path.
14535|    433|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (14535:17): [True: 358, False: 75]
  ------------------
14536|       |              if constexpr (result_type_is_ada_url) {
14537|       |                helpers::shorten_path(url.path, url.type);
14538|    358|              } else {
14539|    358|                std::string_view path = url.get_pathname();
14540|    358|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (14540:21): [True: 347, False: 11]
  ------------------
14541|    347|                  url.update_base_pathname(std::move(std::string(path)));
14542|    347|                }
14543|    358|              }
14544|    358|            }
14545|       |            // Otherwise:
14546|     75|            else {
14547|       |              // Set url's path to an empty list.
14548|     75|              url.clear_pathname();
14549|     75|              url.has_opaque_path = true;
14550|     75|            }
14551|       |
14552|       |            // Set state to path state and decrease pointer by 1.
14553|    433|            state = state::PATH;
14554|    433|            break;
14555|    433|          }
14556|  1.33k|        }
14557|       |        // Otherwise, set state to path state, and decrease pointer by 1.
14558|  1.50k|        else {
14559|  1.50k|          ada_log("FILE go to path");
14560|  1.50k|          state = state::PATH;
14561|  1.50k|          break;
14562|  1.50k|        }
14563|       |
14564|  5.04k|        input_position++;
14565|  5.04k|        break;
14566|  6.98k|      }
14567|      0|      default:
  ------------------
  |  Branch (14567:7): [True: 0, False: 741k]
  ------------------
14568|      0|        unreachable();
14569|   741k|    }
14570|   741k|  }
14571|  31.4k|  if constexpr (store_values) {
14572|  31.4k|    if (fragment.has_value()) {
  ------------------
  |  Branch (14572:9): [True: 1.59k, False: 29.8k]
  ------------------
14573|  1.59k|      url.update_unencoded_base_hash(*fragment);
14574|  1.59k|    }
14575|  31.4k|  }
14576|       |  // Check the resulting (normalized) URL size against the maximum input length.
14577|       |  // Normalization (percent-encoding, IDNA, etc.) can expand the URL beyond the
14578|       |  // original input size.
14579|  31.4k|  if constexpr (store_values) {
14580|  31.4k|    if (url.is_valid) {
  ------------------
  |  Branch (14580:9): [True: 31.4k, False: 0]
  ------------------
14581|  31.4k|      if constexpr (result_type_is_ada_url_aggregator) {
14582|  31.4k|        if (url.buffer.size() > max_input_length) {
  ------------------
  |  Branch (14582:13): [True: 0, False: 31.4k]
  ------------------
14583|      0|          url.is_valid = false;
14584|      0|        }
14585|       |      } else {
14586|       |        if (url.get_href_size() > max_input_length) {
14587|       |          url.is_valid = false;
14588|       |        }
14589|       |      }
14590|  31.4k|    }
14591|  31.4k|  }
14592|  31.4k|  return url;
14593|   132k|}
_ZN3ada14url_aggregator12set_protocolENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14859|  37.0k|bool url_aggregator::set_protocol(const std::string_view input) {
14860|  37.0k|  ada_log("url_aggregator::set_protocol ", input);
14861|  37.0k|  ADA_ASSERT_TRUE(validate());
14862|  37.0k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14863|  37.0k|  std::string view(input);
14864|  37.0k|  helpers::remove_ascii_tab_or_newline(view);
14865|  37.0k|  if (view.empty()) {
  ------------------
  |  Branch (14865:7): [True: 19.9k, False: 17.0k]
  ------------------
14866|  19.9k|    return true;
14867|  19.9k|  }
14868|       |
14869|       |  // Schemes should start with alpha values.
14870|  17.0k|  if (!checkers::is_alpha(view[0])) {
  ------------------
  |  Branch (14870:7): [True: 7.71k, False: 9.37k]
  ------------------
14871|  7.71k|    return false;
14872|  7.71k|  }
14873|       |
14874|  9.37k|  view.append(":");
14875|       |
14876|  9.37k|  std::string::iterator pointer =
14877|  9.37k|      std::ranges::find_if_not(view, unicode::is_alnum_plus);
14878|       |
14879|  9.37k|  if (pointer != view.end() && *pointer == ':') {
  ------------------
  |  Branch (14879:7): [True: 9.37k, False: 0]
  |  Branch (14879:7): [True: 8.32k, False: 1.05k]
  |  Branch (14879:32): [True: 8.32k, False: 1.05k]
  ------------------
14880|  8.32k|    url_aggregator saved_url(*this);
14881|  8.32k|    bool result = parse_scheme_with_colon<true>(
14882|  8.32k|        view.substr(0, pointer - view.begin() + 1));
14883|  8.32k|    if (result && buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (14883:9): [True: 5.09k, False: 3.22k]
  |  Branch (14883:19): [True: 0, False: 5.09k]
  ------------------
14884|      0|      *this = std::move(saved_url);
14885|      0|      return false;
14886|      0|    }
14887|  8.32k|    return result;
14888|  8.32k|  }
14889|  1.05k|  return false;
14890|  9.37k|}
_ZN3ada14url_aggregator12set_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14892|  16.9k|bool url_aggregator::set_username(const std::string_view input) {
14893|  16.9k|  ada_log("url_aggregator::set_username '", input, "' ");
14894|  16.9k|  ADA_ASSERT_TRUE(validate());
14895|  16.9k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14896|  16.9k|  if (cannot_have_credentials_or_port()) {
  ------------------
  |  Branch (14896:7): [True: 935, False: 16.0k]
  ------------------
14897|    935|    return false;
14898|    935|  }
14899|  16.0k|  url_aggregator saved_url(*this);
14900|  16.0k|  size_t idx = ada::unicode::percent_encode_index(
14901|  16.0k|      input, character_sets::USERINFO_PERCENT_ENCODE);
14902|  16.0k|  if (idx == input.size()) {
  ------------------
  |  Branch (14902:7): [True: 4.46k, False: 11.5k]
  ------------------
14903|  4.46k|    update_base_username(input);
14904|  11.5k|  } else {
14905|       |    // We only create a temporary string if we have to!
14906|  11.5k|    update_base_username(ada::unicode::percent_encode(
14907|  11.5k|        input, character_sets::USERINFO_PERCENT_ENCODE, idx));
14908|  11.5k|  }
14909|  16.0k|  if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (14909:7): [True: 0, False: 16.0k]
  ------------------
14910|      0|    *this = std::move(saved_url);
14911|      0|    return false;
14912|      0|  }
14913|  16.0k|  ADA_ASSERT_TRUE(validate());
14914|  16.0k|  return true;
14915|  16.0k|}
_ZN3ada14url_aggregator12set_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14917|  17.0k|bool url_aggregator::set_password(const std::string_view input) {
14918|  17.0k|  ada_log("url_aggregator::set_password '", input, "'");
14919|  17.0k|  ADA_ASSERT_TRUE(validate());
14920|  17.0k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14921|  17.0k|  if (cannot_have_credentials_or_port()) {
  ------------------
  |  Branch (14921:7): [True: 931, False: 16.1k]
  ------------------
14922|    931|    return false;
14923|    931|  }
14924|  16.1k|  url_aggregator saved_url(*this);
14925|  16.1k|  size_t idx = ada::unicode::percent_encode_index(
14926|  16.1k|      input, character_sets::USERINFO_PERCENT_ENCODE);
14927|  16.1k|  if (idx == input.size()) {
  ------------------
  |  Branch (14927:7): [True: 4.55k, False: 11.5k]
  ------------------
14928|  4.55k|    update_base_password(input);
14929|  11.5k|  } else {
14930|       |    // We only create a temporary string if we have to!
14931|  11.5k|    update_base_password(ada::unicode::percent_encode(
14932|  11.5k|        input, character_sets::USERINFO_PERCENT_ENCODE, idx));
14933|  11.5k|  }
14934|  16.1k|  if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (14934:7): [True: 0, False: 16.1k]
  ------------------
14935|      0|    *this = std::move(saved_url);
14936|      0|    return false;
14937|      0|  }
14938|  16.1k|  ADA_ASSERT_TRUE(validate());
14939|  16.1k|  return true;
14940|  16.1k|}
_ZN3ada14url_aggregator8set_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14942|  22.0k|bool url_aggregator::set_port(const std::string_view input) {
14943|  22.0k|  ada_log("url_aggregator::set_port ", input);
14944|  22.0k|  ADA_ASSERT_TRUE(validate());
14945|  22.0k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14946|  22.0k|  if (cannot_have_credentials_or_port()) {
  ------------------
  |  Branch (14946:7): [True: 928, False: 21.1k]
  ------------------
14947|    928|    return false;
14948|    928|  }
14949|       |
14950|  21.1k|  if (input.empty()) {
  ------------------
  |  Branch (14950:7): [True: 2.27k, False: 18.8k]
  ------------------
14951|  2.27k|    clear_port();
14952|  2.27k|    return true;
14953|  2.27k|  }
14954|       |
14955|  18.8k|  std::string trimmed(input);
14956|  18.8k|  helpers::remove_ascii_tab_or_newline(trimmed);
14957|       |
14958|  18.8k|  if (trimmed.empty()) {
  ------------------
  |  Branch (14958:7): [True: 118, False: 18.7k]
  ------------------
14959|    118|    return true;
14960|    118|  }
14961|       |
14962|       |  // Input should not start with a non-digit character.
14963|  18.7k|  if (!ada::unicode::is_ascii_digit(trimmed.front())) {
  ------------------
  |  Branch (14963:7): [True: 15.7k, False: 2.99k]
  ------------------
14964|  15.7k|    return false;
14965|  15.7k|  }
14966|       |
14967|       |  // Find the first non-digit character to determine the length of digits
14968|  2.99k|  auto first_non_digit =
14969|  2.99k|      std::ranges::find_if_not(trimmed, ada::unicode::is_ascii_digit);
14970|  2.99k|  std::string_view digits_to_parse =
14971|  2.99k|      std::string_view(trimmed.data(), first_non_digit - trimmed.begin());
14972|       |
14973|       |  // Revert changes if parse_port fails.
14974|  2.99k|  url_aggregator saved_url(*this);
14975|  2.99k|  parse_port(digits_to_parse);
14976|  2.99k|  if (is_valid) {
  ------------------
  |  Branch (14976:7): [True: 2.32k, False: 664]
  ------------------
14977|  2.32k|    if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (14977:9): [True: 0, False: 2.32k]
  ------------------
14978|      0|      *this = std::move(saved_url);
14979|      0|      return false;
14980|      0|    }
14981|  2.32k|    return true;
14982|  2.32k|  }
14983|    664|  *this = std::move(saved_url);
14984|    664|  is_valid = true;
14985|    664|  ADA_ASSERT_TRUE(validate());
14986|    664|  return false;
14987|  2.99k|}
_ZN3ada14url_aggregator12set_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14989|  17.2k|bool url_aggregator::set_pathname(const std::string_view input) {
14990|  17.2k|  ada_log("url_aggregator::set_pathname ", input);
14991|  17.2k|  ADA_ASSERT_TRUE(validate());
14992|  17.2k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14993|  17.2k|  if (has_opaque_path) {
  ------------------
  |  Branch (14993:7): [True: 0, False: 17.2k]
  ------------------
14994|      0|    return false;
14995|      0|  }
14996|  17.2k|  url_aggregator saved_url(*this);
14997|  17.2k|  clear_pathname();
14998|  17.2k|  parse_path(input);
14999|  17.2k|  if (get_pathname().starts_with("//") && !has_authority() && !has_dash_dot()) {
  ------------------
  |  Branch (14999:7): [True: 970, False: 16.2k]
  |  Branch (14999:7): [True: 0, False: 17.2k]
  |  Branch (14999:43): [True: 0, False: 970]
  |  Branch (14999:63): [True: 0, False: 0]
  ------------------
15000|      0|    buffer.insert(components.pathname_start, "/.");
15001|      0|    components.pathname_start += 2;
15002|      0|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (15002:9): [True: 0, False: 0]
  ------------------
15003|      0|      components.search_start += 2;
15004|      0|    }
15005|      0|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15005:9): [True: 0, False: 0]
  ------------------
15006|      0|      components.hash_start += 2;
15007|      0|    }
15008|      0|  }
15009|  17.2k|  if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (15009:7): [True: 0, False: 17.2k]
  ------------------
15010|      0|    *this = std::move(saved_url);
15011|      0|    return false;
15012|      0|  }
15013|  17.2k|  ADA_ASSERT_TRUE(validate());
15014|  17.2k|  return true;
15015|  17.2k|}
_ZN3ada14url_aggregator10set_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15058|  31.3k|void url_aggregator::set_search(const std::string_view input) {
15059|  31.3k|  ada_log("url_aggregator::set_search ", input);
15060|  31.3k|  ADA_ASSERT_TRUE(validate());
15061|  31.3k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15062|  31.3k|  if (input.empty()) {
  ------------------
  |  Branch (15062:7): [True: 1.46k, False: 29.9k]
  ------------------
15063|  1.46k|    clear_search();
15064|  1.46k|    helpers::strip_trailing_spaces_from_opaque_path(*this);
15065|  1.46k|    return;
15066|  1.46k|  }
15067|       |
15068|  29.9k|  std::string new_value;
15069|  29.9k|  new_value = input[0] == '?' ? input.substr(1) : input;
  ------------------
  |  Branch (15069:15): [True: 106, False: 29.8k]
  ------------------
15070|  29.9k|  helpers::remove_ascii_tab_or_newline(new_value);
15071|       |
15072|  29.9k|  auto query_percent_encode_set =
15073|  29.9k|      is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (15073:7): [True: 29.9k, False: 0]
  ------------------
15074|  29.9k|                   : ada::character_sets::QUERY_PERCENT_ENCODE;
15075|       |
15076|  29.9k|  url_aggregator saved_url(*this);
15077|  29.9k|  update_base_search(new_value, query_percent_encode_set);
15078|  29.9k|  if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (15078:7): [True: 0, False: 29.9k]
  ------------------
15079|      0|    *this = std::move(saved_url);
15080|      0|    return;
15081|      0|  }
15082|  29.9k|  ADA_ASSERT_TRUE(validate());
15083|  29.9k|}
_ZN3ada14url_aggregator8set_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15085|  15.8k|void url_aggregator::set_hash(const std::string_view input) {
15086|  15.8k|  ada_log("url_aggregator::set_hash ", input);
15087|  15.8k|  ADA_ASSERT_TRUE(validate());
15088|  15.8k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15089|  15.8k|  if (input.empty()) {
  ------------------
  |  Branch (15089:7): [True: 1.36k, False: 14.4k]
  ------------------
15090|  1.36k|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15090:9): [True: 137, False: 1.23k]
  ------------------
15091|    137|      buffer.resize(components.hash_start);
15092|    137|      components.hash_start = url_components::omitted;
15093|    137|    }
15094|  1.36k|    helpers::strip_trailing_spaces_from_opaque_path(*this);
15095|  1.36k|    return;
15096|  1.36k|  }
15097|       |
15098|  14.4k|  std::string new_value;
15099|  14.4k|  new_value = input[0] == '#' ? input.substr(1) : input;
  ------------------
  |  Branch (15099:15): [True: 210, False: 14.2k]
  ------------------
15100|  14.4k|  helpers::remove_ascii_tab_or_newline(new_value);
15101|  14.4k|  url_aggregator saved_url(*this);
15102|  14.4k|  update_unencoded_base_hash(new_value);
15103|  14.4k|  if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (15103:7): [True: 0, False: 14.4k]
  ------------------
15104|      0|    *this = std::move(saved_url);
15105|      0|    return;
15106|      0|  }
15107|  14.4k|  ADA_ASSERT_TRUE(validate());
15108|  14.4k|}
_ZN3ada14url_aggregator8set_hrefENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15110|  4.63k|bool url_aggregator::set_href(const std::string_view input) {
15111|  4.63k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15112|  4.63k|  ada_log("url_aggregator::set_href ", input, " [", input.size(), " bytes]");
15113|  4.63k|  ada::result<url_aggregator> out = ada::parse<url_aggregator>(input);
15114|  4.63k|  ada_log("url_aggregator::set_href, success :", out.has_value());
15115|       |
15116|  4.63k|  if (out) {
  ------------------
  |  Branch (15116:7): [True: 4.63k, False: 0]
  ------------------
15117|       |    // The parser enforces get_max_input_length() on both the input and the
15118|       |    // normalized result. This is a defense-in-depth check.
15119|  4.63k|    if (out->buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (15119:9): [True: 0, False: 4.63k]
  ------------------
15120|      0|      return false;
15121|      0|    }
15122|  4.63k|    ada_log("url_aggregator::set_href, parsed ", out->to_string());
15123|       |    // TODO: Figure out why the following line puts test to never finish.
15124|  4.63k|    *this = *out;
15125|  4.63k|  }
15126|       |
15127|  4.63k|  return out.has_value();
15128|  4.63k|}
_ZN3ada14url_aggregator8set_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15374|  18.9k|bool url_aggregator::set_host(const std::string_view input) {
15375|  18.9k|  ada_log("url_aggregator::set_host '", input, "'");
15376|  18.9k|  ADA_ASSERT_TRUE(validate());
15377|  18.9k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15378|  18.9k|  return set_host_or_hostname<false>(input);
15379|  18.9k|}
_ZN3ada14url_aggregator12set_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15381|  18.1k|bool url_aggregator::set_hostname(const std::string_view input) {
15382|  18.1k|  ada_log("url_aggregator::set_hostname '", input, "'");
15383|  18.1k|  ADA_ASSERT_TRUE(validate());
15384|  18.1k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15385|  18.1k|  return set_host_or_hostname<true>(input);
15386|  18.1k|}
_ZNK3ada14url_aggregator10get_originEv:
15388|  44.7k|[[nodiscard]] std::string url_aggregator::get_origin() const {
15389|  44.7k|  ada_log("url_aggregator::get_origin");
15390|  44.7k|  if (is_special()) {
  ------------------
  |  Branch (15390:7): [True: 37.0k, False: 7.64k]
  ------------------
15391|       |    // Return a new opaque origin.
15392|  37.0k|    if (type == scheme::FILE) {
  ------------------
  |  Branch (15392:9): [True: 4.88k, False: 32.1k]
  ------------------
15393|  4.88k|      return "null";
15394|  4.88k|    }
15395|       |
15396|  32.1k|    return helpers::concat(get_protocol(), "//", get_host());
15397|  37.0k|  }
15398|       |
15399|  7.64k|  if (get_protocol() == "blob:") {
  ------------------
  |  Branch (15399:7): [True: 1.99k, False: 5.64k]
  ------------------
15400|  1.99k|    std::string_view path = get_pathname();
15401|  1.99k|    if (!path.empty()) {
  ------------------
  |  Branch (15401:9): [True: 1.96k, False: 31]
  ------------------
15402|  1.96k|      auto out = ada::parse<ada::url_aggregator>(path);
15403|  1.96k|      if (out && (out->type == scheme::HTTP || out->type == scheme::HTTPS)) {
  ------------------
  |  Branch (15403:11): [True: 497, False: 1.47k]
  |  Branch (15403:19): [True: 49, False: 448]
  |  Branch (15403:48): [True: 12, False: 436]
  ------------------
15404|       |        // If pathURL's scheme is not "http" and not "https", then return a
15405|       |        // new opaque origin.
15406|     61|        return helpers::concat(out->get_protocol(), "//", out->get_host());
15407|     61|      }
15408|  1.96k|    }
15409|  1.99k|  }
15410|       |
15411|       |  // Return a new opaque origin.
15412|  7.58k|  return "null";
15413|  7.64k|}
_ZNK3ada14url_aggregator12get_usernameEv:
15416|  53.5k|    ada_lifetime_bound {
15417|  53.5k|  ada_log("url_aggregator::get_username");
15418|  53.5k|  if (has_non_empty_username()) {
  ------------------
  |  Branch (15418:7): [True: 26.1k, False: 27.3k]
  ------------------
15419|  26.1k|    return helpers::substring(buffer, components.protocol_end + 2,
15420|  26.1k|                              components.username_end);
15421|  26.1k|  }
15422|  27.3k|  return "";
15423|  53.5k|}
_ZNK3ada14url_aggregator12get_passwordEv:
15426|  53.5k|    ada_lifetime_bound {
15427|  53.5k|  ada_log("url_aggregator::get_password");
15428|  53.5k|  if (has_non_empty_password()) {
  ------------------
  |  Branch (15428:7): [True: 25.3k, False: 28.2k]
  ------------------
15429|  25.3k|    return helpers::substring(buffer, components.username_end + 1,
15430|  25.3k|                              components.host_start);
15431|  25.3k|  }
15432|  28.2k|  return "";
15433|  53.5k|}
_ZNK3ada14url_aggregator8get_portEv:
15436|  56.9k|    ada_lifetime_bound {
15437|  56.9k|  ada_log("url_aggregator::get_port");
15438|  56.9k|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (15438:7): [True: 54.4k, False: 2.53k]
  ------------------
15439|  54.4k|    return "";
15440|  54.4k|  }
15441|  2.53k|  return helpers::substring(buffer, components.host_end + 1,
15442|  2.53k|                            components.pathname_start);
15443|  56.9k|}
_ZNK3ada14url_aggregator8get_hashEv:
15446|  69.0k|    ada_lifetime_bound {
15447|  69.0k|  ada_log("url_aggregator::get_hash");
15448|       |  // If this's URL's fragment is either null or the empty string, then return
15449|       |  // the empty string. Return U+0023 (#), followed by this's URL's fragment.
15450|  69.0k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (15450:7): [True: 41.1k, False: 27.9k]
  ------------------
15451|  41.1k|    return "";
15452|  41.1k|  }
15453|  27.9k|  if (buffer.size() - components.hash_start <= 1) {
  ------------------
  |  Branch (15453:7): [True: 1.95k, False: 26.0k]
  ------------------
15454|  1.95k|    return "";
15455|  1.95k|  }
15456|  26.0k|  return helpers::substring(buffer, components.hash_start);
15457|  27.9k|}
_ZNK3ada14url_aggregator8get_hostEv:
15460|   107k|    ada_lifetime_bound {
15461|   107k|  ada_log("url_aggregator::get_host");
15462|       |  // Technically, we should check if there is a hostname, but
15463|       |  // the code below works even if there isn't.
15464|       |  // if(!has_hostname()) { return ""; }
15465|   107k|  size_t start = components.host_start;
15466|   107k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (15466:7): [True: 94.5k, False: 13.1k]
  ------------------
15467|  94.5k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (15467:7): [True: 41.8k, False: 52.6k]
  ------------------
15468|  41.8k|    start++;
15469|  41.8k|  }
15470|       |  // if we have an empty host, then the space between components.host_end and
15471|       |  // components.pathname_start may be occupied by /.
15472|   107k|  if (start == components.host_end) {
  ------------------
  |  Branch (15472:7): [True: 13.1k, False: 94.5k]
  ------------------
15473|  13.1k|    return {};
15474|  13.1k|  }
15475|  94.5k|  return helpers::substring(buffer, start, components.pathname_start);
15476|   107k|}
_ZNK3ada14url_aggregator12get_hostnameEv:
15479|   200k|    ada_lifetime_bound {
15480|   200k|  ada_log("url_aggregator::get_hostname");
15481|       |  // Technically, we should check if there is a hostname, but
15482|       |  // the code below works even if there isn't.
15483|       |  // if(!has_hostname()) { return ""; }
15484|   200k|  size_t start = components.host_start;
15485|       |  // So host_start is not where the host begins.
15486|   200k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (15486:7): [True: 183k, False: 17.0k]
  ------------------
15487|   183k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (15487:7): [True: 86.2k, False: 97.1k]
  ------------------
15488|  86.2k|    start++;
15489|  86.2k|  }
15490|   200k|  return helpers::substring(buffer, start, components.host_end);
15491|   200k|}
_ZNK3ada14url_aggregator10get_searchEv:
15494|  94.5k|    ada_lifetime_bound {
15495|  94.5k|  ada_log("url_aggregator::get_search");
15496|       |  // If this's URL's query is either null or the empty string, then return the
15497|       |  // empty string. Return U+003F (?), followed by this's URL's query.
15498|  94.5k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (15498:7): [True: 39.6k, False: 54.9k]
  ------------------
15499|  39.6k|    return "";
15500|  39.6k|  }
15501|  54.9k|  auto ending_index = uint32_t(buffer.size());
15502|  54.9k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15502:7): [True: 34.8k, False: 20.0k]
  ------------------
15503|  34.8k|    ending_index = components.hash_start;
15504|  34.8k|  }
15505|  54.9k|  if (ending_index - components.search_start <= 1) {
  ------------------
  |  Branch (15505:7): [True: 3.28k, False: 51.6k]
  ------------------
15506|  3.28k|    return "";
15507|  3.28k|  }
15508|  51.6k|  return helpers::substring(buffer, components.search_start, ending_index);
15509|  54.9k|}
_ZNK3ada14url_aggregator12get_protocolEv:
15512|   128k|    ada_lifetime_bound {
15513|   128k|  ada_log("url_aggregator::get_protocol");
15514|   128k|  return helpers::substring(buffer, 0, components.protocol_end);
15515|   128k|}
_ZNK3ada14url_aggregator9to_stringEv:
15517|  36.9k|[[nodiscard]] std::string ada::url_aggregator::to_string() const {
15518|  36.9k|  ada_log("url_aggregator::to_string buffer:", buffer, " [", buffer.size(),
15519|  36.9k|          " bytes]");
15520|  36.9k|  if (!is_valid) {
  ------------------
  |  Branch (15520:7): [True: 3.49k, False: 33.4k]
  ------------------
15521|  3.49k|    return "null";
15522|  3.49k|  }
15523|       |
15524|  33.4k|  std::string answer;
15525|  33.4k|  auto back = std::back_insert_iterator(answer);
15526|  33.4k|  answer.append("{\n");
15527|       |
15528|  33.4k|  answer.append("\t\"buffer\":\"");
15529|  33.4k|  helpers::encode_json(buffer, back);
15530|  33.4k|  answer.append("\",\n");
15531|       |
15532|  33.4k|  answer.append("\t\"protocol\":\"");
15533|  33.4k|  helpers::encode_json(get_protocol(), back);
15534|  33.4k|  answer.append("\",\n");
15535|       |
15536|  33.4k|  if (has_credentials()) {
  ------------------
  |  Branch (15536:7): [True: 11.9k, False: 21.5k]
  ------------------
15537|  11.9k|    answer.append("\t\"username\":\"");
15538|  11.9k|    helpers::encode_json(get_username(), back);
15539|  11.9k|    answer.append("\",\n");
15540|  11.9k|    answer.append("\t\"password\":\"");
15541|  11.9k|    helpers::encode_json(get_password(), back);
15542|  11.9k|    answer.append("\",\n");
15543|  11.9k|  }
15544|       |
15545|  33.4k|  answer.append("\t\"host\":\"");
15546|  33.4k|  helpers::encode_json(get_host(), back);
15547|  33.4k|  answer.append("\",\n");
15548|       |
15549|  33.4k|  answer.append("\t\"path\":\"");
15550|  33.4k|  helpers::encode_json(get_pathname(), back);
15551|  33.4k|  answer.append("\",\n");
15552|  33.4k|  answer.append("\t\"opaque path\":");
15553|  33.4k|  answer.append((has_opaque_path ? "true" : "false"));
  ------------------
  |  Branch (15553:18): [True: 2.88k, False: 30.6k]
  ------------------
15554|  33.4k|  answer.append(",\n");
15555|       |
15556|  33.4k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (15556:7): [True: 13.5k, False: 19.9k]
  ------------------
15557|  13.5k|    answer.append("\t\"query\":\"");
15558|  13.5k|    helpers::encode_json(get_search(), back);
15559|  13.5k|    answer.append("\",\n");
15560|  13.5k|  }
15561|  33.4k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15561:7): [True: 12.0k, False: 21.3k]
  ------------------
15562|  12.0k|    answer.append("\t\"fragment\":\"");
15563|  12.0k|    helpers::encode_json(get_hash(), back);
15564|  12.0k|    answer.append("\",\n");
15565|  12.0k|  }
15566|       |
15567|  33.4k|  auto convert_offset_to_string = [](uint32_t offset) -> std::string {
15568|  33.4k|    if (offset == url_components::omitted) {
15569|  33.4k|      return "null";
15570|  33.4k|    } else {
15571|  33.4k|      return std::to_string(offset);
15572|  33.4k|    }
15573|  33.4k|  };
15574|       |
15575|  33.4k|  answer.append("\t\"protocol_end\":");
15576|  33.4k|  answer.append(convert_offset_to_string(components.protocol_end));
15577|  33.4k|  answer.append(",\n");
15578|       |
15579|  33.4k|  answer.append("\t\"username_end\":");
15580|  33.4k|  answer.append(convert_offset_to_string(components.username_end));
15581|  33.4k|  answer.append(",\n");
15582|       |
15583|  33.4k|  answer.append("\t\"host_start\":");
15584|  33.4k|  answer.append(convert_offset_to_string(components.host_start));
15585|  33.4k|  answer.append(",\n");
15586|       |
15587|  33.4k|  answer.append("\t\"host_end\":");
15588|  33.4k|  answer.append(convert_offset_to_string(components.host_end));
15589|  33.4k|  answer.append(",\n");
15590|       |
15591|  33.4k|  answer.append("\t\"port\":");
15592|  33.4k|  answer.append(convert_offset_to_string(components.port));
15593|  33.4k|  answer.append(",\n");
15594|       |
15595|  33.4k|  answer.append("\t\"pathname_start\":");
15596|  33.4k|  answer.append(convert_offset_to_string(components.pathname_start));
15597|  33.4k|  answer.append(",\n");
15598|       |
15599|  33.4k|  answer.append("\t\"search_start\":");
15600|  33.4k|  answer.append(convert_offset_to_string(components.search_start));
15601|  33.4k|  answer.append(",\n");
15602|       |
15603|  33.4k|  answer.append("\t\"hash_start\":");
15604|  33.4k|  answer.append(convert_offset_to_string(components.hash_start));
15605|  33.4k|  answer.append("\n}");
15606|       |
15607|  33.4k|  return answer;
15608|  36.9k|}
_ZNK3ada14url_aggregator16has_valid_domainEv:
15610|  36.9k|[[nodiscard]] bool url_aggregator::has_valid_domain() const noexcept {
15611|  36.9k|  if (components.host_start == components.host_end) {
  ------------------
  |  Branch (15611:7): [True: 5.41k, False: 31.5k]
  ------------------
15612|  5.41k|    return false;
15613|  5.41k|  }
15614|       |  // Avoid allocation: construct a string_view directly into the buffer.
15615|  31.5k|  size_t start = components.host_start;
15616|  31.5k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (15616:7): [True: 31.5k, False: 0]
  ------------------
15617|  31.5k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (15617:7): [True: 14.5k, False: 17.0k]
  ------------------
15618|  14.5k|    start++;
15619|  14.5k|  }
15620|  31.5k|  return checkers::verify_dns_length(
15621|  31.5k|      std::string_view(buffer.data() + start, components.host_end - start));
15622|  36.9k|}
_ZN3ada14url_aggregator10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
15624|  6.97k|bool url_aggregator::parse_ipv4(std::string_view input, bool in_place) {
15625|  6.97k|  ada_log("parse_ipv4 ", input, " [", input.size(),
15626|  6.97k|          " bytes], overlaps with buffer: ",
15627|  6.97k|          helpers::overlaps(input, buffer) ? "yes" : "no");
15628|  6.97k|  ADA_ASSERT_TRUE(validate());
15629|  6.97k|  const bool trailing_dot = (input.back() == '.');
15630|  6.97k|  if (trailing_dot) {
  ------------------
  |  Branch (15630:7): [True: 627, False: 6.35k]
  ------------------
15631|    627|    input.remove_suffix(1);
15632|    627|  }
15633|  6.97k|  size_t digit_count{0};
15634|  6.97k|  int pure_decimal_count = 0;  // entries that are decimal
15635|  6.97k|  uint64_t ipv4{0};
15636|       |  // we could unroll for better performance?
15637|  8.87k|  for (; (digit_count < 4) && !(input.empty()); digit_count++) {
  ------------------
  |  Branch (15637:10): [True: 8.75k, False: 111]
  |  Branch (15637:31): [True: 8.75k, False: 0]
  ------------------
15638|  8.75k|    uint32_t
15639|  8.75k|        segment_result{};  // If any number exceeds 32 bits, we have an error.
15640|  8.75k|    bool is_hex = checkers::has_hex_prefix(input);
15641|  8.75k|    if (is_hex && ((input.length() == 2) ||
  ------------------
  |  Branch (15641:9): [True: 2.20k, False: 6.55k]
  |  Branch (15641:20): [True: 344, False: 1.86k]
  ------------------
15642|  1.86k|                   ((input.length() > 2) && (input[2] == '.')))) {
  ------------------
  |  Branch (15642:21): [True: 1.86k, False: 0]
  |  Branch (15642:45): [True: 55, False: 1.81k]
  ------------------
15643|       |      // special case
15644|    399|      segment_result = 0;
15645|    399|      input.remove_prefix(2);
15646|  8.36k|    } else {
15647|  8.36k|      std::from_chars_result r{};
15648|  8.36k|      if (is_hex) {
  ------------------
  |  Branch (15648:11): [True: 1.81k, False: 6.55k]
  ------------------
15649|  1.81k|        ada_log("parse_ipv4 trying to parse hex number");
15650|  1.81k|        r = std::from_chars(input.data() + 2, input.data() + input.size(),
15651|  1.81k|                            segment_result, 16);
15652|  6.55k|      } else if ((input.length() >= 2) && input[0] == '0' &&
  ------------------
  |  Branch (15652:18): [True: 4.58k, False: 1.97k]
  |  Branch (15652:43): [True: 1.83k, False: 2.74k]
  ------------------
15653|  1.83k|                 checkers::is_digit(input[1])) {
  ------------------
  |  Branch (15653:18): [True: 1.27k, False: 555]
  ------------------
15654|  1.27k|        ada_log("parse_ipv4 trying to parse octal number");
15655|  1.27k|        r = std::from_chars(input.data() + 1, input.data() + input.size(),
15656|  1.27k|                            segment_result, 8);
15657|  5.27k|      } else {
15658|  5.27k|        ada_log("parse_ipv4 trying to parse decimal number");
15659|  5.27k|        pure_decimal_count++;
15660|  5.27k|        r = std::from_chars(input.data(), input.data() + input.size(),
15661|  5.27k|                            segment_result, 10);
15662|  5.27k|      }
15663|  8.36k|      if (r.ec != std::errc()) {
  ------------------
  |  Branch (15663:11): [True: 1.50k, False: 6.85k]
  ------------------
15664|  1.50k|        ada_log("parse_ipv4 parsing failed");
15665|  1.50k|        return is_valid = false;
15666|  1.50k|      }
15667|  6.85k|      ada_log("parse_ipv4 parsed ", segment_result);
15668|  6.85k|      input.remove_prefix(r.ptr - input.data());
15669|  6.85k|    }
15670|  7.25k|    if (input.empty()) {
  ------------------
  |  Branch (15670:9): [True: 4.98k, False: 2.26k]
  ------------------
15671|       |      // We have the last value.
15672|       |      // At this stage, ipv4 contains digit_count*8 bits.
15673|       |      // So we have 32-digit_count*8 bits left.
15674|  4.98k|      if (segment_result >= (uint64_t(1) << (32 - digit_count * 8))) {
  ------------------
  |  Branch (15674:11): [True: 66, False: 4.92k]
  ------------------
15675|     66|        return is_valid = false;
15676|     66|      }
15677|  4.92k|      ipv4 <<= (32 - digit_count * 8);
15678|  4.92k|      ipv4 |= segment_result;
15679|  4.92k|      goto final;
15680|  4.98k|    } else {
15681|       |      // There is more, so that the value must no be larger than 255
15682|       |      // and we must have a '.'.
15683|  2.26k|      if ((segment_result > 255) || (input[0] != '.')) {
  ------------------
  |  Branch (15683:11): [True: 201, False: 2.06k]
  |  Branch (15683:37): [True: 176, False: 1.89k]
  ------------------
15684|    377|        return is_valid = false;
15685|    377|      }
15686|  1.89k|      ipv4 <<= 8;
15687|  1.89k|      ipv4 |= segment_result;
15688|  1.89k|      input.remove_prefix(1);  // remove '.'
15689|  1.89k|    }
15690|  7.25k|  }
15691|    111|  if ((digit_count != 4) || (!input.empty())) {
  ------------------
  |  Branch (15691:7): [True: 0, False: 111]
  |  Branch (15691:29): [True: 111, False: 0]
  ------------------
15692|    111|    ada_log("parse_ipv4 found invalid (more than 4 numbers or empty) ");
15693|    111|    return is_valid = false;
15694|    111|  }
15695|  4.92k|final:
15696|  4.92k|  ada_log("url_aggregator::parse_ipv4 completed ", get_href(),
15697|  4.92k|          " host: ", get_host());
15698|       |
15699|       |  // We could also check r.ptr to see where the parsing ended.
15700|  4.92k|  if (in_place && pure_decimal_count == 4 && !trailing_dot) {
  ------------------
  |  Branch (15700:7): [True: 4.02k, False: 894]
  |  Branch (15700:19): [True: 0, False: 4.02k]
  |  Branch (15700:46): [True: 0, False: 0]
  ------------------
15701|      0|    ada_log(
15702|      0|        "url_aggregator::parse_ipv4 completed and was already correct in the "
15703|      0|        "buffer");
15704|       |    // The original input was already all decimal and we validated it. So we
15705|       |    // don't need to do anything.
15706|  4.92k|  } else {
15707|  4.92k|    ada_log("url_aggregator::parse_ipv4 completed and we need to update it");
15708|       |    // Optimization opportunity: Get rid of unnecessary string return in ipv4
15709|       |    // serializer.
15710|       |    // TODO: This is likely a bug because it goes back update_base_hostname, not
15711|       |    // what we want to do.
15712|  4.92k|    update_base_hostname(
15713|  4.92k|        ada::serializers::ipv4(ipv4));  // We have to reserialize the address.
15714|  4.92k|  }
15715|  4.92k|  host_type = IPV4;
15716|  4.92k|  ADA_ASSERT_TRUE(validate());
15717|  4.92k|  return true;
15718|    111|}
_ZN3ada14url_aggregator10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15720|  1.60k|bool url_aggregator::parse_ipv6(std::string_view input) {
15721|       |  // TODO: Implement in_place optimization: we know that input points
15722|       |  // in the buffer, so we can just check whether the buffer is already
15723|       |  // well formatted.
15724|       |  // TODO: Find a way to merge parse_ipv6 with url.cpp implementation.
15725|  1.60k|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
15726|  1.60k|  ADA_ASSERT_TRUE(validate());
15727|  1.60k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15728|  1.60k|  if (input.empty()) {
  ------------------
  |  Branch (15728:7): [True: 84, False: 1.51k]
  ------------------
15729|     84|    return is_valid = false;
15730|     84|  }
15731|       |  // Let address be a new IPv6 address whose IPv6 pieces are all 0.
15732|  1.51k|  std::array<uint16_t, 8> address{};
15733|       |
15734|       |  // Let pieceIndex be 0.
15735|  1.51k|  int piece_index = 0;
15736|       |
15737|       |  // Let compress be null.
15738|  1.51k|  std::optional<int> compress{};
15739|       |
15740|       |  // Let pointer be a pointer for input.
15741|  1.51k|  std::string_view::iterator pointer = input.begin();
15742|       |
15743|       |  // If c is U+003A (:), then:
15744|  1.51k|  if (input[0] == ':') {
  ------------------
  |  Branch (15744:7): [True: 543, False: 975]
  ------------------
15745|       |    // If remaining does not start with U+003A (:), validation error, return
15746|       |    // failure.
15747|    543|    if (input.size() == 1 || input[1] != ':') {
  ------------------
  |  Branch (15747:9): [True: 19, False: 524]
  |  Branch (15747:30): [True: 49, False: 475]
  ------------------
15748|     68|      ada_log("parse_ipv6 starts with : but the rest does not start with :");
15749|     68|      return is_valid = false;
15750|     68|    }
15751|       |
15752|       |    // Increase pointer by 2.
15753|    475|    pointer += 2;
15754|       |
15755|       |    // Increase pieceIndex by 1 and then set compress to pieceIndex.
15756|    475|    compress = ++piece_index;
15757|    475|  }
15758|       |
15759|       |  // While c is not the EOF code point:
15760|  4.47k|  while (pointer != input.end()) {
  ------------------
  |  Branch (15760:10): [True: 3.64k, False: 822]
  ------------------
15761|       |    // If pieceIndex is 8, validation error, return failure.
15762|  3.64k|    if (piece_index == 8) {
  ------------------
  |  Branch (15762:9): [True: 23, False: 3.62k]
  ------------------
15763|     23|      ada_log("parse_ipv6 piece_index == 8");
15764|     23|      return is_valid = false;
15765|     23|    }
15766|       |
15767|       |    // If c is U+003A (:), then:
15768|  3.62k|    if (*pointer == ':') {
  ------------------
  |  Branch (15768:9): [True: 378, False: 3.24k]
  ------------------
15769|       |      // If compress is non-null, validation error, return failure.
15770|    378|      if (compress.has_value()) {
  ------------------
  |  Branch (15770:11): [True: 26, False: 352]
  ------------------
15771|     26|        ada_log("parse_ipv6 compress is non-null");
15772|     26|        return is_valid = false;
15773|     26|      }
15774|       |
15775|       |      // Increase pointer and pieceIndex by 1, set compress to pieceIndex, and
15776|       |      // then continue.
15777|    352|      pointer++;
15778|    352|      compress = ++piece_index;
15779|    352|      continue;
15780|    378|    }
15781|       |
15782|       |    // Let value and length be 0.
15783|  3.24k|    uint16_t value = 0, length = 0;
15784|       |
15785|       |    // While length is less than 4 and c is an ASCII hex digit,
15786|       |    // set value to value times 0x10 + c interpreted as hexadecimal number, and
15787|       |    // increase pointer and length by 1.
15788|  8.10k|    while (length < 4 && pointer != input.end() &&
  ------------------
  |  Branch (15788:12): [True: 7.80k, False: 303]
  |  Branch (15788:26): [True: 7.30k, False: 496]
  ------------------
15789|  7.30k|           unicode::is_ascii_hex_digit(*pointer)) {
  ------------------
  |  Branch (15789:12): [True: 4.85k, False: 2.44k]
  ------------------
15790|       |      // https://stackoverflow.com/questions/39060852/why-does-the-addition-of-two-shorts-return-an-int
15791|  4.85k|      value = uint16_t(value * 0x10 + unicode::convert_hex_to_binary(*pointer));
15792|  4.85k|      pointer++;
15793|  4.85k|      length++;
15794|  4.85k|    }
15795|       |
15796|       |    // If c is U+002E (.), then:
15797|  3.24k|    if (pointer != input.end() && *pointer == '.') {
  ------------------
  |  Branch (15797:9): [True: 2.69k, False: 553]
  |  Branch (15797:35): [True: 343, False: 2.35k]
  ------------------
15798|       |      // If length is 0, validation error, return failure.
15799|    343|      if (length == 0) {
  ------------------
  |  Branch (15799:11): [True: 18, False: 325]
  ------------------
15800|     18|        ada_log("parse_ipv6 length is 0");
15801|     18|        return is_valid = false;
15802|     18|      }
15803|       |
15804|       |      // Decrease pointer by length.
15805|    325|      pointer -= length;
15806|       |
15807|       |      // If pieceIndex is greater than 6, validation error, return failure.
15808|    325|      if (piece_index > 6) {
  ------------------
  |  Branch (15808:11): [True: 16, False: 309]
  ------------------
15809|     16|        ada_log("parse_ipv6 piece_index > 6");
15810|     16|        return is_valid = false;
15811|     16|      }
15812|       |
15813|       |      // Let numbersSeen be 0.
15814|    309|      int numbers_seen = 0;
15815|       |
15816|       |      // While c is not the EOF code point:
15817|    989|      while (pointer != input.end()) {
  ------------------
  |  Branch (15817:14): [True: 884, False: 105]
  ------------------
15818|       |        // Let ipv4Piece be null.
15819|    884|        std::optional<uint16_t> ipv4_piece{};
15820|       |
15821|       |        // If numbersSeen is greater than 0, then:
15822|    884|        if (numbers_seen > 0) {
  ------------------
  |  Branch (15822:13): [True: 575, False: 309]
  ------------------
15823|       |          // If c is a U+002E (.) and numbersSeen is less than 4, then increase
15824|       |          // pointer by 1.
15825|    575|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (15825:15): [True: 541, False: 34]
  |  Branch (15825:34): [True: 525, False: 16]
  ------------------
15826|    525|            pointer++;
15827|    525|          } else {
15828|       |            // Otherwise, validation error, return failure.
15829|     50|            ada_log("parse_ipv6 Otherwise, validation error, return failure");
15830|     50|            return is_valid = false;
15831|     50|          }
15832|    575|        }
15833|       |
15834|       |        // If c is not an ASCII digit, validation error, return failure.
15835|    834|        if (pointer == input.end() || !checkers::is_digit(*pointer)) {
  ------------------
  |  Branch (15835:13): [True: 28, False: 806]
  |  Branch (15835:39): [True: 57, False: 749]
  ------------------
15836|     85|          ada_log(
15837|     85|              "parse_ipv6 If c is not an ASCII digit, validation error, return "
15838|     85|              "failure");
15839|     85|          return is_valid = false;
15840|     85|        }
15841|       |
15842|       |        // While c is an ASCII digit:
15843|  1.77k|        while (pointer != input.end() && checkers::is_digit(*pointer)) {
  ------------------
  |  Branch (15843:16): [True: 1.67k, False: 105]
  |  Branch (15843:42): [True: 1.09k, False: 575]
  ------------------
15844|       |          // Let number be c interpreted as decimal number.
15845|  1.09k|          int number = *pointer - '0';
15846|       |
15847|       |          // If ipv4Piece is null, then set ipv4Piece to number.
15848|  1.09k|          if (!ipv4_piece.has_value()) {
  ------------------
  |  Branch (15848:15): [True: 749, False: 346]
  ------------------
15849|    749|            ipv4_piece = number;
15850|    749|          }
15851|       |          // Otherwise, if ipv4Piece is 0, validation error, return failure.
15852|    346|          else if (ipv4_piece == 0) {
  ------------------
  |  Branch (15852:20): [True: 34, False: 312]
  ------------------
15853|     34|            ada_log("parse_ipv6 if ipv4Piece is 0, validation error");
15854|     34|            return is_valid = false;
15855|     34|          }
15856|       |          // Otherwise, set ipv4Piece to ipv4Piece times 10 + number.
15857|    312|          else {
15858|    312|            ipv4_piece = *ipv4_piece * 10 + number;
15859|    312|          }
15860|       |
15861|       |          // If ipv4Piece is greater than 255, validation error, return failure.
15862|  1.06k|          if (ipv4_piece > 255) {
  ------------------
  |  Branch (15862:15): [True: 35, False: 1.02k]
  ------------------
15863|     35|            ada_log("parse_ipv6 ipv4_piece > 255");
15864|     35|            return is_valid = false;
15865|     35|          }
15866|       |
15867|       |          // Increase pointer by 1.
15868|  1.02k|          pointer++;
15869|  1.02k|        }
15870|       |
15871|       |        // Set address[pieceIndex] to address[pieceIndex] times 0x100 +
15872|       |        // ipv4Piece.
15873|       |        // https://stackoverflow.com/questions/39060852/why-does-the-addition-of-two-shorts-return-an-int
15874|    680|        if (!ipv4_piece.has_value()) {
  ------------------
  |  Branch (15874:13): [True: 0, False: 680]
  ------------------
15875|      0|          return is_valid = false;
15876|      0|        }
15877|    680|        address[piece_index] =
15878|    680|            uint16_t(address[piece_index] * 0x100 + *ipv4_piece);
15879|       |
15880|       |        // Increase numbersSeen by 1.
15881|    680|        numbers_seen++;
15882|       |
15883|       |        // If numbersSeen is 2 or 4, then increase pieceIndex by 1.
15884|    680|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (15884:13): [True: 199, False: 481]
  |  Branch (15884:34): [True: 95, False: 386]
  ------------------
15885|    294|          piece_index++;
15886|    294|        }
15887|    680|      }
15888|       |
15889|       |      // If numbersSeen is not 4, validation error, return failure.
15890|    105|      if (numbers_seen != 4) {
  ------------------
  |  Branch (15890:11): [True: 27, False: 78]
  ------------------
15891|     27|        return is_valid = false;
15892|     27|      }
15893|       |
15894|       |      // Break.
15895|     78|      break;
15896|    105|    }
15897|       |    // Otherwise, if c is U+003A (:):
15898|  2.90k|    else if ((pointer != input.end()) && (*pointer == ':')) {
  ------------------
  |  Branch (15898:14): [True: 2.35k, False: 553]
  |  Branch (15898:42): [True: 2.13k, False: 214]
  ------------------
15899|       |      // Increase pointer by 1.
15900|  2.13k|      pointer++;
15901|       |
15902|       |      // If c is the EOF code point, validation error, return failure.
15903|  2.13k|      if (pointer == input.end()) {
  ------------------
  |  Branch (15903:11): [True: 22, False: 2.11k]
  ------------------
15904|     22|        ada_log(
15905|     22|            "parse_ipv6 If c is the EOF code point, validation error, return "
15906|     22|            "failure");
15907|     22|        return is_valid = false;
15908|     22|      }
15909|  2.13k|    }
15910|       |    // Otherwise, if c is not the EOF code point, validation error, return
15911|       |    // failure.
15912|    767|    else if (pointer != input.end()) {
  ------------------
  |  Branch (15912:14): [True: 214, False: 553]
  ------------------
15913|    214|      ada_log(
15914|    214|          "parse_ipv6 Otherwise, if c is not the EOF code point, validation "
15915|    214|          "error, return failure");
15916|    214|      return is_valid = false;
15917|    214|    }
15918|       |
15919|       |    // Set address[pieceIndex] to value.
15920|  2.66k|    address[piece_index] = value;
15921|       |
15922|       |    // Increase pieceIndex by 1.
15923|  2.66k|    piece_index++;
15924|  2.66k|  }
15925|       |
15926|       |  // If compress is non-null, then:
15927|    900|  if (compress.has_value()) {
  ------------------
  |  Branch (15927:7): [True: 727, False: 173]
  ------------------
15928|       |    // Let swaps be pieceIndex - compress.
15929|    727|    int swaps = piece_index - *compress;
15930|       |
15931|       |    // Set pieceIndex to 7.
15932|    727|    piece_index = 7;
15933|       |
15934|       |    // While pieceIndex is not 0 and swaps is greater than 0,
15935|       |    // swap address[pieceIndex] with address[compress + swaps - 1], and then
15936|       |    // decrease both pieceIndex and swaps by 1.
15937|  1.88k|    while (piece_index != 0 && swaps > 0) {
  ------------------
  |  Branch (15937:12): [True: 1.82k, False: 55]
  |  Branch (15937:32): [True: 1.15k, False: 672]
  ------------------
15938|  1.15k|      std::swap(address[piece_index], address[*compress + swaps - 1]);
15939|  1.15k|      piece_index--;
15940|  1.15k|      swaps--;
15941|  1.15k|    }
15942|    727|  }
15943|       |  // Otherwise, if compress is null and pieceIndex is not 8, validation error,
15944|       |  // return failure.
15945|    173|  else if (piece_index != 8) {
  ------------------
  |  Branch (15945:12): [True: 90, False: 83]
  ------------------
15946|     90|    ada_log(
15947|     90|        "parse_ipv6 if compress is null and pieceIndex is not 8, validation "
15948|     90|        "error, return failure");
15949|     90|    return is_valid = false;
15950|     90|  }
15951|       |  // TODO: Optimization opportunity: Get rid of unnecessary string creation.
15952|       |  // TODO: This is likely a bug because it goes back update_base_hostname, not
15953|       |  // what we want to do.
15954|    810|  update_base_hostname(ada::serializers::ipv6(address));
15955|    810|  ada_log("parse_ipv6 ", get_hostname());
15956|    810|  ADA_ASSERT_TRUE(validate());
15957|    810|  host_type = IPV6;
15958|    810|  return true;
15959|    900|}
_ZN3ada14url_aggregator17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15961|  1.39k|bool url_aggregator::parse_opaque_host(std::string_view input) {
15962|  1.39k|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
15963|  1.39k|  ADA_ASSERT_TRUE(validate());
15964|  1.39k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15965|  1.39k|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (15965:7): [True: 147, False: 1.25k]
  ------------------
15966|    147|    return is_valid = false;
15967|    147|  }
15968|       |
15969|       |  // Return the result of running UTF-8 percent-encode on input using the C0
15970|       |  // control percent-encode set.
15971|  1.25k|  size_t idx = ada::unicode::percent_encode_index(
15972|  1.25k|      input, character_sets::C0_CONTROL_PERCENT_ENCODE);
15973|  1.25k|  if (idx == input.size()) {
  ------------------
  |  Branch (15973:7): [True: 803, False: 447]
  ------------------
15974|    803|    update_base_hostname(input);
15975|    803|  } else {
15976|       |    // We only create a temporary string if we need to.
15977|    447|    update_base_hostname(ada::unicode::percent_encode(
15978|    447|        input, character_sets::C0_CONTROL_PERCENT_ENCODE, idx));
15979|    447|  }
15980|  1.25k|  ADA_ASSERT_TRUE(validate());
15981|  1.25k|  return true;
15982|  1.39k|}
_ZNK3ada14url_aggregator10to_diagramEv:
15984|  36.9k|[[nodiscard]] std::string url_aggregator::to_diagram() const {
15985|  36.9k|  if (!is_valid) {
  ------------------
  |  Branch (15985:7): [True: 3.49k, False: 33.4k]
  ------------------
15986|  3.49k|    return "invalid";
15987|  3.49k|  }
15988|  33.4k|  std::string answer;
15989|  33.4k|  answer.append(buffer);
15990|  33.4k|  answer.append(" [");
15991|  33.4k|  answer.append(std::to_string(buffer.size()));
15992|  33.4k|  answer.append(" bytes]");
15993|  33.4k|  answer.append("\n");
15994|       |  // first line
15995|  33.4k|  std::string line1;
15996|  33.4k|  line1.resize(buffer.size(), ' ');
15997|  33.4k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15997:7): [True: 12.0k, False: 21.3k]
  ------------------
15998|  12.0k|    line1[components.hash_start] = '|';
15999|  12.0k|  }
16000|  33.4k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (16000:7): [True: 13.5k, False: 19.9k]
  ------------------
16001|  13.5k|    line1[components.search_start] = '|';
16002|  13.5k|  }
16003|  33.4k|  if (components.pathname_start != buffer.size()) {
  ------------------
  |  Branch (16003:7): [True: 32.4k, False: 1.07k]
  ------------------
16004|  32.4k|    line1[components.pathname_start] = '|';
16005|  32.4k|  }
16006|  33.4k|  if (components.host_end != buffer.size()) {
  ------------------
  |  Branch (16006:7): [True: 32.4k, False: 1.00k]
  ------------------
16007|  32.4k|    line1[components.host_end] = '|';
16008|  32.4k|  }
16009|  33.4k|  if (components.host_start != buffer.size()) {
  ------------------
  |  Branch (16009:7): [True: 32.8k, False: 590]
  ------------------
16010|  32.8k|    line1[components.host_start] = '|';
16011|  32.8k|  }
16012|  33.4k|  if (components.username_end != buffer.size()) {
  ------------------
  |  Branch (16012:7): [True: 32.8k, False: 590]
  ------------------
16013|  32.8k|    line1[components.username_end] = '|';
16014|  32.8k|  }
16015|  33.4k|  if (components.protocol_end != buffer.size()) {
  ------------------
  |  Branch (16015:7): [True: 32.9k, False: 547]
  ------------------
16016|  32.9k|    line1[components.protocol_end] = '|';
16017|  32.9k|  }
16018|  33.4k|  answer.append(line1);
16019|  33.4k|  answer.append("\n");
16020|       |
16021|  33.4k|  std::string line2 = line1;
16022|  33.4k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (16022:7): [True: 12.0k, False: 21.3k]
  ------------------
16023|  12.0k|    line2[components.hash_start] = '`';
16024|  12.0k|    line1[components.hash_start] = ' ';
16025|       |
16026|   577k|    for (size_t i = components.hash_start + 1; i < line2.size(); i++) {
  ------------------
  |  Branch (16026:48): [True: 565k, False: 12.0k]
  ------------------
16027|   565k|      line2[i] = '-';
16028|   565k|    }
16029|  12.0k|    line2.append(" hash_start");
16030|  12.0k|    answer.append(line2);
16031|  12.0k|    answer.append("\n");
16032|  12.0k|  }
16033|       |
16034|  33.4k|  std::string line3 = line1;
16035|  33.4k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (16035:7): [True: 13.5k, False: 19.9k]
  ------------------
16036|  13.5k|    line3[components.search_start] = '`';
16037|  13.5k|    line1[components.search_start] = ' ';
16038|       |
16039|  1.13M|    for (size_t i = components.search_start + 1; i < line3.size(); i++) {
  ------------------
  |  Branch (16039:50): [True: 1.11M, False: 13.5k]
  ------------------
16040|  1.11M|      line3[i] = '-';
16041|  1.11M|    }
16042|  13.5k|    line3.append(" search_start ");
16043|  13.5k|    line3.append(std::to_string(components.search_start));
16044|  13.5k|    answer.append(line3);
16045|  13.5k|    answer.append("\n");
16046|  13.5k|  }
16047|       |
16048|  33.4k|  std::string line4 = line1;
16049|  33.4k|  if (components.pathname_start != buffer.size()) {
  ------------------
  |  Branch (16049:7): [True: 32.4k, False: 1.07k]
  ------------------
16050|  32.4k|    line4[components.pathname_start] = '`';
16051|  32.4k|    line1[components.pathname_start] = ' ';
16052|  2.32M|    for (size_t i = components.pathname_start + 1; i < line4.size(); i++) {
  ------------------
  |  Branch (16052:52): [True: 2.29M, False: 32.4k]
  ------------------
16053|  2.29M|      line4[i] = '-';
16054|  2.29M|    }
16055|  32.4k|    line4.append(" pathname_start ");
16056|  32.4k|    line4.append(std::to_string(components.pathname_start));
16057|  32.4k|    answer.append(line4);
16058|  32.4k|    answer.append("\n");
16059|  32.4k|  }
16060|       |
16061|  33.4k|  std::string line5 = line1;
16062|  33.4k|  if (components.host_end != buffer.size()) {
  ------------------
  |  Branch (16062:7): [True: 32.4k, False: 1.00k]
  ------------------
16063|  32.4k|    line5[components.host_end] = '`';
16064|  32.4k|    line1[components.host_end] = ' ';
16065|       |
16066|  2.33M|    for (size_t i = components.host_end + 1; i < line5.size(); i++) {
  ------------------
  |  Branch (16066:46): [True: 2.30M, False: 32.4k]
  ------------------
16067|  2.30M|      line5[i] = '-';
16068|  2.30M|    }
16069|  32.4k|    line5.append(" host_end ");
16070|  32.4k|    line5.append(std::to_string(components.host_end));
16071|  32.4k|    answer.append(line5);
16072|  32.4k|    answer.append("\n");
16073|  32.4k|  }
16074|       |
16075|  33.4k|  std::string line6 = line1;
16076|  33.4k|  if (components.host_start != buffer.size()) {
  ------------------
  |  Branch (16076:7): [True: 32.8k, False: 590]
  ------------------
16077|  32.8k|    line6[components.host_start] = '`';
16078|  32.8k|    line1[components.host_start] = ' ';
16079|       |
16080|  2.91M|    for (size_t i = components.host_start + 1; i < line6.size(); i++) {
  ------------------
  |  Branch (16080:48): [True: 2.88M, False: 32.8k]
  ------------------
16081|  2.88M|      line6[i] = '-';
16082|  2.88M|    }
16083|  32.8k|    line6.append(" host_start ");
16084|  32.8k|    line6.append(std::to_string(components.host_start));
16085|  32.8k|    answer.append(line6);
16086|  32.8k|    answer.append("\n");
16087|  32.8k|  }
16088|       |
16089|  33.4k|  std::string line7 = line1;
16090|  33.4k|  if (components.username_end != buffer.size()) {
  ------------------
  |  Branch (16090:7): [True: 32.8k, False: 590]
  ------------------
16091|  32.8k|    line7[components.username_end] = '`';
16092|  32.8k|    line1[components.username_end] = ' ';
16093|       |
16094|  3.59M|    for (size_t i = components.username_end + 1; i < line7.size(); i++) {
  ------------------
  |  Branch (16094:50): [True: 3.55M, False: 32.8k]
  ------------------
16095|  3.55M|      line7[i] = '-';
16096|  3.55M|    }
16097|  32.8k|    line7.append(" username_end ");
16098|  32.8k|    line7.append(std::to_string(components.username_end));
16099|  32.8k|    answer.append(line7);
16100|  32.8k|    answer.append("\n");
16101|  32.8k|  }
16102|       |
16103|  33.4k|  std::string line8 = line1;
16104|  33.4k|  if (components.protocol_end != buffer.size()) {
  ------------------
  |  Branch (16104:7): [True: 32.9k, False: 547]
  ------------------
16105|  32.9k|    line8[components.protocol_end] = '`';
16106|  32.9k|    line1[components.protocol_end] = ' ';
16107|       |
16108|  4.34M|    for (size_t i = components.protocol_end + 1; i < line8.size(); i++) {
  ------------------
  |  Branch (16108:50): [True: 4.31M, False: 32.9k]
  ------------------
16109|  4.31M|      line8[i] = '-';
16110|  4.31M|    }
16111|  32.9k|    line8.append(" protocol_end ");
16112|  32.9k|    line8.append(std::to_string(components.protocol_end));
16113|  32.9k|    answer.append(line8);
16114|  32.9k|    answer.append("\n");
16115|  32.9k|  }
16116|       |
16117|  33.4k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (16117:7): [True: 21.3k, False: 12.0k]
  ------------------
16118|  21.3k|    answer.append("note: hash omitted\n");
16119|  21.3k|  }
16120|  33.4k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (16120:7): [True: 19.9k, False: 13.5k]
  ------------------
16121|  19.9k|    answer.append("note: search omitted\n");
16122|  19.9k|  }
16123|  33.4k|  if (components.protocol_end > buffer.size()) {
  ------------------
  |  Branch (16123:7): [True: 0, False: 33.4k]
  ------------------
16124|      0|    answer.append("warning: protocol_end overflows\n");
16125|      0|  }
16126|  33.4k|  if (components.username_end > buffer.size()) {
  ------------------
  |  Branch (16126:7): [True: 0, False: 33.4k]
  ------------------
16127|      0|    answer.append("warning: username_end overflows\n");
16128|      0|  }
16129|  33.4k|  if (components.host_start > buffer.size()) {
  ------------------
  |  Branch (16129:7): [True: 0, False: 33.4k]
  ------------------
16130|      0|    answer.append("warning: host_start overflows\n");
16131|      0|  }
16132|  33.4k|  if (components.host_end > buffer.size()) {
  ------------------
  |  Branch (16132:7): [True: 0, False: 33.4k]
  ------------------
16133|      0|    answer.append("warning: host_end overflows\n");
16134|      0|  }
16135|  33.4k|  if (components.pathname_start > buffer.size()) {
  ------------------
  |  Branch (16135:7): [True: 0, False: 33.4k]
  ------------------
16136|      0|    answer.append("warning: pathname_start overflows\n");
16137|      0|  }
16138|  33.4k|  return answer;
16139|  36.9k|}
_ZN3ada14url_aggregator15delete_dash_dotEv:
16141|     30|void url_aggregator::delete_dash_dot() {
16142|     30|  ada_log("url_aggregator::delete_dash_dot");
16143|     30|  ADA_ASSERT_TRUE(validate());
16144|     30|  ADA_ASSERT_TRUE(has_dash_dot());
16145|     30|  buffer.erase(components.host_end, 2);
16146|     30|  components.pathname_start -= 2;
16147|     30|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (16147:7): [True: 0, False: 30]
  ------------------
16148|      0|    components.search_start -= 2;
16149|      0|  }
16150|     30|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (16150:7): [True: 0, False: 30]
  ------------------
16151|      0|    components.hash_start -= 2;
16152|      0|  }
16153|     30|  ADA_ASSERT_TRUE(validate());
16154|     30|  ADA_ASSERT_TRUE(!has_dash_dot());
16155|     30|}
parse.cc:_ZZN3ada4idna22utf32_length_from_utf8EPKcmENK3$_0clEa:
  263|   539k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  264|       |    // -65 is 0b10111111, anything larger in two-complement's
  265|       |    // should start a new code point.
  266|   539k|    return c > -65;
  267|   539k|  });
parse.cc:_ZZN3ada4idna9ascii_mapEPcmENK3$_0clEh:
 2838|  89.3k|  auto broadcast = [](uint8_t v) -> uint64_t {
 2839|  89.3k|    return 0x101010101010101ull * v;
 2840|  89.3k|  };
parse.cc:_ZN3ada4idnaL11idna_lookupEj:
 2786|   273k|static uint16_t idna_lookup(uint32_t cp) noexcept {
 2787|       |  // -- Two-level table covers the full active code-point range ---------------
 2788|   273k|  if (cp < IDNA_LOW_RANGE_END) {
  ------------------
  |  Branch (2788:7): [True: 273k, False: 839]
  ------------------
 2789|   273k|    uint16_t ref = idna_stage1[cp >> IDNA_BLOCK_BITS];
 2790|   273k|    if (ref & IDNA_BOOL_FLAG) {
  ------------------
  |  Branch (2790:9): [True: 131k, False: 141k]
  ------------------
 2791|       |      // Boolean block: one bit per code point, 1 = VALID, 0 = DISALLOWED.
 2792|   131k|      uint32_t bit_idx =
 2793|   131k|          static_cast<uint32_t>(ref & ~IDNA_BOOL_FLAG) * IDNA_BLOCK_SIZE +
 2794|   131k|          (cp & IDNA_BLOCK_MASK);
 2795|   131k|      bool is_valid = (idna_bool_blocks[bit_idx >> 6] >> (bit_idx & 63u)) & 1u;
 2796|   131k|      return is_valid ? IDNA_VALID : IDNA_DISALLOWED;
  ------------------
  |  Branch (2796:14): [True: 131k, False: 45]
  ------------------
 2797|   131k|    }
 2798|   141k|    return idna_stage2[ref + (cp & IDNA_BLOCK_MASK)];
 2799|   273k|  }
 2800|       |
 2801|       |  // -- Variation selectors supplement (U+E0100-U+E01EF): all ignored ---------
 2802|       |  // Everything else above IDNA_LOW_RANGE_END is disallowed.
 2803|    839|  if (cp >= IDNA_HIGH_IGNORED_START && cp < IDNA_HIGH_IGNORED_END) {
  ------------------
  |  Branch (2803:7): [True: 616, False: 223]
  |  Branch (2803:40): [True: 211, False: 405]
  ------------------
 2804|    211|    return IDNA_IGNORED;
 2805|    211|  }
 2806|       |
 2807|    628|  return IDNA_DISALLOWED;
 2808|    839|}
parse.cc:_ZN3ada4idnaL9utf8_nextERPKh:
 2813|   433k|static char32_t utf8_next(const uint8_t*& ptr) noexcept {
 2814|   433k|  uint8_t b0 = *ptr++;
 2815|   433k|  if (b0 < 0x80u) return static_cast<char32_t>(b0);
  ------------------
  |  Branch (2815:7): [True: 58.5k, False: 375k]
  ------------------
 2816|   375k|  if (b0 < 0xE0u) {
  ------------------
  |  Branch (2816:7): [True: 128k, False: 247k]
  ------------------
 2817|   128k|    uint32_t cp = (b0 & 0x1Fu) << 6;
 2818|   128k|    cp |= (*ptr++ & 0x3Fu);
 2819|   128k|    return static_cast<char32_t>(cp);
 2820|   128k|  }
 2821|   247k|  if (b0 < 0xF0u) {
  ------------------
  |  Branch (2821:7): [True: 244k, False: 2.37k]
  ------------------
 2822|   244k|    uint32_t cp = (b0 & 0x0Fu) << 12;
 2823|   244k|    cp |= ((*ptr++ & 0x3Fu) << 6);
 2824|   244k|    cp |= (*ptr++ & 0x3Fu);
 2825|   244k|    return static_cast<char32_t>(cp);
 2826|   244k|  }
 2827|       |  // 4-byte sequence
 2828|  2.37k|  uint32_t cp = (b0 & 0x07u) << 18;
 2829|  2.37k|  cp |= ((*ptr++ & 0x3Fu) << 12);
 2830|  2.37k|  cp |= ((*ptr++ & 0x3Fu) << 6);
 2831|  2.37k|  cp |= (*ptr++ & 0x3Fu);
 2832|  2.37k|  return static_cast<char32_t>(cp);
 2833|   247k|}
parse.cc:_ZN3ada4idnaL19char_to_digit_valueEc:
 8040|   122k|static constexpr int32_t char_to_digit_value(char value) {
 8041|   122k|  if (value >= 'a' && value <= 'z') return value - 'a';
  ------------------
  |  Branch (8041:7): [True: 90.6k, False: 31.5k]
  |  Branch (8041:23): [True: 90.6k, False: 26]
  ------------------
 8042|  31.5k|  if (value >= '0' && value <= '9') return value - '0' + 26;
  ------------------
  |  Branch (8042:7): [True: 31.4k, False: 127]
  |  Branch (8042:23): [True: 31.3k, False: 55]
  ------------------
 8043|    182|  return -1;
 8044|  31.5k|}
parse.cc:_ZN3ada4idnaL5adaptEiib:
 8050|   356k|static constexpr int32_t adapt(int32_t d, int32_t n, bool firsttime) {
 8051|   356k|  if (firsttime) {
  ------------------
  |  Branch (8051:7): [True: 54.0k, False: 302k]
  ------------------
 8052|  54.0k|    d = d / damp;
 8053|   302k|  } else {
 8054|   302k|    d = d / 2;
 8055|   302k|  }
 8056|   356k|  d += d / n;
 8057|   356k|  int32_t k = 0;
 8058|   549k|  while (d > ((base - tmin) * tmax) / 2) {
  ------------------
  |  Branch (8058:10): [True: 193k, False: 356k]
  ------------------
 8059|   193k|    d /= base - tmin;
 8060|   193k|    k += base;
 8061|   193k|  }
 8062|   356k|  return k + (((base - tmin + 1) * d) / (d + skew));
 8063|   356k|}
parse.cc:_ZN3ada4idnaL13digit_to_charEi:
 8046|   817k|static constexpr char digit_to_char(int32_t digit) {
 8047|   817k|  return digit < 26 ? char(digit + 97) : char(digit + 22);
  ------------------
  |  Branch (8047:10): [True: 539k, False: 277k]
  ------------------
 8048|   817k|}
parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_0clEj:
 9477|  2.82k|      auto is_l_or_d = [](uint32_t code) {
 9478|  2.82k|        return std::binary_search(std::begin(L), std::end(L), code) ||
  ------------------
  |  Branch (9478:16): [True: 199, False: 2.62k]
  ------------------
 9479|  2.62k|               std::binary_search(std::begin(D), std::end(D), code);
  ------------------
  |  Branch (9479:16): [True: 846, False: 1.77k]
  ------------------
 9480|  2.82k|      };
parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_1clEj:
 9481|  3.03k|      auto is_r_or_d = [](uint32_t code) {
 9482|  3.03k|        return std::binary_search(std::begin(R), std::end(R), code) ||
  ------------------
  |  Branch (9482:16): [True: 285, False: 2.74k]
  ------------------
 9483|  2.74k|               std::binary_search(std::begin(D), std::end(D), code);
  ------------------
  |  Branch (9483:16): [True: 515, False: 2.23k]
  ------------------
 9484|  3.03k|      };
parse.cc:_ZN3ada4idnaL20find_last_not_of_nsmENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9057|  52.1k|    const std::u32string_view label) noexcept {
 9058|  53.4k|  for (int i = static_cast<int>(label.size() - 1); i >= 0; i--)
  ------------------
  |  Branch (9058:52): [True: 53.4k, False: 0]
  ------------------
 9059|  53.4k|    if (find_direction(label[i]) != direction::NSM) return i;
  ------------------
  |  Branch (9059:9): [True: 52.1k, False: 1.21k]
  ------------------
 9060|       |
 9061|      0|  return std::u32string_view::npos;
 9062|  52.1k|}
parse.cc:_ZN3ada4idnaL12is_rtl_labelENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9066|  52.1k|inline static bool is_rtl_label(const std::u32string_view label) noexcept {
 9067|  52.1k|  const size_t mask =
 9068|  52.1k|      (1u << direction::R) | (1u << direction::AL) | (1u << direction::AN);
 9069|       |
 9070|  52.1k|  size_t directions = 0;
 9071|   412k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (9071:22): [True: 360k, False: 52.1k]
  ------------------
 9072|   360k|    directions |= 1u << find_direction(label[i]);
 9073|   360k|  }
 9074|  52.1k|  return (directions & mask) != 0;
 9075|  52.1k|}
parse.cc:_ZN3ada4idnaL14find_directionEj:
 9040|   431k|inline static direction find_direction(uint32_t code_point) noexcept {
 9041|   431k|  auto it = std::lower_bound(
 9042|   431k|      std::begin(dir_table), std::end(dir_table), code_point,
 9043|   431k|      [](const directions& d, uint32_t c) { return d.final_code < c; });
 9044|       |
 9045|       |  // next check is almost surely in vain, but we use it for safety.
 9046|   431k|  if (it == std::end(dir_table)) {
  ------------------
  |  Branch (9046:7): [True: 0, False: 431k]
  ------------------
 9047|      0|    return direction::NONE;
 9048|      0|  }
 9049|       |  // We have that d.final_code >= c.
 9050|   431k|  if (code_point >= it->start_code) {
  ------------------
  |  Branch (9050:7): [True: 430k, False: 1.63k]
  ------------------
 9051|   430k|    return it->direct;
 9052|   430k|  }
 9053|  1.63k|  return direction::NONE;
 9054|   431k|}
parse.cc:_ZZN3ada4idnaL14find_directionEjENKUlRKNS0_10directionsEjE_clES3_j:
 9043|  4.39M|      [](const directions& d, uint32_t c) { return d.final_code < c; });
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9626|  36.4k|bool constexpr is_ascii(std::string_view view) {
 9627|   365k|  for (uint8_t c : view) {
  ------------------
  |  Branch (9627:18): [True: 365k, False: 18.0k]
  ------------------
 9628|   365k|    if (c >= 0x80) {
  ------------------
  |  Branch (9628:9): [True: 18.4k, False: 346k]
  ------------------
 9629|  18.4k|      return false;
 9630|  18.4k|    }
 9631|   365k|  }
 9632|  18.0k|  return true;
 9633|  36.4k|}
parse.cc:_ZN3ada4idnaL19from_ascii_to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9659|  18.0k|static std::string from_ascii_to_ascii(std::string_view ut8_string) {
 9660|  18.0k|  static const std::string error = "";
 9661|  18.0k|  std::string out;
 9662|  18.0k|  out.reserve(ut8_string.size());
 9663|  18.0k|  std::u32string tmp_buffer;
 9664|  18.0k|  std::u32string post_map;
 9665|  18.0k|  size_t label_start = 0;
 9666|       |
 9667|  49.4k|  while (label_start != ut8_string.size()) {
  ------------------
  |  Branch (9667:10): [True: 32.7k, False: 16.6k]
  ------------------
 9668|  32.7k|    size_t loc_dot = ut8_string.find('.', label_start);
 9669|  32.7k|    bool is_last_label = (loc_dot == std::string_view::npos);
 9670|  32.7k|    size_t label_size =
 9671|  32.7k|        is_last_label ? ut8_string.size() - label_start : loc_dot - label_start;
  ------------------
  |  Branch (9671:9): [True: 16.4k, False: 16.3k]
  ------------------
 9672|  32.7k|    size_t label_size_with_dot = is_last_label ? label_size : label_size + 1;
  ------------------
  |  Branch (9672:34): [True: 16.4k, False: 16.3k]
  ------------------
 9673|  32.7k|    std::string_view label_view(ut8_string.data() + label_start, label_size);
 9674|  32.7k|    label_start += label_size_with_dot;
 9675|  32.7k|    if (label_size == 0) {
  ------------------
  |  Branch (9675:9): [True: 3.01k, False: 29.7k]
  ------------------
 9676|       |      // empty label? Nothing to do.
 9677|  29.7k|    } else {
 9678|       |      // Append the label to out and lowercase it in-place, avoiding a separate
 9679|       |      // copy of the entire input string.
 9680|  29.7k|      size_t label_out_start = out.size();
 9681|  29.7k|      out.append(label_view);
 9682|  29.7k|      ascii_map(out.data() + label_out_start, label_size);
 9683|  29.7k|      std::string_view mapped_label(out.data() + label_out_start, label_size);
 9684|  29.7k|      if (mapped_label.starts_with("xn--")) {
  ------------------
  |  Branch (9684:11): [True: 9.17k, False: 20.6k]
  ------------------
 9685|       |        // The xn-- part is the expensive game.
 9686|  9.17k|        std::string_view puny_segment_ascii(out.data() + label_out_start + 4,
 9687|  9.17k|                                            label_size - 4);
 9688|  9.17k|        tmp_buffer.clear();
 9689|  9.17k|        bool is_ok =
 9690|  9.17k|            ada::idna::punycode_to_utf32(puny_segment_ascii, tmp_buffer);
 9691|  9.17k|        if (!is_ok) {
  ------------------
  |  Branch (9691:13): [True: 353, False: 8.82k]
  ------------------
 9692|    353|          return error;
 9693|    353|        }
 9694|       |        // If the input is just ASCII, it should not have been encoded
 9695|       |        // as punycode.
 9696|       |        // https://github.com/whatwg/url/issues/760
 9697|  8.82k|        if (is_ascii(tmp_buffer)) {
  ------------------
  |  Branch (9697:13): [True: 212, False: 8.61k]
  ------------------
 9698|    212|          return error;
 9699|    212|        }
 9700|  8.61k|        if (!ada::idna::map(tmp_buffer, post_map)) {
  ------------------
  |  Branch (9700:13): [True: 470, False: 8.14k]
  ------------------
 9701|    470|          return error;
 9702|    470|        }
 9703|  8.14k|        if (tmp_buffer != post_map) {
  ------------------
  |  Branch (9703:13): [True: 175, False: 7.96k]
  ------------------
 9704|    175|          return error;
 9705|    175|        }
 9706|  7.96k|        normalize(post_map);
 9707|  7.96k|        if (post_map != tmp_buffer) {
  ------------------
  |  Branch (9707:13): [True: 96, False: 7.87k]
  ------------------
 9708|     96|          return error;
 9709|     96|        }
 9710|  7.87k|        if (post_map.empty()) {
  ------------------
  |  Branch (9710:13): [True: 0, False: 7.87k]
  ------------------
 9711|      0|          return error;
 9712|      0|        }
 9713|  7.87k|        if (!is_label_valid(post_map)) {
  ------------------
  |  Branch (9713:13): [True: 59, False: 7.81k]
  ------------------
 9714|     59|          return error;
 9715|     59|        }
 9716|  7.87k|      }
 9717|  29.7k|    }
 9718|  31.4k|    if (!is_last_label) {
  ------------------
  |  Branch (9718:9): [True: 16.2k, False: 15.1k]
  ------------------
 9719|  16.2k|      out.push_back('.');
 9720|  16.2k|    }
 9721|  31.4k|  }
 9722|  16.6k|  return out;
 9723|  18.0k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9617|  61.0k|bool constexpr is_ascii(std::u32string_view view) {
 9618|   103k|  for (uint32_t c : view) {
  ------------------
  |  Branch (9618:19): [True: 103k, False: 5.47k]
  ------------------
 9619|   103k|    if (c >= 0x80) {
  ------------------
  |  Branch (9619:9): [True: 55.5k, False: 47.6k]
  ------------------
 9620|  55.5k|      return false;
 9621|  55.5k|    }
 9622|   103k|  }
 9623|  5.47k|  return true;
 9624|  61.0k|}
_ZN3ada7unicode18is_ascii_hex_digitEc:
10904|  1.41M|ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept {
10905|  1.41M|  return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
  ------------------
  |  Branch (10905:11): [True: 1.41M, False: 3.03k]
  |  Branch (10905:23): [True: 754k, False: 655k]
  |  Branch (10905:37): [True: 652k, False: 6.31k]
  |  Branch (10905:49): [True: 645k, False: 7.28k]
  ------------------
10906|  13.6k|         (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (10906:11): [True: 7.01k, False: 6.58k]
  |  Branch (10906:23): [True: 6.58k, False: 433]
  ------------------
10907|  1.41M|}
_ZN3ada7unicode21convert_hex_to_binaryEc:
10994|  1.40M|unsigned constexpr convert_hex_to_binary(const char c) noexcept {
10995|  1.40M|  return hex_to_binary_table[c - '0'];
10996|  1.40M|}
parse.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
11033|  1.31M|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11034|  1.31M|    return character_sets::bit_at(character_set, c);
11035|  1.31M|  });
_ZN3ada7unicode36contains_forbidden_domain_code_pointEPKcm:
10831|  89.1k|    const char* input, size_t length) noexcept {
10832|  89.1k|  size_t i = 0;
10833|  89.1k|  uint8_t accumulator{};
10834|   599k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (10834:10): [True: 510k, False: 89.1k]
  ------------------
10835|   510k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
10836|   510k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 1])];
10837|   510k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 2])];
10838|   510k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 3])];
10839|   510k|  }
10840|   268k|  for (; i < length; i++) {
  ------------------
  |  Branch (10840:10): [True: 179k, False: 89.1k]
  ------------------
10841|   179k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
10842|   179k|  }
10843|  89.1k|  return accumulator;
10844|  89.1k|}
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10680|   255k|    std::string_view user_input) noexcept {
10681|       |  // first check for short strings in which case we do it naively.
10682|   255k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (10682:7): [True: 119k, False: 136k]
  ------------------
10683|   119k|    return std::ranges::any_of(user_input, is_tabs_or_newline);
10684|   119k|  }
10685|       |  // fast path for long strings (expected to be common)
10686|   136k|  size_t i = 0;
10687|   136k|  const __m128i mask1 = _mm_set1_epi8('\r');
10688|   136k|  const __m128i mask2 = _mm_set1_epi8('\n');
10689|   136k|  const __m128i mask3 = _mm_set1_epi8('\t');
10690|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
10691|   136k|  __m128i running{0};
10692|   508k|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (10692:10): [True: 372k, False: 136k]
  ------------------
10693|   372k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
10694|   372k|    running = _mm_or_si128(
10695|   372k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
10696|   372k|                                           _mm_cmpeq_epi8(word, mask2))),
10697|   372k|        _mm_cmpeq_epi8(word, mask3));
10698|   372k|  }
10699|   136k|  if (i < user_input.size()) {
  ------------------
  |  Branch (10699:7): [True: 129k, False: 7.34k]
  ------------------
10700|   129k|    __m128i word = _mm_loadu_si128(
10701|   129k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
10702|   129k|    running = _mm_or_si128(
10703|   129k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
10704|   129k|                                           _mm_cmpeq_epi8(word, mask2))),
10705|   129k|        _mm_cmpeq_epi8(word, mask3));
10706|   129k|  }
10707|   136k|  return _mm_movemask_epi8(running) != 0;
10708|   255k|}
_ZN3ada7unicode18is_tabs_or_newlineEc:
10575|   526k|constexpr bool is_tabs_or_newline(char c) noexcept {
10576|   526k|  return c == '\r' || c == '\n' || c == '\t';
  ------------------
  |  Branch (10576:10): [True: 482, False: 525k]
  |  Branch (10576:23): [True: 829, False: 525k]
  |  Branch (10576:36): [True: 777, False: 524k]
  ------------------
10577|   526k|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
11715|   263k|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
11716|       |  // if this ever becomes a performance issue, we could use an approach similar
11717|       |  // to has_tabs_or_newline
11718|   263k|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
11719|   263k|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
10925|  6.65M|    const char c) noexcept {
10926|  6.65M|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (10926:10): [True: 15.3k, False: 6.63M]
  |  Branch (10926:23): [True: 20.6k, False: 6.61M]
  |  Branch (10926:36): [True: 4.14k, False: 6.61M]
  ------------------
10927|  6.65M|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
10920|   379k|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
10921|   379k|  return (unsigned char)c <= ' ';
10922|   379k|}
_ZN3ada7helpers19parse_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
12397|  51.2k|                                           std::string& path) {
12398|  51.2k|  ada_log("parse_prepared_path ", input);
12399|  51.2k|  uint8_t accumulator = checkers::path_signature(input);
12400|       |  // Let us first detect a trivial case.
12401|       |  // If it is special, we check that we have no dot, no %,  no \ and no
12402|       |  // character needing percent encoding. Otherwise, we check that we have no %,
12403|       |  // no dot, and no character needing percent encoding.
12404|  51.2k|  constexpr uint8_t need_encoding = 1;
12405|  51.2k|  constexpr uint8_t backslash_char = 2;
12406|  51.2k|  constexpr uint8_t dot_char = 4;
12407|  51.2k|  constexpr uint8_t percent_char = 8;
12408|  51.2k|  bool special = type != ada::scheme::NOT_SPECIAL;
12409|  51.2k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (12409:39): [True: 17.3k, False: 33.8k]
  ------------------
12410|  17.3k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (12410:39): [True: 1.18k, False: 16.1k]
  ------------------
12411|  51.2k|  bool trivial_path =
12412|  51.2k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (12412:7): [True: 25.8k, False: 25.3k]
  |  Branch (12412:8): [True: 49.3k, False: 1.86k]
  ------------------
12413|  51.2k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
12414|  1.86k|                  0)) &&
12415|  25.8k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (12415:7): [True: 25.4k, False: 390]
  ------------------
12416|  51.2k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (12416:7): [True: 6.23k, False: 45.0k]
  |  Branch (12416:34): [True: 6.05k, False: 175]
  ------------------
12417|       |    // '4' means that we have at least one dot, but nothing that requires
12418|       |    // percent encoding or decoding. The only part that is not trivial is
12419|       |    // that we may have single dots and double dots path segments.
12420|       |    // If we have such segments, then we either have a path that begins
12421|       |    // with '.' (easy to check), or we have the sequence './'.
12422|       |    // Note: input cannot be empty, it must at least contain one character ('.')
12423|       |    // Note: we know that '\' is not present.
12424|  6.05k|    if (input[0] != '.') {
  ------------------
  |  Branch (12424:9): [True: 4.26k, False: 1.79k]
  ------------------
12425|  4.26k|      size_t slashdot = 0;
12426|  4.26k|      bool dot_is_file = true;
12427|  16.3k|      for (;;) {
12428|  16.3k|        slashdot = input.find("/.", slashdot);
12429|  16.3k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (12429:13): [True: 4.26k, False: 12.0k]
  ------------------
12430|  4.26k|          break;
12431|  12.0k|        } else {  // uncommon
12432|       |          // only three cases matter: /./, /.. or a final /
12433|  12.0k|          slashdot += 2;
12434|  12.0k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (12434:28): [True: 515, False: 11.5k]
  |  Branch (12434:56): [True: 8.28k, False: 3.25k]
  ------------------
12435|  3.25k|                           input[slashdot] == '/');
  ------------------
  |  Branch (12435:28): [True: 2.07k, False: 1.18k]
  ------------------
12436|  12.0k|        }
12437|  16.3k|      }
12438|  4.26k|      trivial_path = dot_is_file;
12439|  4.26k|    }
12440|  6.05k|  }
12441|  51.2k|  if (trivial_path) {
  ------------------
  |  Branch (12441:7): [True: 27.6k, False: 23.6k]
  ------------------
12442|  27.6k|    ada_log("parse_path trivial");
12443|  27.6k|    path += '/';
12444|  27.6k|    path += input;
12445|  27.6k|    return;
12446|  27.6k|  }
12447|       |  // We are going to need to look a bit at the path, but let us see if we can
12448|       |  // ignore percent encoding *and* backslashes *and* percent characters.
12449|       |  // Except for the trivial case, this is likely to capture 99% of paths out
12450|       |  // there.
12451|  23.6k|  bool fast_path =
12452|  23.6k|      (special &&
  ------------------
  |  Branch (12452:8): [True: 22.3k, False: 1.30k]
  ------------------
12453|  22.3k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (12453:8): [True: 4.13k, False: 18.1k]
  ------------------
12454|  4.13k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (12454:7): [True: 2.23k, False: 1.89k]
  ------------------
12455|  23.6k|  if (fast_path) {
  ------------------
  |  Branch (12455:7): [True: 2.23k, False: 21.3k]
  ------------------
12456|  2.23k|    ada_log("parse_prepared_path fast");
12457|       |    // Here we don't need to worry about \ or percent encoding.
12458|       |    // We also do not have a file protocol. We might have dots, however,
12459|       |    // but dots must as appear as '.', and they cannot be encoded because
12460|       |    // the symbol '%' is not present.
12461|  2.23k|    size_t previous_location = 0;  // We start at 0.
12462|  22.5k|    do {
12463|  22.5k|      size_t new_location = input.find('/', previous_location);
12464|       |      // std::string_view path_view = input;
12465|       |      //  We process the last segment separately:
12466|  22.5k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (12466:11): [True: 2.23k, False: 20.2k]
  ------------------
12467|  2.23k|        std::string_view path_view = input.substr(previous_location);
12468|  2.23k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (12468:13): [True: 483, False: 1.74k]
  ------------------
12469|       |          // e.g., if you receive ".." with an empty path, you go to "/".
12470|    483|          if (path.empty()) {
  ------------------
  |  Branch (12470:15): [True: 170, False: 313]
  ------------------
12471|    170|            path = '/';
12472|    170|            return;
12473|    170|          }
12474|       |          // Fast case where we have nothing to do:
12475|    313|          if (path.back() == '/') {
  ------------------
  |  Branch (12475:15): [True: 97, False: 216]
  ------------------
12476|     97|            return;
12477|     97|          }
12478|       |          // If you have the path "/joe/myfriend",
12479|       |          // then you delete 'myfriend'.
12480|    216|          path.resize(path.rfind('/') + 1);
12481|    216|          return;
12482|    313|        }
12483|  1.74k|        path += '/';
12484|  1.74k|        if (path_view != ".") {
  ------------------
  |  Branch (12484:13): [True: 1.31k, False: 435]
  ------------------
12485|  1.31k|          path.append(path_view);
12486|  1.31k|        }
12487|  1.74k|        return;
12488|  20.2k|      } else {
12489|       |        // This is a non-final segment.
12490|  20.2k|        std::string_view path_view =
12491|  20.2k|            input.substr(previous_location, new_location - previous_location);
12492|  20.2k|        previous_location = new_location + 1;
12493|  20.2k|        if (path_view == "..") {
  ------------------
  |  Branch (12493:13): [True: 4.64k, False: 15.6k]
  ------------------
12494|  4.64k|          size_t last_delimiter = path.rfind('/');
12495|  4.64k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (12495:15): [True: 2.88k, False: 1.75k]
  ------------------
12496|  2.88k|            path.erase(last_delimiter);
12497|  2.88k|          }
12498|  15.6k|        } else if (path_view != ".") {
  ------------------
  |  Branch (12498:20): [True: 13.2k, False: 2.38k]
  ------------------
12499|  13.2k|          path += '/';
12500|  13.2k|          path.append(path_view);
12501|  13.2k|        }
12502|  20.2k|      }
12503|  22.5k|    } while (true);
  ------------------
  |  Branch (12503:14): [True: 20.2k, Folded]
  ------------------
12504|  21.3k|  } else {
12505|  21.3k|    ada_log("parse_path slow");
12506|       |    // we have reached the general case
12507|  21.3k|    bool needs_percent_encoding = (accumulator & 1);
12508|  21.3k|    std::string path_buffer_tmp;
12509|  94.9k|    do {
12510|  94.9k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (12510:26): [True: 86.5k, False: 8.39k]
  |  Branch (12510:37): [True: 7.32k, False: 79.2k]
  ------------------
12511|  94.9k|                            ? input.find_first_of("/\\")
12512|  94.9k|                            : input.find('/');
12513|  94.9k|      std::string_view path_view = input;
12514|  94.9k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (12514:11): [True: 73.5k, False: 21.3k]
  ------------------
12515|  73.5k|        path_view.remove_suffix(path_view.size() - location);
12516|  73.5k|        input.remove_prefix(location + 1);
12517|  73.5k|      }
12518|       |      // path_buffer is either path_view or it might point at a percent encoded
12519|       |      // temporary file.
12520|  94.9k|      std::string_view path_buffer =
12521|  94.9k|          (needs_percent_encoding &&
  ------------------
  |  Branch (12521:12): [True: 63.0k, False: 31.8k]
  ------------------
12522|  63.0k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (12522:12): [True: 28.5k, False: 34.4k]
  ------------------
12523|  63.0k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
12524|  94.9k|              ? path_buffer_tmp
12525|  94.9k|              : path_view;
12526|  94.9k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (12526:11): [True: 18.3k, False: 76.5k]
  ------------------
12527|  18.3k|        helpers::shorten_path(path, type);
12528|  18.3k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (12528:13): [True: 1.19k, False: 17.1k]
  ------------------
12529|  1.19k|          path += '/';
12530|  1.19k|        }
12531|  76.5k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (12531:18): [True: 4.95k, False: 71.6k]
  ------------------
12532|  4.95k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (12532:18): [True: 631, False: 4.32k]
  ------------------
12533|    631|        path += '/';
12534|    631|      }
12535|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
12536|  75.9k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (12536:16): [True: 71.6k, False: 4.32k]
  ------------------
12537|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
12538|       |        // Windows drive letter, then replace the second code point in
12539|       |        // path_buffer with U+003A (:).
12540|  71.6k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (12540:13): [True: 40.7k, False: 30.9k]
  |  Branch (12540:48): [True: 16.7k, False: 24.0k]
  ------------------
12541|  16.7k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (12541:13): [True: 1.24k, False: 15.4k]
  ------------------
12542|  1.24k|          path += '/';
12543|  1.24k|          path += path_buffer[0];
12544|  1.24k|          path += ':';
12545|  1.24k|          path_buffer.remove_prefix(2);
12546|  1.24k|          path.append(path_buffer);
12547|  70.3k|        } else {
12548|       |          // Append path_buffer to url's path.
12549|  70.3k|          path += '/';
12550|  70.3k|          path.append(path_buffer);
12551|  70.3k|        }
12552|  71.6k|      }
12553|  94.9k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (12553:11): [True: 21.3k, False: 73.5k]
  ------------------
12554|  21.3k|        return;
12555|  21.3k|      }
12556|  94.9k|    } while (true);
  ------------------
  |  Branch (12556:14): [True: 73.5k, Folded]
  ------------------
12557|  21.3k|  }
12558|  23.6k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   87|   132k|    std::string_view input) noexcept {
   88|       |  // The path percent-encode set is the query percent-encode set and U+003F (?),
   89|       |  // U+0060 (`), U+007B ({), and U+007D (}). The query percent-encode set is the
   90|       |  // C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+0023 (#),
   91|       |  // U+003C (<), and U+003E (>). The C0 control percent-encode set are the C0
   92|       |  // controls and all code points greater than U+007E (~).
   93|   132k|  size_t i = 0;
   94|   132k|  uint8_t accumulator{};
   95|   317k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (95:10): [True: 185k, False: 132k]
  ------------------
   96|   185k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   97|   185k|                           path_signature_table[uint8_t(input[i + 1])] |
   98|   185k|                           path_signature_table[uint8_t(input[i + 2])] |
   99|   185k|                           path_signature_table[uint8_t(input[i + 3])] |
  100|   185k|                           path_signature_table[uint8_t(input[i + 4])] |
  101|   185k|                           path_signature_table[uint8_t(input[i + 5])] |
  102|   185k|                           path_signature_table[uint8_t(input[i + 6])] |
  103|   185k|                           path_signature_table[uint8_t(input[i + 7])]);
  104|   185k|  }
  105|   542k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (105:10): [True: 409k, False: 132k]
  ------------------
  106|   409k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
  107|   409k|  }
  108|   132k|  return accumulator;
  109|   132k|}
_ZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
11059|   120k|                    std::string& out) {
11060|   120k|  ada_log("percent_encode ", input, " to output string while ",
11061|   120k|          append ? "appending" : "overwriting");
11062|   120k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11063|   120k|    return character_sets::bit_at(character_set, c);
11064|   120k|  });
11065|   120k|  ada_log("percent_encode done checking, moved to ",
11066|   120k|          std::distance(input.begin(), pointer));
11067|       |
11068|       |  // Optimization: Don't iterate if percent encode is not required
11069|   120k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11069:7): [True: 66.5k, False: 54.0k]
  ------------------
11070|  66.5k|    ada_log("percent_encode encoding not needed.");
11071|  66.5k|    return false;
11072|  66.5k|  }
11073|  54.0k|  if constexpr (!append) {
11074|  54.0k|    out.clear();
11075|  54.0k|  }
11076|  54.0k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
11077|  54.0k|          " bytes");
11078|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
11079|  54.0k|  out.append(input.data(), std::distance(input.begin(), pointer));
11080|  54.0k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
11081|  54.0k|          " bytes");
11082|   926k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11082:10): [True: 872k, False: 54.0k]
  ------------------
11083|   872k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11083:9): [True: 733k, False: 139k]
  ------------------
11084|   733k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11085|   733k|    } else {
11086|   139k|      out += *pointer;
11087|   139k|    }
11088|   872k|  }
11089|  54.0k|  return true;
11090|   120k|}
_ZZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
11062|   250k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11063|   250k|    return character_sets::bit_at(character_set, c);
11064|   250k|  });
_ZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10933|   178k|    std::string_view input) noexcept {
10934|       |  // This will catch most cases:
10935|       |  // The length must be 2,4 or 6.
10936|       |  // We divide by two and require
10937|       |  // that the result be between 1 and 3 inclusively.
10938|   178k|  uint64_t half_length = uint64_t(input.size()) / 2;
10939|   178k|  if (half_length - 1 > 2) {
  ------------------
  |  Branch (10939:7): [True: 94.1k, False: 83.9k]
  ------------------
10940|  94.1k|    return false;
10941|  94.1k|  }
10942|       |  // We have a string of length 2, 4 or 6.
10943|       |  // We now check the first character:
10944|  83.9k|  if ((input[0] != '.') && (input[0] != '%')) {
  ------------------
  |  Branch (10944:7): [True: 41.7k, False: 42.2k]
  |  Branch (10944:28): [True: 16.7k, False: 25.0k]
  ------------------
10945|  16.7k|    return false;
10946|  16.7k|  }
10947|       |  // We are unlikely the get beyond this point.
10948|  67.2k|  int hash_value = (input.size() + (unsigned)(input[0])) & 3;
10949|  67.2k|  const std::string_view target = table_is_double_dot_path_segment[hash_value];
10950|  67.2k|  if (target.size() != input.size()) {
  ------------------
  |  Branch (10950:7): [True: 20.0k, False: 47.2k]
  ------------------
10951|  20.0k|    return false;
10952|  20.0k|  }
10953|       |  // We almost never get here.
10954|       |  // Optimizing the rest is relatively unimportant.
10955|  47.2k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
10956|  47.2k|    uint16_t A, B;
10957|  47.2k|    memcpy(&A, a.data(), sizeof(A));
10958|  47.2k|    memcpy(&B, b.data(), sizeof(B));
10959|  47.2k|    return A == B;
10960|  47.2k|  };
10961|  47.2k|  if (!prefix_equal_unsafe(input, target)) {
  ------------------
  |  Branch (10961:7): [True: 5.50k, False: 41.6k]
  ------------------
10962|  5.50k|    return false;
10963|  5.50k|  }
10964|  51.9k|  for (size_t i = 2; i < input.size(); i++) {
  ------------------
  |  Branch (10964:22): [True: 16.4k, False: 35.4k]
  ------------------
10965|  16.4k|    char c = input[i];
10966|  16.4k|    if ((uint8_t((c | 0x20) - 0x61) <= 25 ? (c | 0x20) : c) != target[i]) {
  ------------------
  |  Branch (10966:9): [True: 6.20k, False: 10.2k]
  |  Branch (10966:10): [True: 5.65k, False: 10.7k]
  ------------------
10967|  6.20k|      return false;
10968|  6.20k|    }
10969|  16.4k|  }
10970|  35.4k|  return true;
10971|       |  // The above code might be a bit better than the code below. Compilers
10972|       |  // are not stupid and may use the fact that these strings have length 2,4 and
10973|       |  // 6 and other tricks.
10974|       |  // return input == ".." ||
10975|       |  //  input == ".%2e" || input == ".%2E" ||
10976|       |  //  input == "%2e." || input == "%2E." ||
10977|       |  //  input == "%2e%2e" || input == "%2E%2E" || input == "%2E%2e" || input ==
10978|       |  //  "%2e%2E";
10979|  41.6k|}
_ZZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_S5_E_clES5_S5_:
10955|  47.2k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
10956|  47.2k|    uint16_t A, B;
10957|  47.2k|    memcpy(&A, a.data(), sizeof(A));
10958|  47.2k|    memcpy(&B, b.data(), sizeof(B));
10959|  47.2k|    return A == B;
10960|  47.2k|  };
_ZN3ada7unicode26is_single_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10982|   284k|    std::string_view input) noexcept {
10983|   284k|  return input == "." || input == "%2e" || input == "%2E";
  ------------------
  |  Branch (10983:10): [True: 12.5k, False: 271k]
  |  Branch (10983:26): [True: 3.46k, False: 268k]
  |  Branch (10983:44): [True: 89, False: 268k]
  ------------------
10984|   284k|}
_ZN3ada7unicode28is_forbidden_host_code_pointEc:
10803|  53.6k|    const char c) noexcept {
10804|  53.6k|  return is_forbidden_host_code_point_table[uint8_t(c)];
10805|  53.6k|}
_ZN3ada8checkers17verify_dns_lengthENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
  112|  67.6k|    std::string_view input) noexcept {
  113|  67.6k|  if (input.empty()) return false;
  ------------------
  |  Branch (113:7): [True: 2.21k, False: 65.4k]
  ------------------
  114|  65.4k|  if (input.back() == '.') {
  ------------------
  |  Branch (114:7): [True: 3.11k, False: 62.3k]
  ------------------
  115|  3.11k|    if (input.size() > 254) return false;
  ------------------
  |  Branch (115:9): [True: 209, False: 2.90k]
  ------------------
  116|  62.3k|  } else if (input.size() > 253)
  ------------------
  |  Branch (116:14): [True: 557, False: 61.7k]
  ------------------
  117|    557|    return false;
  118|       |
  119|  64.6k|  size_t start = 0;
  120|   193k|  while (start < input.size()) {
  ------------------
  |  Branch (120:10): [True: 133k, False: 60.0k]
  ------------------
  121|   133k|    auto dot_location = input.find('.', start);
  122|       |    // If not found, it's likely the end of the domain
  123|   133k|    if (dot_location == std::string_view::npos) dot_location = input.size();
  ------------------
  |  Branch (123:9): [True: 60.0k, False: 73.6k]
  ------------------
  124|       |
  125|   133k|    auto label_size = dot_location - start;
  126|   133k|    if (label_size > 63 || label_size == 0) return false;
  ------------------
  |  Branch (126:9): [True: 1.11k, False: 132k]
  |  Branch (126:28): [True: 3.45k, False: 129k]
  ------------------
  127|       |
  128|   129k|    start = dot_location + 1;
  129|   129k|  }
  130|       |
  131|  60.0k|  return true;
  132|  64.6k|}
_ZN3ada7unicode14is_ascii_digitEc:
10909|  61.4k|ada_really_inline constexpr bool is_ascii_digit(const char c) noexcept {
10910|       |  // An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
10911|       |  // inclusive.
10912|  61.4k|  return (c >= '0' && c <= '9');
  ------------------
  |  Branch (10912:11): [True: 45.5k, False: 15.9k]
  |  Branch (10912:23): [True: 25.2k, False: 20.2k]
  ------------------
10913|  61.4k|}
_ZN3ada3url10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13152|  17.2k|ada_really_inline void url::parse_path(std::string_view input) {
13153|  17.2k|  ada_log("parse_path ", input);
13154|  17.2k|  std::string tmp_buffer;
13155|  17.2k|  std::string_view internal_input;
13156|  17.2k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (13156:7): [True: 533, False: 16.6k]
  ------------------
13157|    533|    tmp_buffer = input;
13158|       |    // Optimization opportunity: Instead of copying and then pruning, we could
13159|       |    // just directly build the string from user_input.
13160|    533|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
13161|    533|    internal_input = tmp_buffer;
13162|  16.6k|  } else {
13163|  16.6k|    internal_input = input;
13164|  16.6k|  }
13165|       |
13166|       |  // If url is special, then:
13167|  17.2k|  if (is_special()) {
  ------------------
  |  Branch (13167:7): [True: 17.2k, False: 0]
  ------------------
13168|  17.2k|    if (internal_input.empty()) {
  ------------------
  |  Branch (13168:9): [True: 1.50k, False: 15.7k]
  ------------------
13169|  1.50k|      path = "/";
13170|  15.7k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (13170:16): [True: 2.18k, False: 13.5k]
  |  Branch (13170:46): [True: 140, False: 13.3k]
  ------------------
13171|  2.32k|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
13172|  13.3k|    } else {
13173|  13.3k|      helpers::parse_prepared_path(internal_input, type, path);
13174|  13.3k|    }
13175|  17.2k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (13175:14): [True: 0, False: 0]
  ------------------
13176|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (13176:9): [True: 0, False: 0]
  ------------------
13177|      0|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
13178|      0|    } else {
13179|      0|      helpers::parse_prepared_path(internal_input, type, path);
13180|      0|    }
13181|      0|  } else {
13182|      0|    if (!host.has_value()) {
  ------------------
  |  Branch (13182:9): [True: 0, False: 0]
  ------------------
13183|      0|      path = "/";
13184|      0|    }
13185|      0|  }
13186|  17.2k|}
_ZN3ada7unicode13is_alnum_plusEc:
10897|   773k|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
10898|   773k|  return is_alnum_plus_table[uint8_t(c)];
10899|       |  // A table is almost surely much faster than the
10900|       |  // following under most compilers: return
10901|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
10902|   773k|}
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
11655|   206k|    std::string_view& input) noexcept {
11656|       |  // compiles down to 20--30 instructions including a class to memchr (C
11657|       |  // function). this function should be quite fast.
11658|   206k|  size_t location_of_first = input.find('#');
11659|   206k|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (11659:7): [True: 154k, False: 51.9k]
  ------------------
11660|   154k|    return std::nullopt;
11661|   154k|  }
11662|  51.9k|  std::string_view hash = input;
11663|  51.9k|  hash.remove_prefix(location_of_first + 1);
11664|  51.9k|  input.remove_suffix(input.size() - location_of_first);
11665|  51.9k|  return hash;
11666|   206k|}
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12593|  97.9k|find_authority_delimiter_special(std::string_view view) noexcept {
12594|       |  // performance note: we might be able to gain further performance
12595|       |  // with SIMD intrinsics.
12596|  1.10M|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (12596:33): [True: 1.09M, False: 3.05k]
  ------------------
12597|  1.09M|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (12597:9): [True: 94.9k, False: 1.00M]
  ------------------
12598|  94.9k|      return pos - view.begin();
12599|  94.9k|    }
12600|  1.09M|  }
12601|  3.05k|  return size_t(view.size());
12602|  97.9k|}
_ZN3ada7helpers24find_authority_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12614|  5.18k|find_authority_delimiter(std::string_view view) noexcept {
12615|       |  // performance note: we might be able to gain further performance
12616|       |  // with SIMD instrinsics.
12617|  44.0k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (12617:33): [True: 43.3k, False: 689]
  ------------------
12618|  43.3k|    if (authority_delimiter[(uint8_t)*pos]) {
  ------------------
  |  Branch (12618:9): [True: 4.49k, False: 38.8k]
  ------------------
12619|  4.49k|      return pos - view.begin();
12620|  4.49k|    }
12621|  43.3k|  }
12622|    689|  return size_t(view.size());
12623|  5.18k|}
_ZN3ada7helpers12shorten_pathERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_6scheme4typeE:
11668|  36.2k|ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) {
11669|       |  // Let path be url's path.
11670|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
11671|       |  // Windows drive letter, then return.
11672|  36.2k|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (11672:7): [True: 23.4k, False: 12.7k]
  ------------------
11673|  23.4k|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (11673:7): [True: 19.6k, False: 3.76k]
  |  Branch (11673:54): [True: 14.6k, False: 5.03k]
  ------------------
11674|  14.6k|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (11674:9): [True: 3.58k, False: 11.0k]
  ------------------
11675|  14.6k|            helpers::substring(path, 1))) {
11676|  3.58k|      return false;
11677|  3.58k|    }
11678|  14.6k|  }
11679|       |
11680|       |  // Remove path's last item, if any.
11681|  32.6k|  size_t last_delimiter = path.rfind('/');
11682|  32.6k|  if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (11682:7): [True: 21.4k, False: 11.2k]
  ------------------
11683|  21.4k|    path.erase(last_delimiter);
11684|  21.4k|    return true;
11685|  21.4k|  }
11686|       |
11687|  11.2k|  return false;
11688|  32.6k|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12306|   187k|    const bool is_special, std::string_view& view) noexcept {
12307|       |  /**
12308|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
12309|       |   * compute a variable called insideBrackets but this variable is only used
12310|       |   * once, to check whether a ':' character was found outside brackets. Exact
12311|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
12312|       |   * It is conceptually simpler and arguably more efficient to just return a
12313|       |   * Boolean indicating whether ':' was found outside brackets.
12314|       |   */
12315|   187k|  const size_t view_size = view.size();
12316|   187k|  size_t location = 0;
12317|   187k|  bool found_colon = false;
12318|       |  /**
12319|       |   * Performance analysis:
12320|       |   *
12321|       |   * We are basically seeking the end of the hostname which can be indicated
12322|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
12323|       |   * (where '\\' is only applicable for special URLs). However, these must
12324|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
12325|       |   * '?' does not count.
12326|       |   *
12327|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
12328|       |   * the set of delimiters, and that we handle it first.
12329|       |   *
12330|       |   * So the trick is to have a fast function that locates the next delimiter.
12331|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
12332|       |   * function would be provided by the C++ standard library, but it seems that
12333|       |   * find_first_of is not very fast, so we are forced to roll our own.
12334|       |   *
12335|       |   * We do not break into two loops for speed, but for clarity.
12336|       |   */
12337|   187k|  if (is_special) {
  ------------------
  |  Branch (12337:7): [True: 183k, False: 3.89k]
  ------------------
12338|       |    // We move to the next delimiter.
12339|   183k|    location = find_next_host_delimiter_special(view, location);
12340|       |    // Unless we find '[' then we are going only going to have to call
12341|       |    // find_next_host_delimiter_special once.
12342|   191k|    for (; location < view_size;
  ------------------
  |  Branch (12342:12): [True: 119k, False: 72.5k]
  ------------------
12343|   183k|         location = find_next_host_delimiter_special(view, location)) {
12344|   119k|      if (view[location] == '[') {
  ------------------
  |  Branch (12344:11): [True: 8.76k, False: 110k]
  ------------------
12345|  8.76k|        location = view.find(']', location);
12346|  8.76k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (12346:13): [True: 720, False: 8.04k]
  ------------------
12347|       |          // performance: view.find might get translated to a memchr, which
12348|       |          // has no notion of std::string_view::npos, so the code does not
12349|       |          // reflect the assembly.
12350|    720|          location = view_size;
12351|    720|          break;
12352|    720|        }
12353|   110k|      } else {
12354|   110k|        found_colon = view[location] == ':';
12355|   110k|        break;
12356|   110k|      }
12357|   119k|    }
12358|   183k|  } else {
12359|       |    // We move to the next delimiter.
12360|  3.89k|    location = find_next_host_delimiter(view, location);
12361|       |    // Unless we find '[' then we are going only going to have to call
12362|       |    // find_next_host_delimiter_special once.
12363|  6.29k|    for (; location < view_size;
  ------------------
  |  Branch (12363:12): [True: 4.11k, False: 2.18k]
  ------------------
12364|  4.11k|         location = find_next_host_delimiter(view, location)) {
12365|  4.11k|      if (view[location] == '[') {
  ------------------
  |  Branch (12365:11): [True: 2.57k, False: 1.54k]
  ------------------
12366|  2.57k|        location = view.find(']', location);
12367|  2.57k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (12367:13): [True: 162, False: 2.40k]
  ------------------
12368|       |          // performance: view.find might get translated to a memchr, which
12369|       |          // has no notion of std::string_view::npos, so the code does not
12370|       |          // reflect the assembly.
12371|    162|          location = view_size;
12372|    162|          break;
12373|    162|        }
12374|  2.57k|      } else {
12375|  1.54k|        found_colon = view[location] == ':';
12376|  1.54k|        break;
12377|  1.54k|      }
12378|  4.11k|    }
12379|  3.89k|  }
12380|       |  // performance: remove_suffix may translate into a single instruction.
12381|   187k|  view.remove_suffix(view_size - location);
12382|   187k|  return {location, found_colon};
12383|   187k|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
11883|   191k|    std::string_view view, size_t location) noexcept {
11884|       |  // first check for short strings in which case we do it naively.
11885|   191k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (11885:7): [True: 104k, False: 87.1k]
  ------------------
11886|   795k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (11886:31): [True: 729k, False: 65.9k]
  ------------------
11887|   729k|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (11887:11): [True: 20.3k, False: 709k]
  |  Branch (11887:29): [True: 13.0k, False: 696k]
  |  Branch (11887:47): [True: 646, False: 695k]
  ------------------
11888|   695k|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (11888:11): [True: 1.16k, False: 694k]
  |  Branch (11888:29): [True: 3.49k, False: 690k]
  ------------------
11889|  38.7k|        return i;
11890|  38.7k|      }
11891|   729k|    }
11892|  65.9k|    return size_t(view.size());
11893|   104k|  }
11894|       |  // fast path for long strings (expected to be common)
11895|  87.1k|  size_t i = location;
11896|  87.1k|  const __m128i mask1 = _mm_set1_epi8(':');
11897|  87.1k|  const __m128i mask2 = _mm_set1_epi8('/');
11898|  87.1k|  const __m128i mask3 = _mm_set1_epi8('\\');
11899|  87.1k|  const __m128i mask4 = _mm_set1_epi8('?');
11900|  87.1k|  const __m128i mask5 = _mm_set1_epi8('[');
11901|       |
11902|   131k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (11902:10): [True: 122k, False: 9.58k]
  ------------------
11903|   122k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
11904|   122k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
11905|   122k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
11906|   122k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
11907|   122k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
11908|   122k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
11909|   122k|    __m128i m = _mm_or_si128(
11910|   122k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
11911|   122k|    int mask = _mm_movemask_epi8(m);
11912|   122k|    if (mask != 0) {
  ------------------
  |  Branch (11912:9): [True: 77.6k, False: 44.4k]
  ------------------
11913|  77.6k|      return i + trailing_zeroes(mask);
11914|  77.6k|    }
11915|   122k|  }
11916|  9.58k|  if (i < view.size()) {
  ------------------
  |  Branch (11916:7): [True: 8.22k, False: 1.35k]
  ------------------
11917|  8.22k|    __m128i word =
11918|  8.22k|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
11919|  8.22k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
11920|  8.22k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
11921|  8.22k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
11922|  8.22k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
11923|  8.22k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
11924|  8.22k|    __m128i m = _mm_or_si128(
11925|  8.22k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
11926|  8.22k|    int mask = _mm_movemask_epi8(m);
11927|  8.22k|    if (mask != 0) {
  ------------------
  |  Branch (11927:9): [True: 2.94k, False: 5.28k]
  ------------------
11928|  2.94k|      return view.length() - 16 + trailing_zeroes(mask);
11929|  2.94k|    }
11930|  8.22k|  }
11931|  6.63k|  return size_t(view.length());
11932|  9.58k|}
_ZN3ada7helpers15trailing_zeroesEj:
11736|  82.8k|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
11737|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
11738|       |  unsigned long ret;
11739|       |  // Search the mask data from least significant bit (LSB)
11740|       |  // to the most significant bit (MSB) for a set bit (1).
11741|       |  _BitScanForward(&ret, input_num);
11742|       |  return (int)ret;
11743|       |#else   // ADA_REGULAR_VISUAL_STUDIO
11744|  82.8k|  return __builtin_ctzl(input_num);
11745|  82.8k|#endif  // ADA_REGULAR_VISUAL_STUDIO
11746|  82.8k|}
_ZN3ada7helpers24find_next_host_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
12172|  6.29k|                                                  size_t location) noexcept {
12173|       |  // first check for short strings in which case we do it naively.
12174|  6.29k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (12174:7): [True: 3.48k, False: 2.81k]
  ------------------
12175|  9.47k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (12175:31): [True: 7.82k, False: 1.64k]
  ------------------
12176|  7.82k|      if (view[i] == ':' || view[i] == '/' || view[i] == '?' ||
  ------------------
  |  Branch (12176:11): [True: 403, False: 7.42k]
  |  Branch (12176:29): [True: 437, False: 6.98k]
  |  Branch (12176:47): [True: 144, False: 6.84k]
  ------------------
12177|  6.84k|          view[i] == '[') {
  ------------------
  |  Branch (12177:11): [True: 859, False: 5.98k]
  ------------------
12178|  1.84k|        return i;
12179|  1.84k|      }
12180|  7.82k|    }
12181|  1.64k|    return size_t(view.size());
12182|  3.48k|  }
12183|       |  // fast path for long strings (expected to be common)
12184|  2.81k|  size_t i = location;
12185|  2.81k|  const __m128i mask1 = _mm_set1_epi8(':');
12186|  2.81k|  const __m128i mask2 = _mm_set1_epi8('/');
12187|  2.81k|  const __m128i mask4 = _mm_set1_epi8('?');
12188|  2.81k|  const __m128i mask5 = _mm_set1_epi8('[');
12189|       |
12190|  7.70k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (12190:10): [True: 6.86k, False: 841]
  ------------------
12191|  6.86k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
12192|  6.86k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
12193|  6.86k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
12194|  6.86k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
12195|  6.86k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
12196|  6.86k|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
12197|  6.86k|    int mask = _mm_movemask_epi8(m);
12198|  6.86k|    if (mask != 0) {
  ------------------
  |  Branch (12198:9): [True: 1.97k, False: 4.89k]
  ------------------
12199|  1.97k|      return i + trailing_zeroes(mask);
12200|  1.97k|    }
12201|  6.86k|  }
12202|    841|  if (i < view.size()) {
  ------------------
  |  Branch (12202:7): [True: 650, False: 191]
  ------------------
12203|    650|    __m128i word =
12204|    650|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
12205|    650|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
12206|    650|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
12207|    650|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
12208|    650|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
12209|    650|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
12210|    650|    int mask = _mm_movemask_epi8(m);
12211|    650|    if (mask != 0) {
  ------------------
  |  Branch (12211:9): [True: 300, False: 350]
  ------------------
12212|    300|      return view.length() - 16 + trailing_zeroes(mask);
12213|    300|    }
12214|    650|  }
12215|    541|  return size_t(view.length());
12216|    841|}
_ZN3ada3url10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13062|  65.9k|ada_really_inline bool url::parse_host(std::string_view input) {
13063|  65.9k|  ada_log("parse_host ", input, " [", input.size(), " bytes]");
13064|  65.9k|  if (input.empty()) {
  ------------------
  |  Branch (13064:7): [True: 33, False: 65.8k]
  ------------------
13065|     33|    return is_valid = false;
13066|     33|  }  // technically unnecessary.
13067|       |  // If input starts with U+005B ([), then:
13068|  65.8k|  if (input[0] == '[') {
  ------------------
  |  Branch (13068:7): [True: 1.82k, False: 64.0k]
  ------------------
13069|       |    // If input does not end with U+005D (]), validation error, return failure.
13070|  1.82k|    if (input.back() != ']') {
  ------------------
  |  Branch (13070:9): [True: 650, False: 1.17k]
  ------------------
13071|    650|      return is_valid = false;
13072|    650|    }
13073|  1.17k|    ada_log("parse_host ipv6");
13074|       |
13075|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
13076|       |    // trailing U+005D (]) removed.
13077|  1.17k|    input.remove_prefix(1);
13078|  1.17k|    input.remove_suffix(1);
13079|  1.17k|    return parse_ipv6(input);
13080|  1.82k|  }
13081|       |
13082|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
13083|       |  // input.
13084|  64.0k|  if (!is_special()) {
  ------------------
  |  Branch (13084:7): [True: 818, False: 63.2k]
  ------------------
13085|    818|    return parse_opaque_host(input);
13086|    818|  }
13087|       |
13088|       |  // Fast path: try to parse as pure decimal IPv4(a.b.c.d) first.
13089|  63.2k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
13090|  63.2k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (13090:7): [True: 341, False: 62.8k]
  ------------------
13091|       |    // Fast path succeeded - input is pure decimal IPv4
13092|    341|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (13092:9): [True: 341, False: 0]
  |  Branch (13092:27): [True: 132, False: 209]
  ------------------
13093|    132|      host = input.substr(0, input.size() - 1);
13094|    209|    } else {
13095|    209|      host = input;
13096|    209|    }
13097|    341|    host_type = IPV4;
13098|    341|    is_valid = true;
13099|    341|    ada_log("parse_host fast path decimal ipv4");
13100|    341|    return true;
13101|    341|  }
13102|       |  // Let domain be the result of running UTF-8 decode without BOM on the
13103|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
13104|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
13105|       |  // which case we do not need to call the expensive 'to_ascii' if a few
13106|       |  // conditions are met: no '%' and no 'xn-' subsequence.
13107|  62.8k|  std::string buffer = std::string(input);
13108|       |  // This next function checks that the result is ascii, but we are going to
13109|       |  // to check anyhow with is_forbidden.
13110|       |  // bool is_ascii =
13111|  62.8k|  unicode::to_lower_ascii(buffer.data(), buffer.size());
13112|  62.8k|  bool is_forbidden = unicode::contains_forbidden_domain_code_point(
13113|  62.8k|      buffer.data(), buffer.size());
13114|  62.8k|  if (is_forbidden == 0 && buffer.find("xn-") == std::string_view::npos) {
  ------------------
  |  Branch (13114:7): [True: 50.1k, False: 12.7k]
  |  Branch (13114:28): [True: 48.7k, False: 1.38k]
  ------------------
13115|       |    // fast path
13116|  48.7k|    host = std::move(buffer);
13117|       |
13118|       |    // Check for other IPv4 formats (hex, octal, etc.)
13119|  48.7k|    if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (13119:9): [True: 4.78k, False: 43.9k]
  ------------------
13120|  4.78k|      ada_log("parse_host fast path ipv4");
13121|  4.78k|      return parse_ipv4(host.value());
13122|  4.78k|    }
13123|  43.9k|    ada_log("parse_host fast path ", *host);
13124|  43.9k|    is_valid = true;
13125|  43.9k|    return true;
13126|  48.7k|  }
13127|  14.1k|  ada_log("parse_host calling to_ascii");
13128|  14.1k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
13129|  14.1k|  if (!is_valid || !host.has_value()) {
  ------------------
  |  Branch (13129:7): [True: 8.50k, False: 5.65k]
  |  Branch (13129:20): [True: 0, False: 5.65k]
  ------------------
13130|  8.50k|    ada_log("parse_host to_ascii returns false");
13131|  8.50k|    return is_valid = false;
13132|  8.50k|  }
13133|  5.65k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
13134|  5.65k|          " bytes]");
13135|       |
13136|  5.65k|  if (std::any_of(host->begin(), host->end(),
  ------------------
  |  Branch (13136:7): [True: 0, False: 5.65k]
  ------------------
13137|  5.65k|                  ada::unicode::is_forbidden_domain_code_point)) {
13138|      0|    host = std::nullopt;
13139|      0|    return is_valid = false;
13140|      0|  }
13141|       |
13142|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
13143|       |  // asciiDomain.
13144|  5.65k|  if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (13144:7): [True: 821, False: 4.83k]
  ------------------
13145|    821|    ada_log("parse_host got ipv4 ", *host);
13146|    821|    return parse_ipv4(*host);
13147|    821|  }
13148|       |
13149|  4.83k|  return true;
13150|  5.65k|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   12|   145k|ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept {
   13|       |  // The string is not empty and does not contain upper case ASCII characters.
   14|       |  //
   15|       |  // Optimization. To be considered as a possible ipv4, the string must end
   16|       |  // with 'x' or a lowercase hex character.
   17|       |  // Most of the time, this will be false so this simple check will save a lot
   18|       |  // of effort.
   19|       |  // If the address ends with a dot, we need to prune it (special case).
   20|   145k|  if (view.ends_with('.')) {
  ------------------
  |  Branch (20:7): [True: 6.03k, False: 139k]
  ------------------
   21|  6.03k|    view.remove_suffix(1);
   22|  6.03k|    if (view.empty()) {
  ------------------
  |  Branch (22:9): [True: 2.05k, False: 3.98k]
  ------------------
   23|  2.05k|      return false;
   24|  2.05k|    }
   25|  6.03k|  }
   26|   143k|  char last_char = view.back();
   27|   143k|  bool possible_ipv4 = (last_char >= '0' && last_char <= '9') ||
  ------------------
  |  Branch (27:25): [True: 138k, False: 4.72k]
  |  Branch (27:45): [True: 12.5k, False: 126k]
  ------------------
   28|   130k|                       (last_char >= 'a' && last_char <= 'f') ||
  ------------------
  |  Branch (28:25): [True: 125k, False: 5.23k]
  |  Branch (28:45): [True: 13.6k, False: 112k]
  ------------------
   29|   117k|                       last_char == 'x';
  ------------------
  |  Branch (29:24): [True: 2.79k, False: 114k]
  ------------------
   30|   143k|  if (!possible_ipv4) {
  ------------------
  |  Branch (30:7): [True: 114k, False: 28.9k]
  ------------------
   31|   114k|    return false;
   32|   114k|  }
   33|       |  // From the last character, find the last dot.
   34|  28.9k|  size_t last_dot = view.rfind('.');
   35|  28.9k|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (35:7): [True: 7.72k, False: 21.2k]
  ------------------
   36|       |    // We have at least one dot.
   37|  7.72k|    view.remove_prefix(last_dot + 1);
   38|  7.72k|  }
   39|       |  /** Optimization opportunity: we have basically identified the last number of
   40|       |     the ipv4 if we return true here. We might as well parse it and have at
   41|       |     least one number parsed when we get to parse_ipv4. */
   42|  28.9k|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (42:7): [True: 8.67k, False: 20.3k]
  ------------------
   43|  8.67k|    return true;
   44|  8.67k|  }
   45|       |  // It could be hex (0x), but not if there is a single character.
   46|  20.3k|  if (view.size() == 1) {
  ------------------
  |  Branch (46:7): [True: 4.42k, False: 15.8k]
  ------------------
   47|  4.42k|    return false;
   48|  4.42k|  }
   49|       |  // It must start with 0x.
   50|  15.8k|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (50:7): [True: 10.0k, False: 5.85k]
  ------------------
   51|  10.0k|    return false;
   52|  10.0k|  }
   53|       |  // We must allow "0x".
   54|  5.85k|  if (view.size() == 2) {
  ------------------
  |  Branch (54:7): [True: 1.04k, False: 4.80k]
  ------------------
   55|  1.04k|    return true;
   56|  1.04k|  }
   57|       |  // We have 0x followed by some characters, we need to check that they are
   58|       |  // hexadecimals.
   59|  4.80k|  view.remove_prefix(2);
   60|  4.80k|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   61|  5.85k|}
_ZN3ada7unicode16is_lowercase_hexEc:
10986|  39.0k|ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept {
10987|  39.0k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (10987:11): [True: 38.1k, False: 892]
  |  Branch (10987:23): [True: 18.9k, False: 19.1k]
  |  Branch (10987:37): [True: 19.1k, False: 939]
  |  Branch (10987:49): [True: 18.1k, False: 1.00k]
  ------------------
10988|  39.0k|}
_ZN3ada7unicode30is_forbidden_domain_code_pointEc:
10826|  1.24M|    const char c) noexcept {
10827|  1.24M|  return is_forbidden_domain_code_point_table[uint8_t(c)];
10828|  1.24M|}
_ZN3ada7helpers6resizeERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
11729|    107|ada_really_inline void resize(std::string_view& input, size_t pos) noexcept {
11730|    107|  ADA_ASSERT_TRUE(pos <= input.size());
11731|    107|  input.remove_suffix(input.size() - pos);
11732|    107|}
_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
11059|   126k|                    std::string& out) {
11060|   126k|  ada_log("percent_encode ", input, " to output string while ",
11061|   126k|          append ? "appending" : "overwriting");
11062|   126k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11063|   126k|    return character_sets::bit_at(character_set, c);
11064|   126k|  });
11065|   126k|  ada_log("percent_encode done checking, moved to ",
11066|   126k|          std::distance(input.begin(), pointer));
11067|       |
11068|       |  // Optimization: Don't iterate if percent encode is not required
11069|   126k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11069:7): [True: 103k, False: 22.4k]
  ------------------
11070|   103k|    ada_log("percent_encode encoding not needed.");
11071|   103k|    return false;
11072|   103k|  }
11073|       |  if constexpr (!append) {
11074|       |    out.clear();
11075|       |  }
11076|  22.4k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
11077|  22.4k|          " bytes");
11078|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
11079|  22.4k|  out.append(input.data(), std::distance(input.begin(), pointer));
11080|  22.4k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
11081|  22.4k|          " bytes");
11082|   743k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11082:10): [True: 720k, False: 22.4k]
  ------------------
11083|   720k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11083:9): [True: 549k, False: 171k]
  ------------------
11084|   549k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11085|   549k|    } else {
11086|   171k|      out += *pointer;
11087|   171k|    }
11088|   720k|  }
11089|  22.4k|  return true;
11090|   126k|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
11062|  2.35M|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11063|  2.35M|    return character_sets::bit_at(character_set, c);
11064|  2.35M|  });
_ZN3ada7helpers12shorten_pathERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeE:
11691|  7.77k|                                    ada::scheme::type type) {
11692|       |  // Let path be url's path.
11693|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
11694|       |  // Windows drive letter, then return.
11695|  7.77k|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (11695:7): [True: 358, False: 7.41k]
  ------------------
11696|    358|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (11696:7): [True: 239, False: 119]
  |  Branch (11696:54): [True: 239, False: 0]
  ------------------
11697|    239|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (11697:9): [True: 11, False: 228]
  ------------------
11698|    239|            helpers::substring(path, 1))) {
11699|     11|      return false;
11700|     11|    }
11701|    239|  }
11702|       |
11703|       |  // Remove path's last item, if any.
11704|  7.76k|  if (!path.empty()) {
  ------------------
  |  Branch (11704:7): [True: 7.66k, False: 98]
  ------------------
11705|  7.66k|    size_t slash_loc = path.rfind('/');
11706|  7.66k|    if (slash_loc != std::string_view::npos) {
  ------------------
  |  Branch (11706:9): [True: 7.53k, False: 130]
  ------------------
11707|  7.53k|      path.remove_suffix(path.size() - slash_loc);
11708|  7.53k|      return true;
11709|  7.53k|    }
11710|  7.66k|  }
11711|       |
11712|    228|  return false;
11713|  7.76k|}
_ZN3ada14url_aggregator11copy_schemeERKS0_:
14792|  13.3k|inline void url_aggregator::copy_scheme(const url_aggregator& u) {
14793|  13.3k|  ada_log("url_aggregator::copy_scheme ", u.buffer);
14794|  13.3k|  ADA_ASSERT_TRUE(validate());
14795|       |  // next line could overflow but unsigned arithmetic has well-defined
14796|       |  // overflows.
14797|  13.3k|  uint32_t new_difference = u.components.protocol_end - components.protocol_end;
14798|       |
14799|  13.3k|  type = u.type;
14800|  13.3k|  buffer.erase(0, components.protocol_end);
14801|  13.3k|  buffer.insert(0, u.get_protocol());
14802|  13.3k|  components.protocol_end = u.components.protocol_end;
14803|       |
14804|       |  // No need to update the components
14805|  13.3k|  if (new_difference == 0) {
  ------------------
  |  Branch (14805:7): [True: 249, False: 13.1k]
  ------------------
14806|    249|    return;
14807|    249|  }
14808|       |
14809|  13.1k|  apply_shifted_non_scheme_offsets(components, new_difference);
14810|  13.1k|  ADA_ASSERT_TRUE(validate());
14811|  13.1k|}
parse.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
14674|   113k|    ada::url_components& components, uint32_t new_difference) {
14675|   113k|  components.username_end += new_difference;
14676|   113k|  components.host_start += new_difference;
14677|   113k|  components.host_end += new_difference;
14678|   113k|  components.pathname_start += new_difference;
14679|   113k|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (14679:7): [True: 1.19k, False: 111k]
  ------------------
14680|  1.19k|    components.search_start += new_difference;
14681|  1.19k|  }
14682|   113k|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (14682:7): [True: 1.18k, False: 111k]
  ------------------
14683|  1.18k|    components.hash_start += new_difference;
14684|  1.18k|  }
14685|   113k|}
_ZN3ada14url_aggregator10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15017|  17.2k|ada_really_inline void url_aggregator::parse_path(std::string_view input) {
15018|  17.2k|  ada_log("url_aggregator::parse_path ", input);
15019|  17.2k|  ADA_ASSERT_TRUE(validate());
15020|  17.2k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15021|  17.2k|  std::string tmp_buffer;
15022|  17.2k|  std::string_view internal_input;
15023|  17.2k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (15023:7): [True: 533, False: 16.6k]
  ------------------
15024|    533|    tmp_buffer = input;
15025|       |    // Optimization opportunity: Instead of copying and then pruning, we could
15026|       |    // just directly build the string from user_input.
15027|    533|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
15028|    533|    internal_input = tmp_buffer;
15029|  16.6k|  } else {
15030|  16.6k|    internal_input = input;
15031|  16.6k|  }
15032|       |
15033|       |  // If url is special, then:
15034|  17.2k|  if (is_special()) {
  ------------------
  |  Branch (15034:7): [True: 17.2k, False: 0]
  ------------------
15035|  17.2k|    if (internal_input.empty()) {
  ------------------
  |  Branch (15035:9): [True: 1.50k, False: 15.7k]
  ------------------
15036|  1.50k|      update_base_pathname("/");
15037|  15.7k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (15037:16): [True: 2.18k, False: 13.5k]
  |  Branch (15037:46): [True: 140, False: 13.3k]
  ------------------
15038|  2.32k|      consume_prepared_path(internal_input.substr(1));
15039|  13.3k|    } else {
15040|  13.3k|      consume_prepared_path(internal_input);
15041|  13.3k|    }
15042|  17.2k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (15042:14): [True: 0, False: 0]
  ------------------
15043|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (15043:9): [True: 0, False: 0]
  ------------------
15044|      0|      consume_prepared_path(internal_input.substr(1));
15045|      0|    } else {
15046|      0|      consume_prepared_path(internal_input);
15047|      0|    }
15048|      0|  } else {
15049|       |    // Non-special URLs with an empty host can have their paths erased
15050|       |    // Path-only URLs cannot have their paths erased
15051|      0|    if (components.host_start == components.host_end && !has_authority()) {
  ------------------
  |  Branch (15051:9): [True: 0, False: 0]
  |  Branch (15051:57): [True: 0, False: 0]
  ------------------
15052|      0|      update_base_pathname("/");
15053|      0|    }
15054|      0|  }
15055|  17.2k|  ADA_ASSERT_TRUE(validate());
15056|  17.2k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15130|   105k|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
15131|   105k|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
15132|   105k|          " bytes]");
15133|   105k|  ADA_ASSERT_TRUE(validate());
15134|   105k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15135|   105k|  if (input.empty()) {
  ------------------
  |  Branch (15135:7): [True: 57, False: 105k]
  ------------------
15136|     57|    return is_valid = false;
15137|     57|  }  // technically unnecessary.
15138|       |  // If input starts with U+005B ([), then:
15139|   105k|  if (input[0] == '[') {
  ------------------
  |  Branch (15139:7): [True: 2.42k, False: 103k]
  ------------------
15140|       |    // If input does not end with U+005D (]), validation error, return failure.
15141|  2.42k|    if (input.back() != ']') {
  ------------------
  |  Branch (15141:9): [True: 825, False: 1.60k]
  ------------------
15142|    825|      return is_valid = false;
15143|    825|    }
15144|  1.60k|    ada_log("parse_host ipv6");
15145|       |
15146|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
15147|       |    // trailing U+005D (]) removed.
15148|  1.60k|    input.remove_prefix(1);
15149|  1.60k|    input.remove_suffix(1);
15150|  1.60k|    return parse_ipv6(input);
15151|  2.42k|  }
15152|       |
15153|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
15154|       |  // input.
15155|   103k|  if (!is_special()) {
  ------------------
  |  Branch (15155:7): [True: 1.39k, False: 101k]
  ------------------
15156|  1.39k|    return parse_opaque_host(input);
15157|  1.39k|  }
15158|       |  // Let domain be the result of running UTF-8 decode without BOM on the
15159|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
15160|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
15161|       |  // which case we do not need to call the expensive 'to_ascii' if a few
15162|       |  // conditions are met: no '%' and no 'xn-' subsequence.
15163|       |
15164|       |  // Often, the input does not contain any forbidden code points, and no upper
15165|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
15166|       |  // optimize for such a common case.
15167|       |
15168|       |  // Fast path: try to parse as pure decimal IPv4(a.b.c.d) first.
15169|   101k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
15170|   101k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (15170:7): [True: 1.05k, False: 100k]
  ------------------
15171|       |    // Fast path succeeded - input is pure decimal IPv4
15172|  1.05k|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (15172:9): [True: 1.05k, False: 0]
  |  Branch (15172:27): [True: 161, False: 890]
  ------------------
15173|    161|      update_base_hostname(input.substr(0, input.size() - 1));
15174|    890|    } else {
15175|    890|      update_base_hostname(input);
15176|    890|    }
15177|  1.05k|    host_type = IPV4;
15178|  1.05k|    is_valid = true;
15179|  1.05k|    ada_log("parse_host fast path decimal ipv4");
15180|  1.05k|    ADA_ASSERT_TRUE(validate());
15181|  1.05k|    return true;
15182|  1.05k|  }
15183|   100k|  uint8_t is_forbidden_or_upper =
15184|   100k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
15185|   100k|                                                             input.size());
15186|       |  // Minor optimization opportunity:
15187|       |  // contains_forbidden_domain_code_point_or_upper could be extend to check for
15188|       |  // the presence of characters that cannot appear in the ipv4 address and we
15189|       |  // could also check whether x and n and - are present, and so we could skip
15190|       |  // some of the checks below. However, the gains are likely to be small, and
15191|       |  // the code would be more complex.
15192|   100k|  if (is_forbidden_or_upper == 0 &&
  ------------------
  |  Branch (15192:7): [True: 79.9k, False: 20.7k]
  ------------------
15193|  79.9k|      input.find("xn-") == std::string_view::npos) {
  ------------------
  |  Branch (15193:7): [True: 78.3k, False: 1.60k]
  ------------------
15194|       |    // fast path
15195|  78.3k|    update_base_hostname(input);
15196|       |
15197|       |    // Check for other IPv4 formats (hex, octal, etc.)
15198|  78.3k|    if (checkers::is_ipv4(get_hostname())) {
  ------------------
  |  Branch (15198:9): [True: 5.13k, False: 73.2k]
  ------------------
15199|  5.13k|      ada_log("parse_host fast path ipv4");
15200|  5.13k|      return parse_ipv4(get_hostname(), true);
15201|  5.13k|    }
15202|  73.2k|    ada_log("parse_host fast path ", get_hostname());
15203|  73.2k|    is_valid = true;
15204|  73.2k|    return true;
15205|  78.3k|  }
15206|       |  // We have encountered at least one forbidden code point or the input contains
15207|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
15208|       |  // conversion.
15209|       |
15210|  22.3k|  ada_log("parse_host calling to_ascii");
15211|  22.3k|  std::optional<std::string> host = std::string(get_hostname());
15212|  22.3k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
15213|  22.3k|  if (!is_valid) {
  ------------------
  |  Branch (15213:7): [True: 9.52k, False: 12.8k]
  ------------------
15214|  9.52k|    ada_log("parse_host to_ascii returns false");
15215|  9.52k|    return is_valid = false;
15216|  9.52k|  }
15217|  12.8k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
15218|  12.8k|          " bytes]");
15219|       |
15220|  12.8k|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (15220:7): [True: 0, False: 12.8k]
  ------------------
15221|  12.8k|                          ada::unicode::is_forbidden_domain_code_point)) {
15222|      0|    return is_valid = false;
15223|      0|  }
15224|       |
15225|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
15226|       |  // asciiDomain.
15227|  12.8k|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (15227:7): [True: 1.84k, False: 10.9k]
  ------------------
15228|  1.84k|    ada_log("parse_host got ipv4 ", *host);
15229|  1.84k|    return parse_ipv4(host.value(), false);
15230|  1.84k|  }
15231|       |
15232|  10.9k|  update_base_hostname(host.value());
15233|  10.9k|  ADA_ASSERT_TRUE(validate());
15234|  10.9k|  return true;
15235|  12.8k|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
10867|   100k|                                              size_t length) noexcept {
10868|   100k|  size_t i = 0;
10869|   100k|  uint8_t accumulator{};
10870|   382k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (10870:10): [True: 281k, False: 100k]
  ------------------
10871|   281k|    accumulator |=
10872|   281k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
10873|   281k|    accumulator |=
10874|   281k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
10875|   281k|    accumulator |=
10876|   281k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
10877|   281k|    accumulator |=
10878|   281k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
10879|   281k|  }
10880|   343k|  for (; i < length; i++) {
  ------------------
  |  Branch (10880:10): [True: 242k, False: 100k]
  ------------------
10881|   242k|    accumulator |=
10882|   242k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
10883|   242k|  }
10884|   100k|  return accumulator;
10885|   100k|}
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
11722|  40.9k|                                                       size_t pos) {
11723|  40.9k|  ADA_ASSERT_TRUE(pos <= input.size());
11724|       |  // The following is safer but unneeded if we have the above line:
11725|       |  // return pos > input.size() ? std::string_view() : input.substr(pos);
11726|  40.9k|  return input.substr(pos);
11727|  40.9k|}
parse.cc:_ZZNK3ada14url_aggregator9to_stringEvENK3$_0clEj:
15567|   267k|  auto convert_offset_to_string = [](uint32_t offset) -> std::string {
15568|   267k|    if (offset == url_components::omitted) {
  ------------------
  |  Branch (15568:9): [True: 72.5k, False: 195k]
  ------------------
15569|  72.5k|      return "null";
15570|   195k|    } else {
15571|   195k|      return std::to_string(offset);
15572|   195k|    }
15573|   267k|  };
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
16157|  81.6k|inline void url_aggregator::consume_prepared_path(std::string_view input) {
16158|  81.6k|  ada_log("url_aggregator::consume_prepared_path ", input);
16159|       |  /***
16160|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
16161|       |   * unfortunate. This particular function is nearly identical, except that it
16162|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
16163|       |   * very common) merely appends to the buffer. This is the same trivial path as
16164|       |   * with helpers::parse_prepared_path, except that we have the additional check
16165|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
16166|       |   * modify it, and then insert it back into the buffer.
16167|       |   */
16168|  81.6k|  uint8_t accumulator = checkers::path_signature(input);
16169|       |  // Let us first detect a trivial case.
16170|       |  // If it is special, we check that we have no dot, no %,  no \ and no
16171|       |  // character needing percent encoding. Otherwise, we check that we have no %,
16172|       |  // no dot, and no character needing percent encoding.
16173|  81.6k|  constexpr uint8_t need_encoding = 1;
16174|  81.6k|  constexpr uint8_t backslash_char = 2;
16175|  81.6k|  constexpr uint8_t dot_char = 4;
16176|  81.6k|  constexpr uint8_t percent_char = 8;
16177|  81.6k|  bool special = type != ada::scheme::NOT_SPECIAL;
16178|  81.6k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (16178:39): [True: 5.16k, False: 76.4k]
  ------------------
16179|  5.16k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (16179:39): [True: 588, False: 4.57k]
  ------------------
16180|  81.6k|  bool trivial_path =
16181|  81.6k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (16181:7): [True: 58.7k, False: 22.8k]
  |  Branch (16181:8): [True: 78.3k, False: 3.23k]
  ------------------
16182|  81.6k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
16183|  3.23k|                  0)) &&
16184|  58.7k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (16184:7): [True: 58.4k, False: 358]
  ------------------
16185|  81.6k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (16185:7): [True: 6.31k, False: 75.3k]
  |  Branch (16185:34): [True: 6.20k, False: 107]
  ------------------
16186|       |    // '4' means that we have at least one dot, but nothing that requires
16187|       |    // percent encoding or decoding. The only part that is not trivial is
16188|       |    // that we may have single dots and double dots path segments.
16189|       |    // If we have such segments, then we either have a path that begins
16190|       |    // with '.' (easy to check), or we have the sequence './'.
16191|       |    // Note: input cannot be empty, it must at least contain one character ('.')
16192|       |    // Note: we know that '\' is not present.
16193|  6.20k|    if (input[0] != '.') {
  ------------------
  |  Branch (16193:9): [True: 3.79k, False: 2.41k]
  ------------------
16194|  3.79k|      size_t slashdot = 0;
16195|  3.79k|      bool dot_is_file = true;
16196|  15.6k|      for (;;) {
16197|  15.6k|        slashdot = input.find("/.", slashdot);
16198|  15.6k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (16198:13): [True: 3.79k, False: 11.8k]
  ------------------
16199|  3.79k|          break;
16200|  11.8k|        } else {  // uncommon
16201|       |          // only three cases matter: /./, /.. or a final /
16202|  11.8k|          slashdot += 2;
16203|  11.8k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (16203:28): [True: 507, False: 11.3k]
  |  Branch (16203:56): [True: 7.89k, False: 3.45k]
  ------------------
16204|  3.45k|                           input[slashdot] == '/');
  ------------------
  |  Branch (16204:28): [True: 2.10k, False: 1.34k]
  ------------------
16205|  11.8k|        }
16206|  15.6k|      }
16207|  3.79k|      trivial_path = dot_is_file;
16208|  3.79k|    }
16209|  6.20k|  }
16210|  81.6k|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (16210:7): [True: 60.1k, False: 21.5k]
  |  Branch (16210:23): [True: 56.7k, False: 3.34k]
  ------------------
16211|  56.7k|    ada_log("parse_path trivial");
16212|  56.7k|    buffer += '/';
16213|  56.7k|    buffer += input;
16214|  56.7k|    return;
16215|  56.7k|  }
16216|  24.8k|  std::string path = std::string(get_pathname());
16217|       |  // We are going to need to look a bit at the path, but let us see if we can
16218|       |  // ignore percent encoding *and* backslashes *and* percent characters.
16219|       |  // Except for the trivial case, this is likely to capture 99% of paths out
16220|       |  // there.
16221|  24.8k|  bool fast_path =
16222|  24.8k|      (special &&
  ------------------
  |  Branch (16222:8): [True: 22.5k, False: 2.35k]
  ------------------
16223|  22.5k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (16223:8): [True: 7.68k, False: 14.8k]
  ------------------
16224|  7.68k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (16224:7): [True: 6.59k, False: 1.09k]
  ------------------
16225|  24.8k|  if (fast_path) {
  ------------------
  |  Branch (16225:7): [True: 6.59k, False: 18.2k]
  ------------------
16226|  6.59k|    ada_log("parse_prepared_path fast");
16227|       |    // Here we don't need to worry about \ or percent encoding.
16228|       |    // We also do not have a file protocol. We might have dots, however,
16229|       |    // but dots must as appear as '.', and they cannot be encoded because
16230|       |    // the symbol '%' is not present.
16231|  6.59k|    size_t previous_location = 0;  // We start at 0.
16232|  38.5k|    do {
16233|  38.5k|      size_t new_location = input.find('/', previous_location);
16234|       |      // std::string_view path_view = input;
16235|       |      //  We process the last segment separately:
16236|  38.5k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (16236:11): [True: 6.59k, False: 31.9k]
  ------------------
16237|  6.59k|        std::string_view path_view = input.substr(previous_location);
16238|  6.59k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (16238:13): [True: 696, False: 5.89k]
  ------------------
16239|       |          // e.g., if you receive ".." with an empty path, you go to "/".
16240|    696|          if (path.empty()) {
  ------------------
  |  Branch (16240:15): [True: 235, False: 461]
  ------------------
16241|    235|            path = '/';
16242|    235|            update_base_pathname(path);
16243|    235|            return;
16244|    235|          }
16245|       |          // Fast case where we have nothing to do:
16246|    461|          if (path.back() == '/') {
  ------------------
  |  Branch (16246:15): [True: 141, False: 320]
  ------------------
16247|    141|            update_base_pathname(path);
16248|    141|            return;
16249|    141|          }
16250|       |          // If you have the path "/joe/myfriend",
16251|       |          // then you delete 'myfriend'.
16252|    320|          path.resize(path.rfind('/') + 1);
16253|    320|          update_base_pathname(path);
16254|    320|          return;
16255|    461|        }
16256|  5.89k|        path += '/';
16257|  5.89k|        if (path_view != ".") {
  ------------------
  |  Branch (16257:13): [True: 5.22k, False: 668]
  ------------------
16258|  5.22k|          path.append(path_view);
16259|  5.22k|        }
16260|  5.89k|        update_base_pathname(path);
16261|  5.89k|        return;
16262|  31.9k|      } else {
16263|       |        // This is a non-final segment.
16264|  31.9k|        std::string_view path_view =
16265|  31.9k|            input.substr(previous_location, new_location - previous_location);
16266|  31.9k|        previous_location = new_location + 1;
16267|  31.9k|        if (path_view == "..") {
  ------------------
  |  Branch (16267:13): [True: 7.05k, False: 24.9k]
  ------------------
16268|  7.05k|          size_t last_delimiter = path.rfind('/');
16269|  7.05k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (16269:15): [True: 4.33k, False: 2.72k]
  ------------------
16270|  4.33k|            path.erase(last_delimiter);
16271|  4.33k|          }
16272|  24.9k|        } else if (path_view != ".") {
  ------------------
  |  Branch (16272:20): [True: 20.9k, False: 3.93k]
  ------------------
16273|  20.9k|          path += '/';
16274|  20.9k|          path.append(path_view);
16275|  20.9k|        }
16276|  31.9k|      }
16277|  38.5k|    } while (true);
  ------------------
  |  Branch (16277:14): [True: 31.9k, Folded]
  ------------------
16278|  18.2k|  } else {
16279|  18.2k|    ada_log("parse_path slow");
16280|       |    // we have reached the general case
16281|  18.2k|    bool needs_percent_encoding = (accumulator & 1);
16282|  18.2k|    std::string path_buffer_tmp;
16283|  83.1k|    do {
16284|  83.1k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (16284:26): [True: 68.4k, False: 14.7k]
  |  Branch (16284:37): [True: 6.08k, False: 62.4k]
  ------------------
16285|  83.1k|                            ? input.find_first_of("/\\")
16286|  83.1k|                            : input.find('/');
16287|  83.1k|      std::string_view path_view = input;
16288|  83.1k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (16288:11): [True: 64.9k, False: 18.2k]
  ------------------
16289|  64.9k|        path_view.remove_suffix(path_view.size() - location);
16290|  64.9k|        input.remove_prefix(location + 1);
16291|  64.9k|      }
16292|       |      // path_buffer is either path_view or it might point at a percent encoded
16293|       |      // temporary string.
16294|  83.1k|      std::string_view path_buffer =
16295|  83.1k|          (needs_percent_encoding &&
  ------------------
  |  Branch (16295:12): [True: 57.5k, False: 25.6k]
  ------------------
16296|  57.5k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (16296:12): [True: 25.4k, False: 32.0k]
  ------------------
16297|  57.5k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
16298|  83.1k|              ? path_buffer_tmp
16299|  83.1k|              : path_view;
16300|  83.1k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (16300:11): [True: 17.1k, False: 66.0k]
  ------------------
16301|  17.1k|        helpers::shorten_path(path, type);
16302|  17.1k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (16302:13): [True: 1.09k, False: 16.0k]
  ------------------
16303|  1.09k|          path += '/';
16304|  1.09k|        }
16305|  66.0k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (16305:18): [True: 3.66k, False: 62.4k]
  ------------------
16306|  3.66k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (16306:18): [True: 463, False: 3.20k]
  ------------------
16307|    463|        path += '/';
16308|    463|      }
16309|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
16310|  65.6k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (16310:16): [True: 62.4k, False: 3.20k]
  ------------------
16311|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
16312|       |        // Windows drive letter, then replace the second code point in
16313|       |        // path_buffer with U+003A (:).
16314|  62.4k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (16314:13): [True: 16.0k, False: 46.3k]
  |  Branch (16314:48): [True: 8.05k, False: 8.01k]
  ------------------
16315|  8.05k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (16315:13): [True: 769, False: 7.29k]
  ------------------
16316|    769|          path += '/';
16317|    769|          path += path_buffer[0];
16318|    769|          path += ':';
16319|    769|          path_buffer.remove_prefix(2);
16320|    769|          path.append(path_buffer);
16321|  61.6k|        } else {
16322|       |          // Append path_buffer to url's path.
16323|  61.6k|          path += '/';
16324|  61.6k|          path.append(path_buffer);
16325|  61.6k|        }
16326|  62.4k|      }
16327|  83.1k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (16327:11): [True: 18.2k, False: 64.9k]
  ------------------
16328|  18.2k|        update_base_pathname(path);
16329|  18.2k|        return;
16330|  18.2k|      }
16331|  83.1k|    } while (true);
  ------------------
  |  Branch (16331:14): [True: 64.9k, Folded]
  ------------------
16332|  18.2k|  }
16333|  24.8k|}
_ZN3ada7unicode14to_lower_asciiEPcm:
10583|   100k|constexpr bool to_lower_ascii(char* input, size_t length) noexcept {
10584|   100k|  uint64_t broadcast_80 = broadcast(0x80);
10585|   100k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
10586|   100k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
10587|   100k|  uint64_t non_ascii = 0;
10588|   100k|  size_t i = 0;
10589|       |
10590|   185k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (10590:10): [True: 84.4k, False: 100k]
  ------------------
10591|  84.4k|    uint64_t word{};
10592|  84.4k|    memcpy(&word, input + i, sizeof(word));
10593|  84.4k|    non_ascii |= (word & broadcast_80);
10594|  84.4k|    word ^=
10595|  84.4k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
10596|  84.4k|    memcpy(input + i, &word, sizeof(word));
10597|  84.4k|  }
10598|   100k|  if (i < length) {
  ------------------
  |  Branch (10598:7): [True: 98.2k, False: 2.66k]
  ------------------
10599|  98.2k|    uint64_t word{};
10600|  98.2k|    memcpy(&word, input + i, length - i);
10601|  98.2k|    non_ascii |= (word & broadcast_80);
10602|  98.2k|    word ^=
10603|  98.2k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
10604|  98.2k|    memcpy(input + i, &word, length - i);
10605|  98.2k|  }
10606|   100k|  return non_ascii == 0;
10607|   100k|}
_ZN3ada7unicode9broadcastEh:
10579|   302k|constexpr uint64_t broadcast(uint8_t v) noexcept {
10580|   302k|  return 0x101010101010101ull * v;
10581|   302k|}
_ZN3ada7helpers11encode_jsonINSt3__120back_insert_iteratorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEEEEvNS2_17basic_string_viewIcS6_EET_:
11583|   285k|void encode_json(std::string_view view, out_iter out) {
11584|       |  // trivial implementation. could be faster.
11585|   285k|  const char* hexvalues =
11586|   285k|      "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
11587|  12.5M|  for (uint8_t c : view) {
  ------------------
  |  Branch (11587:18): [True: 12.5M, False: 285k]
  ------------------
11588|  12.5M|    if (c == '\\') {
  ------------------
  |  Branch (11588:9): [True: 5.21k, False: 12.5M]
  ------------------
11589|  5.21k|      *out++ = '\\';
11590|  5.21k|      *out++ = '\\';
11591|  12.5M|    } else if (c == '"') {
  ------------------
  |  Branch (11591:16): [True: 2.90k, False: 12.5M]
  ------------------
11592|  2.90k|      *out++ = '\\';
11593|  2.90k|      *out++ = '"';
11594|  12.5M|    } else if (c <= 0x1f) {
  ------------------
  |  Branch (11594:16): [True: 0, False: 12.5M]
  ------------------
11595|      0|      *out++ = '\\';
11596|      0|      *out++ = 'u';
11597|      0|      *out++ = '0';
11598|      0|      *out++ = '0';
11599|      0|      *out++ = hexvalues[2 * c];
11600|      0|      *out++ = hexvalues[2 * c + 1];
11601|  12.5M|    } else {
11602|  12.5M|      *out++ = c;
11603|  12.5M|    }
11604|  12.5M|  }
11605|   285k|}
_ZN3ada3url20set_host_or_hostnameILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
13322|  18.9k|bool url::set_host_or_hostname(const std::string_view input) {
13323|  18.9k|  if (has_opaque_path) {
  ------------------
  |  Branch (13323:7): [True: 0, False: 18.9k]
  ------------------
13324|      0|    return false;
13325|      0|  }
13326|       |
13327|  18.9k|  url saved_url(*this);
13328|       |
13329|  18.9k|  std::optional<std::string> previous_host = host;
13330|  18.9k|  std::optional<uint16_t> previous_port = port;
13331|       |
13332|  18.9k|  size_t host_end_pos = input.find('#');
13333|  18.9k|  std::string _host(input.data(), host_end_pos != std::string_view::npos
  ------------------
  |  Branch (13333:35): [True: 905, False: 18.0k]
  ------------------
13334|  18.9k|                                      ? host_end_pos
13335|  18.9k|                                      : input.size());
13336|  18.9k|  helpers::remove_ascii_tab_or_newline(_host);
13337|  18.9k|  std::string_view new_host(_host);
13338|       |
13339|  18.9k|  auto check_url_size = [&]() -> bool {
13340|  18.9k|    if (get_href_size() > ada::get_max_input_length()) {
13341|  18.9k|      *this = std::move(saved_url);
13342|  18.9k|      return false;
13343|  18.9k|    }
13344|  18.9k|    return true;
13345|  18.9k|  };
13346|       |
13347|       |  // If url's scheme is "file", then set state to file host state, instead of
13348|       |  // host state.
13349|  18.9k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (13349:7): [True: 17.2k, False: 1.70k]
  ------------------
13350|  17.2k|    std::string_view host_view(_host.data(), _host.length());
13351|  17.2k|    auto [location, found_colon] =
13352|  17.2k|        helpers::get_host_delimiter_location(is_special(), host_view);
13353|       |
13354|       |    // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13355|       |    // Note: the 'found_colon' value is true if and only if a colon was
13356|       |    // encountered while not inside brackets.
13357|  17.2k|    if (found_colon) {
  ------------------
  |  Branch (13357:9): [True: 7.10k, False: 10.1k]
  ------------------
13358|       |      // If buffer is the empty string, host-missing validation error, return
13359|       |      // failure.
13360|  7.10k|      std::string_view buffer = host_view.substr(0, location);
13361|  7.10k|      if (buffer.empty()) {
  ------------------
  |  Branch (13361:11): [True: 175, False: 6.93k]
  ------------------
13362|    175|        return false;
13363|    175|      }
13364|       |
13365|       |      // If state override is given and state override is hostname state, then
13366|       |      // return failure.
13367|       |      if constexpr (override_hostname) {
13368|       |        return false;
13369|       |      }
13370|       |
13371|       |      // Let host be the result of host parsing buffer with url is not special.
13372|  6.93k|      bool succeeded = parse_host(buffer);
13373|  6.93k|      if (!succeeded) {
  ------------------
  |  Branch (13373:11): [True: 862, False: 6.06k]
  ------------------
13374|    862|        host = std::move(previous_host);
13375|    862|        update_base_port(previous_port);
13376|    862|        return false;
13377|    862|      }
13378|       |
13379|       |      // Set url's host to host, buffer to the empty string, and state to port
13380|       |      // state.
13381|  6.06k|      std::string_view port_buffer = new_host.substr(location + 1);
13382|  6.06k|      if (!port_buffer.empty()) {
  ------------------
  |  Branch (13382:11): [True: 5.27k, False: 793]
  ------------------
13383|  5.27k|        set_port(port_buffer);
13384|  5.27k|      }
13385|  6.06k|      return check_url_size();
13386|  6.93k|    }
13387|       |    // Otherwise, if one of the following is true:
13388|       |    // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13389|       |    // - url is special and c is U+005C (\)
13390|  10.1k|    else {
13391|       |      // If url is special and host_view is the empty string, host-missing
13392|       |      // validation error, return failure.
13393|  10.1k|      if (host_view.empty() && is_special()) {
  ------------------
  |  Branch (13393:11): [True: 3.33k, False: 6.78k]
  |  Branch (13393:32): [True: 3.33k, False: 0]
  ------------------
13394|  3.33k|        return false;
13395|  3.33k|      }
13396|       |
13397|       |      // Otherwise, if state override is given, host_view is the empty string,
13398|       |      // and either url includes credentials or url's port is non-null, then
13399|       |      // return failure.
13400|  6.78k|      if (host_view.empty() && (has_credentials() || port.has_value())) {
  ------------------
  |  Branch (13400:11): [True: 0, False: 6.78k]
  |  Branch (13400:33): [True: 0, False: 0]
  |  Branch (13400:54): [True: 0, False: 0]
  ------------------
13401|      0|        return false;
13402|      0|      }
13403|       |
13404|       |      // Let host be the result of host parsing host_view with url is not
13405|       |      // special.
13406|  6.78k|      if (host_view.empty() && !is_special()) {
  ------------------
  |  Branch (13406:11): [True: 0, False: 6.78k]
  |  Branch (13406:32): [True: 0, False: 0]
  ------------------
13407|      0|        host = "";
13408|      0|        return check_url_size();
13409|      0|      }
13410|       |
13411|  6.78k|      bool succeeded = parse_host(host_view);
13412|  6.78k|      if (!succeeded) {
  ------------------
  |  Branch (13412:11): [True: 3.06k, False: 3.71k]
  ------------------
13413|  3.06k|        host = std::move(previous_host);
13414|  3.06k|        update_base_port(previous_port);
13415|  3.06k|        return false;
13416|  3.06k|      }
13417|  3.71k|      return check_url_size();
13418|  6.78k|    }
13419|  17.2k|  }
13420|       |
13421|  1.70k|  size_t location = new_host.find_first_of("/\\?");
13422|  1.70k|  if (location != std::string_view::npos) {
  ------------------
  |  Branch (13422:7): [True: 987, False: 718]
  ------------------
13423|    987|    new_host.remove_suffix(new_host.length() - location);
13424|    987|  }
13425|       |
13426|  1.70k|  if (new_host.empty()) {
  ------------------
  |  Branch (13426:7): [True: 42, False: 1.66k]
  ------------------
13427|       |    // Set url's host to the empty string.
13428|     42|    host = "";
13429|  1.66k|  } else {
13430|       |    // Let host be the result of host parsing buffer with url is not special.
13431|  1.66k|    if (!parse_host(new_host)) {
  ------------------
  |  Branch (13431:9): [True: 1.12k, False: 541]
  ------------------
13432|  1.12k|      host = std::move(previous_host);
13433|  1.12k|      update_base_port(previous_port);
13434|  1.12k|      return false;
13435|  1.12k|    }
13436|       |
13437|       |    // If host is "localhost", then set host to the empty string.
13438|    541|    if (host == "localhost") {
  ------------------
  |  Branch (13438:9): [True: 10, False: 531]
  ------------------
13439|     10|      host = "";
13440|     10|    }
13441|    541|  }
13442|    583|  return check_url_size();
13443|  1.70k|}
_ZZN3ada3url20set_host_or_hostnameILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEENKUlvE_clEv:
13339|  10.3k|  auto check_url_size = [&]() -> bool {
13340|  10.3k|    if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13340:9): [True: 0, False: 10.3k]
  ------------------
13341|      0|      *this = std::move(saved_url);
13342|      0|      return false;
13343|      0|    }
13344|  10.3k|    return true;
13345|  10.3k|  };
_ZN3ada3url20set_host_or_hostnameILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
13322|  18.1k|bool url::set_host_or_hostname(const std::string_view input) {
13323|  18.1k|  if (has_opaque_path) {
  ------------------
  |  Branch (13323:7): [True: 0, False: 18.1k]
  ------------------
13324|      0|    return false;
13325|      0|  }
13326|       |
13327|  18.1k|  url saved_url(*this);
13328|       |
13329|  18.1k|  std::optional<std::string> previous_host = host;
13330|  18.1k|  std::optional<uint16_t> previous_port = port;
13331|       |
13332|  18.1k|  size_t host_end_pos = input.find('#');
13333|  18.1k|  std::string _host(input.data(), host_end_pos != std::string_view::npos
  ------------------
  |  Branch (13333:35): [True: 845, False: 17.3k]
  ------------------
13334|  18.1k|                                      ? host_end_pos
13335|  18.1k|                                      : input.size());
13336|  18.1k|  helpers::remove_ascii_tab_or_newline(_host);
13337|  18.1k|  std::string_view new_host(_host);
13338|       |
13339|  18.1k|  auto check_url_size = [&]() -> bool {
13340|  18.1k|    if (get_href_size() > ada::get_max_input_length()) {
13341|  18.1k|      *this = std::move(saved_url);
13342|  18.1k|      return false;
13343|  18.1k|    }
13344|  18.1k|    return true;
13345|  18.1k|  };
13346|       |
13347|       |  // If url's scheme is "file", then set state to file host state, instead of
13348|       |  // host state.
13349|  18.1k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (13349:7): [True: 16.4k, False: 1.75k]
  ------------------
13350|  16.4k|    std::string_view host_view(_host.data(), _host.length());
13351|  16.4k|    auto [location, found_colon] =
13352|  16.4k|        helpers::get_host_delimiter_location(is_special(), host_view);
13353|       |
13354|       |    // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13355|       |    // Note: the 'found_colon' value is true if and only if a colon was
13356|       |    // encountered while not inside brackets.
13357|  16.4k|    if (found_colon) {
  ------------------
  |  Branch (13357:9): [True: 6.03k, False: 10.3k]
  ------------------
13358|       |      // If buffer is the empty string, host-missing validation error, return
13359|       |      // failure.
13360|  6.03k|      std::string_view buffer = host_view.substr(0, location);
13361|  6.03k|      if (buffer.empty()) {
  ------------------
  |  Branch (13361:11): [True: 168, False: 5.86k]
  ------------------
13362|    168|        return false;
13363|    168|      }
13364|       |
13365|       |      // If state override is given and state override is hostname state, then
13366|       |      // return failure.
13367|  5.86k|      if constexpr (override_hostname) {
13368|  5.86k|        return false;
13369|  5.86k|      }
13370|       |
13371|       |      // Let host be the result of host parsing buffer with url is not special.
13372|      0|      bool succeeded = parse_host(buffer);
13373|  5.86k|      if (!succeeded) {
  ------------------
  |  Branch (13373:11): [True: 0, False: 5.86k]
  ------------------
13374|      0|        host = std::move(previous_host);
13375|      0|        update_base_port(previous_port);
13376|      0|        return false;
13377|      0|      }
13378|       |
13379|       |      // Set url's host to host, buffer to the empty string, and state to port
13380|       |      // state.
13381|  5.86k|      std::string_view port_buffer = new_host.substr(location + 1);
13382|  5.86k|      if (!port_buffer.empty()) {
  ------------------
  |  Branch (13382:11): [True: 0, False: 5.86k]
  ------------------
13383|      0|        set_port(port_buffer);
13384|      0|      }
13385|  5.86k|      return check_url_size();
13386|  5.86k|    }
13387|       |    // Otherwise, if one of the following is true:
13388|       |    // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13389|       |    // - url is special and c is U+005C (\)
13390|  10.3k|    else {
13391|       |      // If url is special and host_view is the empty string, host-missing
13392|       |      // validation error, return failure.
13393|  10.3k|      if (host_view.empty() && is_special()) {
  ------------------
  |  Branch (13393:11): [True: 3.36k, False: 6.99k]
  |  Branch (13393:32): [True: 3.36k, False: 0]
  ------------------
13394|  3.36k|        return false;
13395|  3.36k|      }
13396|       |
13397|       |      // Otherwise, if state override is given, host_view is the empty string,
13398|       |      // and either url includes credentials or url's port is non-null, then
13399|       |      // return failure.
13400|  6.99k|      if (host_view.empty() && (has_credentials() || port.has_value())) {
  ------------------
  |  Branch (13400:11): [True: 0, False: 6.99k]
  |  Branch (13400:33): [True: 0, False: 0]
  |  Branch (13400:54): [True: 0, False: 0]
  ------------------
13401|      0|        return false;
13402|      0|      }
13403|       |
13404|       |      // Let host be the result of host parsing host_view with url is not
13405|       |      // special.
13406|  6.99k|      if (host_view.empty() && !is_special()) {
  ------------------
  |  Branch (13406:11): [True: 0, False: 6.99k]
  |  Branch (13406:32): [True: 0, False: 0]
  ------------------
13407|      0|        host = "";
13408|      0|        return check_url_size();
13409|      0|      }
13410|       |
13411|  6.99k|      bool succeeded = parse_host(host_view);
13412|  6.99k|      if (!succeeded) {
  ------------------
  |  Branch (13412:11): [True: 3.11k, False: 3.88k]
  ------------------
13413|  3.11k|        host = std::move(previous_host);
13414|  3.11k|        update_base_port(previous_port);
13415|  3.11k|        return false;
13416|  3.11k|      }
13417|  3.88k|      return check_url_size();
13418|  6.99k|    }
13419|  16.4k|  }
13420|       |
13421|  1.75k|  size_t location = new_host.find_first_of("/\\?");
13422|  1.75k|  if (location != std::string_view::npos) {
  ------------------
  |  Branch (13422:7): [True: 1.01k, False: 739]
  ------------------
13423|  1.01k|    new_host.remove_suffix(new_host.length() - location);
13424|  1.01k|  }
13425|       |
13426|  1.75k|  if (new_host.empty()) {
  ------------------
  |  Branch (13426:7): [True: 59, False: 1.69k]
  ------------------
13427|       |    // Set url's host to the empty string.
13428|     59|    host = "";
13429|  1.69k|  } else {
13430|       |    // Let host be the result of host parsing buffer with url is not special.
13431|  1.69k|    if (!parse_host(new_host)) {
  ------------------
  |  Branch (13431:9): [True: 1.13k, False: 567]
  ------------------
13432|  1.13k|      host = std::move(previous_host);
13433|  1.13k|      update_base_port(previous_port);
13434|  1.13k|      return false;
13435|  1.13k|    }
13436|       |
13437|       |    // If host is "localhost", then set host to the empty string.
13438|    567|    if (host == "localhost") {
  ------------------
  |  Branch (13438:9): [True: 12, False: 555]
  ------------------
13439|     12|      host = "";
13440|     12|    }
13441|    567|  }
13442|    626|  return check_url_size();
13443|  1.75k|}
_ZZN3ada3url20set_host_or_hostnameILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEENKUlvE_clEv:
13339|  4.51k|  auto check_url_size = [&]() -> bool {
13340|  4.51k|    if (get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (13340:9): [True: 0, False: 4.51k]
  ------------------
13341|      0|      *this = std::move(saved_url);
13342|      0|      return false;
13343|      0|    }
13344|  4.51k|    return true;
13345|  4.51k|  };
_ZN3ada7helpers38strip_trailing_spaces_from_opaque_pathINS_3urlEEEvRT_:
12569|  2.83k|ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url) {
12570|  2.83k|  ada_log("helpers::strip_trailing_spaces_from_opaque_path");
12571|  2.83k|  if (!url.has_opaque_path) return;
  ------------------
  |  Branch (12571:7): [True: 2.83k, False: 0]
  ------------------
12572|      0|  if (url.has_hash()) return;
  ------------------
  |  Branch (12572:7): [True: 0, False: 0]
  ------------------
12573|      0|  if (url.has_search()) return;
  ------------------
  |  Branch (12573:7): [True: 0, False: 0]
  ------------------
12574|       |
12575|      0|  auto path = std::string(url.get_pathname());
12576|      0|  while (!path.empty() && path.back() == ' ') {
  ------------------
  |  Branch (12576:10): [True: 0, False: 0]
  |  Branch (12576:27): [True: 0, False: 0]
  ------------------
12577|      0|    path.resize(path.size() - 1);
12578|      0|  }
12579|      0|  url.update_base_pathname(path);
12580|      0|}
_ZN3ada3url12parse_schemeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
12964|  8.32k|ada_really_inline bool url::parse_scheme(const std::string_view input) {
12965|  8.32k|  auto parsed_type = ada::scheme::get_scheme_type(input);
12966|  8.32k|  bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
12967|       |  /**
12968|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
12969|       |   *http, https), in which case, we can go really fast.
12970|       |   **/
12971|  8.32k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (12971:7): [True: 3.79k, False: 4.52k]
  ------------------
12972|  3.79k|    if constexpr (has_state_override) {
12973|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
12974|       |      // then return.
12975|  3.79k|      if (is_special() != is_input_special) {
  ------------------
  |  Branch (12975:11): [True: 0, False: 3.79k]
  ------------------
12976|      0|        return false;
12977|      0|      }
12978|       |
12979|       |      // If url includes credentials or has a non-null port, and buffer is
12980|       |      // "file", then return.
12981|  3.79k|      if ((has_credentials() || port.has_value()) &&
  ------------------
  |  Branch (12981:12): [True: 156, False: 3.64k]
  |  Branch (12981:33): [True: 18, False: 3.62k]
  ------------------
12982|    174|          parsed_type == ada::scheme::type::FILE) {
  ------------------
  |  Branch (12982:11): [True: 53, False: 121]
  ------------------
12983|     53|        return false;
12984|     53|      }
12985|       |
12986|       |      // If url's scheme is "file" and its host is an empty host, then return.
12987|       |      // An empty host is the empty string.
12988|  3.74k|      if (type == ada::scheme::type::FILE && host.has_value() &&
  ------------------
  |  Branch (12988:11): [True: 26, False: 3.71k]
  |  Branch (12988:46): [True: 26, False: 0]
  ------------------
12989|     26|          host->empty()) {
  ------------------
  |  Branch (12989:11): [True: 6, False: 20]
  ------------------
12990|      6|        return false;
12991|      6|      }
12992|  3.74k|    }
12993|       |
12994|  3.73k|    type = parsed_type;
12995|       |
12996|  3.79k|    if constexpr (has_state_override) {
12997|       |      // This is uncommon.
12998|  3.79k|      uint16_t urls_scheme_port = get_special_port();
12999|       |
13000|  3.79k|      if (urls_scheme_port) {
  ------------------
  |  Branch (13000:11): [True: 2.19k, False: 1.60k]
  ------------------
13001|       |        // If url's port is url's scheme's default port, then set url's port to
13002|       |        // null.
13003|  2.19k|        if (port.has_value() && *port == urls_scheme_port) {
  ------------------
  |  Branch (13003:13): [True: 112, False: 2.07k]
  |  Branch (13003:33): [True: 9, False: 103]
  ------------------
13004|      9|          port = std::nullopt;
13005|      9|        }
13006|  2.19k|      }
13007|  3.79k|    }
13008|  4.52k|  } else {  // slow path
13009|  4.52k|    std::string _buffer(input);
13010|       |    // Next function is only valid if the input is ASCII and returns false
13011|       |    // otherwise, but it seems that we always have ascii content so we do not
13012|       |    // need to check the return value.
13013|       |    // bool is_ascii =
13014|  4.52k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
13015|       |
13016|  4.52k|    if constexpr (has_state_override) {
13017|       |      // The state-override validation errors below ("return" in the WHATWG URL
13018|       |      // parser) leave the URL unchanged. The setter contract is
13019|       |      // "true on success, false if the scheme is invalid" -- the fast path
13020|       |      // above already returns false here, so the slow path must agree.
13021|       |
13022|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
13023|       |      // then return. If url's scheme is not a special scheme and buffer is a
13024|       |      // special scheme, then return.
13025|  4.52k|      if (is_special() != ada::scheme::is_special(_buffer)) {
  ------------------
  |  Branch (13025:11): [True: 3.14k, False: 1.37k]
  ------------------
13026|  3.14k|        return false;
13027|  3.14k|      }
13028|       |
13029|       |      // If url includes credentials or has a non-null port, and buffer is
13030|       |      // "file", then return.
13031|  1.37k|      if ((has_credentials() || port.has_value()) && _buffer == "file") {
  ------------------
  |  Branch (13031:12): [True: 145, False: 1.22k]
  |  Branch (13031:33): [True: 34, False: 1.19k]
  |  Branch (13031:54): [True: 10, False: 169]
  ------------------
13032|     10|        return false;
13033|     10|      }
13034|       |
13035|       |      // If url's scheme is "file" and its host is an empty host, then return.
13036|       |      // An empty host is the empty string.
13037|  1.36k|      if (type == ada::scheme::type::FILE && host.has_value() &&
  ------------------
  |  Branch (13037:11): [True: 26, False: 1.33k]
  |  Branch (13037:46): [True: 26, False: 0]
  ------------------
13038|     26|          host->empty()) {
  ------------------
  |  Branch (13038:11): [True: 6, False: 20]
  ------------------
13039|      6|        return false;
13040|      6|      }
13041|  1.36k|    }
13042|       |
13043|  1.35k|    set_scheme(std::move(_buffer));
13044|       |
13045|  4.52k|    if constexpr (has_state_override) {
13046|       |      // This is uncommon.
13047|  4.52k|      uint16_t urls_scheme_port = get_special_port();
13048|       |
13049|  4.52k|      if (urls_scheme_port) {
  ------------------
  |  Branch (13049:11): [True: 1.08k, False: 3.43k]
  ------------------
13050|       |        // If url's port is url's scheme's default port, then set url's port to
13051|       |        // null.
13052|  1.08k|        if (port.has_value() && *port == urls_scheme_port) {
  ------------------
  |  Branch (13052:13): [True: 160, False: 925]
  |  Branch (13052:33): [True: 12, False: 148]
  ------------------
13053|     12|          port = std::nullopt;
13054|     12|        }
13055|  1.08k|      }
13056|  4.52k|    }
13057|  4.52k|  }
13058|       |
13059|      0|  return true;
13060|  8.32k|}
_ZN3ada3url12parse_schemeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
12964|  50.6k|ada_really_inline bool url::parse_scheme(const std::string_view input) {
12965|  50.6k|  auto parsed_type = ada::scheme::get_scheme_type(input);
12966|  50.6k|  bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
12967|       |  /**
12968|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
12969|       |   *http, https), in which case, we can go really fast.
12970|       |   **/
12971|  50.6k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (12971:7): [True: 39.2k, False: 11.3k]
  ------------------
12972|       |    if constexpr (has_state_override) {
12973|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
12974|       |      // then return.
12975|       |      if (is_special() != is_input_special) {
12976|       |        return false;
12977|       |      }
12978|       |
12979|       |      // If url includes credentials or has a non-null port, and buffer is
12980|       |      // "file", then return.
12981|       |      if ((has_credentials() || port.has_value()) &&
12982|       |          parsed_type == ada::scheme::type::FILE) {
12983|       |        return false;
12984|       |      }
12985|       |
12986|       |      // If url's scheme is "file" and its host is an empty host, then return.
12987|       |      // An empty host is the empty string.
12988|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
12989|       |          host->empty()) {
12990|       |        return false;
12991|       |      }
12992|       |    }
12993|       |
12994|  39.2k|    type = parsed_type;
12995|       |
12996|       |    if constexpr (has_state_override) {
12997|       |      // This is uncommon.
12998|       |      uint16_t urls_scheme_port = get_special_port();
12999|       |
13000|       |      if (urls_scheme_port) {
13001|       |        // If url's port is url's scheme's default port, then set url's port to
13002|       |        // null.
13003|       |        if (port.has_value() && *port == urls_scheme_port) {
13004|       |          port = std::nullopt;
13005|       |        }
13006|       |      }
13007|       |    }
13008|  39.2k|  } else {  // slow path
13009|  11.3k|    std::string _buffer(input);
13010|       |    // Next function is only valid if the input is ASCII and returns false
13011|       |    // otherwise, but it seems that we always have ascii content so we do not
13012|       |    // need to check the return value.
13013|       |    // bool is_ascii =
13014|  11.3k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
13015|       |
13016|       |    if constexpr (has_state_override) {
13017|       |      // The state-override validation errors below ("return" in the WHATWG URL
13018|       |      // parser) leave the URL unchanged. The setter contract is
13019|       |      // "true on success, false if the scheme is invalid" -- the fast path
13020|       |      // above already returns false here, so the slow path must agree.
13021|       |
13022|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
13023|       |      // then return. If url's scheme is not a special scheme and buffer is a
13024|       |      // special scheme, then return.
13025|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
13026|       |        return false;
13027|       |      }
13028|       |
13029|       |      // If url includes credentials or has a non-null port, and buffer is
13030|       |      // "file", then return.
13031|       |      if ((has_credentials() || port.has_value()) && _buffer == "file") {
13032|       |        return false;
13033|       |      }
13034|       |
13035|       |      // If url's scheme is "file" and its host is an empty host, then return.
13036|       |      // An empty host is the empty string.
13037|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
13038|       |          host->empty()) {
13039|       |        return false;
13040|       |      }
13041|       |    }
13042|       |
13043|  11.3k|    set_scheme(std::move(_buffer));
13044|       |
13045|       |    if constexpr (has_state_override) {
13046|       |      // This is uncommon.
13047|       |      uint16_t urls_scheme_port = get_special_port();
13048|       |
13049|       |      if (urls_scheme_port) {
13050|       |        // If url's port is url's scheme's default port, then set url's port to
13051|       |        // null.
13052|       |        if (port.has_value() && *port == urls_scheme_port) {
13053|       |          port = std::nullopt;
13054|       |        }
13055|       |      }
13056|       |    }
13057|  11.3k|  }
13058|       |
13059|  50.6k|  return true;
13060|  50.6k|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
14692|  94.7k|    const std::string_view input_with_colon) {
14693|  94.7k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
14694|  94.7k|  ADA_ASSERT_TRUE(validate());
14695|  94.7k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
14696|  94.7k|  std::string_view input{input_with_colon};
14697|  94.7k|  input.remove_suffix(1);
14698|  94.7k|  auto parsed_type = ada::scheme::get_scheme_type(input);
14699|  94.7k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
14700|       |  /**
14701|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
14702|       |   *http, https), in which case, we can go really fast.
14703|       |   **/
14704|  94.7k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (14704:7): [True: 77.1k, False: 17.6k]
  ------------------
14705|       |    if constexpr (has_state_override) {
14706|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
14707|       |      // then return.
14708|       |      if (is_special() != is_input_special) {
14709|       |        return false;
14710|       |      }
14711|       |
14712|       |      // If url includes credentials or has a non-null port, and buffer is
14713|       |      // "file", then return.
14714|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14715|       |          parsed_type == ada::scheme::type::FILE) {
14716|       |        return false;
14717|       |      }
14718|       |
14719|       |      // If url's scheme is "file" and its host is an empty host, then return.
14720|       |      // An empty host is the empty string.
14721|       |      if (type == ada::scheme::type::FILE &&
14722|       |          components.host_start == components.host_end) {
14723|       |        return false;
14724|       |      }
14725|       |    }
14726|       |
14727|  77.1k|    type = parsed_type;
14728|  77.1k|    set_scheme_from_view_with_colon(input_with_colon);
14729|       |
14730|       |    if constexpr (has_state_override) {
14731|       |      // This is uncommon.
14732|       |      uint16_t urls_scheme_port = get_special_port();
14733|       |
14734|       |      // If url's port is url's scheme's default port, then set url's port to
14735|       |      // null.
14736|       |      if (components.port == urls_scheme_port) {
14737|       |        clear_port();
14738|       |      }
14739|       |    }
14740|  77.1k|  } else {  // slow path
14741|  17.6k|    std::string _buffer(input);
14742|       |    // Next function is only valid if the input is ASCII and returns false
14743|       |    // otherwise, but it seems that we always have ascii content so we do not
14744|       |    // need to check the return value.
14745|  17.6k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
14746|       |
14747|       |    if constexpr (has_state_override) {
14748|       |      // The state-override validation errors below ("return" in the WHATWG URL
14749|       |      // parser) leave the URL unchanged. The setter contract is
14750|       |      // "true on success, false if the scheme is invalid" -- the fast path
14751|       |      // above already returns false here, so the slow path must agree.
14752|       |
14753|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
14754|       |      // then return. If url's scheme is not a special scheme and buffer is a
14755|       |      // special scheme, then return.
14756|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
14757|       |        return false;
14758|       |      }
14759|       |
14760|       |      // If url includes credentials or has a non-null port, and buffer is
14761|       |      // "file", then return.
14762|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14763|       |          _buffer == "file") {
14764|       |        return false;
14765|       |      }
14766|       |
14767|       |      // If url's scheme is "file" and its host is an empty host, then return.
14768|       |      // An empty host is the empty string.
14769|       |      if (type == ada::scheme::type::FILE &&
14770|       |          components.host_start == components.host_end) {
14771|       |        return false;
14772|       |      }
14773|       |    }
14774|       |
14775|  17.6k|    set_scheme(_buffer);
14776|       |
14777|       |    if constexpr (has_state_override) {
14778|       |      // This is uncommon.
14779|       |      uint16_t urls_scheme_port = get_special_port();
14780|       |
14781|       |      // If url's port is url's scheme's default port, then set url's port to
14782|       |      // null.
14783|       |      if (components.port == urls_scheme_port) {
14784|       |        clear_port();
14785|       |      }
14786|       |    }
14787|  17.6k|  }
14788|  94.7k|  ADA_ASSERT_TRUE(validate());
14789|  94.7k|  return true;
14790|  94.7k|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14814|  80.8k|    std::string_view new_scheme_with_colon) {
14815|  80.8k|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
14816|  80.8k|          new_scheme_with_colon);
14817|  80.8k|  ADA_ASSERT_TRUE(validate());
14818|  80.8k|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
14819|  80.8k|                  new_scheme_with_colon.back() == ':');
14820|       |  // next line could overflow but unsigned arithmetic has well-defined
14821|       |  // overflows.
14822|  80.8k|  uint32_t new_difference =
14823|  80.8k|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
14824|       |
14825|  80.8k|  if (buffer.empty()) {
  ------------------
  |  Branch (14825:7): [True: 77.1k, False: 3.73k]
  ------------------
14826|  77.1k|    buffer.append(new_scheme_with_colon);
14827|  77.1k|  } else {
14828|  3.73k|    buffer.erase(0, components.protocol_end);
14829|  3.73k|    buffer.insert(0, new_scheme_with_colon);
14830|  3.73k|  }
14831|  80.8k|  components.protocol_end += new_difference;
14832|       |
14833|  80.8k|  apply_shifted_non_scheme_offsets(components, new_difference);
14834|  80.8k|  ADA_ASSERT_TRUE(validate());
14835|  80.8k|}
_ZN3ada14url_aggregator10set_schemeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14837|  18.9k|inline void url_aggregator::set_scheme(std::string_view new_scheme) {
14838|  18.9k|  ada_log("url_aggregator::set_scheme ", new_scheme);
14839|  18.9k|  ADA_ASSERT_TRUE(validate());
14840|  18.9k|  ADA_ASSERT_TRUE(new_scheme.empty() || new_scheme.back() != ':');
14841|       |  // next line could overflow but unsigned arithmetic has well-defined
14842|       |  // overflows.
14843|  18.9k|  uint32_t new_difference =
14844|  18.9k|      uint32_t(new_scheme.size()) - components.protocol_end + 1;
14845|       |
14846|  18.9k|  type = ada::scheme::get_scheme_type(new_scheme);
14847|  18.9k|  if (buffer.empty()) {
  ------------------
  |  Branch (14847:7): [True: 17.6k, False: 1.35k]
  ------------------
14848|  17.6k|    buffer.append(helpers::concat(new_scheme, ":"));
14849|  17.6k|  } else {
14850|  1.35k|    buffer.erase(0, components.protocol_end);
14851|  1.35k|    buffer.insert(0, helpers::concat(new_scheme, ":"));
14852|  1.35k|  }
14853|  18.9k|  components.protocol_end = uint32_t(new_scheme.size() + 1);
14854|       |
14855|  18.9k|  apply_shifted_non_scheme_offsets(components, new_difference);
14856|  18.9k|  ADA_ASSERT_TRUE(validate());
14857|  18.9k|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
14692|  8.32k|    const std::string_view input_with_colon) {
14693|  8.32k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
14694|  8.32k|  ADA_ASSERT_TRUE(validate());
14695|  8.32k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
14696|  8.32k|  std::string_view input{input_with_colon};
14697|  8.32k|  input.remove_suffix(1);
14698|  8.32k|  auto parsed_type = ada::scheme::get_scheme_type(input);
14699|  8.32k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
14700|       |  /**
14701|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
14702|       |   *http, https), in which case, we can go really fast.
14703|       |   **/
14704|  8.32k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (14704:7): [True: 3.79k, False: 4.52k]
  ------------------
14705|  3.79k|    if constexpr (has_state_override) {
14706|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
14707|       |      // then return.
14708|  3.79k|      if (is_special() != is_input_special) {
  ------------------
  |  Branch (14708:11): [True: 0, False: 3.79k]
  ------------------
14709|      0|        return false;
14710|      0|      }
14711|       |
14712|       |      // If url includes credentials or has a non-null port, and buffer is
14713|       |      // "file", then return.
14714|  3.79k|      if ((has_credentials() || components.port != url_components::omitted) &&
  ------------------
  |  Branch (14714:12): [True: 156, False: 3.64k]
  |  Branch (14714:33): [True: 18, False: 3.62k]
  ------------------
14715|    174|          parsed_type == ada::scheme::type::FILE) {
  ------------------
  |  Branch (14715:11): [True: 53, False: 121]
  ------------------
14716|     53|        return false;
14717|     53|      }
14718|       |
14719|       |      // If url's scheme is "file" and its host is an empty host, then return.
14720|       |      // An empty host is the empty string.
14721|  3.74k|      if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (14721:11): [True: 26, False: 3.71k]
  ------------------
14722|     26|          components.host_start == components.host_end) {
  ------------------
  |  Branch (14722:11): [True: 6, False: 20]
  ------------------
14723|      6|        return false;
14724|      6|      }
14725|  3.74k|    }
14726|       |
14727|  3.73k|    type = parsed_type;
14728|  3.79k|    set_scheme_from_view_with_colon(input_with_colon);
14729|       |
14730|  3.79k|    if constexpr (has_state_override) {
14731|       |      // This is uncommon.
14732|  3.79k|      uint16_t urls_scheme_port = get_special_port();
14733|       |
14734|       |      // If url's port is url's scheme's default port, then set url's port to
14735|       |      // null.
14736|  3.79k|      if (components.port == urls_scheme_port) {
  ------------------
  |  Branch (14736:11): [True: 9, False: 3.78k]
  ------------------
14737|      9|        clear_port();
14738|      9|      }
14739|  3.79k|    }
14740|  4.52k|  } else {  // slow path
14741|  4.52k|    std::string _buffer(input);
14742|       |    // Next function is only valid if the input is ASCII and returns false
14743|       |    // otherwise, but it seems that we always have ascii content so we do not
14744|       |    // need to check the return value.
14745|  4.52k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
14746|       |
14747|  4.52k|    if constexpr (has_state_override) {
14748|       |      // The state-override validation errors below ("return" in the WHATWG URL
14749|       |      // parser) leave the URL unchanged. The setter contract is
14750|       |      // "true on success, false if the scheme is invalid" -- the fast path
14751|       |      // above already returns false here, so the slow path must agree.
14752|       |
14753|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
14754|       |      // then return. If url's scheme is not a special scheme and buffer is a
14755|       |      // special scheme, then return.
14756|  4.52k|      if (is_special() != ada::scheme::is_special(_buffer)) {
  ------------------
  |  Branch (14756:11): [True: 3.14k, False: 1.37k]
  ------------------
14757|  3.14k|        return false;
14758|  3.14k|      }
14759|       |
14760|       |      // If url includes credentials or has a non-null port, and buffer is
14761|       |      // "file", then return.
14762|  1.37k|      if ((has_credentials() || components.port != url_components::omitted) &&
  ------------------
  |  Branch (14762:12): [True: 145, False: 1.22k]
  |  Branch (14762:33): [True: 34, False: 1.19k]
  ------------------
14763|    179|          _buffer == "file") {
  ------------------
  |  Branch (14763:11): [True: 10, False: 169]
  ------------------
14764|     10|        return false;
14765|     10|      }
14766|       |
14767|       |      // If url's scheme is "file" and its host is an empty host, then return.
14768|       |      // An empty host is the empty string.
14769|  1.36k|      if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (14769:11): [True: 26, False: 1.33k]
  ------------------
14770|     26|          components.host_start == components.host_end) {
  ------------------
  |  Branch (14770:11): [True: 6, False: 20]
  ------------------
14771|      6|        return false;
14772|      6|      }
14773|  1.36k|    }
14774|       |
14775|  1.35k|    set_scheme(_buffer);
14776|       |
14777|  4.52k|    if constexpr (has_state_override) {
14778|       |      // This is uncommon.
14779|  4.52k|      uint16_t urls_scheme_port = get_special_port();
14780|       |
14781|       |      // If url's port is url's scheme's default port, then set url's port to
14782|       |      // null.
14783|  4.52k|      if (components.port == urls_scheme_port) {
  ------------------
  |  Branch (14783:11): [True: 12, False: 4.51k]
  ------------------
14784|     12|        clear_port();
14785|     12|      }
14786|  4.52k|    }
14787|  4.52k|  }
14788|      0|  ADA_ASSERT_TRUE(validate());
14789|  8.32k|  return true;
14790|  8.32k|}
_ZN3ada7helpers38strip_trailing_spaces_from_opaque_pathINS_14url_aggregatorEEEvRT_:
12569|  2.83k|ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url) {
12570|  2.83k|  ada_log("helpers::strip_trailing_spaces_from_opaque_path");
12571|  2.83k|  if (!url.has_opaque_path) return;
  ------------------
  |  Branch (12571:7): [True: 2.83k, False: 0]
  ------------------
12572|      0|  if (url.has_hash()) return;
  ------------------
  |  Branch (12572:7): [True: 0, False: 0]
  ------------------
12573|      0|  if (url.has_search()) return;
  ------------------
  |  Branch (12573:7): [True: 0, False: 0]
  ------------------
12574|       |
12575|      0|  auto path = std::string(url.get_pathname());
12576|      0|  while (!path.empty() && path.back() == ' ') {
  ------------------
  |  Branch (12576:10): [True: 0, False: 0]
  |  Branch (12576:27): [True: 0, False: 0]
  ------------------
12577|      0|    path.resize(path.size() - 1);
12578|      0|  }
12579|      0|  url.update_base_pathname(path);
12580|      0|}
_ZN3ada14url_aggregator20set_host_or_hostnameILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
15238|  18.9k|bool url_aggregator::set_host_or_hostname(const std::string_view input) {
15239|  18.9k|  ada_log("url_aggregator::set_host_or_hostname ", input);
15240|  18.9k|  ADA_ASSERT_TRUE(validate());
15241|  18.9k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15242|  18.9k|  if (has_opaque_path) {
  ------------------
  |  Branch (15242:7): [True: 0, False: 18.9k]
  ------------------
15243|      0|    return false;
15244|      0|  }
15245|       |
15246|  18.9k|  url_aggregator saved_url(*this);
15247|       |
15248|  18.9k|  std::string previous_host(get_hostname());
15249|  18.9k|  uint32_t previous_port = components.port;
15250|       |
15251|  18.9k|  size_t host_end_pos = input.find('#');
15252|  18.9k|  std::string _host(input.data(), host_end_pos != std::string_view::npos
  ------------------
  |  Branch (15252:35): [True: 905, False: 18.0k]
  ------------------
15253|  18.9k|                                      ? host_end_pos
15254|  18.9k|                                      : input.size());
15255|  18.9k|  helpers::remove_ascii_tab_or_newline(_host);
15256|  18.9k|  std::string_view new_host(_host);
15257|       |
15258|  18.9k|  auto check_url_size = [&]() -> bool {
15259|  18.9k|    if (buffer.size() > ada::get_max_input_length()) {
15260|  18.9k|      *this = std::move(saved_url);
15261|  18.9k|      return false;
15262|  18.9k|    }
15263|  18.9k|    return true;
15264|  18.9k|  };
15265|       |
15266|       |  // If url's scheme is "file", then set state to file host state, instead of
15267|       |  // host state.
15268|  18.9k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (15268:7): [True: 17.2k, False: 1.70k]
  ------------------
15269|  17.2k|    std::string_view host_view(_host.data(), _host.length());
15270|  17.2k|    auto [location, found_colon] =
15271|  17.2k|        helpers::get_host_delimiter_location(is_special(), host_view);
15272|       |
15273|       |    // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
15274|       |    // Note: the 'found_colon' value is true if and only if a colon was
15275|       |    // encountered while not inside brackets.
15276|  17.2k|    if (found_colon) {
  ------------------
  |  Branch (15276:9): [True: 7.10k, False: 10.1k]
  ------------------
15277|       |      // If buffer is the empty string, host-missing validation error, return
15278|       |      // failure.
15279|  7.10k|      std::string_view host_buffer = host_view.substr(0, location);
15280|  7.10k|      if (host_buffer.empty()) {
  ------------------
  |  Branch (15280:11): [True: 175, False: 6.93k]
  ------------------
15281|    175|        return false;
15282|    175|      }
15283|       |
15284|       |      // If state override is given and state override is hostname state, then
15285|       |      // return failure.
15286|       |      if constexpr (override_hostname) {
15287|       |        return false;
15288|       |      }
15289|       |
15290|       |      // Let host be the result of host parsing buffer with url is not special.
15291|  6.93k|      bool succeeded = parse_host(host_buffer);
15292|  6.93k|      if (!succeeded) {
  ------------------
  |  Branch (15292:11): [True: 862, False: 6.06k]
  ------------------
15293|    862|        update_base_hostname(previous_host);
15294|    862|        update_base_port(previous_port);
15295|    862|        return false;
15296|    862|      }
15297|       |
15298|       |      // Set url's host to host, buffer to the empty string, and state to port
15299|       |      // state.
15300|  6.06k|      std::string_view port_buffer = new_host.substr(location + 1);
15301|  6.06k|      if (!port_buffer.empty()) {
  ------------------
  |  Branch (15301:11): [True: 5.27k, False: 793]
  ------------------
15302|  5.27k|        set_port(port_buffer);
15303|  5.27k|      }
15304|  6.06k|      return check_url_size();
15305|  6.93k|    }
15306|       |    // Otherwise, if one of the following is true:
15307|       |    // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
15308|       |    // - url is special and c is U+005C (\)
15309|  10.1k|    else {
15310|       |      // If url is special and host_view is the empty string, host-missing
15311|       |      // validation error, return failure.
15312|  10.1k|      if (host_view.empty() && is_special()) {
  ------------------
  |  Branch (15312:11): [True: 3.33k, False: 6.78k]
  |  Branch (15312:32): [True: 3.33k, False: 0]
  ------------------
15313|  3.33k|        return false;
15314|  3.33k|      }
15315|       |
15316|       |      // Otherwise, if state override is given, host_view is the empty string,
15317|       |      // and either url includes credentials or url's port is non-null, then
15318|       |      // return failure.
15319|  6.78k|      if (host_view.empty() && (has_credentials() || has_port())) {
  ------------------
  |  Branch (15319:11): [True: 0, False: 6.78k]
  |  Branch (15319:33): [True: 0, False: 0]
  |  Branch (15319:54): [True: 0, False: 0]
  ------------------
15320|      0|        return false;
15321|      0|      }
15322|       |
15323|       |      // Let host be the result of host parsing host_view with url is not
15324|       |      // special.
15325|  6.78k|      if (host_view.empty() && !is_special()) {
  ------------------
  |  Branch (15325:11): [True: 0, False: 6.78k]
  |  Branch (15325:32): [True: 0, False: 0]
  ------------------
15326|      0|        if (has_hostname()) {
  ------------------
  |  Branch (15326:13): [True: 0, False: 0]
  ------------------
15327|      0|          clear_hostname();  // easy!
15328|      0|        } else if (has_dash_dot()) {
  ------------------
  |  Branch (15328:20): [True: 0, False: 0]
  ------------------
15329|      0|          add_authority_slashes_if_needed();
15330|      0|          delete_dash_dot();
15331|      0|        }
15332|      0|        return check_url_size();
15333|      0|      }
15334|       |
15335|  6.78k|      bool succeeded = parse_host(host_view);
15336|  6.78k|      if (!succeeded) {
  ------------------
  |  Branch (15336:11): [True: 3.06k, False: 3.71k]
  ------------------
15337|  3.06k|        update_base_hostname(previous_host);
15338|  3.06k|        update_base_port(previous_port);
15339|  3.06k|        return false;
15340|  3.71k|      } else if (has_dash_dot()) {
  ------------------
  |  Branch (15340:18): [True: 0, False: 3.71k]
  ------------------
15341|       |        // Should remove dash_dot from pathname
15342|      0|        delete_dash_dot();
15343|      0|      }
15344|  3.71k|      return check_url_size();
15345|  6.78k|    }
15346|  17.2k|  }
15347|       |
15348|  1.70k|  size_t location = new_host.find_first_of("/\\?");
15349|  1.70k|  if (location != std::string_view::npos) {
  ------------------
  |  Branch (15349:7): [True: 987, False: 718]
  ------------------
15350|    987|    new_host.remove_suffix(new_host.length() - location);
15351|    987|  }
15352|       |
15353|  1.70k|  if (new_host.empty()) {
  ------------------
  |  Branch (15353:7): [True: 42, False: 1.66k]
  ------------------
15354|       |    // Set url's host to the empty string.
15355|     42|    clear_hostname();
15356|  1.66k|  } else {
15357|       |    // Let host be the result of host parsing buffer with url is not special.
15358|  1.66k|    if (!parse_host(new_host)) {
  ------------------
  |  Branch (15358:9): [True: 1.12k, False: 541]
  ------------------
15359|  1.12k|      update_base_hostname(previous_host);
15360|  1.12k|      update_base_port(previous_port);
15361|  1.12k|      return false;
15362|  1.12k|    }
15363|       |
15364|       |    // If host is "localhost", then set host to the empty string.
15365|    541|    if (helpers::substring(buffer, components.host_start,
  ------------------
  |  Branch (15365:9): [True: 10, False: 531]
  ------------------
15366|    541|                           components.host_end) == "localhost") {
15367|     10|      clear_hostname();
15368|     10|    }
15369|    541|  }
15370|    583|  ADA_ASSERT_TRUE(validate());
15371|    583|  return check_url_size();
15372|  1.70k|}
_ZZN3ada14url_aggregator20set_host_or_hostnameILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEENKUlvE_clEv:
15258|  10.3k|  auto check_url_size = [&]() -> bool {
15259|  10.3k|    if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (15259:9): [True: 0, False: 10.3k]
  ------------------
15260|      0|      *this = std::move(saved_url);
15261|      0|      return false;
15262|      0|    }
15263|  10.3k|    return true;
15264|  10.3k|  };
_ZN3ada14url_aggregator20set_host_or_hostnameILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
15238|  18.1k|bool url_aggregator::set_host_or_hostname(const std::string_view input) {
15239|  18.1k|  ada_log("url_aggregator::set_host_or_hostname ", input);
15240|  18.1k|  ADA_ASSERT_TRUE(validate());
15241|  18.1k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15242|  18.1k|  if (has_opaque_path) {
  ------------------
  |  Branch (15242:7): [True: 0, False: 18.1k]
  ------------------
15243|      0|    return false;
15244|      0|  }
15245|       |
15246|  18.1k|  url_aggregator saved_url(*this);
15247|       |
15248|  18.1k|  std::string previous_host(get_hostname());
15249|  18.1k|  uint32_t previous_port = components.port;
15250|       |
15251|  18.1k|  size_t host_end_pos = input.find('#');
15252|  18.1k|  std::string _host(input.data(), host_end_pos != std::string_view::npos
  ------------------
  |  Branch (15252:35): [True: 845, False: 17.3k]
  ------------------
15253|  18.1k|                                      ? host_end_pos
15254|  18.1k|                                      : input.size());
15255|  18.1k|  helpers::remove_ascii_tab_or_newline(_host);
15256|  18.1k|  std::string_view new_host(_host);
15257|       |
15258|  18.1k|  auto check_url_size = [&]() -> bool {
15259|  18.1k|    if (buffer.size() > ada::get_max_input_length()) {
15260|  18.1k|      *this = std::move(saved_url);
15261|  18.1k|      return false;
15262|  18.1k|    }
15263|  18.1k|    return true;
15264|  18.1k|  };
15265|       |
15266|       |  // If url's scheme is "file", then set state to file host state, instead of
15267|       |  // host state.
15268|  18.1k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (15268:7): [True: 16.4k, False: 1.75k]
  ------------------
15269|  16.4k|    std::string_view host_view(_host.data(), _host.length());
15270|  16.4k|    auto [location, found_colon] =
15271|  16.4k|        helpers::get_host_delimiter_location(is_special(), host_view);
15272|       |
15273|       |    // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
15274|       |    // Note: the 'found_colon' value is true if and only if a colon was
15275|       |    // encountered while not inside brackets.
15276|  16.4k|    if (found_colon) {
  ------------------
  |  Branch (15276:9): [True: 6.03k, False: 10.3k]
  ------------------
15277|       |      // If buffer is the empty string, host-missing validation error, return
15278|       |      // failure.
15279|  6.03k|      std::string_view host_buffer = host_view.substr(0, location);
15280|  6.03k|      if (host_buffer.empty()) {
  ------------------
  |  Branch (15280:11): [True: 168, False: 5.86k]
  ------------------
15281|    168|        return false;
15282|    168|      }
15283|       |
15284|       |      // If state override is given and state override is hostname state, then
15285|       |      // return failure.
15286|  5.86k|      if constexpr (override_hostname) {
15287|  5.86k|        return false;
15288|  5.86k|      }
15289|       |
15290|       |      // Let host be the result of host parsing buffer with url is not special.
15291|      0|      bool succeeded = parse_host(host_buffer);
15292|  5.86k|      if (!succeeded) {
  ------------------
  |  Branch (15292:11): [True: 0, False: 5.86k]
  ------------------
15293|      0|        update_base_hostname(previous_host);
15294|      0|        update_base_port(previous_port);
15295|      0|        return false;
15296|      0|      }
15297|       |
15298|       |      // Set url's host to host, buffer to the empty string, and state to port
15299|       |      // state.
15300|  5.86k|      std::string_view port_buffer = new_host.substr(location + 1);
15301|  5.86k|      if (!port_buffer.empty()) {
  ------------------
  |  Branch (15301:11): [True: 0, False: 5.86k]
  ------------------
15302|      0|        set_port(port_buffer);
15303|      0|      }
15304|  5.86k|      return check_url_size();
15305|  5.86k|    }
15306|       |    // Otherwise, if one of the following is true:
15307|       |    // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
15308|       |    // - url is special and c is U+005C (\)
15309|  10.3k|    else {
15310|       |      // If url is special and host_view is the empty string, host-missing
15311|       |      // validation error, return failure.
15312|  10.3k|      if (host_view.empty() && is_special()) {
  ------------------
  |  Branch (15312:11): [True: 3.36k, False: 6.99k]
  |  Branch (15312:32): [True: 3.36k, False: 0]
  ------------------
15313|  3.36k|        return false;
15314|  3.36k|      }
15315|       |
15316|       |      // Otherwise, if state override is given, host_view is the empty string,
15317|       |      // and either url includes credentials or url's port is non-null, then
15318|       |      // return failure.
15319|  6.99k|      if (host_view.empty() && (has_credentials() || has_port())) {
  ------------------
  |  Branch (15319:11): [True: 0, False: 6.99k]
  |  Branch (15319:33): [True: 0, False: 0]
  |  Branch (15319:54): [True: 0, False: 0]
  ------------------
15320|      0|        return false;
15321|      0|      }
15322|       |
15323|       |      // Let host be the result of host parsing host_view with url is not
15324|       |      // special.
15325|  6.99k|      if (host_view.empty() && !is_special()) {
  ------------------
  |  Branch (15325:11): [True: 0, False: 6.99k]
  |  Branch (15325:32): [True: 0, False: 0]
  ------------------
15326|      0|        if (has_hostname()) {
  ------------------
  |  Branch (15326:13): [True: 0, False: 0]
  ------------------
15327|      0|          clear_hostname();  // easy!
15328|      0|        } else if (has_dash_dot()) {
  ------------------
  |  Branch (15328:20): [True: 0, False: 0]
  ------------------
15329|      0|          add_authority_slashes_if_needed();
15330|      0|          delete_dash_dot();
15331|      0|        }
15332|      0|        return check_url_size();
15333|      0|      }
15334|       |
15335|  6.99k|      bool succeeded = parse_host(host_view);
15336|  6.99k|      if (!succeeded) {
  ------------------
  |  Branch (15336:11): [True: 3.11k, False: 3.88k]
  ------------------
15337|  3.11k|        update_base_hostname(previous_host);
15338|  3.11k|        update_base_port(previous_port);
15339|  3.11k|        return false;
15340|  3.88k|      } else if (has_dash_dot()) {
  ------------------
  |  Branch (15340:18): [True: 0, False: 3.88k]
  ------------------
15341|       |        // Should remove dash_dot from pathname
15342|      0|        delete_dash_dot();
15343|      0|      }
15344|  3.88k|      return check_url_size();
15345|  6.99k|    }
15346|  16.4k|  }
15347|       |
15348|  1.75k|  size_t location = new_host.find_first_of("/\\?");
15349|  1.75k|  if (location != std::string_view::npos) {
  ------------------
  |  Branch (15349:7): [True: 1.01k, False: 739]
  ------------------
15350|  1.01k|    new_host.remove_suffix(new_host.length() - location);
15351|  1.01k|  }
15352|       |
15353|  1.75k|  if (new_host.empty()) {
  ------------------
  |  Branch (15353:7): [True: 59, False: 1.69k]
  ------------------
15354|       |    // Set url's host to the empty string.
15355|     59|    clear_hostname();
15356|  1.69k|  } else {
15357|       |    // Let host be the result of host parsing buffer with url is not special.
15358|  1.69k|    if (!parse_host(new_host)) {
  ------------------
  |  Branch (15358:9): [True: 1.13k, False: 567]
  ------------------
15359|  1.13k|      update_base_hostname(previous_host);
15360|  1.13k|      update_base_port(previous_port);
15361|  1.13k|      return false;
15362|  1.13k|    }
15363|       |
15364|       |    // If host is "localhost", then set host to the empty string.
15365|    567|    if (helpers::substring(buffer, components.host_start,
  ------------------
  |  Branch (15365:9): [True: 12, False: 555]
  ------------------
15366|    567|                           components.host_end) == "localhost") {
15367|     12|      clear_hostname();
15368|     12|    }
15369|    567|  }
15370|    626|  ADA_ASSERT_TRUE(validate());
15371|    626|  return check_url_size();
15372|  1.75k|}
_ZZN3ada14url_aggregator20set_host_or_hostnameILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEENKUlvE_clEv:
15258|  4.51k|  auto check_url_size = [&]() -> bool {
15259|  4.51k|    if (buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (15259:9): [True: 0, False: 4.51k]
  ------------------
15260|      0|      *this = std::move(saved_url);
15261|      0|      return false;
15262|      0|    }
15263|  4.51k|    return true;
15264|  4.51k|  };

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5559|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1031|  9.12M|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1032|  9.12M|  return !!(a[i >> 3] & (1 << (i & 7)));
 1033|  9.12M|}
_ZN2tl10unexpectedIN3ada6errorsEEC2EOS2_:
 2011|   105k|  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3497|  24.7k|      : impl_base(unexpect, std::move(e.value())),
 3498|  24.7k|        ctor_base(detail::default_constructor_tag{}) {}
_ZNR2tl10unexpectedIN3ada6errorsEE5valueEv:
 2025|  52.5k|  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2483|  24.7k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada3urlENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3101|  74.0k|  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_:
 3583|  49.3k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3456|  49.3k|      : impl_base(in_place, std::forward<Args>(args)...),
 3457|  49.3k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2471|  49.3k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada3urlD2Ev:
 4851|   182k|  ~url() override = default;
_ZN3ada8url_baseD2Ev:
 1515|   579k|  virtual ~url_base() = default;
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3497|  27.8k|      : impl_base(unexpect, std::move(e.value())),
 3498|  27.8k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2483|  27.8k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3101|   132k|  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_:
 3583|   104k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3456|   104k|      : impl_base(in_place, std::forward<Args>(args)...),
 3457|   104k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2471|   104k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorD2Ev:
 7507|   397k|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6585|   193k|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6586|   193k|  if (scheme.empty()) {
  ------------------
  |  Branch (6586:7): [True: 0, False: 193k]
  ------------------
 6587|      0|    return ada::scheme::NOT_SPECIAL;
 6588|      0|  }
 6589|   193k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6590|   193k|  const std::string_view target = details::is_special_list[hash_value];
 6591|   193k|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6591:7): [True: 158k, False: 35.4k]
  ------------------
 6592|   158k|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6592:7): [True: 134k, False: 23.3k]
  ------------------
 6593|   158k|          details::scheme_keys[hash_value]) {
 6594|   134k|    return ada::scheme::type(hash_value);
 6595|   134k|  } else {
 6596|  58.7k|    return ada::scheme::NOT_SPECIAL;
 6597|  58.7k|  }
 6598|   193k|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6526|   158k|inline uint64_t branchless_load5(const char* p, size_t n) {
 6527|   158k|  uint64_t input = (uint8_t)p[0];
 6528|   158k|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6529|   158k|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6530|   158k|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6531|   158k|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6532|   158k|  return input;
 6533|   158k|}
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1226|   164k|    std::string_view input) noexcept {
 1227|   164k|  const char* p = input.data();
 1228|   164k|  const char* const pend = p + input.size();
 1229|       |
 1230|   164k|  uint32_t ipv4 = 0;
 1231|       |
 1232|   177k|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1232:19): [True: 175k, False: 2.50k]
  ------------------
 1233|   175k|    if (p == pend) {
  ------------------
  |  Branch (1233:9): [True: 607, False: 174k]
  ------------------
 1234|    607|      return ipv4_fast_fail;
 1235|    607|    }
 1236|       |
 1237|   174k|    uint32_t val;
 1238|   174k|    char c = *p;
 1239|   174k|    if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1239:9): [True: 153k, False: 21.6k]
  |  Branch (1239:21): [True: 29.1k, False: 123k]
  ------------------
 1240|  29.1k|      val = c - '0';
 1241|  29.1k|      p++;
 1242|   145k|    } else {
 1243|   145k|      return ipv4_fast_fail;
 1244|   145k|    }
 1245|       |
 1246|  29.1k|    if (p < pend) {
  ------------------
  |  Branch (1246:9): [True: 25.8k, False: 3.30k]
  ------------------
 1247|  25.8k|      c = *p;
 1248|  25.8k|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1248:11): [True: 15.1k, False: 10.6k]
  |  Branch (1248:23): [True: 9.13k, False: 6.05k]
  ------------------
 1249|  9.13k|        if (val == 0) return ipv4_fast_fail;
  ------------------
  |  Branch (1249:13): [True: 2.15k, False: 6.97k]
  ------------------
 1250|  6.97k|        val = val * 10 + (c - '0');
 1251|  6.97k|        p++;
 1252|  6.97k|        if (p < pend) {
  ------------------
  |  Branch (1252:13): [True: 6.23k, False: 743]
  ------------------
 1253|  6.23k|          c = *p;
 1254|  6.23k|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1254:15): [True: 4.41k, False: 1.81k]
  |  Branch (1254:27): [True: 3.92k, False: 493]
  ------------------
 1255|  3.92k|            val = val * 10 + (c - '0');
 1256|  3.92k|            p++;
 1257|  3.92k|            if (val > 255) return ipv4_fast_fail;
  ------------------
  |  Branch (1257:17): [True: 1.78k, False: 2.13k]
  ------------------
 1258|  3.92k|          }
 1259|  6.23k|        }
 1260|  6.97k|      }
 1261|  25.8k|    }
 1262|       |
 1263|  25.2k|    ipv4 = (ipv4 << 8) | val;
 1264|       |
 1265|  25.2k|    if (i < 3) {
  ------------------
  |  Branch (1265:9): [True: 22.7k, False: 2.50k]
  ------------------
 1266|  22.7k|      if (p == pend || *p != '.') {
  ------------------
  |  Branch (1266:11): [True: 3.34k, False: 19.3k]
  |  Branch (1266:24): [True: 9.04k, False: 10.3k]
  ------------------
 1267|  12.3k|        return ipv4_fast_fail;
 1268|  12.3k|      }
 1269|  10.3k|      p++;
 1270|  10.3k|    }
 1271|  25.2k|  }
 1272|       |
 1273|  2.50k|  if (p != pend) {
  ------------------
  |  Branch (1273:7): [True: 1.40k, False: 1.09k]
  ------------------
 1274|  1.40k|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1274:9): [True: 743, False: 666]
  |  Branch (1274:26): [True: 293, False: 450]
  ------------------
 1275|    293|      return ipv4;
 1276|    293|    }
 1277|  1.11k|    return ipv4_fast_fail;
 1278|  1.40k|  }
 1279|       |
 1280|  1.09k|  return ipv4;
 1281|  2.50k|}
_ZN3ada14url_aggregatorC2Ev:
 7502|   141k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 4757|   164k|  url_components() = default;
_ZN3ada14url_aggregatoraSEOS0_:
 7505|  5.30k|  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
_ZN3ada8checkers14has_hex_prefixENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1201|  15.7k|constexpr bool has_hex_prefix(std::string_view input) {
 1202|  15.7k|  return input.size() >= 2 && has_hex_prefix_unsafe(input);
  ------------------
  |  Branch (1202:10): [True: 12.3k, False: 3.46k]
  |  Branch (1202:31): [True: 3.95k, False: 8.36k]
  ------------------
 1203|  15.7k|}
_ZN3ada8checkers21has_hex_prefix_unsafeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1186|  12.3k|constexpr bool has_hex_prefix_unsafe(std::string_view input) {
 1187|       |  // This is actually efficient code, see has_hex_prefix for the assembly.
 1188|  12.3k|  constexpr bool is_little_endian = std::endian::native == std::endian::little;
 1189|  12.3k|  constexpr uint16_t word0x = 0x7830;
 1190|  12.3k|  uint16_t two_first_bytes =
 1191|  12.3k|      static_cast<uint16_t>(input[0]) |
 1192|  12.3k|      static_cast<uint16_t>((static_cast<uint16_t>(input[1]) << 8));
 1193|  12.3k|  if constexpr (is_little_endian) {
 1194|  12.3k|    two_first_bytes |= 0x2000;
 1195|       |  } else {
 1196|       |    two_first_bytes |= 0x020;
 1197|       |  }
 1198|  12.3k|  return two_first_bytes == word0x;
 1199|  12.3k|}
_ZN3ada8checkers8is_digitEc:
 1205|  78.3k|constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
_ZNK3ada3url15has_credentialsEv:
 7092|   250k|[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
 7093|   250k|  return !username.empty() || !password.empty();
  ------------------
  |  Branch (7093:10): [True: 158k, False: 91.8k]
  |  Branch (7093:31): [True: 3.06k, False: 88.7k]
  ------------------
 7094|   250k|}
_ZNK3ada8url_base10is_specialEv:
 7058|  1.42M|    const noexcept {
 7059|  1.42M|  return type != ada::scheme::NOT_SPECIAL;
 7060|  1.42M|}
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEEcvbEv:
 3896|   100k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEptEv:
 3865|   609k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 3866|   609k|    TL_ASSERT(has_value());
  ------------------
  |  | 1918|   609k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3866:5): [True: 609k, False: 0]
  ------------------
 3867|   609k|    return valptr();
 3868|   609k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE6valptrEv:
 3161|   609k|  T* valptr() { return std::addressof(this->m_val); }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EED2Ev:
 2493|  74.0k|  ~expected_storage_base() {
 2494|  74.0k|    if (m_has_val) {
  ------------------
  |  Branch (2494:9): [True: 49.3k, False: 24.7k]
  ------------------
 2495|  49.3k|      m_val.~T();
 2496|  49.3k|    }
 2497|  74.0k|  }
_ZNK3ada3url31cannot_have_credentials_or_portEv:
 7098|  56.1k|[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
 7099|  56.1k|  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
  ------------------
  |  Branch (7099:10): [True: 0, False: 56.1k]
  |  Branch (7099:31): [True: 21, False: 56.0k]
  |  Branch (7099:48): [True: 2.77k, False: 53.3k]
  ------------------
 7100|  56.1k|}
_ZNK3ada3url13get_href_sizeEv:
 7292|   116k|[[nodiscard]] inline size_t url::get_href_size() const noexcept {
 7293|       |  // Mirrors the logic of get_href() but only computes the total size.
 7294|   116k|  size_t size = 0;
 7295|       |  // Protocol: scheme + ":"
 7296|   116k|  if (is_special()) {
  ------------------
  |  Branch (7296:7): [True: 107k, False: 9.36k]
  ------------------
 7297|   107k|    size += ada::scheme::details::is_special_list[type].size() + 1;
 7298|   107k|  } else {
 7299|  9.36k|    size += non_special_scheme.size() + 1;
 7300|  9.36k|  }
 7301|   116k|  if (host.has_value()) {
  ------------------
  |  Branch (7301:7): [True: 108k, False: 8.01k]
  ------------------
 7302|   108k|    size += host->size();
 7303|   108k|    size += 2;  // "//"
 7304|   108k|    if (has_credentials()) {
  ------------------
  |  Branch (7304:9): [True: 87.4k, False: 21.3k]
  ------------------
 7305|  87.4k|      size += username.size();
 7306|  87.4k|      if (!password.empty()) {
  ------------------
  |  Branch (7306:11): [True: 72.3k, False: 15.1k]
  ------------------
 7307|  72.3k|        size += 1 + password.size();  // ":" + password
 7308|  72.3k|      }
 7309|  87.4k|      size += 1;  // "@"
 7310|  87.4k|    }
 7311|   108k|    if (port.has_value()) {
  ------------------
  |  Branch (7311:9): [True: 14.5k, False: 94.2k]
  ------------------
 7312|  14.5k|      size += 1;  // ":"
 7313|       |      // Count digits of port value without calling std::to_string.
 7314|  14.5k|      uint16_t p = *port;
 7315|  14.5k|      size += (p >= 10000)  ? 5
  ------------------
  |  Branch (7315:15): [True: 213, False: 14.3k]
  ------------------
 7316|  14.5k|              : (p >= 1000) ? 4
  ------------------
  |  Branch (7316:17): [True: 7.75k, False: 6.59k]
  ------------------
 7317|  14.3k|              : (p >= 100)  ? 3
  ------------------
  |  Branch (7317:17): [True: 569, False: 6.02k]
  ------------------
 7318|  6.59k|              : (p >= 10)   ? 2
  ------------------
  |  Branch (7318:17): [True: 784, False: 5.24k]
  ------------------
 7319|  6.02k|                            : 1;
 7320|  14.5k|    }
 7321|   108k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7321:14): [True: 1.87k, False: 6.14k]
  |  Branch (7321:34): [True: 142, False: 1.73k]
  ------------------
 7322|    142|    size += 2;  // "/."
 7323|    142|  }
 7324|   116k|  size += path.size();
 7325|   116k|  if (query.has_value()) {
  ------------------
  |  Branch (7325:7): [True: 40.1k, False: 76.6k]
  ------------------
 7326|  40.1k|    size += 1 + query->size();  // "?" + query
 7327|  40.1k|  }
 7328|   116k|  if (hash.has_value()) {
  ------------------
  |  Branch (7328:7): [True: 26.8k, False: 89.9k]
  ------------------
 7329|  26.8k|    size += 1 + hash->size();  // "#" + hash
 7330|  26.8k|  }
 7331|   116k|  return size;
 7332|   116k|}
_ZN3ada8checkers8is_alphaEc:
 1209|   264k|constexpr bool is_alpha(char x) noexcept {
 1210|   264k|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1210:10): [True: 205k, False: 58.4k]
  |  Branch (1210:34): [True: 202k, False: 3.09k]
  ------------------
 1211|   264k|}
_ZN3ada8checkers8to_lowerEc:
 1207|   470k|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZN3ada3urlC2ERKS0_:
 4847|  50.0k|  url(const url& u) = default;
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3878|  28.0k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 3879|  28.0k|    TL_ASSERT(has_value());
  ------------------
  |  | 1918|  28.0k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3879:5): [True: 28.0k, False: 0]
  ------------------
 3880|  28.0k|    return val();
 3881|  28.0k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3170|  28.0k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3171|  28.0k|    return this->m_val;
 3172|  28.0k|  }
_ZN3ada3urlaSERKS0_:
 4850|  9.27k|  url& operator=(const url& u) = default;
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEE9has_valueEv:
 3895|   657k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN3ada3urlC2Ev:
 4846|  78.6k|  url() = default;
_ZN3ada3url11copy_schemeERKS0_:
 7255|  1.21k|constexpr void url::copy_scheme(const ada::url& u) {
 7256|  1.21k|  non_special_scheme = u.non_special_scheme;
 7257|  1.21k|  type = u.type;
 7258|  1.21k|}
_ZN3ada3url26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7197|  16.6k|inline void url::update_unencoded_base_hash(std::string_view input) {
 7198|       |  // We do the percent encoding
 7199|  16.6k|  hash = unicode::percent_encode(input,
 7200|  16.6k|                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
 7201|  16.6k|}
_ZN3ada3url12clear_searchEv:
 7230|    757|constexpr void url::clear_search() { query = std::nullopt; }
_ZN3ada3url18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7204|  16.6k|                                    const uint8_t query_percent_encode_set[]) {
 7205|  16.6k|  query = ada::unicode::percent_encode(input, query_percent_encode_set);
 7206|  16.6k|}
_ZN3ada3url20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7195|    185|inline void url::update_base_hostname(std::string_view input) { host = input; }
_ZN3ada3url20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7212|  26.3k|inline void url::update_base_pathname(const std::string_view input) {
 7213|  26.3k|  path = input;
 7214|  26.3k|}
_ZN3ada3url10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 7335|  20.1k|                                         bool check_trailing_content) noexcept {
 7336|  20.1k|  ada_log("parse_port('", view, "') ", view.size());
 7337|  20.1k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (7337:7): [True: 19.4k, False: 722]
  |  Branch (7337:24): [True: 22, False: 19.4k]
  ------------------
 7338|     22|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 7339|     22|    is_valid = false;
 7340|     22|    return 0;
 7341|     22|  }
 7342|  20.1k|  uint16_t parsed_port{};
 7343|  20.1k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 7344|  20.1k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (7344:7): [True: 355, False: 19.8k]
  ------------------
 7345|    355|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 7346|    355|    is_valid = false;
 7347|    355|    return 0;
 7348|    355|  }
 7349|  19.8k|  ada_log("parse_port: ", parsed_port);
 7350|  19.8k|  const auto consumed = size_t(r.ptr - view.data());
 7351|  19.8k|  ada_log("parse_port: consumed ", consumed);
 7352|  19.8k|  if (check_trailing_content) {
  ------------------
  |  Branch (7352:7): [True: 17.1k, False: 2.67k]
  ------------------
 7353|  17.1k|    is_valid &=
 7354|  17.1k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (7354:10): [True: 1.24k, False: 15.8k]
  |  Branch (7354:37): [True: 15.5k, False: 301]
  ------------------
 7355|    301|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (7355:10): [True: 102, False: 199]
  |  Branch (7355:36): [True: 181, False: 18]
  |  Branch (7355:52): [True: 20, False: 161]
  ------------------
 7356|  17.1k|  }
 7357|  19.8k|  ada_log("parse_port: is_valid = ", is_valid);
 7358|  19.8k|  if (is_valid) {
  ------------------
  |  Branch (7358:7): [True: 19.2k, False: 525]
  ------------------
 7359|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 7360|  19.2k|    auto default_port = scheme_default_port();
 7361|  19.2k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (7361:27): [True: 157, False: 19.1k]
  |  Branch (7361:48): [True: 80, False: 77]
  ------------------
 7362|  19.2k|                         (default_port != parsed_port);
  ------------------
  |  Branch (7362:26): [True: 19.0k, False: 153]
  ------------------
 7363|  19.2k|    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
  ------------------
  |  Branch (7363:13): [True: 18.4k, False: 881]
  |  Branch (7363:36): [True: 18.2k, False: 153]
  ------------------
 7364|  19.2k|                                                  : std::nullopt;
 7365|  19.2k|  }
 7366|  19.8k|  return consumed;
 7367|  20.1k|}
_ZNK3ada8url_base19scheme_default_portEv:
 7067|  39.6k|url_base::scheme_default_port() const noexcept {
 7068|  39.6k|  return scheme::get_special_port(type);
 7069|  39.6k|}
_ZN3ada6scheme16get_special_portENS0_4typeE:
 6582|  49.8k|constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
 6583|  49.8k|  return details::special_ports[int(type)];
 6584|  49.8k|}
_ZNK3ada3url12get_pathnameEv:
 7118|  28.0k|[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
 7119|  28.0k|  return path;
 7120|  28.0k|}
_ZN3ada8checkers23is_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1213|  53.4k|constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
 1214|  53.4k|  return input.size() >= 2 &&
  ------------------
  |  Branch (1214:10): [True: 42.9k, False: 10.5k]
  ------------------
 1215|  42.9k|         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
  ------------------
  |  Branch (1215:11): [True: 23.5k, False: 19.4k]
  |  Branch (1215:34): [True: 4.21k, False: 19.2k]
  |  Branch (1215:55): [True: 3.26k, False: 16.0k]
  ------------------
 1216|  7.47k|         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
  ------------------
  |  Branch (1216:11): [True: 2.64k, False: 4.83k]
  |  Branch (1216:35): [True: 1.07k, False: 3.76k]
  |  Branch (1216:54): [True: 163, False: 3.59k]
  ------------------
 1217|  3.59k|                                  input[2] == '?' || input[2] == '#'));
  ------------------
  |  Branch (1217:35): [True: 143, False: 3.45k]
  |  Branch (1217:54): [True: 53, False: 3.40k]
  ------------------
 1218|  53.4k|}
_ZN3ada8checkers34is_normalized_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1221|  15.3k|    std::string_view input) noexcept {
 1222|  15.3k|  return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':'));
  ------------------
  |  Branch (1222:10): [True: 10.5k, False: 4.79k]
  |  Branch (1222:32): [True: 7.58k, False: 2.97k]
  |  Branch (1222:54): [True: 3.66k, False: 3.91k]
  ------------------
 1223|  15.3k|}
_ZN3ada3url20set_protocol_as_fileEv:
 7240|  3.68k|constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
_ZN3ada3url14clear_pathnameEv:
 7228|     41|constexpr void url::clear_pathname() { path.clear(); }
_ZN3ada7helpers14leading_zeroesEj:
 1800|   163k|inline int leading_zeroes(uint32_t input_num) noexcept {
 1801|       |#if ADA_REGULAR_VISUAL_STUDIO
 1802|       |  unsigned long leading_zero(0);
 1803|       |  unsigned long in(input_num);
 1804|       |  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
 1805|       |#else
 1806|   163k|  return __builtin_clz(input_num);
 1807|   163k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 1808|   163k|}
_ZN3ada14url_aggregator7reserveEj:
 8712|   132k|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 8713|   132k|  buffer.reserve(capacity);
 8714|   132k|}
_ZN3ada14url_aggregator20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8202|  78.7k|inline void url_aggregator::update_base_pathname(const std::string_view input) {
 8203|  78.7k|  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
 8204|  78.7k|          " bytes] \n", to_diagram());
 8205|  78.7k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8206|  78.7k|  ADA_ASSERT_TRUE(validate());
 8207|       |
 8208|  78.7k|  const bool begins_with_dashdash = input.starts_with("//");
 8209|  78.7k|  if (!begins_with_dashdash && has_dash_dot()) {
  ------------------
  |  Branch (8209:7): [True: 76.9k, False: 1.82k]
  |  Branch (8209:32): [True: 30, False: 76.9k]
  ------------------
 8210|       |    // We must delete the ./
 8211|     30|    delete_dash_dot();
 8212|     30|  }
 8213|       |
 8214|  78.7k|  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
  ------------------
  |  Branch (8214:7): [True: 1.82k, False: 76.9k]
  |  Branch (8214:31): [True: 1.81k, False: 12]
  |  Branch (8214:51): [True: 320, False: 1.49k]
  ------------------
 8215|    320|      !has_dash_dot()) {
  ------------------
  |  Branch (8215:7): [True: 264, False: 56]
  ------------------
 8216|       |    // If url's host is null, url does not have an opaque path, url's path's
 8217|       |    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
 8218|       |    // output.
 8219|    264|    buffer.insert(components.pathname_start, "/.");
 8220|    264|    components.pathname_start += 2;
 8221|    264|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8221:9): [True: 0, False: 264]
  ------------------
 8222|      0|      components.search_start += 2;
 8223|      0|    }
 8224|    264|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8224:9): [True: 0, False: 264]
  ------------------
 8225|      0|      components.hash_start += 2;
 8226|      0|    }
 8227|    264|  }
 8228|       |
 8229|  78.7k|  uint32_t difference = replace_and_resize(
 8230|  78.7k|      components.pathname_start,
 8231|  78.7k|      components.pathname_start + get_pathname_length(), input);
 8232|  78.7k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8232:7): [True: 1.67k, False: 77.0k]
  ------------------
 8233|  1.67k|    components.search_start += difference;
 8234|  1.67k|  }
 8235|  78.7k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8235:7): [True: 1.78k, False: 76.9k]
  ------------------
 8236|  1.78k|    components.hash_start += difference;
 8237|  1.78k|  }
 8238|  78.7k|  ADA_ASSERT_TRUE(validate());
 8239|  78.7k|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8045|   223k|    uint32_t start, uint32_t end, std::string_view input) {
 8046|   223k|  uint32_t current_length = end - start;
 8047|   223k|  uint32_t input_size = uint32_t(input.size());
 8048|   223k|  uint32_t new_difference = input_size - current_length;
 8049|       |
 8050|   223k|  if (current_length == 0) {
  ------------------
  |  Branch (8050:7): [True: 160k, False: 62.8k]
  ------------------
 8051|   160k|    buffer.insert(start, input);
 8052|   160k|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8052:14): [True: 4.60k, False: 58.2k]
  ------------------
 8053|  4.60k|    buffer.replace(start, input_size, input);
 8054|  58.2k|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8054:14): [True: 29.5k, False: 28.7k]
  ------------------
 8055|  29.5k|    buffer.erase(start, current_length - input_size);
 8056|  29.5k|    buffer.replace(start, input_size, input);
 8057|  29.5k|  } else {
 8058|  28.7k|    buffer.replace(start, current_length, input.substr(0, current_length));
 8059|  28.7k|    buffer.insert(start + current_length, input.substr(current_length));
 8060|  28.7k|  }
 8061|       |
 8062|   223k|  return new_difference;
 8063|   223k|}
_ZNK3ada14url_aggregator12get_pathnameEv:
 9039|   140k|    ada_lifetime_bound {
 9040|   140k|  ada_log("url_aggregator::get_pathname pathname_start = ",
 9041|   140k|          components.pathname_start, " buffer.size() = ", buffer.size(),
 9042|   140k|          " components.search_start = ", components.search_start,
 9043|   140k|          " components.hash_start = ", components.hash_start);
 9044|   140k|  auto ending_index = uint32_t(buffer.size());
 9045|   140k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9045:7): [True: 42.8k, False: 97.5k]
  ------------------
 9046|  42.8k|    ending_index = components.search_start;
 9047|  97.5k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9047:14): [True: 2.49k, False: 95.0k]
  ------------------
 9048|  2.49k|    ending_index = components.hash_start;
 9049|  2.49k|  }
 9050|   140k|  return helpers::substring(buffer, components.pathname_start, ending_index);
 9051|   140k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8110|  8.61k|inline void url_aggregator::update_base_search(std::string_view input) {
 8111|  8.61k|  ada_log("url_aggregator::update_base_search ", input);
 8112|  8.61k|  ADA_ASSERT_TRUE(validate());
 8113|  8.61k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8114|  8.61k|  if (input.empty()) {
  ------------------
  |  Branch (8114:7): [True: 0, False: 8.61k]
  ------------------
 8115|      0|    clear_search();
 8116|      0|    return;
 8117|      0|  }
 8118|       |
 8119|  8.61k|  if (input[0] == '?') {
  ------------------
  |  Branch (8119:7): [True: 8.61k, False: 0]
  ------------------
 8120|  8.61k|    input.remove_prefix(1);
 8121|  8.61k|  }
 8122|       |
 8123|  8.61k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8123:7): [True: 8.61k, False: 0]
  ------------------
 8124|  8.61k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8124:9): [True: 8.61k, False: 0]
  ------------------
 8125|  8.61k|      components.search_start = uint32_t(buffer.size());
 8126|  8.61k|      buffer += "?";
 8127|  8.61k|    } else {
 8128|      0|      buffer.resize(components.search_start + 1);
 8129|      0|    }
 8130|       |
 8131|  8.61k|    buffer.append(input);
 8132|  8.61k|  } else {
 8133|      0|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8133:9): [True: 0, False: 0]
  ------------------
 8134|      0|      components.search_start = components.hash_start;
 8135|      0|    } else {
 8136|      0|      buffer.erase(components.search_start,
 8137|      0|                   components.hash_start - components.search_start);
 8138|      0|      components.hash_start = components.search_start;
 8139|      0|    }
 8140|       |
 8141|      0|    buffer.insert(components.search_start, "?");
 8142|      0|    buffer.insert(components.search_start + 1, input);
 8143|      0|    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8144|      0|  }
 8145|       |
 8146|  8.61k|  ADA_ASSERT_TRUE(validate());
 8147|  8.61k|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8021|  48.7k|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8022|  48.7k|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8023|  48.7k|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8024|  48.7k|          " bytes] components.hash_start = ", components.hash_start);
 8025|  48.7k|  ADA_ASSERT_TRUE(validate());
 8026|  48.7k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8027|  48.7k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8027:7): [True: 259, False: 48.4k]
  ------------------
 8028|    259|    buffer.resize(components.hash_start);
 8029|    259|  }
 8030|  48.7k|  components.hash_start = uint32_t(buffer.size());
 8031|  48.7k|  buffer += "#";
 8032|  48.7k|  bool encoding_required = unicode::percent_encode<true>(
 8033|  48.7k|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8034|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8035|       |  // encoding was not deemed required.
 8036|  48.7k|  if (!encoding_required) {
  ------------------
  |  Branch (8036:7): [True: 40.6k, False: 8.04k]
  ------------------
 8037|  40.6k|    buffer.append(input);
 8038|  40.6k|  }
 8039|  48.7k|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8040|  48.7k|          buffer, "' [", buffer.size(), " bytes]");
 8041|  48.7k|  ADA_ASSERT_TRUE(validate());
 8042|  48.7k|}
_ZN3ada14url_aggregator20append_base_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8430|  31.6k|inline void url_aggregator::append_base_password(const std::string_view input) {
 8431|  31.6k|  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
 8432|  31.6k|          "\n", to_diagram());
 8433|  31.6k|  ADA_ASSERT_TRUE(validate());
 8434|  31.6k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8435|       |#if ADA_DEVELOPMENT_CHECKS
 8436|       |  // computing the expected password.
 8437|       |  std::string password_expected = std::string(get_password());
 8438|       |  password_expected.append(input);
 8439|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8440|  31.6k|  add_authority_slashes_if_needed();
 8441|       |
 8442|       |  // If input is empty, do nothing.
 8443|  31.6k|  if (input.empty()) {
  ------------------
  |  Branch (8443:7): [True: 5.53k, False: 26.1k]
  ------------------
 8444|  5.53k|    return;
 8445|  5.53k|  }
 8446|       |
 8447|  26.1k|  uint32_t difference = uint32_t(input.size());
 8448|  26.1k|  if (has_password()) {
  ------------------
  |  Branch (8448:7): [True: 9.40k, False: 16.7k]
  ------------------
 8449|  9.40k|    buffer.insert(components.host_start, input);
 8450|  16.7k|  } else {
 8451|  16.7k|    difference++;  // Increment for ":"
 8452|  16.7k|    buffer.insert(components.username_end, ":");
 8453|  16.7k|    buffer.insert(components.username_end + 1, input);
 8454|  16.7k|  }
 8455|  26.1k|  components.host_start += difference;
 8456|       |
 8457|       |  // The following line is required to add "@" to hostname. When updating
 8458|       |  // password if hostname does not start with "@", it is "append_base_password"s
 8459|       |  // responsibility to set it.
 8460|  26.1k|  if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8460:7): [True: 705, False: 25.4k]
  ------------------
 8461|    705|    buffer.insert(components.host_start, "@");
 8462|    705|    difference++;
 8463|    705|  }
 8464|       |
 8465|  26.1k|  components.host_end += difference;
 8466|  26.1k|  components.pathname_start += difference;
 8467|  26.1k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8467:7): [True: 0, False: 26.1k]
  ------------------
 8468|      0|    components.search_start += difference;
 8469|      0|  }
 8470|  26.1k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8470:7): [True: 0, False: 26.1k]
  ------------------
 8471|      0|    components.hash_start += difference;
 8472|      0|  }
 8473|       |#if ADA_DEVELOPMENT_CHECKS
 8474|       |  std::string password_after(get_password());
 8475|       |  ADA_ASSERT_EQUAL(
 8476|       |      password_expected, password_after,
 8477|       |      "append_base_password problem after inserting " + std::string(input));
 8478|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8479|  26.1k|  ADA_ASSERT_TRUE(validate());
 8480|  26.1k|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 8686|   232k|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 8687|   232k|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 8688|   232k|  ADA_ASSERT_TRUE(validate());
 8689|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 8690|       |  // to insert
 8691|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 8692|   232k|  if (has_authority()) {
  ------------------
  |  Branch (8692:7): [True: 149k, False: 83.2k]
  ------------------
 8693|   149k|    return;
 8694|   149k|  }
 8695|       |  // Performance: the common case is components.protocol_end == buffer.size()
 8696|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 8697|       |  // the insert could be fused with another insert.
 8698|  83.2k|  buffer.insert(components.protocol_end, "//");
 8699|  83.2k|  components.username_end += 2;
 8700|  83.2k|  components.host_start += 2;
 8701|  83.2k|  components.host_end += 2;
 8702|  83.2k|  components.pathname_start += 2;
 8703|  83.2k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8703:7): [True: 0, False: 83.2k]
  ------------------
 8704|      0|    components.search_start += 2;
 8705|      0|  }
 8706|  83.2k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8706:7): [True: 0, False: 83.2k]
  ------------------
 8707|      0|    components.hash_start += 2;
 8708|      0|  }
 8709|  83.2k|  ADA_ASSERT_TRUE(validate());
 8710|  83.2k|}
_ZN3ada14url_aggregator20append_base_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8312|  40.3k|inline void url_aggregator::append_base_username(const std::string_view input) {
 8313|  40.3k|  ada_log("url_aggregator::append_base_username ", input);
 8314|  40.3k|  ADA_ASSERT_TRUE(validate());
 8315|  40.3k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8316|       |#if ADA_DEVELOPMENT_CHECKS
 8317|       |  // computing the expected password.
 8318|       |  std::string username_expected(get_username());
 8319|       |  username_expected.append(input);
 8320|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8321|  40.3k|  add_authority_slashes_if_needed();
 8322|       |
 8323|       |  // If input is empty, do nothing.
 8324|  40.3k|  if (input.empty()) {
  ------------------
  |  Branch (8324:7): [True: 8.80k, False: 31.5k]
  ------------------
 8325|  8.80k|    return;
 8326|  8.80k|  }
 8327|       |
 8328|  31.5k|  uint32_t difference = uint32_t(input.size());
 8329|  31.5k|  buffer.insert(components.username_end, input);
 8330|  31.5k|  components.username_end += difference;
 8331|  31.5k|  components.host_start += difference;
 8332|       |
 8333|  31.5k|  if (buffer[components.host_start] != '@' &&
  ------------------
  |  Branch (8333:7): [True: 17.6k, False: 13.8k]
  ------------------
 8334|  17.6k|      components.host_start != components.host_end) {
  ------------------
  |  Branch (8334:7): [True: 17.6k, False: 0]
  ------------------
 8335|  17.6k|    buffer.insert(components.host_start, "@");
 8336|  17.6k|    difference++;
 8337|  17.6k|  }
 8338|       |
 8339|  31.5k|  components.host_end += difference;
 8340|  31.5k|  components.pathname_start += difference;
 8341|  31.5k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8341:7): [True: 0, False: 31.5k]
  ------------------
 8342|      0|    components.search_start += difference;
 8343|      0|  }
 8344|  31.5k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8344:7): [True: 0, False: 31.5k]
  ------------------
 8345|      0|    components.hash_start += difference;
 8346|      0|  }
 8347|       |#if ADA_DEVELOPMENT_CHECKS
 8348|       |  std::string username_after(get_username());
 8349|       |  ADA_ASSERT_EQUAL(
 8350|       |      username_expected, username_after,
 8351|       |      "append_base_username problem after inserting " + std::string(input));
 8352|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8353|  31.5k|  ADA_ASSERT_TRUE(validate());
 8354|  31.5k|}
_ZN3ada14url_aggregator21update_base_authorityENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS_14url_componentsE:
 7963|  12.2k|    std::string_view base_buffer, const ada::url_components& base) {
 7964|  12.2k|  std::string_view input = base_buffer.substr(
 7965|  12.2k|      base.protocol_end, base.host_start - base.protocol_end);
 7966|  12.2k|  ada_log("url_aggregator::update_base_authority ", input);
 7967|       |
 7968|  12.2k|  bool input_starts_with_dash = input.starts_with("//");
 7969|  12.2k|  uint32_t diff = components.host_start - components.protocol_end;
 7970|       |
 7971|  12.2k|  buffer.erase(components.protocol_end,
 7972|  12.2k|               components.host_start - components.protocol_end);
 7973|  12.2k|  components.username_end = components.protocol_end;
 7974|       |
 7975|  12.2k|  if (input_starts_with_dash) {
  ------------------
  |  Branch (7975:7): [True: 11.1k, False: 1.09k]
  ------------------
 7976|  11.1k|    input.remove_prefix(2);
 7977|  11.1k|    diff += 2;  // add "//"
 7978|  11.1k|    buffer.insert(components.protocol_end, "//");
 7979|  11.1k|    components.username_end += 2;
 7980|  11.1k|  }
 7981|       |
 7982|  12.2k|  size_t password_delimiter = input.find(':');
 7983|       |
 7984|       |  // Check if input contains both username and password by checking the
 7985|       |  // delimiter: ":" A typical input that contains authority would be "user:pass"
 7986|  12.2k|  if (password_delimiter != std::string_view::npos) {
  ------------------
  |  Branch (7986:7): [True: 231, False: 11.9k]
  ------------------
 7987|       |    // Insert both username and password
 7988|    231|    std::string_view username = input.substr(0, password_delimiter);
 7989|    231|    std::string_view password = input.substr(password_delimiter + 1);
 7990|       |
 7991|    231|    buffer.insert(components.protocol_end + diff, username);
 7992|    231|    diff += uint32_t(username.size());
 7993|    231|    buffer.insert(components.protocol_end + diff, ":");
 7994|    231|    components.username_end = components.protocol_end + diff;
 7995|    231|    buffer.insert(components.protocol_end + diff + 1, password);
 7996|    231|    diff += uint32_t(password.size()) + 1;
 7997|  11.9k|  } else if (!input.empty()) {
  ------------------
  |  Branch (7997:14): [True: 221, False: 11.7k]
  ------------------
 7998|       |    // Insert only username
 7999|    221|    buffer.insert(components.protocol_end + diff, input);
 8000|    221|    components.username_end =
 8001|    221|        components.protocol_end + diff + uint32_t(input.size());
 8002|    221|    diff += uint32_t(input.size());
 8003|    221|  }
 8004|       |
 8005|  12.2k|  components.host_start += diff;
 8006|       |
 8007|  12.2k|  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
  ------------------
  |  Branch (8007:7): [True: 0, False: 12.2k]
  |  Branch (8007:42): [True: 0, False: 0]
  ------------------
 8008|      0|    buffer.insert(components.host_start, "@");
 8009|      0|    diff++;
 8010|      0|  }
 8011|  12.2k|  components.host_end += diff;
 8012|  12.2k|  components.pathname_start += diff;
 8013|  12.2k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8013:7): [True: 0, False: 12.2k]
  ------------------
 8014|      0|    components.search_start += diff;
 8015|      0|  }
 8016|  12.2k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8016:7): [True: 0, False: 12.2k]
  ------------------
 8017|      0|    components.hash_start += diff;
 8018|      0|  }
 8019|  12.2k|}
_ZNK3ada14url_aggregator8get_hrefEv:
 8790|   151k|    const noexcept ada_lifetime_bound {
 8791|   151k|  ada_log("url_aggregator::get_href");
 8792|   151k|  return buffer;
 8793|   151k|}
_ZNK3ada14url_aggregator14get_componentsEv:
 8672|  49.1k|url_aggregator::get_components() const noexcept {
 8673|  49.1k|  return components;
 8674|  49.1k|}
_ZN3ada14url_aggregator24update_host_to_base_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9058|  13.9k|void url_aggregator::update_host_to_base_host(const std::string_view input) {
 9059|  13.9k|  ada_log("url_aggregator::update_host_to_base_host ", input);
 9060|  13.9k|  ADA_ASSERT_TRUE(validate());
 9061|  13.9k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 9062|  13.9k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (9062:7): [True: 12.2k, False: 1.69k]
  ------------------
 9063|       |    // Let host be the result of host parsing host_view with url is not special.
 9064|  12.2k|    if (input.empty() && !is_special()) {
  ------------------
  |  Branch (9064:9): [True: 1.18k, False: 11.0k]
  |  Branch (9064:26): [True: 1.18k, False: 0]
  ------------------
 9065|  1.18k|      if (has_hostname()) {
  ------------------
  |  Branch (9065:11): [True: 89, False: 1.09k]
  ------------------
 9066|     89|        clear_hostname();
 9067|  1.09k|      } else if (has_dash_dot()) {
  ------------------
  |  Branch (9067:18): [True: 0, False: 1.09k]
  ------------------
 9068|      0|        add_authority_slashes_if_needed();
 9069|      0|        delete_dash_dot();
 9070|      0|      }
 9071|  1.18k|      return;
 9072|  1.18k|    }
 9073|  12.2k|  }
 9074|  12.7k|  update_base_hostname(input);
 9075|  12.7k|  ADA_ASSERT_TRUE(validate());
 9076|  12.7k|  return;
 9077|  13.9k|}
_ZN3ada14url_aggregator14clear_hostnameEv:
 8613|    212|constexpr void url_aggregator::clear_hostname() {
 8614|    212|  ada_log("url_aggregator::clear_hostname");
 8615|    212|  ADA_ASSERT_TRUE(validate());
 8616|    212|  if (!has_authority()) {
  ------------------
  |  Branch (8616:7): [True: 0, False: 212]
  ------------------
 8617|      0|    return;
 8618|      0|  }
 8619|    212|  ADA_ASSERT_TRUE(has_authority());
 8620|       |
 8621|    212|  uint32_t hostname_length = components.host_end - components.host_start;
 8622|    212|  uint32_t start = components.host_start;
 8623|       |
 8624|       |  // If hostname starts with "@", we should not remove that character.
 8625|    212|  if (hostname_length > 0 && buffer[start] == '@') {
  ------------------
  |  Branch (8625:7): [True: 94, False: 118]
  |  Branch (8625:30): [True: 0, False: 94]
  ------------------
 8626|      0|    start++;
 8627|      0|    hostname_length--;
 8628|      0|  }
 8629|    212|  buffer.erase(start, hostname_length);
 8630|    212|  components.host_end = start;
 8631|    212|  components.pathname_start -= hostname_length;
 8632|    212|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8632:7): [True: 123, False: 89]
  ------------------
 8633|    123|    components.search_start -= hostname_length;
 8634|    123|  }
 8635|    212|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8635:7): [True: 122, False: 90]
  ------------------
 8636|    122|    components.hash_start -= hostname_length;
 8637|    122|  }
 8638|       |#if ADA_DEVELOPMENT_CHECKS
 8639|       |  ADA_ASSERT_EQUAL(get_hostname(), "",
 8640|       |                   "hostname should have been cleared on buffer=" + buffer +
 8641|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8642|       |#endif
 8643|    212|  ADA_ASSERT_TRUE(has_authority());
 8644|    212|  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
 8645|    212|                   "hostname should have been cleared on buffer=" + buffer +
 8646|    212|                       " with " + components.to_string() + "\n" + to_diagram());
 8647|    212|  ADA_ASSERT_TRUE(validate());
 8648|    212|}
_ZN3ada14url_aggregator16update_base_portEj:
 8482|  40.3k|inline void url_aggregator::update_base_port(uint32_t input) {
 8483|  40.3k|  ada_log("url_aggregator::update_base_port");
 8484|  40.3k|  ADA_ASSERT_TRUE(validate());
 8485|  40.3k|  if (input == url_components::omitted) {
  ------------------
  |  Branch (8485:7): [True: 19.7k, False: 20.5k]
  ------------------
 8486|  19.7k|    clear_port();
 8487|  19.7k|    return;
 8488|  19.7k|  }
 8489|       |  // calling std::to_string(input.value()) is unfortunate given that the port
 8490|       |  // value is probably already available as a string.
 8491|  20.5k|  std::string value = helpers::concat(":", std::to_string(input));
 8492|  20.5k|  uint32_t difference = uint32_t(value.size());
 8493|       |
 8494|  20.5k|  if (components.port != url_components::omitted) {
  ------------------
  |  Branch (8494:7): [True: 1.94k, False: 18.6k]
  ------------------
 8495|  1.94k|    difference -= components.pathname_start - components.host_end;
 8496|  1.94k|    buffer.erase(components.host_end,
 8497|  1.94k|                 components.pathname_start - components.host_end);
 8498|  1.94k|  }
 8499|       |
 8500|  20.5k|  buffer.insert(components.host_end, value);
 8501|  20.5k|  components.pathname_start += difference;
 8502|  20.5k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8502:7): [True: 2.70k, False: 17.8k]
  ------------------
 8503|  2.70k|    components.search_start += difference;
 8504|  2.70k|  }
 8505|  20.5k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8505:7): [True: 2.74k, False: 17.8k]
  ------------------
 8506|  2.74k|    components.hash_start += difference;
 8507|  2.74k|  }
 8508|  20.5k|  components.port = input;
 8509|  20.5k|  ADA_ASSERT_TRUE(validate());
 8510|  20.5k|}
_ZN3ada7helpers6concatIJPKcNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEESA_DpT_:
 1790|  20.5k|std::string concat(Args... args) {
 1791|  20.5k|  std::string answer;
 1792|  20.5k|  inner_concat(answer, args...);
 1793|  20.5k|  return answer;
 1794|  20.5k|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEEvRSA_T_DpT0_:
 1779|  41.5k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1780|  41.5k|  buffer.append(t);
 1781|  41.5k|  return inner_concat(buffer, args...);
 1782|  41.5k|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJEEEvRS8_T_:
 1771|  41.5k|inline void inner_concat(std::string& buffer, T t) {
 1772|  41.5k|  buffer.append(t);
 1773|  41.5k|}
_ZNK3ada14url_aggregator18retrieve_base_portEv:
 8531|  12.2k|[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
 8532|  12.2k|  ada_log("url_aggregator::retrieve_base_port");
 8533|  12.2k|  return components.port;
 8534|  12.2k|}
_ZN3ada14url_aggregator12clear_searchEv:
 8536|  24.6k|inline void url_aggregator::clear_search() {
 8537|  24.6k|  ada_log("url_aggregator::clear_search");
 8538|  24.6k|  ADA_ASSERT_TRUE(validate());
 8539|  24.6k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8539:7): [True: 3.83k, False: 20.8k]
  ------------------
 8540|  3.83k|    return;
 8541|  3.83k|  }
 8542|       |
 8543|  20.8k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8543:7): [True: 6.50k, False: 14.3k]
  ------------------
 8544|  6.50k|    buffer.resize(components.search_start);
 8545|  14.3k|  } else {
 8546|  14.3k|    buffer.erase(components.search_start,
 8547|  14.3k|                 components.hash_start - components.search_start);
 8548|  14.3k|    components.hash_start = components.search_start;
 8549|  14.3k|  }
 8550|       |
 8551|  20.8k|  components.search_start = url_components::omitted;
 8552|       |
 8553|       |#if ADA_DEVELOPMENT_CHECKS
 8554|       |  ADA_ASSERT_EQUAL(get_search(), "",
 8555|       |                   "search should have been cleared on buffer=" + buffer +
 8556|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8557|       |#endif
 8558|  20.8k|  ADA_ASSERT_TRUE(validate());
 8559|  20.8k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8150|  78.5k|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8151|  78.5k|  ada_log("url_aggregator::update_base_search ", input,
 8152|  78.5k|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8153|  78.5k|  ADA_ASSERT_TRUE(validate());
 8154|  78.5k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8155|       |
 8156|  78.5k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8156:7): [True: 77.4k, False: 1.12k]
  ------------------
 8157|  77.4k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8157:9): [True: 62.6k, False: 14.7k]
  ------------------
 8158|  62.6k|      components.search_start = uint32_t(buffer.size());
 8159|  62.6k|      buffer += "?";
 8160|  62.6k|    } else {
 8161|  14.7k|      buffer.resize(components.search_start + 1);
 8162|  14.7k|    }
 8163|       |
 8164|  77.4k|    bool encoding_required =
 8165|  77.4k|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8166|       |    // When encoding_required is false, then buffer is left unchanged, and
 8167|       |    // percent encoding was not deemed required.
 8168|  77.4k|    if (!encoding_required) {
  ------------------
  |  Branch (8168:9): [True: 62.9k, False: 14.4k]
  ------------------
 8169|  62.9k|      buffer.append(input);
 8170|  62.9k|    }
 8171|  77.4k|  } else {
 8172|  1.12k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8172:9): [True: 29, False: 1.09k]
  ------------------
 8173|     29|      components.search_start = components.hash_start;
 8174|  1.09k|    } else {
 8175|  1.09k|      buffer.erase(components.search_start,
 8176|  1.09k|                   components.hash_start - components.search_start);
 8177|  1.09k|      components.hash_start = components.search_start;
 8178|  1.09k|    }
 8179|       |
 8180|  1.12k|    buffer.insert(components.search_start, "?");
 8181|  1.12k|    size_t idx =
 8182|  1.12k|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8183|  1.12k|    if (idx == input.size()) {
  ------------------
  |  Branch (8183:9): [True: 923, False: 197]
  ------------------
 8184|    923|      buffer.insert(components.search_start + 1, input);
 8185|    923|      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8186|    923|    } else {
 8187|    197|      buffer.insert(components.search_start + 1, input, 0, idx);
 8188|    197|      input.remove_prefix(idx);
 8189|       |      // We only create a temporary string if we need percent encoding and
 8190|       |      // we attempt to create as small a temporary string as we can.
 8191|    197|      std::string encoded =
 8192|    197|          ada::unicode::percent_encode(input, query_percent_encode_set);
 8193|    197|      buffer.insert(components.search_start + idx + 1, encoded);
 8194|    197|      components.hash_start +=
 8195|    197|          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
 8196|    197|    }
 8197|  1.12k|  }
 8198|       |
 8199|  78.5k|  ADA_ASSERT_TRUE(validate());
 8200|  78.5k|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8065|   127k|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8066|   127k|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8067|   127k|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8068|   127k|  ADA_ASSERT_TRUE(validate());
 8069|   127k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8070|       |
 8071|       |  // This next line is required for when parsing a URL like `foo://`
 8072|   127k|  add_authority_slashes_if_needed();
 8073|       |
 8074|   127k|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8075|   127k|  uint32_t new_difference =
 8076|   127k|      replace_and_resize(components.host_start, components.host_end, input);
 8077|       |
 8078|   127k|  if (has_credentials) {
  ------------------
  |  Branch (8078:7): [True: 41.5k, False: 85.7k]
  ------------------
 8079|  41.5k|    buffer.insert(components.host_start, "@");
 8080|  41.5k|    new_difference++;
 8081|  41.5k|  }
 8082|   127k|  components.host_end += new_difference;
 8083|   127k|  components.pathname_start += new_difference;
 8084|   127k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8084:7): [True: 6.45k, False: 120k]
  ------------------
 8085|  6.45k|    components.search_start += new_difference;
 8086|  6.45k|  }
 8087|   127k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8087:7): [True: 6.57k, False: 120k]
  ------------------
 8088|  6.57k|    components.hash_start += new_difference;
 8089|  6.57k|  }
 8090|   127k|  ADA_ASSERT_TRUE(validate());
 8091|   127k|}
_ZN3ada14url_aggregator10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 8800|  21.4k|url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
 8801|  21.4k|  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
 8802|  21.4k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (8802:7): [True: 20.3k, False: 1.12k]
  |  Branch (8802:24): [True: 38, False: 20.3k]
  ------------------
 8803|     38|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 8804|     38|    is_valid = false;
 8805|     38|    return 0;
 8806|     38|  }
 8807|  21.4k|  uint16_t parsed_port{};
 8808|  21.4k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 8809|  21.4k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (8809:7): [True: 386, False: 21.0k]
  ------------------
 8810|    386|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 8811|    386|    is_valid = false;
 8812|    386|    return 0;
 8813|    386|  }
 8814|  21.0k|  ada_log("parse_port: ", parsed_port);
 8815|  21.0k|  const size_t consumed = size_t(r.ptr - view.data());
 8816|  21.0k|  ada_log("parse_port: consumed ", consumed);
 8817|  21.0k|  if (check_trailing_content) {
  ------------------
  |  Branch (8817:7): [True: 18.3k, False: 2.67k]
  ------------------
 8818|  18.3k|    is_valid &=
 8819|  18.3k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (8819:10): [True: 1.94k, False: 16.4k]
  |  Branch (8819:37): [True: 15.8k, False: 532]
  ------------------
 8820|    532|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (8820:10): [True: 158, False: 374]
  |  Branch (8820:36): [True: 346, False: 28]
  |  Branch (8820:52): [True: 31, False: 315]
  ------------------
 8821|  18.3k|  }
 8822|  21.0k|  ada_log("parse_port: is_valid = ", is_valid);
 8823|  21.0k|  if (is_valid) {
  ------------------
  |  Branch (8823:7): [True: 20.3k, False: 689]
  ------------------
 8824|  20.3k|    ada_log("parse_port", r.ec == std::errc());
 8825|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 8826|  20.3k|    auto default_port = scheme_default_port();
 8827|  20.3k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (8827:27): [True: 262, False: 20.0k]
  |  Branch (8827:48): [True: 119, False: 143]
  ------------------
 8828|  20.2k|                         (default_port != parsed_port);
  ------------------
  |  Branch (8828:26): [True: 20.0k, False: 159]
  ------------------
 8829|  20.3k|    if (r.ec == std::errc() && is_port_valid) {
  ------------------
  |  Branch (8829:9): [True: 18.9k, False: 1.36k]
  |  Branch (8829:32): [True: 18.8k, False: 159]
  ------------------
 8830|  18.8k|      update_base_port(parsed_port);
 8831|  18.8k|    } else {
 8832|  1.52k|      clear_port();
 8833|  1.52k|    }
 8834|  20.3k|  }
 8835|  21.0k|  return consumed;
 8836|  21.4k|}
_ZN3ada14url_aggregator20append_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8241|     49|inline void url_aggregator::append_base_pathname(const std::string_view input) {
 8242|     49|  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
 8243|     49|          "\n", to_diagram());
 8244|     49|  ADA_ASSERT_TRUE(validate());
 8245|     49|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8246|       |#if ADA_DEVELOPMENT_CHECKS
 8247|       |  // computing the expected password.
 8248|       |  std::string path_expected(get_pathname());
 8249|       |  path_expected.append(input);
 8250|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8251|     49|  uint32_t ending_index = uint32_t(buffer.size());
 8252|     49|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8252:7): [True: 0, False: 49]
  ------------------
 8253|      0|    ending_index = components.search_start;
 8254|     49|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8254:14): [True: 0, False: 49]
  ------------------
 8255|      0|    ending_index = components.hash_start;
 8256|      0|  }
 8257|     49|  buffer.insert(ending_index, input);
 8258|       |
 8259|     49|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8259:7): [True: 0, False: 49]
  ------------------
 8260|      0|    components.search_start += uint32_t(input.size());
 8261|      0|  }
 8262|     49|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8262:7): [True: 0, False: 49]
  ------------------
 8263|      0|    components.hash_start += uint32_t(input.size());
 8264|      0|  }
 8265|       |#if ADA_DEVELOPMENT_CHECKS
 8266|       |  std::string path_after = std::string(get_pathname());
 8267|       |  ADA_ASSERT_EQUAL(
 8268|       |      path_expected, path_after,
 8269|       |      "append_base_pathname problem after inserting " + std::string(input));
 8270|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8271|     49|  ADA_ASSERT_TRUE(validate());
 8272|     49|}
_ZN3ada14url_aggregator20set_protocol_as_fileEv:
 8838|  6.98k|constexpr void url_aggregator::set_protocol_as_file() {
 8839|  6.98k|  ada_log("url_aggregator::set_protocol_as_file ");
 8840|  6.98k|  ADA_ASSERT_TRUE(validate());
 8841|  6.98k|  type = ada::scheme::type::FILE;
 8842|       |  // next line could overflow but unsigned arithmetic has well-defined
 8843|       |  // overflows.
 8844|  6.98k|  uint32_t new_difference = 5 - components.protocol_end;
 8845|       |
 8846|  6.98k|  if (buffer.empty()) {
  ------------------
  |  Branch (8846:7): [True: 1.76k, False: 5.21k]
  ------------------
 8847|  1.76k|    buffer.append("file:");
 8848|  5.21k|  } else {
 8849|  5.21k|    buffer.erase(0, components.protocol_end);
 8850|  5.21k|    buffer.insert(0, "file:");
 8851|  5.21k|  }
 8852|  6.98k|  components.protocol_end = 5;
 8853|       |
 8854|       |  // Update the rest of the components.
 8855|  6.98k|  components.username_end += new_difference;
 8856|  6.98k|  components.host_start += new_difference;
 8857|  6.98k|  components.host_end += new_difference;
 8858|  6.98k|  components.pathname_start += new_difference;
 8859|  6.98k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8859:7): [True: 0, False: 6.98k]
  ------------------
 8860|      0|    components.search_start += new_difference;
 8861|      0|  }
 8862|  6.98k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8862:7): [True: 0, False: 6.98k]
  ------------------
 8863|      0|    components.hash_start += new_difference;
 8864|      0|  }
 8865|  6.98k|  ADA_ASSERT_TRUE(validate());
 8866|  6.98k|}
_ZN3ada14url_aggregator14clear_pathnameEv:
 8578|  17.2k|constexpr void url_aggregator::clear_pathname() {
 8579|  17.2k|  ada_log("url_aggregator::clear_pathname");
 8580|  17.2k|  ADA_ASSERT_TRUE(validate());
 8581|  17.2k|  uint32_t ending_index = uint32_t(buffer.size());
 8582|  17.2k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8582:7): [True: 1.67k, False: 15.6k]
  ------------------
 8583|  1.67k|    ending_index = components.search_start;
 8584|  15.6k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8584:14): [True: 126, False: 15.4k]
  ------------------
 8585|    126|    ending_index = components.hash_start;
 8586|    126|  }
 8587|  17.2k|  uint32_t pathname_length = ending_index - components.pathname_start;
 8588|  17.2k|  buffer.erase(components.pathname_start, pathname_length);
 8589|  17.2k|  uint32_t difference = pathname_length;
 8590|  17.2k|  if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8590:7): [True: 802, False: 16.4k]
  ------------------
 8591|    802|      buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (8591:7): [True: 0, False: 802]
  ------------------
 8592|      0|      buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (8592:7): [True: 0, False: 0]
  ------------------
 8593|      0|    components.pathname_start -= 2;
 8594|      0|    buffer.erase(components.host_end, 2);
 8595|      0|    difference += 2;
 8596|      0|  }
 8597|  17.2k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8597:7): [True: 1.67k, False: 15.6k]
  ------------------
 8598|  1.67k|    components.search_start -= difference;
 8599|  1.67k|  }
 8600|  17.2k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8600:7): [True: 1.78k, False: 15.4k]
  ------------------
 8601|  1.78k|    components.hash_start -= difference;
 8602|  1.78k|  }
 8603|  17.2k|  ada_log("url_aggregator::clear_pathname completed, running checks...");
 8604|       |#if ADA_DEVELOPMENT_CHECKS
 8605|       |  ADA_ASSERT_EQUAL(get_pathname(), "",
 8606|       |                   "pathname should have been cleared on buffer=" + buffer +
 8607|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8608|       |#endif
 8609|  17.2k|  ADA_ASSERT_TRUE(validate());
 8610|  17.2k|  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
 8611|  17.2k|}
_ZN3ada14url_aggregatorC2ERKS0_:
 7503|   146k|  url_aggregator(const url_aggregator& u) = default;
_ZNK3ada14url_aggregator31cannot_have_credentials_or_portEv:
 8665|  56.1k|constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
 8666|  56.1k|  ada_log("url_aggregator::cannot_have_credentials_or_port");
 8667|  56.1k|  return type == ada::scheme::type::FILE ||
  ------------------
  |  Branch (8667:10): [True: 2.79k, False: 53.3k]
  ------------------
 8668|  53.3k|         components.host_start == components.host_end;
  ------------------
  |  Branch (8668:10): [True: 0, False: 53.3k]
  ------------------
 8669|  56.1k|}
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7923|  34.5k|                                              const uint8_t character_set[]) {
 7924|  34.5k|  const char* data = input.data();
 7925|  34.5k|  const size_t size = input.size();
 7926|       |
 7927|       |  // Process 8 bytes at a time using unrolled loop
 7928|  34.5k|  size_t i = 0;
 7929|  68.9k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (7929:10): [True: 47.3k, False: 21.6k]
  ------------------
 7930|  47.3k|    unsigned char chunk[8];
 7931|  47.3k|    std::memcpy(&chunk, data + i,
 7932|  47.3k|                8);  // entices compiler to unconditionally process 8 characters
 7933|       |
 7934|       |    // Check 8 characters at once
 7935|   343k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (7935:24): [True: 309k, False: 34.4k]
  ------------------
 7936|   309k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (7936:11): [True: 12.9k, False: 296k]
  ------------------
 7937|  12.9k|        return i + j;
 7938|  12.9k|      }
 7939|   309k|    }
 7940|  47.3k|  }
 7941|       |
 7942|       |  // Handle remaining bytes
 7943|  45.8k|  for (; i < size; i++) {
  ------------------
  |  Branch (7943:10): [True: 35.1k, False: 10.7k]
  ------------------
 7944|  35.1k|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (7944:9): [True: 10.8k, False: 24.2k]
  ------------------
 7945|  10.8k|      return i;
 7946|  10.8k|    }
 7947|  35.1k|  }
 7948|       |
 7949|  10.7k|  return size;
 7950|  21.6k|}
_ZN3ada14url_aggregator20update_base_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8274|  17.2k|inline void url_aggregator::update_base_username(const std::string_view input) {
 8275|  17.2k|  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
 8276|  17.2k|          "\n", to_diagram());
 8277|  17.2k|  ADA_ASSERT_TRUE(validate());
 8278|  17.2k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8279|       |
 8280|  17.2k|  add_authority_slashes_if_needed();
 8281|       |
 8282|  17.2k|  bool has_password = has_non_empty_password();
 8283|  17.2k|  bool host_starts_with_at = buffer.size() > components.host_start &&
  ------------------
  |  Branch (8283:30): [True: 17.2k, False: 0]
  ------------------
 8284|  17.2k|                             buffer[components.host_start] == '@';
  ------------------
  |  Branch (8284:30): [True: 1.55k, False: 15.7k]
  ------------------
 8285|  17.2k|  uint32_t diff = replace_and_resize(components.protocol_end + 2,
 8286|  17.2k|                                     components.username_end, input);
 8287|       |
 8288|  17.2k|  components.username_end += diff;
 8289|  17.2k|  components.host_start += diff;
 8290|       |
 8291|  17.2k|  if (!input.empty() && !host_starts_with_at) {
  ------------------
  |  Branch (8291:7): [True: 13.6k, False: 3.68k]
  |  Branch (8291:25): [True: 13.2k, False: 337]
  ------------------
 8292|  13.2k|    buffer.insert(components.host_start, "@");
 8293|  13.2k|    diff++;
 8294|  13.2k|  } else if (input.empty() && host_starts_with_at && !has_password) {
  ------------------
  |  Branch (8294:14): [True: 3.68k, False: 337]
  |  Branch (8294:31): [True: 1.21k, False: 2.47k]
  |  Branch (8294:54): [True: 1.04k, False: 169]
  ------------------
 8295|       |    // Input is empty, there is no password, and we need to remove "@" from
 8296|       |    // hostname
 8297|  1.04k|    buffer.erase(components.host_start, 1);
 8298|  1.04k|    diff--;
 8299|  1.04k|  }
 8300|       |
 8301|  17.2k|  components.host_end += diff;
 8302|  17.2k|  components.pathname_start += diff;
 8303|  17.2k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8303:7): [True: 1.48k, False: 15.8k]
  ------------------
 8304|  1.48k|    components.search_start += diff;
 8305|  1.48k|  }
 8306|  17.2k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8306:7): [True: 1.57k, False: 15.7k]
  ------------------
 8307|  1.57k|    components.hash_start += diff;
 8308|  1.57k|  }
 8309|  17.2k|  ADA_ASSERT_TRUE(validate());
 8310|  17.2k|}
_ZN3ada14url_aggregator20update_base_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8376|  16.1k|inline void url_aggregator::update_base_password(const std::string_view input) {
 8377|  16.1k|  ada_log("url_aggregator::update_base_password ", input);
 8378|  16.1k|  ADA_ASSERT_TRUE(validate());
 8379|  16.1k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8380|       |
 8381|  16.1k|  add_authority_slashes_if_needed();
 8382|       |
 8383|       |  // TODO: Optimization opportunity. Merge the following removal functions.
 8384|  16.1k|  if (input.empty()) {
  ------------------
  |  Branch (8384:7): [True: 2.46k, False: 13.6k]
  ------------------
 8385|  2.46k|    clear_password();
 8386|       |
 8387|       |    // Remove username too, if it is empty.
 8388|  2.46k|    if (!has_non_empty_username()) {
  ------------------
  |  Branch (8388:9): [True: 1.27k, False: 1.18k]
  ------------------
 8389|  1.27k|      update_base_username("");
 8390|  1.27k|    }
 8391|       |
 8392|  2.46k|    return;
 8393|  2.46k|  }
 8394|       |
 8395|  13.6k|  bool password_exists = has_password();
 8396|  13.6k|  uint32_t difference = uint32_t(input.size());
 8397|       |
 8398|  13.6k|  if (password_exists) {
  ------------------
  |  Branch (8398:7): [True: 386, False: 13.3k]
  ------------------
 8399|    386|    uint32_t current_length =
 8400|    386|        components.host_start - components.username_end - 1;
 8401|    386|    buffer.erase(components.username_end + 1, current_length);
 8402|    386|    difference -= current_length;
 8403|  13.3k|  } else {
 8404|  13.3k|    buffer.insert(components.username_end, ":");
 8405|  13.3k|    difference++;
 8406|  13.3k|  }
 8407|       |
 8408|  13.6k|  buffer.insert(components.username_end + 1, input);
 8409|  13.6k|  components.host_start += difference;
 8410|       |
 8411|       |  // The following line is required to add "@" to hostname. When updating
 8412|       |  // password if hostname does not start with "@", it is "update_base_password"s
 8413|       |  // responsibility to set it.
 8414|  13.6k|  if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8414:7): [True: 13, False: 13.6k]
  ------------------
 8415|     13|    buffer.insert(components.host_start, "@");
 8416|     13|    difference++;
 8417|     13|  }
 8418|       |
 8419|  13.6k|  components.host_end += difference;
 8420|  13.6k|  components.pathname_start += difference;
 8421|  13.6k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8421:7): [True: 391, False: 13.3k]
  ------------------
 8422|    391|    components.search_start += difference;
 8423|    391|  }
 8424|  13.6k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8424:7): [True: 412, False: 13.2k]
  ------------------
 8425|    412|    components.hash_start += difference;
 8426|    412|  }
 8427|  13.6k|  ADA_ASSERT_TRUE(validate());
 8428|  13.6k|}
_ZN3ada14url_aggregator14clear_passwordEv:
 8356|  2.46k|constexpr void url_aggregator::clear_password() {
 8357|  2.46k|  ada_log("url_aggregator::clear_password ", to_string());
 8358|  2.46k|  ADA_ASSERT_TRUE(validate());
 8359|  2.46k|  if (!has_password()) {
  ------------------
  |  Branch (8359:7): [True: 1.28k, False: 1.18k]
  ------------------
 8360|  1.28k|    return;
 8361|  1.28k|  }
 8362|       |
 8363|  1.18k|  uint32_t diff = components.host_start - components.username_end;
 8364|  1.18k|  buffer.erase(components.username_end, diff);
 8365|  1.18k|  components.host_start -= diff;
 8366|  1.18k|  components.host_end -= diff;
 8367|  1.18k|  components.pathname_start -= diff;
 8368|  1.18k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8368:7): [True: 1.10k, False: 78]
  ------------------
 8369|  1.10k|    components.search_start -= diff;
 8370|  1.10k|  }
 8371|  1.18k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8371:7): [True: 1.16k, False: 22]
  ------------------
 8372|  1.16k|    components.hash_start -= diff;
 8373|  1.16k|  }
 8374|  1.18k|}
_ZN3ada14url_aggregator10clear_portEv:
 8512|  38.9k|inline void url_aggregator::clear_port() {
 8513|  38.9k|  ada_log("url_aggregator::clear_port");
 8514|  38.9k|  ADA_ASSERT_TRUE(validate());
 8515|  38.9k|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (8515:7): [True: 36.1k, False: 2.86k]
  ------------------
 8516|  36.1k|    return;
 8517|  36.1k|  }
 8518|  2.86k|  uint32_t length = components.pathname_start - components.host_end;
 8519|  2.86k|  buffer.erase(components.host_end, length);
 8520|  2.86k|  components.pathname_start -= length;
 8521|  2.86k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8521:7): [True: 2.81k, False: 50]
  ------------------
 8522|  2.81k|    components.search_start -= length;
 8523|  2.81k|  }
 8524|  2.86k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8524:7): [True: 2.82k, False: 37]
  ------------------
 8525|  2.82k|    components.hash_start -= length;
 8526|  2.82k|  }
 8527|  2.86k|  components.port = url_components::omitted;
 8528|  2.86k|  ADA_ASSERT_TRUE(validate());
 8529|  2.86k|}
_ZNK3ada14url_aggregator13has_authorityEv:
 8677|   363k|    const noexcept {
 8678|   363k|  ada_log("url_aggregator::has_authority");
 8679|       |  // Performance: instead of doing this potentially expensive check, we could
 8680|       |  // have a boolean in the struct.
 8681|   363k|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (8681:10): [True: 266k, False: 96.4k]
  ------------------
 8682|   266k|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (8682:10): [True: 266k, False: 0]
  ------------------
 8683|   266k|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (8683:10): [True: 266k, False: 0]
  ------------------
 8684|   363k|}
_ZNK3ada14url_aggregator12has_dash_dotEv:
 8757|  85.9k|[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
 8758|       |  // If url's host is null, url does not have an opaque path, url's path's size
 8759|       |  // is greater than 1, and url's path[0] is the empty string, then append
 8760|       |  // U+002F (/) followed by U+002E (.) to output.
 8761|  85.9k|  ada_log("url_aggregator::has_dash_dot");
 8762|       |#if ADA_DEVELOPMENT_CHECKS
 8763|       |  // If pathname_start and host_end are exactly two characters apart, then we
 8764|       |  // either have a one-digit port such as http://test.com:5?param=1 or else we
 8765|       |  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
 8766|       |  if (components.pathname_start == components.host_end + 2) {
 8767|       |    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
 8768|       |                     buffer[components.host_end + 1] == '.') ||
 8769|       |                    (buffer[components.host_end] == ':' &&
 8770|       |                     checkers::is_digit(buffer[components.host_end + 1])));
 8771|       |  }
 8772|       |  if (components.pathname_start == components.host_end + 2 &&
 8773|       |      buffer[components.host_end] == '/' &&
 8774|       |      buffer[components.host_end + 1] == '.') {
 8775|       |    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
 8776|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
 8777|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
 8778|       |  }
 8779|       |#endif
 8780|       |  // Performance: it should be uncommon for components.pathname_start ==
 8781|       |  // components.host_end + 2 to be true. So we put this check first in the
 8782|       |  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
 8783|       |  // more expensive, but should be uncommon.
 8784|  85.9k|  return components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8784:10): [True: 1.17k, False: 84.7k]
  ------------------
 8785|  1.17k|         !has_opaque_path && buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (8785:10): [True: 1.17k, False: 0]
  |  Branch (8785:30): [True: 86, False: 1.09k]
  ------------------
 8786|     86|         buffer[components.host_end + 1] == '.';
  ------------------
  |  Branch (8786:10): [True: 86, False: 0]
  ------------------
 8787|  85.9k|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 3896|   180k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 3865|   944k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 3866|   944k|    TL_ASSERT(has_value());
  ------------------
  |  | 1918|   944k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3866:5): [True: 944k, False: 0]
  ------------------
 3867|   944k|    return valptr();
 3868|   944k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3161|   944k|  T* valptr() { return std::addressof(this->m_val); }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3878|  64.1k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 3879|  64.1k|    TL_ASSERT(has_value());
  ------------------
  |  | 1918|  64.1k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3879:5): [True: 64.1k, False: 0]
  ------------------
 3880|  64.1k|    return val();
 3881|  64.1k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3170|  64.1k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3171|  64.1k|    return this->m_val;
 3172|  64.1k|  }
_ZN3ada14url_aggregatoraSERKS0_:
 7506|  9.27k|  url_aggregator& operator=(const url_aggregator& u) = default;
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 3895|  1.02M|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2493|   132k|  ~expected_storage_base() {
 2494|   132k|    if (m_has_val) {
  ------------------
  |  Branch (2494:9): [True: 104k, False: 27.8k]
  ------------------
 2495|   104k|      m_val.~T();
 2496|   104k|    }
 2497|   132k|  }
_ZNK3ada14url_aggregator22has_non_empty_usernameEv:
 8716|   168k|constexpr bool url_aggregator::has_non_empty_username() const noexcept {
 8717|   168k|  ada_log("url_aggregator::has_non_empty_username");
 8718|   168k|  return components.protocol_end + 2 < components.username_end;
 8719|   168k|}
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1712|   670k|                                                       size_t pos2) {
 1713|       |#if ADA_DEVELOPMENT_CHECKS
 1714|       |  if (pos2 < pos1) {
 1715|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1716|       |              << std::endl;
 1717|       |    abort();
 1718|       |  }
 1719|       |#endif
 1720|   670k|  return input.substr(pos1, pos2 - pos1);
 1721|   670k|}
_ZNK3ada14url_aggregator22has_non_empty_passwordEv:
 8721|   157k|constexpr bool url_aggregator::has_non_empty_password() const noexcept {
 8722|   157k|  ada_log("url_aggregator::has_non_empty_password");
 8723|   157k|  return components.host_start > components.username_end;
 8724|   157k|}
_ZNK3ada14url_aggregator15has_credentialsEv:
 8660|  75.6k|constexpr bool url_aggregator::has_credentials() const noexcept {
 8661|  75.6k|  ada_log("url_aggregator::has_credentials");
 8662|  75.6k|  return has_non_empty_username() || has_non_empty_password();
  ------------------
  |  Branch (8662:10): [True: 26.0k, False: 49.5k]
  |  Branch (8662:38): [True: 668, False: 48.9k]
  ------------------
 8663|  75.6k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8106|  60.1k|    const noexcept {
 8107|  60.1k|  return buffer.size() == components.pathname_start;
 8108|  60.1k|}
_ZN3ada6scheme10is_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6560|  9.04k|ada_really_inline constexpr bool is_special(std::string_view scheme) {
 6561|  9.04k|  if (scheme.empty()) {
  ------------------
  |  Branch (6561:7): [True: 0, False: 9.04k]
  ------------------
 6562|      0|    return false;
 6563|      0|  }
 6564|  9.04k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6565|  9.04k|  const std::string_view target = details::is_special_list[hash_value];
 6566|  9.04k|  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
  ------------------
  |  Branch (6566:10): [True: 3.27k, False: 5.76k]
  |  Branch (6566:38): [True: 2.74k, False: 532]
  ------------------
 6567|  9.04k|}
_ZN3ada14url_aggregator10clear_hashEv:
 8561|  15.3k|inline void url_aggregator::clear_hash() {
 8562|  15.3k|  ada_log("url_aggregator::clear_hash");
 8563|  15.3k|  ADA_ASSERT_TRUE(validate());
 8564|  15.3k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8564:7): [True: 1.21k, False: 14.1k]
  ------------------
 8565|  1.21k|    return;
 8566|  1.21k|  }
 8567|  14.1k|  buffer.resize(components.hash_start);
 8568|  14.1k|  components.hash_start = url_components::omitted;
 8569|       |
 8570|       |#if ADA_DEVELOPMENT_CHECKS
 8571|       |  ADA_ASSERT_EQUAL(get_hash(), "",
 8572|       |                   "hash should have been cleared on buffer=" + buffer +
 8573|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8574|       |#endif
 8575|  14.1k|  ADA_ASSERT_TRUE(validate());
 8576|  14.1k|}
_ZNK3ada14url_aggregator18has_empty_hostnameEv:
 8733|  36.9k|constexpr bool url_aggregator::has_empty_hostname() const noexcept {
 8734|  36.9k|  if (!has_hostname()) {
  ------------------
  |  Branch (8734:7): [True: 3.92k, False: 33.0k]
  ------------------
 8735|  3.92k|    return false;
 8736|  3.92k|  }
 8737|  33.0k|  if (components.host_start == components.host_end) {
  ------------------
  |  Branch (8737:7): [True: 1.48k, False: 31.5k]
  ------------------
 8738|  1.48k|    return true;
 8739|  1.48k|  }
 8740|  31.5k|  if (components.host_end > components.host_start + 1) {
  ------------------
  |  Branch (8740:7): [True: 30.3k, False: 1.22k]
  ------------------
 8741|  30.3k|    return false;
 8742|  30.3k|  }
 8743|  1.22k|  return components.username_end != components.host_start;
 8744|  31.5k|}
_ZNK3ada14url_aggregator12has_hostnameEv:
 8746|   127k|constexpr bool url_aggregator::has_hostname() const noexcept {
 8747|   127k|  return has_authority();
 8748|   127k|}
_ZNK3ada14url_aggregator8has_portEv:
 8750|  52.3k|constexpr bool url_aggregator::has_port() const noexcept {
 8751|  52.3k|  ada_log("url_aggregator::has_port");
 8752|       |  // A URL cannot have a username/password/port if its host is null or the empty
 8753|       |  // string, or its scheme is "file".
 8754|  52.3k|  return has_hostname() && components.pathname_start != components.host_end;
  ------------------
  |  Branch (8754:10): [True: 48.4k, False: 3.92k]
  |  Branch (8754:28): [True: 2.31k, False: 46.1k]
  ------------------
 8755|  52.3k|}
_ZNK3ada14url_aggregator12has_passwordEv:
 8726|  79.2k|constexpr bool url_aggregator::has_password() const noexcept {
 8727|  79.2k|  ada_log("url_aggregator::has_password");
 8728|       |  // This function does not care about the length of the password
 8729|  79.2k|  return components.host_start > components.username_end &&
  ------------------
  |  Branch (8729:10): [True: 24.8k, False: 54.4k]
  ------------------
 8730|  24.8k|         buffer[components.username_end] == ':';
  ------------------
  |  Branch (8730:10): [True: 24.8k, False: 0]
  ------------------
 8731|  79.2k|}
_ZN3ada17url_search_paramsC2ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9149|  30.7k|  explicit url_search_params(const std::string_view input) {
 9150|  30.7k|    initialize(input);
 9151|  30.7k|  }
_ZN3ada17url_search_params10initializeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9367|  30.7k|inline void url_search_params::initialize(std::string_view input) {
 9368|  30.7k|  if (!input.empty() && input.front() == '?') {
  ------------------
  |  Branch (9368:7): [True: 29.1k, False: 1.57k]
  |  Branch (9368:25): [True: 86, False: 29.0k]
  ------------------
 9369|     86|    input.remove_prefix(1);
 9370|     86|  }
 9371|       |
 9372|  30.7k|  auto process_key_value = [&](const std::string_view current) {
 9373|  30.7k|    auto equal = current.find('=');
 9374|       |
 9375|  30.7k|    if (equal == std::string_view::npos) {
 9376|  30.7k|      std::string name(current);
 9377|  30.7k|      std::ranges::replace(name, '+', ' ');
 9378|  30.7k|      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
 9379|  30.7k|    } else {
 9380|  30.7k|      std::string name(current.substr(0, equal));
 9381|  30.7k|      std::string value(current.substr(equal + 1));
 9382|       |
 9383|  30.7k|      std::ranges::replace(name, '+', ' ');
 9384|  30.7k|      std::ranges::replace(value, '+', ' ');
 9385|       |
 9386|  30.7k|      params.emplace_back(unicode::percent_decode(name, name.find('%')),
 9387|  30.7k|                          unicode::percent_decode(value, value.find('%')));
 9388|  30.7k|    }
 9389|  30.7k|  };
 9390|       |
 9391|  49.6k|  while (!input.empty()) {
  ------------------
  |  Branch (9391:10): [True: 47.9k, False: 1.66k]
  ------------------
 9392|  47.9k|    auto ampersand_index = input.find('&');
 9393|       |
 9394|  47.9k|    if (ampersand_index == std::string_view::npos) {
  ------------------
  |  Branch (9394:9): [True: 29.0k, False: 18.8k]
  ------------------
 9395|  29.0k|      if (!input.empty()) {
  ------------------
  |  Branch (9395:11): [True: 29.0k, False: 0]
  ------------------
 9396|  29.0k|        process_key_value(input);
 9397|  29.0k|      }
 9398|  29.0k|      break;
 9399|  29.0k|    } else if (ampersand_index != 0) {
  ------------------
  |  Branch (9399:16): [True: 18.3k, False: 564]
  ------------------
 9400|  18.3k|      process_key_value(input.substr(0, ampersand_index));
 9401|  18.3k|    }
 9402|       |
 9403|  18.8k|    input.remove_prefix(ampersand_index + 1);
 9404|  18.8k|  }
 9405|  30.7k|}
_ZZN3ada17url_search_params10initializeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_E_clES5_:
 9372|  47.3k|  auto process_key_value = [&](const std::string_view current) {
 9373|  47.3k|    auto equal = current.find('=');
 9374|       |
 9375|  47.3k|    if (equal == std::string_view::npos) {
  ------------------
  |  Branch (9375:9): [True: 15.0k, False: 32.3k]
  ------------------
 9376|  15.0k|      std::string name(current);
 9377|  15.0k|      std::ranges::replace(name, '+', ' ');
 9378|  15.0k|      params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
 9379|  32.3k|    } else {
 9380|  32.3k|      std::string name(current.substr(0, equal));
 9381|  32.3k|      std::string value(current.substr(equal + 1));
 9382|       |
 9383|  32.3k|      std::ranges::replace(name, '+', ' ');
 9384|  32.3k|      std::ranges::replace(value, '+', ' ');
 9385|       |
 9386|  32.3k|      params.emplace_back(unicode::percent_decode(name, name.find('%')),
 9387|  32.3k|                          unicode::percent_decode(value, value.find('%')));
 9388|  32.3k|    }
 9389|  47.3k|  };
_ZN3ada17url_search_paramsD2Ev:
 9157|  30.7k|  ~url_search_params() = default;
_ZNK3ada17url_search_params9to_stringEv:
 9453|  30.7k|inline std::string url_search_params::to_string() const {
 9454|  30.7k|  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
 9455|  30.7k|  std::string out{};
 9456|  93.5k|  for (size_t i = 0; i < params.size(); i++) {
  ------------------
  |  Branch (9456:22): [True: 62.7k, False: 30.7k]
  ------------------
 9457|  62.7k|    auto key = ada::unicode::percent_encode(params[i].first, character_set);
 9458|  62.7k|    auto value = ada::unicode::percent_encode(params[i].second, character_set);
 9459|       |
 9460|       |    // Performance optimization: Move this inside percent_encode.
 9461|  62.7k|    std::ranges::replace(key, ' ', '+');
 9462|  62.7k|    std::ranges::replace(value, ' ', '+');
 9463|       |
 9464|  62.7k|    if (i != 0) {
  ------------------
  |  Branch (9464:9): [True: 32.0k, False: 30.7k]
  ------------------
 9465|  32.0k|      out += "&";
 9466|  32.0k|    }
 9467|  62.7k|    out.append(key);
 9468|  62.7k|    out += "=";
 9469|  62.7k|    out.append(value);
 9470|  62.7k|  }
 9471|  30.7k|  return out;
 9472|  30.7k|}
_ZN3ada17url_search_params6appendENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_:
 9408|  15.3k|                                      const std::string_view value) {
 9409|  15.3k|  params.emplace_back(key, value);
 9410|  15.3k|}
_ZNK3ada3url8get_hrefEv:
 7260|  90.2k|[[nodiscard]] ada_really_inline std::string url::get_href() const {
 7261|  90.2k|  std::string output = get_protocol();
 7262|       |
 7263|  90.2k|  if (host.has_value()) {
  ------------------
  |  Branch (7263:7): [True: 72.8k, False: 17.3k]
  ------------------
 7264|  72.8k|    output += "//";
 7265|  72.8k|    if (has_credentials()) {
  ------------------
  |  Branch (7265:9): [True: 34.2k, False: 38.6k]
  ------------------
 7266|  34.2k|      output += username;
 7267|  34.2k|      if (!password.empty()) {
  ------------------
  |  Branch (7267:11): [True: 29.6k, False: 4.63k]
  ------------------
 7268|  29.6k|        output += ":" + get_password();
 7269|  29.6k|      }
 7270|  34.2k|      output += "@";
 7271|  34.2k|    }
 7272|  72.8k|    output += host.value();
 7273|  72.8k|    if (port.has_value()) {
  ------------------
  |  Branch (7273:9): [True: 32.0k, False: 40.7k]
  ------------------
 7274|  32.0k|      output += ":" + get_port();
 7275|  32.0k|    }
 7276|  72.8k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7276:14): [True: 4.60k, False: 12.7k]
  |  Branch (7276:34): [True: 321, False: 4.28k]
  ------------------
 7277|       |    // If url's host is null, url does not have an opaque path, url's path's
 7278|       |    // size is greater than 1, and url's path[0] is the empty string, then
 7279|       |    // append U+002F (/) followed by U+002E (.) to output.
 7280|    321|    output += "/.";
 7281|    321|  }
 7282|  90.2k|  output += path;
 7283|  90.2k|  if (query.has_value()) {
  ------------------
  |  Branch (7283:7): [True: 38.4k, False: 51.7k]
  ------------------
 7284|  38.4k|    output += "?" + query.value();
 7285|  38.4k|  }
 7286|  90.2k|  if (hash.has_value()) {
  ------------------
  |  Branch (7286:7): [True: 38.7k, False: 51.4k]
  ------------------
 7287|  38.7k|    output += "#" + hash.value();
 7288|  38.7k|  }
 7289|  90.2k|  return output;
 7290|  90.2k|}
_ZNK3ada14url_aggregator8validateEv:
 8868|  95.0k|[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
 8869|  95.0k|  if (!is_valid) {
  ------------------
  |  Branch (8869:7): [True: 7.16k, False: 87.9k]
  ------------------
 8870|  7.16k|    return true;
 8871|  7.16k|  }
 8872|  87.9k|  if (!components.check_offset_consistency()) {
  ------------------
  |  Branch (8872:7): [True: 0, False: 87.9k]
  ------------------
 8873|      0|    ada_log("url_aggregator::validate inconsistent components \n",
 8874|      0|            to_diagram());
 8875|      0|    return false;
 8876|      0|  }
 8877|       |  // We have a credible components struct, but let us investivate more
 8878|       |  // carefully:
 8879|       |  /**
 8880|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 8881|       |   *       |     |    |          | ^^^^|       |   |
 8882|       |   *       |     |    |          | |   |       |   `----- hash_start
 8883|       |   *       |     |    |          | |   |       `--------- search_start
 8884|       |   *       |     |    |          | |   `----------------- pathname_start
 8885|       |   *       |     |    |          | `--------------------- port
 8886|       |   *       |     |    |          `----------------------- host_end
 8887|       |   *       |     |    `---------------------------------- host_start
 8888|       |   *       |     `--------------------------------------- username_end
 8889|       |   *       `--------------------------------------------- protocol_end
 8890|       |   */
 8891|  87.9k|  if (components.protocol_end == url_components::omitted) {
  ------------------
  |  Branch (8891:7): [True: 0, False: 87.9k]
  ------------------
 8892|      0|    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
 8893|      0|    return false;
 8894|      0|  }
 8895|  87.9k|  if (components.username_end == url_components::omitted) {
  ------------------
  |  Branch (8895:7): [True: 0, False: 87.9k]
  ------------------
 8896|      0|    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
 8897|      0|    return false;
 8898|      0|  }
 8899|  87.9k|  if (components.host_start == url_components::omitted) {
  ------------------
  |  Branch (8899:7): [True: 0, False: 87.9k]
  ------------------
 8900|      0|    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
 8901|      0|    return false;
 8902|      0|  }
 8903|  87.9k|  if (components.host_end == url_components::omitted) {
  ------------------
  |  Branch (8903:7): [True: 0, False: 87.9k]
  ------------------
 8904|      0|    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
 8905|      0|    return false;
 8906|      0|  }
 8907|  87.9k|  if (components.pathname_start == url_components::omitted) {
  ------------------
  |  Branch (8907:7): [True: 0, False: 87.9k]
  ------------------
 8908|      0|    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
 8909|      0|    return false;
 8910|      0|  }
 8911|       |
 8912|  87.9k|  if (components.protocol_end > buffer.size()) {
  ------------------
  |  Branch (8912:7): [True: 0, False: 87.9k]
  ------------------
 8913|      0|    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
 8914|      0|    return false;
 8915|      0|  }
 8916|  87.9k|  if (components.username_end > buffer.size()) {
  ------------------
  |  Branch (8916:7): [True: 0, False: 87.9k]
  ------------------
 8917|      0|    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
 8918|      0|    return false;
 8919|      0|  }
 8920|  87.9k|  if (components.host_start > buffer.size()) {
  ------------------
  |  Branch (8920:7): [True: 0, False: 87.9k]
  ------------------
 8921|      0|    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
 8922|      0|    return false;
 8923|      0|  }
 8924|  87.9k|  if (components.host_end > buffer.size()) {
  ------------------
  |  Branch (8924:7): [True: 0, False: 87.9k]
  ------------------
 8925|      0|    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
 8926|      0|    return false;
 8927|      0|  }
 8928|  87.9k|  if (components.pathname_start > buffer.size()) {
  ------------------
  |  Branch (8928:7): [True: 0, False: 87.9k]
  ------------------
 8929|      0|    ada_log("url_aggregator::validate pathname_start overflow \n",
 8930|      0|            to_diagram());
 8931|      0|    return false;
 8932|      0|  }
 8933|       |
 8934|  87.9k|  if (components.protocol_end > 0) {
  ------------------
  |  Branch (8934:7): [True: 87.9k, False: 0]
  ------------------
 8935|  87.9k|    if (buffer[components.protocol_end - 1] != ':') {
  ------------------
  |  Branch (8935:9): [True: 0, False: 87.9k]
  ------------------
 8936|      0|      ada_log(
 8937|      0|          "url_aggregator::validate missing : at the end of the protocol \n",
 8938|      0|          to_diagram());
 8939|      0|      return false;
 8940|      0|    }
 8941|  87.9k|  }
 8942|       |
 8943|  87.9k|  if (components.username_end != buffer.size() &&
  ------------------
  |  Branch (8943:7): [True: 87.0k, False: 874]
  ------------------
 8944|  87.0k|      components.username_end > components.protocol_end + 2) {
  ------------------
  |  Branch (8944:7): [True: 37.4k, False: 49.6k]
  ------------------
 8945|  37.4k|    if (buffer[components.username_end] != ':' &&
  ------------------
  |  Branch (8945:9): [True: 2.70k, False: 34.7k]
  ------------------
 8946|  2.70k|        buffer[components.username_end] != '@') {
  ------------------
  |  Branch (8946:9): [True: 0, False: 2.70k]
  ------------------
 8947|      0|      ada_log(
 8948|      0|          "url_aggregator::validate missing : or @ at the end of the username "
 8949|      0|          "\n",
 8950|      0|          to_diagram());
 8951|      0|      return false;
 8952|      0|    }
 8953|  37.4k|  }
 8954|       |
 8955|  87.9k|  if (components.host_start != buffer.size()) {
  ------------------
  |  Branch (8955:7): [True: 87.0k, False: 874]
  ------------------
 8956|  87.0k|    if (components.host_start > components.username_end) {
  ------------------
  |  Branch (8956:9): [True: 35.8k, False: 51.2k]
  ------------------
 8957|  35.8k|      if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8957:11): [True: 0, False: 35.8k]
  ------------------
 8958|      0|        ada_log(
 8959|      0|            "url_aggregator::validate missing @ at the end of the password \n",
 8960|      0|            to_diagram());
 8961|      0|        return false;
 8962|      0|      }
 8963|  51.2k|    } else if (components.host_start == components.username_end &&
  ------------------
  |  Branch (8963:16): [True: 51.2k, False: 0]
  ------------------
 8964|  51.2k|               components.host_end > components.host_start) {
  ------------------
  |  Branch (8964:16): [True: 43.6k, False: 7.62k]
  ------------------
 8965|  43.6k|      if (components.host_start == components.protocol_end + 2) {
  ------------------
  |  Branch (8965:11): [True: 40.8k, False: 2.70k]
  ------------------
 8966|  40.8k|        if (buffer[components.protocol_end] != '/' ||
  ------------------
  |  Branch (8966:13): [True: 0, False: 40.8k]
  ------------------
 8967|  40.8k|            buffer[components.protocol_end + 1] != '/') {
  ------------------
  |  Branch (8967:13): [True: 0, False: 40.8k]
  ------------------
 8968|      0|          ada_log(
 8969|      0|              "url_aggregator::validate missing // between protocol and host "
 8970|      0|              "\n",
 8971|      0|              to_diagram());
 8972|      0|          return false;
 8973|      0|        }
 8974|  40.8k|      } else {
 8975|  2.70k|        if (components.host_start > components.protocol_end &&
  ------------------
  |  Branch (8975:13): [True: 2.70k, False: 0]
  ------------------
 8976|  2.70k|            buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8976:13): [True: 0, False: 2.70k]
  ------------------
 8977|      0|          ada_log(
 8978|      0|              "url_aggregator::validate missing @ at the end of the username "
 8979|      0|              "\n",
 8980|      0|              to_diagram());
 8981|      0|          return false;
 8982|      0|        }
 8983|  2.70k|      }
 8984|  43.6k|    } else {
 8985|  7.62k|      if (components.host_end != components.host_start) {
  ------------------
  |  Branch (8985:11): [True: 0, False: 7.62k]
  ------------------
 8986|      0|        ada_log("url_aggregator::validate expected omitted host \n",
 8987|      0|                to_diagram());
 8988|      0|        return false;
 8989|      0|      }
 8990|  7.62k|    }
 8991|  87.0k|  }
 8992|  87.9k|  if (components.host_end != buffer.size() &&
  ------------------
  |  Branch (8992:7): [True: 86.3k, False: 1.54k]
  ------------------
 8993|  86.3k|      components.pathname_start > components.host_end) {
  ------------------
  |  Branch (8993:7): [True: 29.3k, False: 56.9k]
  ------------------
 8994|  29.3k|    if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8994:9): [True: 2.67k, False: 26.7k]
  ------------------
 8995|  2.67k|        buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (8995:9): [True: 135, False: 2.53k]
  ------------------
 8996|    135|        buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (8996:9): [True: 135, False: 0]
  ------------------
 8997|    135|      if (components.pathname_start + 1 >= buffer.size() ||
  ------------------
  |  Branch (8997:11): [True: 0, False: 135]
  ------------------
 8998|    135|          buffer[components.pathname_start] != '/' ||
  ------------------
  |  Branch (8998:11): [True: 0, False: 135]
  ------------------
 8999|    135|          buffer[components.pathname_start + 1] != '/') {
  ------------------
  |  Branch (8999:11): [True: 0, False: 135]
  ------------------
 9000|      0|        ada_log(
 9001|      0|            "url_aggregator::validate expected the path to begin with // \n",
 9002|      0|            to_diagram());
 9003|      0|        return false;
 9004|      0|      }
 9005|  29.2k|    } else if (buffer[components.host_end] != ':') {
  ------------------
  |  Branch (9005:16): [True: 0, False: 29.2k]
  ------------------
 9006|      0|      ada_log("url_aggregator::validate missing : at the port \n",
 9007|      0|              to_diagram());
 9008|      0|      return false;
 9009|      0|    }
 9010|  29.3k|  }
 9011|  87.9k|  if (components.pathname_start != buffer.size() &&
  ------------------
  |  Branch (9011:7): [True: 86.2k, False: 1.65k]
  ------------------
 9012|  86.2k|      components.pathname_start < components.search_start &&
  ------------------
  |  Branch (9012:7): [True: 85.8k, False: 358]
  ------------------
 9013|  85.8k|      components.pathname_start < components.hash_start && !has_opaque_path) {
  ------------------
  |  Branch (9013:7): [True: 85.6k, False: 238]
  |  Branch (9013:60): [True: 82.8k, False: 2.80k]
  ------------------
 9014|  82.8k|    if (buffer[components.pathname_start] != '/') {
  ------------------
  |  Branch (9014:9): [True: 0, False: 82.8k]
  ------------------
 9015|      0|      ada_log("url_aggregator::validate missing / at the path \n",
 9016|      0|              to_diagram());
 9017|      0|      return false;
 9018|      0|    }
 9019|  82.8k|  }
 9020|  87.9k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9020:7): [True: 60.1k, False: 27.7k]
  ------------------
 9021|  60.1k|    if (buffer[components.search_start] != '?') {
  ------------------
  |  Branch (9021:9): [True: 0, False: 60.1k]
  ------------------
 9022|      0|      ada_log("url_aggregator::validate missing ? at the search \n",
 9023|      0|              to_diagram());
 9024|      0|      return false;
 9025|      0|    }
 9026|  60.1k|  }
 9027|  87.9k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9027:7): [True: 44.4k, False: 43.4k]
  ------------------
 9028|  44.4k|    if (buffer[components.hash_start] != '#') {
  ------------------
  |  Branch (9028:9): [True: 0, False: 44.4k]
  ------------------
 9029|      0|      ada_log("url_aggregator::validate missing # at the hash \n",
 9030|      0|              to_diagram());
 9031|      0|      return false;
 9032|      0|    }
 9033|  44.4k|  }
 9034|       |
 9035|  87.9k|  return true;
 9036|  87.9k|}
_ZNK3ada14url_components24check_offset_consistencyEv:
 7385|  87.9k|    const noexcept {
 7386|       |  /**
 7387|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 7388|       |   *       |     |    |          | ^^^^|       |   |
 7389|       |   *       |     |    |          | |   |       |   `----- hash_start
 7390|       |   *       |     |    |          | |   |       `--------- search_start
 7391|       |   *       |     |    |          | |   `----------------- pathname_start
 7392|       |   *       |     |    |          | `--------------------- port
 7393|       |   *       |     |    |          `----------------------- host_end
 7394|       |   *       |     |    `---------------------------------- host_start
 7395|       |   *       |     `--------------------------------------- username_end
 7396|       |   *       `--------------------------------------------- protocol_end
 7397|       |   */
 7398|       |  // These conditions can be made more strict.
 7399|  87.9k|  if (protocol_end == url_components::omitted) {
  ------------------
  |  Branch (7399:7): [True: 0, False: 87.9k]
  ------------------
 7400|      0|    return false;
 7401|      0|  }
 7402|  87.9k|  uint32_t index = protocol_end;
 7403|       |
 7404|  87.9k|  if (username_end == url_components::omitted) {
  ------------------
  |  Branch (7404:7): [True: 0, False: 87.9k]
  ------------------
 7405|      0|    return false;
 7406|      0|  }
 7407|  87.9k|  if (username_end < index) {
  ------------------
  |  Branch (7407:7): [True: 0, False: 87.9k]
  ------------------
 7408|      0|    return false;
 7409|      0|  }
 7410|  87.9k|  index = username_end;
 7411|       |
 7412|  87.9k|  if (host_start == url_components::omitted) {
  ------------------
  |  Branch (7412:7): [True: 0, False: 87.9k]
  ------------------
 7413|      0|    return false;
 7414|      0|  }
 7415|  87.9k|  if (host_start < index) {
  ------------------
  |  Branch (7415:7): [True: 0, False: 87.9k]
  ------------------
 7416|      0|    return false;
 7417|      0|  }
 7418|  87.9k|  index = host_start;
 7419|       |
 7420|  87.9k|  if (port != url_components::omitted) {
  ------------------
  |  Branch (7420:7): [True: 29.2k, False: 58.6k]
  ------------------
 7421|  29.2k|    if (port > 0xffff) {
  ------------------
  |  Branch (7421:9): [True: 0, False: 29.2k]
  ------------------
 7422|      0|      return false;
 7423|      0|    }
 7424|  29.2k|    uint32_t port_length = helpers::fast_digit_count(port) + 1;
 7425|  29.2k|    if (index + port_length < index) {
  ------------------
  |  Branch (7425:9): [True: 0, False: 29.2k]
  ------------------
 7426|      0|      return false;
 7427|      0|    }
 7428|  29.2k|    index += port_length;
 7429|  29.2k|  }
 7430|       |
 7431|  87.9k|  if (pathname_start == url_components::omitted) {
  ------------------
  |  Branch (7431:7): [True: 0, False: 87.9k]
  ------------------
 7432|      0|    return false;
 7433|      0|  }
 7434|  87.9k|  if (pathname_start < index) {
  ------------------
  |  Branch (7434:7): [True: 0, False: 87.9k]
  ------------------
 7435|      0|    return false;
 7436|      0|  }
 7437|  87.9k|  index = pathname_start;
 7438|       |
 7439|  87.9k|  if (search_start != url_components::omitted) {
  ------------------
  |  Branch (7439:7): [True: 60.1k, False: 27.7k]
  ------------------
 7440|  60.1k|    if (search_start < index) {
  ------------------
  |  Branch (7440:9): [True: 0, False: 60.1k]
  ------------------
 7441|      0|      return false;
 7442|      0|    }
 7443|  60.1k|    index = search_start;
 7444|  60.1k|  }
 7445|       |
 7446|  87.9k|  if (hash_start != url_components::omitted) {
  ------------------
  |  Branch (7446:7): [True: 44.4k, False: 43.4k]
  ------------------
 7447|  44.4k|    if (hash_start < index) {
  ------------------
  |  Branch (7447:9): [True: 0, False: 44.4k]
  ------------------
 7448|      0|      return false;
 7449|      0|    }
 7450|  44.4k|  }
 7451|       |
 7452|  87.9k|  return true;
 7453|  87.9k|}
_ZN3ada7helpers16fast_digit_countEj:
 1816|  31.3k|inline int fast_digit_count(uint32_t x) noexcept {
 1817|  31.3k|  auto int_log2 = [](uint32_t z) -> int {
 1818|  31.3k|    return 31 - ada::helpers::leading_zeroes(z | 1);
 1819|  31.3k|  };
 1820|       |  // Compiles to very few instructions. Note that the
 1821|       |  // table is static and thus effectively a constant.
 1822|       |  // We leave it inside the function because it is meaningless
 1823|       |  // outside of it (this comes at no performance cost).
 1824|  31.3k|  const static uint64_t table[] = {
 1825|  31.3k|      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
 1826|  31.3k|      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
 1827|  31.3k|      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
 1828|  31.3k|      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
 1829|  31.3k|      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
 1830|  31.3k|      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
 1831|  31.3k|      42949672960, 42949672960};
 1832|  31.3k|  return int((x + table[int_log2(x)]) >> 32);
 1833|  31.3k|}
_ZZN3ada7helpers16fast_digit_countEjENKUljE_clEj:
 1817|  31.3k|  auto int_log2 = [](uint32_t z) -> int {
 1818|  31.3k|    return 31 - ada::helpers::leading_zeroes(z | 1);
 1819|  31.3k|  };
_ZN3ada3urlC2EOS0_:
 4848|  53.9k|  url(url&& u) noexcept = default;
_ZN3ada14url_aggregatorC2EOS0_:
 7504|   108k|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZNK3ada3url19get_pathname_lengthEv:
 7114|  23.1k|[[nodiscard]] size_t url::get_pathname_length() const noexcept {
 7115|  23.1k|  return path.size();
 7116|  23.1k|}
_ZNK3ada3url18has_empty_hostnameEv:
 7101|  23.1k|[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
 7102|  23.1k|  if (!host.has_value()) {
  ------------------
  |  Branch (7102:7): [True: 2.42k, False: 20.6k]
  ------------------
 7103|  2.42k|    return false;
 7104|  2.42k|  }
 7105|  20.6k|  return host->empty();
 7106|  23.1k|}
_ZNK3ada3url12has_hostnameEv:
 7107|  23.1k|[[nodiscard]] inline bool url::has_hostname() const noexcept {
 7108|  23.1k|  return host.has_value();
 7109|  23.1k|}
_ZNK3ada3url8has_portEv:
 7095|  23.1k|[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
 7096|  23.1k|  return port.has_value();
 7097|  23.1k|}
_ZNK3ada3url14get_componentsEv:
 7123|  23.1k|    const {
 7124|  23.1k|  url_components out{};
 7125|       |
 7126|       |  // protocol ends with ':'. for example: "https:"
 7127|  23.1k|  out.protocol_end = uint32_t(get_protocol().size());
 7128|       |
 7129|       |  // Trailing index is always the next character of the current one.
 7130|       |  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
 7131|  23.1k|  size_t running_index = out.protocol_end;
 7132|       |
 7133|  23.1k|  if (host.has_value()) {
  ------------------
  |  Branch (7133:7): [True: 20.6k, False: 2.42k]
  ------------------
 7134|       |    // 2 characters for "//" and 1 character for starting index
 7135|  20.6k|    out.host_start = out.protocol_end + 2;
 7136|       |
 7137|  20.6k|    if (has_credentials()) {
  ------------------
  |  Branch (7137:9): [True: 14.0k, False: 6.67k]
  ------------------
 7138|  14.0k|      out.username_end = uint32_t(out.host_start + username.size());
 7139|       |
 7140|  14.0k|      out.host_start += uint32_t(username.size());
 7141|       |
 7142|  14.0k|      if (!password.empty()) {
  ------------------
  |  Branch (7142:11): [True: 13.6k, False: 410]
  ------------------
 7143|  13.6k|        out.host_start += uint32_t(password.size() + 1);
 7144|  13.6k|      }
 7145|       |
 7146|  14.0k|      out.host_end = uint32_t(out.host_start + host->size());
 7147|  14.0k|    } else {
 7148|  6.67k|      out.username_end = out.host_start;
 7149|       |
 7150|       |      // Host does not start with "@" if it does not include credentials.
 7151|  6.67k|      out.host_end = uint32_t(out.host_start + host->size()) - 1;
 7152|  6.67k|    }
 7153|       |
 7154|  20.6k|    running_index = out.host_end + 1;
 7155|  20.6k|  } else {
 7156|       |    // Update host start and end date to the same index, since it does not
 7157|       |    // exist.
 7158|  2.42k|    out.host_start = out.protocol_end;
 7159|  2.42k|    out.host_end = out.host_start;
 7160|       |
 7161|  2.42k|    if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7161:9): [True: 693, False: 1.73k]
  |  Branch (7161:29): [True: 57, False: 636]
  ------------------
 7162|       |      // If url's host is null, url does not have an opaque path, url's path's
 7163|       |      // size is greater than 1, and url's path[0] is the empty string, then
 7164|       |      // append U+002F (/) followed by U+002E (.) to output.
 7165|     57|      running_index = out.protocol_end + 2;
 7166|  2.37k|    } else {
 7167|  2.37k|      running_index = out.protocol_end;
 7168|  2.37k|    }
 7169|  2.42k|  }
 7170|       |
 7171|  23.1k|  if (port.has_value()) {
  ------------------
  |  Branch (7171:7): [True: 2.06k, False: 21.0k]
  ------------------
 7172|  2.06k|    out.port = *port;
 7173|  2.06k|    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
 7174|  2.06k|  }
 7175|       |
 7176|  23.1k|  out.pathname_start = uint32_t(running_index);
 7177|       |
 7178|  23.1k|  running_index += path.size();
 7179|       |
 7180|  23.1k|  if (query.has_value()) {
  ------------------
  |  Branch (7180:7): [True: 14.8k, False: 8.26k]
  ------------------
 7181|  14.8k|    out.search_start = uint32_t(running_index);
 7182|  14.8k|    running_index += get_search().size();
 7183|  14.8k|    if (get_search().empty()) {
  ------------------
  |  Branch (7183:9): [True: 403, False: 14.4k]
  ------------------
 7184|    403|      running_index++;
 7185|    403|    }
 7186|  14.8k|  }
 7187|       |
 7188|  23.1k|  if (hash.has_value()) {
  ------------------
  |  Branch (7188:7): [True: 14.8k, False: 8.27k]
  ------------------
 7189|  14.8k|    out.hash_start = uint32_t(running_index);
 7190|  14.8k|  }
 7191|       |
 7192|  23.1k|  return out;
 7193|  23.1k|}
_ZNK3ada14url_aggregator19get_pathname_lengthEv:
 8094|   115k|url_aggregator::get_pathname_length() const noexcept {
 8095|   115k|  ada_log("url_aggregator::get_pathname_length");
 8096|   115k|  uint32_t ending_index = uint32_t(buffer.size());
 8097|   115k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8097:7): [True: 18.7k, False: 97.0k]
  ------------------
 8098|  18.7k|    ending_index = components.search_start;
 8099|  97.0k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8099:14): [True: 1.00k, False: 96.0k]
  ------------------
 8100|  1.00k|    ending_index = components.hash_start;
 8101|  1.00k|  }
 8102|   115k|  return ending_index - components.pathname_start;
 8103|   115k|}
_ZN3ada14url_aggregator10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7814|  2.99k|  ada_really_inline size_t parse_port(std::string_view view) override {
 7815|  2.99k|    return this->parse_port(view, false);
 7816|  2.99k|  }
_ZNK3ada14url_aggregator8has_hashEv:
 8650|  52.3k|[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
 8651|  52.3k|  ada_log("url_aggregator::has_hash");
 8652|  52.3k|  return components.hash_start != url_components::omitted;
 8653|  52.3k|}
_ZNK3ada14url_aggregator10has_searchEv:
 8655|  65.1k|[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
 8656|  65.1k|  ada_log("url_aggregator::has_search");
 8657|  65.1k|  return components.search_start != url_components::omitted;
 8658|  65.1k|}
_ZN3ada3url10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 5225|  2.99k|  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
 5226|  2.99k|    return this->parse_port(view, false);
 5227|  2.99k|  }
_ZNK3ada3url8has_hashEv:
 7232|  23.1k|[[nodiscard]] constexpr bool url::has_hash() const noexcept {
 7233|  23.1k|  return hash.has_value();
 7234|  23.1k|}
_ZNK3ada3url10has_searchEv:
 7236|  42.7k|[[nodiscard]] constexpr bool url::has_search() const noexcept {
 7237|  42.7k|  return query.has_value();
 7238|  42.7k|}
_ZN3ada7helpers6concatIJNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEPKcS8_EEES8_DpT_:
 1790|  20.9k|std::string concat(Args... args) {
 1791|  20.9k|  std::string answer;
 1792|  20.9k|  inner_concat(answer, args...);
 1793|  20.9k|  return answer;
 1794|  20.9k|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJPKcS8_EEEvRS8_T_DpT0_:
 1779|  20.9k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1780|  20.9k|  buffer.append(t);
 1781|  20.9k|  return inner_concat(buffer, args...);
 1782|  20.9k|}
_ZN3ada7helpers6concatIJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKcEEENS2_12basic_stringIcS5_NS2_9allocatorIcEEEEDpT_:
 1790|   169k|std::string concat(Args... args) {
 1791|   169k|  std::string answer;
 1792|   169k|  inner_concat(answer, args...);
 1793|   169k|  return answer;
 1794|   169k|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJPKcEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_DpT0_:
 1779|   169k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1780|   169k|  buffer.append(t);
 1781|   169k|  return inner_concat(buffer, args...);
 1782|   169k|}
_ZN3ada7helpers12inner_concatIPKcJEEEvRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEET_:
 1771|   200k|inline void inner_concat(std::string& buffer, T t) {
 1772|   200k|  buffer.append(t);
 1773|   200k|}
_ZN3ada7helpers6concatIJNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEPKcEEES8_DpT_:
 1790|  31.3k|std::string concat(Args... args) {
 1791|  31.3k|  std::string answer;
 1792|  31.3k|  inner_concat(answer, args...);
 1793|  31.3k|  return answer;
 1794|  31.3k|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJPKcEEEvRS8_T_DpT0_:
 1779|  31.3k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1780|  31.3k|  buffer.append(t);
 1781|  31.3k|  return inner_concat(buffer, args...);
 1782|  31.3k|}
_ZN3ada3url16update_base_portENSt3__18optionalItEE:
 7224|  9.29k|inline void url::update_base_port(std::optional<uint16_t> input) {
 7225|  9.29k|  port = input;
 7226|  9.29k|}
_ZNK3ada8url_base16get_special_portEv:
 7062|  10.1k|[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
 7063|  10.1k|  return ada::scheme::get_special_port(type);
 7064|  10.1k|}
_ZN3ada3url10set_schemeEONSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 7242|  12.6k|inline void url::set_scheme(std::string&& new_scheme) noexcept {
 7243|  12.6k|  type = ada::scheme::get_scheme_type(new_scheme);
 7244|       |  // We only move the 'scheme' if it is non-special.
 7245|  12.6k|  if (!is_special()) {
  ------------------
  |  Branch (7245:7): [True: 8.08k, False: 4.61k]
  ------------------
 7246|  8.08k|    non_special_scheme = std::move(new_scheme);
 7247|  8.08k|  }
 7248|  12.6k|}
_ZN3ada7helpers6concatIJPKcNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEENS4_12basic_stringIcS7_NS4_9allocatorIcEEEEDpT_:
 1790|     49|std::string concat(Args... args) {
 1791|     49|  std::string answer;
 1792|     49|  inner_concat(answer, args...);
 1793|     49|  return answer;
 1794|     49|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEEvRNS4_12basic_stringIcS7_NS4_9allocatorIcEEEET_DpT0_:
 1779|  32.2k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1780|  32.2k|  buffer.append(t);
 1781|  32.2k|  return inner_concat(buffer, args...);
 1782|  32.2k|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_:
 1771|  32.2k|inline void inner_concat(std::string& buffer, T t) {
 1772|  32.2k|  buffer.append(t);
 1773|  32.2k|}
_ZN3ada7helpers6concatIJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKcS6_EEENS2_12basic_stringIcS5_NS2_9allocatorIcEEEEDpT_:
 1790|  32.2k|std::string concat(Args... args) {
 1791|  32.2k|  std::string answer;
 1792|  32.2k|  inner_concat(answer, args...);
 1793|  32.2k|  return answer;
 1794|  32.2k|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJPKcS6_EEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_DpT0_:
 1779|  32.2k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1780|  32.2k|  buffer.append(t);
 1781|  32.2k|  return inner_concat(buffer, args...);
 1782|  32.2k|}

_Z20is_valid_utf8_stringPKcm:
   11|  43.0k|bool is_valid_utf8_string(const char* buf, size_t len) {
   12|  43.0k|  const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
   13|  43.0k|  uint64_t pos = 0;
   14|  43.0k|  uint32_t code_point = 0;
   15|  94.2k|  while (pos < len) {
  ------------------
  |  Branch (15:10): [True: 80.5k, False: 13.6k]
  ------------------
   16|  80.5k|    uint64_t next_pos = pos + 16;
   17|  80.5k|    if (next_pos <= len) {  // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (17:9): [True: 44.9k, False: 35.5k]
  ------------------
   18|       |                            // they are ascii
   19|  44.9k|      uint64_t v1;
   20|  44.9k|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
   21|  44.9k|      uint64_t v2;
   22|  44.9k|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   23|  44.9k|      uint64_t v{v1 | v2};
   24|  44.9k|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (24:11): [True: 13.6k, False: 31.2k]
  ------------------
   25|  13.6k|        pos = next_pos;
   26|  13.6k|        continue;
   27|  13.6k|      }
   28|  44.9k|    }
   29|  66.8k|    unsigned char byte = data[pos];
   30|   223k|    while (byte < 0b10000000) {
  ------------------
  |  Branch (30:12): [True: 179k, False: 44.4k]
  ------------------
   31|   179k|      if (++pos == len) {
  ------------------
  |  Branch (31:11): [True: 22.4k, False: 156k]
  ------------------
   32|  22.4k|        return true;
   33|  22.4k|      }
   34|   156k|      byte = data[pos];
   35|   156k|    }
   36|       |
   37|  44.4k|    if ((byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (37:9): [True: 6.82k, False: 37.5k]
  ------------------
   38|  6.82k|      next_pos = pos + 2;
   39|  6.82k|      if (next_pos > len) {
  ------------------
  |  Branch (39:11): [True: 360, False: 6.46k]
  ------------------
   40|    360|        return false;
   41|    360|      }
   42|  6.46k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (42:11): [True: 909, False: 5.55k]
  ------------------
   43|    909|        return false;
   44|    909|      }
   45|  5.55k|      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   46|  5.55k|      if ((code_point < 0x80) || (0x7ff < code_point)) {
  ------------------
  |  Branch (46:11): [True: 60, False: 5.49k]
  |  Branch (46:34): [True: 0, False: 5.49k]
  ------------------
   47|     60|        return false;
   48|     60|      }
   49|  37.5k|    } else if ((byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (49:16): [True: 31.6k, False: 5.89k]
  ------------------
   50|  31.6k|      next_pos = pos + 3;
   51|  31.6k|      if (next_pos > len) {
  ------------------
  |  Branch (51:11): [True: 192, False: 31.4k]
  ------------------
   52|    192|        return false;
   53|    192|      }
   54|  31.4k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (54:11): [True: 382, False: 31.1k]
  ------------------
   55|    382|        return false;
   56|    382|      }
   57|  31.1k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (57:11): [True: 88, False: 31.0k]
  ------------------
   58|     88|        return false;
   59|     88|      }
   60|  31.0k|      code_point = (byte & 0b00001111) << 12 |
   61|  31.0k|                   (data[pos + 1] & 0b00111111) << 6 |
   62|  31.0k|                   (data[pos + 2] & 0b00111111);
   63|  31.0k|      if ((code_point < 0x800) || (0xffff < code_point) ||
  ------------------
  |  Branch (63:11): [True: 25, False: 30.9k]
  |  Branch (63:35): [True: 0, False: 30.9k]
  ------------------
   64|  30.9k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (64:12): [True: 2.42k, False: 28.5k]
  |  Branch (64:35): [True: 13, False: 2.41k]
  ------------------
   65|     38|        return false;
   66|     38|      }
   67|  31.0k|    } else if ((byte & 0b11111000) == 0b11110000) {  // 0b11110000
  ------------------
  |  Branch (67:16): [True: 1.51k, False: 4.38k]
  ------------------
   68|  1.51k|      next_pos = pos + 4;
   69|  1.51k|      if (next_pos > len) {
  ------------------
  |  Branch (69:11): [True: 134, False: 1.37k]
  ------------------
   70|    134|        return false;
   71|    134|      }
   72|  1.37k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (72:11): [True: 217, False: 1.15k]
  ------------------
   73|    217|        return false;
   74|    217|      }
   75|  1.15k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (75:11): [True: 80, False: 1.07k]
  ------------------
   76|     80|        return false;
   77|     80|      }
   78|  1.07k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (78:11): [True: 51, False: 1.02k]
  ------------------
   79|     51|        return false;
   80|     51|      }
   81|  1.02k|      code_point =
   82|  1.02k|          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
   83|  1.02k|          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
   84|  1.02k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (84:11): [True: 21, False: 1.00k]
  |  Branch (84:35): [True: 21, False: 986]
  ------------------
   85|     42|        return false;
   86|     42|      }
   87|  4.38k|    } else {
   88|  4.38k|      return false;
   89|  4.38k|    }
   90|  37.4k|    pos = next_pos;
   91|  37.4k|  }
   92|  13.6k|  return true;
   93|  43.0k|}
LLVMFuzzerTestOneInput:
  153|  15.3k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  154|  15.3k|  FuzzedDataProvider fdp(data, size);
  155|  15.3k|  std::string source = fdp.ConsumeRandomLengthString(256);
  156|  15.3k|  std::string base = fdp.ConsumeRandomLengthString(256);
  157|       |
  158|       |  // volatile forces the compiler to store the results without undue
  159|       |  // optimizations
  160|  15.3k|  volatile size_t length = 0;
  161|       |
  162|  15.3k|  auto parse_url = ada::parse<ada::url>(source);
  163|  15.3k|  auto parse_url_aggregator = ada::parse<ada::url_aggregator>(source);
  164|       |
  165|  15.3k|  if (is_valid_utf8_string(source.data(), source.length())) {
  ------------------
  |  Branch (165:7): [True: 12.2k, False: 3.08k]
  ------------------
  166|  12.2k|    if (parse_url.has_value() ^ parse_url_aggregator.has_value()) {
  ------------------
  |  Branch (166:9): [True: 0, False: 12.2k]
  ------------------
  167|      0|      printf("Source used to parse: %s", source.c_str());
  168|      0|      abort();
  169|      0|    }
  170|  12.2k|  }
  171|       |
  172|  15.3k|  if (parse_url) {
  ------------------
  |  Branch (172:7): [True: 4.63k, False: 10.7k]
  ------------------
  173|  4.63k|    length += parse_url->get_href().size();
  174|  4.63k|    length += parse_url->get_origin().size();
  175|  4.63k|  }
  176|       |
  177|  15.3k|  if (parse_url_aggregator) {
  ------------------
  |  Branch (177:7): [True: 4.63k, False: 10.7k]
  ------------------
  178|  4.63k|    length += parse_url_aggregator->get_href().size();
  179|  4.63k|    length += parse_url_aggregator->get_origin().size();
  180|       |
  181|  4.63k|    volatile bool is_parse_url_aggregator_output_valid = false;
  182|  4.63k|    is_parse_url_aggregator_output_valid = parse_url_aggregator->validate();
  183|       |
  184|  4.63k|    assert(parse_url->get_protocol() == parse_url_aggregator->get_protocol());
  ------------------
  |  Branch (184:5): [True: 4.63k, False: 0]
  ------------------
  185|  4.63k|    assert(parse_url->get_href() == parse_url_aggregator->get_href());
  ------------------
  |  Branch (185:5): [True: 4.63k, False: 0]
  ------------------
  186|  4.63k|    assert(std::string(parse_url->get_hostname()) ==
  ------------------
  |  Branch (186:5): [True: 4.63k, False: 0]
  ------------------
  187|  4.63k|           std::string(parse_url_aggregator->get_hostname()));
  188|  4.63k|    assert(std::string(parse_url->get_pathname()) ==
  ------------------
  |  Branch (188:5): [True: 4.63k, False: 0]
  ------------------
  189|  4.63k|           std::string(parse_url_aggregator->get_pathname()));
  190|  4.63k|    assert(std::string(parse_url->get_search()) ==
  ------------------
  |  Branch (190:5): [True: 4.63k, False: 0]
  ------------------
  191|  4.63k|           std::string(parse_url_aggregator->get_search()));
  192|  4.63k|    assert(std::string(parse_url->get_hash()) ==
  ------------------
  |  Branch (192:5): [True: 4.63k, False: 0]
  ------------------
  193|  4.63k|           std::string(parse_url_aggregator->get_hash()));
  194|  4.63k|    assert(std::string(parse_url->get_port()) ==
  ------------------
  |  Branch (194:5): [True: 4.63k, False: 0]
  ------------------
  195|  4.63k|           std::string(parse_url_aggregator->get_port()));
  196|  4.63k|    assert(parse_url->get_username() ==
  ------------------
  |  Branch (196:5): [True: 4.63k, False: 0]
  ------------------
  197|  4.63k|           std::string(parse_url_aggregator->get_username()));
  198|  4.63k|    assert(parse_url->get_password() ==
  ------------------
  |  Branch (198:5): [True: 4.63k, False: 0]
  ------------------
  199|  4.63k|           std::string(parse_url_aggregator->get_password()));
  200|  4.63k|    assert(std::string(parse_url->get_host()) ==
  ------------------
  |  Branch (200:5): [True: 4.63k, False: 0]
  ------------------
  201|  4.63k|           std::string(parse_url_aggregator->get_host()));
  202|       |
  203|       |    // Exercise all predicates on both types
  204|  4.63k|    exercise_url_predicates(*parse_url);
  205|  4.63k|    exercise_aggregator_predicates(*parse_url_aggregator);
  206|       |
  207|       |    // Test set_href consistency
  208|  4.63k|    parse_url->set_href(source);
  209|  4.63k|    parse_url_aggregator->set_href(source);
  210|  4.63k|    assert(parse_url->get_href() == parse_url_aggregator->get_href());
  ------------------
  |  Branch (210:5): [True: 4.63k, False: 0]
  ------------------
  211|  4.63k|  }
  212|       |
  213|       |  /**
  214|       |   * Test copy and move semantics
  215|       |   */
  216|  15.3k|  if (parse_url) {
  ------------------
  |  Branch (216:7): [True: 4.63k, False: 10.7k]
  ------------------
  217|       |    // Copy constructor
  218|  4.63k|    ada::url copied_url = *parse_url;
  219|  4.63k|    assert(copied_url.get_href() == parse_url->get_href());
  ------------------
  |  Branch (219:5): [True: 4.63k, False: 0]
  ------------------
  220|       |
  221|       |    // Copy assignment
  222|  4.63k|    ada::url assigned_url;
  223|  4.63k|    assigned_url = *parse_url;
  224|  4.63k|    assert(assigned_url.get_href() == parse_url->get_href());
  ------------------
  |  Branch (224:5): [True: 4.63k, False: 0]
  ------------------
  225|       |
  226|       |    // Move constructor
  227|  4.63k|    ada::url moved_url = std::move(copied_url);
  228|  4.63k|    assert(moved_url.get_href() == parse_url->get_href());
  ------------------
  |  Branch (228:5): [True: 4.63k, False: 0]
  ------------------
  229|  4.63k|  }
  230|       |
  231|  15.3k|  if (parse_url_aggregator) {
  ------------------
  |  Branch (231:7): [True: 4.63k, False: 10.7k]
  ------------------
  232|       |    // Copy constructor
  233|  4.63k|    ada::url_aggregator copied_agg = *parse_url_aggregator;
  234|  4.63k|    assert(std::string(copied_agg.get_href()) ==
  ------------------
  |  Branch (234:5): [True: 4.63k, False: 0]
  ------------------
  235|  4.63k|           std::string(parse_url_aggregator->get_href()));
  236|       |
  237|       |    // Copy assignment
  238|  4.63k|    ada::url_aggregator assigned_agg;
  239|  4.63k|    assigned_agg = *parse_url_aggregator;
  240|  4.63k|    assert(std::string(assigned_agg.get_href()) ==
  ------------------
  |  Branch (240:5): [True: 4.63k, False: 0]
  ------------------
  241|  4.63k|           std::string(parse_url_aggregator->get_href()));
  242|       |
  243|       |    // Move constructor
  244|  4.63k|    ada::url_aggregator moved_agg = std::move(copied_agg);
  245|  4.63k|    assert(std::string(moved_agg.get_href()) ==
  ------------------
  |  Branch (245:5): [True: 4.63k, False: 0]
  ------------------
  246|  4.63k|           std::string(parse_url_aggregator->get_href()));
  247|       |
  248|       |    // Move assignment
  249|  4.63k|    ada::url_aggregator move_assigned_agg;
  250|  4.63k|    move_assigned_agg = std::move(assigned_agg);
  251|  4.63k|    assert(std::string(move_assigned_agg.get_href()) ==
  ------------------
  |  Branch (251:5): [True: 4.63k, False: 0]
  ------------------
  252|  4.63k|           std::string(parse_url_aggregator->get_href()));
  253|  4.63k|  }
  254|       |
  255|       |  /**
  256|       |   * ada::parse<ada::url>
  257|       |   */
  258|  15.3k|  auto out_url = ada::parse<ada::url>("https://www.ada-url.com");
  259|       |
  260|  15.3k|  if (out_url) {
  ------------------
  |  Branch (260:7): [True: 15.3k, False: 0]
  ------------------
  261|  15.3k|    out_url->set_protocol(source);
  262|  15.3k|    out_url->set_username(source);
  263|  15.3k|    out_url->set_password(source);
  264|  15.3k|    out_url->set_hostname(source);
  265|  15.3k|    out_url->set_host(source);
  266|  15.3k|    out_url->set_pathname(source);
  267|  15.3k|    out_url->set_search(source);
  268|  15.3k|    out_url->set_hash(source);
  269|  15.3k|    out_url->set_port(source);
  270|       |
  271|       |    // getters
  272|  15.3k|    length += out_url->get_protocol().size();
  273|  15.3k|    length += out_url->get_username().size();
  274|  15.3k|    length += out_url->get_password().size();
  275|  15.3k|    length += out_url->get_hostname().size();
  276|  15.3k|    length += out_url->get_host().size();
  277|  15.3k|    length += out_url->get_pathname().size();
  278|  15.3k|    length += out_url->get_search().size();
  279|  15.3k|    length += out_url->get_hash().size();
  280|  15.3k|    length += out_url->get_origin().size();
  281|  15.3k|    length += out_url->get_port().size();
  282|  15.3k|    length += out_url->get_pathname_length();
  283|       |
  284|  15.3k|    length += out_url->to_string().size();
  285|       |
  286|       |    // boolean predicates after setters
  287|  15.3k|    (void)out_url->has_valid_domain();
  288|  15.3k|    (void)out_url->has_credentials();
  289|  15.3k|    (void)out_url->has_empty_hostname();
  290|  15.3k|    (void)out_url->has_hostname();
  291|  15.3k|    (void)out_url->has_port();
  292|  15.3k|    (void)out_url->has_hash();
  293|  15.3k|    (void)out_url->has_search();
  294|  15.3k|    (void)out_url->get_components();
  295|  15.3k|  }
  296|       |
  297|       |  /**
  298|       |   * ada::parse<ada::url_aggregator>
  299|       |   */
  300|  15.3k|  auto out_aggregator =
  301|  15.3k|      ada::parse<ada::url_aggregator>("https://www.ada-url.com");
  302|       |
  303|  15.3k|  if (out_aggregator) {
  ------------------
  |  Branch (303:7): [True: 15.3k, False: 0]
  ------------------
  304|  15.3k|    out_aggregator->set_protocol(source);
  305|  15.3k|    out_aggregator->set_username(source);
  306|  15.3k|    out_aggregator->set_password(source);
  307|  15.3k|    out_aggregator->set_hostname(source);
  308|  15.3k|    out_aggregator->set_host(source);
  309|  15.3k|    out_aggregator->set_pathname(source);
  310|  15.3k|    out_aggregator->set_search(source);
  311|  15.3k|    out_aggregator->set_hash(source);
  312|  15.3k|    out_aggregator->set_port(source);
  313|       |
  314|       |    // getters
  315|  15.3k|    length += out_aggregator->get_protocol().size();
  316|  15.3k|    length += out_aggregator->get_username().size();
  317|  15.3k|    length += out_aggregator->get_password().size();
  318|  15.3k|    length += out_aggregator->get_hostname().size();
  319|  15.3k|    length += out_aggregator->get_host().size();
  320|  15.3k|    length += out_aggregator->get_pathname().size();
  321|  15.3k|    length += out_aggregator->get_search().size();
  322|  15.3k|    length += out_aggregator->get_hash().size();
  323|  15.3k|    length += out_aggregator->get_origin().size();
  324|  15.3k|    length += out_aggregator->get_port().size();
  325|  15.3k|    length += out_aggregator->get_pathname_length();
  326|       |
  327|  15.3k|    length += out_aggregator->to_string().size();
  328|       |
  329|  15.3k|    volatile bool is_output_valid = false;
  330|  15.3k|    is_output_valid = out_aggregator->validate();
  331|       |
  332|  15.3k|    (void)out_aggregator->to_diagram();
  333|       |
  334|       |    // boolean predicates after setters
  335|  15.3k|    (void)out_aggregator->has_valid_domain();
  336|  15.3k|    (void)out_aggregator->has_credentials();
  337|  15.3k|    (void)out_aggregator->has_empty_hostname();
  338|  15.3k|    (void)out_aggregator->has_hostname();
  339|  15.3k|    (void)out_aggregator->has_non_empty_username();
  340|  15.3k|    (void)out_aggregator->has_non_empty_password();
  341|  15.3k|    (void)out_aggregator->has_password();
  342|  15.3k|    (void)out_aggregator->has_port();
  343|  15.3k|    (void)out_aggregator->has_hash();
  344|  15.3k|    (void)out_aggregator->has_search();
  345|  15.3k|    (void)out_aggregator->get_components();
  346|       |
  347|       |    // clear methods + postcondition assertions
  348|  15.3k|    out_aggregator->clear_port();
  349|  15.3k|    if (out_aggregator->has_port()) {
  ------------------
  |  Branch (349:9): [True: 0, False: 15.3k]
  ------------------
  350|      0|      printf("clear_port() did not clear has_port()\n");
  351|      0|      abort();
  352|      0|    }
  353|  15.3k|    if (!out_aggregator->get_port().empty()) {
  ------------------
  |  Branch (353:9): [True: 0, False: 15.3k]
  ------------------
  354|      0|      printf("clear_port() left non-empty get_port()\n");
  355|      0|      abort();
  356|      0|    }
  357|       |
  358|  15.3k|    out_aggregator->clear_search();
  359|  15.3k|    if (out_aggregator->has_search()) {
  ------------------
  |  Branch (359:9): [True: 0, False: 15.3k]
  ------------------
  360|      0|      printf("clear_search() did not clear has_search()\n");
  361|      0|      abort();
  362|      0|    }
  363|  15.3k|    if (!out_aggregator->get_search().empty()) {
  ------------------
  |  Branch (363:9): [True: 0, False: 15.3k]
  ------------------
  364|      0|      printf("clear_search() left non-empty get_search()\n");
  365|      0|      abort();
  366|      0|    }
  367|       |
  368|  15.3k|    out_aggregator->clear_hash();
  369|  15.3k|    if (out_aggregator->has_hash()) {
  ------------------
  |  Branch (369:9): [True: 0, False: 15.3k]
  ------------------
  370|      0|      printf("clear_hash() did not clear has_hash()\n");
  371|      0|      abort();
  372|      0|    }
  373|  15.3k|    if (!out_aggregator->get_hash().empty()) {
  ------------------
  |  Branch (373:9): [True: 0, False: 15.3k]
  ------------------
  374|      0|      printf("clear_hash() left non-empty get_hash()\n");
  375|      0|      abort();
  376|      0|    }
  377|  15.3k|  }
  378|       |
  379|       |  /**
  380|       |   * Relative URL parsing with base (tests the base URL resolution code path)
  381|       |   */
  382|  15.3k|  auto base_url = ada::parse<ada::url>(base);
  383|  15.3k|  auto base_agg = ada::parse<ada::url_aggregator>(base);
  384|       |
  385|  15.3k|  if (base_url) {
  ------------------
  |  Branch (385:7): [True: 3.73k, False: 11.6k]
  ------------------
  386|  3.73k|    auto result = ada::parse<ada::url>(source, &*base_url);
  387|  3.73k|    if (result) {
  ------------------
  |  Branch (387:9): [True: 3.10k, False: 635]
  ------------------
  388|  3.10k|      length += result->get_href().size();
  389|  3.10k|      length += result->get_origin().size();
  390|  3.10k|      exercise_url_predicates(*result);
  391|  3.10k|    }
  392|  3.73k|  }
  393|       |
  394|  15.3k|  if (base_agg) {
  ------------------
  |  Branch (394:7): [True: 3.73k, False: 11.6k]
  ------------------
  395|  3.73k|    auto result = ada::parse<ada::url_aggregator>(source, &*base_agg);
  396|  3.73k|    if (result) {
  ------------------
  |  Branch (396:9): [True: 3.10k, False: 635]
  ------------------
  397|  3.10k|      length += result->get_href().size();
  398|  3.10k|      length += result->get_origin().size();
  399|  3.10k|      exercise_aggregator_predicates(*result);
  400|  3.10k|    }
  401|  3.73k|  }
  402|       |
  403|       |  // Cross-type consistency: relative URL parsing with a base should agree
  404|       |  // between url and url_aggregator representations for valid UTF-8 inputs.
  405|  15.3k|  if (is_valid_utf8_string(source.data(), source.length()) &&
  ------------------
  |  Branch (405:7): [True: 12.2k, False: 3.08k]
  ------------------
  406|  12.2k|      is_valid_utf8_string(base.data(), base.length()) && base_url &&
  ------------------
  |  Branch (406:7): [True: 11.5k, False: 756]
  |  Branch (406:59): [True: 2.66k, False: 8.87k]
  ------------------
  407|  2.66k|      base_agg) {
  ------------------
  |  Branch (407:7): [True: 2.66k, False: 0]
  ------------------
  408|  2.66k|    auto res_url = ada::parse<ada::url>(source, &*base_url);
  409|  2.66k|    auto res_agg = ada::parse<ada::url_aggregator>(source, &*base_agg);
  410|  2.66k|    if (res_url.has_value() ^ res_agg.has_value()) {
  ------------------
  |  Branch (410:9): [True: 0, False: 2.66k]
  ------------------
  411|      0|      printf("Relative parse inconsistency for source=%s base=%s\n",
  412|      0|             source.c_str(), base.c_str());
  413|      0|      abort();
  414|      0|    }
  415|  2.66k|    if (res_url && res_agg) {
  ------------------
  |  Branch (415:9): [True: 2.09k, False: 570]
  |  Branch (415:20): [True: 2.09k, False: 0]
  ------------------
  416|  2.09k|      if (res_url->get_href() != std::string(res_agg->get_href())) {
  ------------------
  |  Branch (416:11): [True: 0, False: 2.09k]
  ------------------
  417|      0|        printf("Relative parse href mismatch for source=%s base=%s\n",
  418|      0|               source.c_str(), base.c_str());
  419|      0|        abort();
  420|      0|      }
  421|  2.09k|    }
  422|  2.66k|  }
  423|       |
  424|       |  /**
  425|       |   * Chained relative URL resolution: parse source against base, then use the
  426|       |   * result as the base for a second parse. Exercises multi-level inheritance.
  427|       |   */
  428|  15.3k|  if (base_agg) {
  ------------------
  |  Branch (428:7): [True: 3.73k, False: 11.6k]
  ------------------
  429|  3.73k|    auto level1 = ada::parse<ada::url_aggregator>(source, &*base_agg);
  430|  3.73k|    if (level1) {
  ------------------
  |  Branch (430:9): [True: 3.10k, False: 635]
  ------------------
  431|  3.10k|      std::string input2 = fdp.ConsumeRandomLengthString(128);
  432|  3.10k|      auto level2 = ada::parse<ada::url_aggregator>(input2, &*level1);
  433|  3.10k|      if (level2) {
  ------------------
  |  Branch (433:11): [True: 2.51k, False: 591]
  ------------------
  434|  2.51k|        length += level2->get_href().size();
  435|  2.51k|        volatile bool v = level2->validate();
  436|  2.51k|        (void)v;
  437|  2.51k|      }
  438|  3.10k|    }
  439|  3.73k|  }
  440|       |
  441|       |  /**
  442|       |   * Known-good base URL with fuzzed relative input. Using a fixed valid base
  443|       |   * lets the fuzzer focus entropy entirely on the relative-input code paths
  444|       |   * (path resolution, query/fragment inheritance, scheme-relative URLs, etc.)
  445|       |   */
  446|  15.3k|  {
  447|  15.3k|    auto known_base =
  448|  15.3k|        ada::parse<ada::url_aggregator>("https://example.com/a/b/c?query#hash");
  449|  15.3k|    if (known_base) {
  ------------------
  |  Branch (449:9): [True: 15.3k, False: 0]
  ------------------
  450|  15.3k|      auto result = ada::parse<ada::url_aggregator>(source, &*known_base);
  451|  15.3k|      if (result) {
  ------------------
  |  Branch (451:11): [True: 13.8k, False: 1.52k]
  ------------------
  452|  13.8k|        length += result->get_href().size();
  453|  13.8k|        exercise_aggregator_predicates(*result);
  454|  13.8k|      }
  455|  15.3k|    }
  456|  15.3k|  }
  457|       |
  458|       |  /**
  459|       |   * Node.js specific
  460|       |   */
  461|  15.3k|  length += ada::href_from_file(source).size();
  462|       |
  463|       |  /**
  464|       |   * Others
  465|       |   */
  466|  15.3k|  bool is_valid = ada::checkers::verify_dns_length(source);
  467|       |
  468|  15.3k|  (void)is_valid;
  469|       |
  470|       |  /**
  471|       |   * Sequential setter interactions with FDP-controlled ordering.
  472|       |   *
  473|       |   * The existing code calls every setter with the same `source` value in a
  474|       |   * fixed order. Here we let the fuzzer choose an arbitrary sequence of
  475|       |   * setter/value pairs, checking that url and url_aggregator stay in sync
  476|       |   * after every step. This exercises setter-interaction state bugs that
  477|       |   * fixed-order testing would miss.
  478|       |   */
  479|  15.3k|  {
  480|  15.3k|    auto url_seq = ada::parse<ada::url>(
  481|  15.3k|        "https://user:pass@example.com:8080/path?query=1#hash");
  482|  15.3k|    auto agg_seq = ada::parse<ada::url_aggregator>(
  483|  15.3k|        "https://user:pass@example.com:8080/path?query=1#hash");
  484|  15.3k|    if (url_seq && agg_seq) {
  ------------------
  |  Branch (484:9): [True: 15.3k, False: 0]
  |  Branch (484:20): [True: 15.3k, False: 0]
  ------------------
  485|  15.3k|      int steps = fdp.ConsumeIntegralInRange(1, 8);
  486|  50.9k|      for (int i = 0; i < steps; ++i) {
  ------------------
  |  Branch (486:23): [True: 35.5k, False: 15.3k]
  ------------------
  487|  35.5k|        std::string val = fdp.ConsumeRandomLengthString(64);
  488|  35.5k|        int which = fdp.ConsumeIntegralInRange(0, 8);
  489|  35.5k|        switch (which) {
  ------------------
  |  Branch (489:17): [True: 35.5k, False: 0]
  ------------------
  490|  21.6k|          case 0:
  ------------------
  |  Branch (490:11): [True: 21.6k, False: 13.9k]
  ------------------
  491|  21.6k|            url_seq->set_protocol(val);
  492|  21.6k|            agg_seq->set_protocol(val);
  493|  21.6k|            break;
  494|  1.56k|          case 1:
  ------------------
  |  Branch (494:11): [True: 1.56k, False: 34.0k]
  ------------------
  495|  1.56k|            url_seq->set_username(val);
  496|  1.56k|            agg_seq->set_username(val);
  497|  1.56k|            break;
  498|  1.70k|          case 2:
  ------------------
  |  Branch (498:11): [True: 1.70k, False: 33.8k]
  ------------------
  499|  1.70k|            url_seq->set_password(val);
  500|  1.70k|            agg_seq->set_password(val);
  501|  1.70k|            break;
  502|  2.77k|          case 3:
  ------------------
  |  Branch (502:11): [True: 2.77k, False: 32.7k]
  ------------------
  503|  2.77k|            url_seq->set_hostname(val);
  504|  2.77k|            agg_seq->set_hostname(val);
  505|  2.77k|            break;
  506|  3.55k|          case 4:
  ------------------
  |  Branch (506:11): [True: 3.55k, False: 32.0k]
  ------------------
  507|  3.55k|            url_seq->set_host(val);
  508|  3.55k|            agg_seq->set_host(val);
  509|  3.55k|            break;
  510|  1.82k|          case 5:
  ------------------
  |  Branch (510:11): [True: 1.82k, False: 33.7k]
  ------------------
  511|  1.82k|            url_seq->set_pathname(val);
  512|  1.82k|            agg_seq->set_pathname(val);
  513|  1.82k|            break;
  514|    631|          case 6:
  ------------------
  |  Branch (514:11): [True: 631, False: 34.9k]
  ------------------
  515|    631|            url_seq->set_search(val);
  516|    631|            agg_seq->set_search(val);
  517|    631|            break;
  518|    440|          case 7:
  ------------------
  |  Branch (518:11): [True: 440, False: 35.1k]
  ------------------
  519|    440|            url_seq->set_hash(val);
  520|    440|            agg_seq->set_hash(val);
  521|    440|            break;
  522|  1.42k|          case 8:
  ------------------
  |  Branch (522:11): [True: 1.42k, False: 34.1k]
  ------------------
  523|  1.42k|            url_seq->set_port(val);
  524|  1.42k|            agg_seq->set_port(val);
  525|  1.42k|            break;
  526|  35.5k|        }
  527|       |        // After every setter both representations must agree on href.
  528|  35.5k|        if (url_seq->get_href() != std::string(agg_seq->get_href())) {
  ------------------
  |  Branch (528:13): [True: 0, False: 35.5k]
  ------------------
  529|      0|          printf(
  530|      0|              "Sequential setter href mismatch after setter=%d val='%s'\n"
  531|      0|              "  url:  %s\n  agg:  %s\n",
  532|      0|              which, val.c_str(), url_seq->get_href().c_str(),
  533|      0|              std::string(agg_seq->get_href()).c_str());
  534|      0|          abort();
  535|      0|        }
  536|       |        // url_aggregator internal invariant must still hold.
  537|  35.5k|        volatile bool v = agg_seq->validate();
  538|  35.5k|        (void)v;
  539|  35.5k|      }
  540|  15.3k|    }
  541|  15.3k|  }
  542|       |
  543|       |  /**
  544|       |   * Re-parse idempotency.
  545|       |   *
  546|       |   * If parse(source) succeeds, then parse(href) must also succeed and
  547|       |   * produce the same href. Serialization and parsing must be consistent:
  548|       |   * a normalized URL is always its own fixed point.
  549|       |   */
  550|  15.3k|  if (parse_url_aggregator) {
  ------------------
  |  Branch (550:7): [True: 4.63k, False: 10.7k]
  ------------------
  551|  4.63k|    std::string href1 = std::string(parse_url_aggregator->get_href());
  552|  4.63k|    auto reparsed = ada::parse<ada::url_aggregator>(href1);
  553|  4.63k|    if (!reparsed) {
  ------------------
  |  Branch (553:9): [True: 0, False: 4.63k]
  ------------------
  554|      0|      printf("Re-parse of href failed unexpectedly: '%s'\n", href1.c_str());
  555|      0|      abort();
  556|      0|    }
  557|  4.63k|    std::string href2 = std::string(reparsed->get_href());
  558|  4.63k|    if (href1 != href2) {
  ------------------
  |  Branch (558:9): [True: 0, False: 4.63k]
  ------------------
  559|      0|      printf(
  560|      0|          "Re-parse idempotency failure!\n"
  561|      0|          "  href1: %s\n  href2: %s\n",
  562|      0|          href1.c_str(), href2.c_str());
  563|      0|      abort();
  564|      0|    }
  565|  4.63k|  }
  566|       |
  567|       |  /**
  568|       |   * URL search params round-trip via URL integration.
  569|       |   *
  570|       |   * Construct a URL whose query is the fuzz source, extract the search
  571|       |   * component as a url_search_params, mutate it, serialise it back, and
  572|       |   * set it on the URL. Exercises the interaction between URL objects and
  573|       |   * url_search_params and verifies that the combined pipeline doesn't crash.
  574|       |   *
  575|       |   * Also verifies the url_search_params serialisation idempotency property:
  576|       |   *   url_search_params(sp.to_string()).to_string() == sp.to_string()
  577|       |   */
  578|  15.3k|  {
  579|  15.3k|    std::string search_url = "https://example.com/?" + source;
  580|  15.3k|    auto url_with_search = ada::parse<ada::url_aggregator>(search_url);
  581|  15.3k|    if (url_with_search) {
  ------------------
  |  Branch (581:9): [True: 15.3k, False: 0]
  ------------------
  582|       |      // Extract the search string (may include leading '?').
  583|  15.3k|      std::string search_raw = std::string(url_with_search->get_search());
  584|  15.3k|      std::string_view search_view = search_raw;
  585|  15.3k|      if (!search_view.empty() && search_view[0] == '?') {
  ------------------
  |  Branch (585:11): [True: 13.8k, False: 1.57k]
  |  Branch (585:35): [True: 13.8k, False: 0]
  ------------------
  586|  13.8k|        search_view = search_view.substr(1);
  587|  13.8k|      }
  588|       |
  589|  15.3k|      ada::url_search_params sp(search_view);
  590|       |
  591|       |      // Mutate with additional entries from the fuzz corpus.
  592|  15.3k|      sp.append(source, base);
  593|       |
  594|  15.3k|      std::string serialized = sp.to_string();
  595|       |
  596|       |      // Idempotency: re-parsing the serialised form must yield the same string.
  597|  15.3k|      ada::url_search_params sp2(serialized);
  598|  15.3k|      std::string serialized2 = sp2.to_string();
  599|  15.3k|      if (serialized2 != serialized) {
  ------------------
  |  Branch (599:11): [True: 0, False: 15.3k]
  ------------------
  600|      0|        printf(
  601|      0|            "url_search_params serialisation not idempotent!\n"
  602|      0|            "  first:  %s\n  second: %s\n",
  603|      0|            serialized.c_str(), serialized2.c_str());
  604|      0|        abort();
  605|      0|      }
  606|       |
  607|       |      // Set the serialised params back on the URL.
  608|  15.3k|      url_with_search->set_search(serialized);
  609|  15.3k|      volatile bool v = url_with_search->validate();
  610|  15.3k|      (void)v;
  611|  15.3k|    }
  612|  15.3k|  }
  613|       |
  614|  15.3k|  return 0;
  615|  15.3k|}  // extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
parse.cc:_ZL23exercise_url_predicatesRKN3ada3urlE:
   96|  7.73k|static void exercise_url_predicates(const ada::url& u) {
   97|  7.73k|  volatile size_t length = 0;
   98|  7.73k|  length += u.get_href().size();
   99|  7.73k|  length += u.get_origin().size();
  100|  7.73k|  length += u.get_protocol().size();
  101|  7.73k|  length += u.get_username().size();
  102|  7.73k|  length += u.get_password().size();
  103|  7.73k|  length += u.get_host().size();
  104|  7.73k|  length += u.get_hostname().size();
  105|  7.73k|  length += u.get_pathname().size();
  106|  7.73k|  length += u.get_search().size();
  107|  7.73k|  length += u.get_hash().size();
  108|  7.73k|  length += u.get_port().size();
  109|  7.73k|  length += u.to_string().size();
  110|  7.73k|  length += u.get_pathname_length();
  111|  7.73k|  (void)u.has_valid_domain();
  112|  7.73k|  (void)u.has_credentials();
  113|  7.73k|  (void)u.has_empty_hostname();
  114|  7.73k|  (void)u.has_hostname();
  115|  7.73k|  (void)u.has_port();
  116|  7.73k|  (void)u.has_hash();
  117|  7.73k|  (void)u.has_search();
  118|  7.73k|  (void)u.get_components();
  119|  7.73k|}
parse.cc:_ZL30exercise_aggregator_predicatesRKN3ada14url_aggregatorE:
  122|  21.5k|static void exercise_aggregator_predicates(const ada::url_aggregator& u) {
  123|  21.5k|  volatile size_t length = 0;
  124|  21.5k|  length += u.get_href().size();
  125|  21.5k|  length += u.get_origin().size();
  126|  21.5k|  length += u.get_protocol().size();
  127|  21.5k|  length += u.get_username().size();
  128|  21.5k|  length += u.get_password().size();
  129|  21.5k|  length += u.get_host().size();
  130|  21.5k|  length += u.get_hostname().size();
  131|  21.5k|  length += u.get_pathname().size();
  132|  21.5k|  length += u.get_search().size();
  133|  21.5k|  length += u.get_hash().size();
  134|  21.5k|  length += u.get_port().size();
  135|  21.5k|  length += u.to_string().size();
  136|  21.5k|  length += u.get_pathname_length();
  137|  21.5k|  (void)u.has_valid_domain();
  138|  21.5k|  (void)u.has_credentials();
  139|  21.5k|  (void)u.has_empty_hostname();
  140|  21.5k|  (void)u.has_hostname();
  141|  21.5k|  (void)u.has_non_empty_username();
  142|  21.5k|  (void)u.has_non_empty_password();
  143|  21.5k|  (void)u.has_password();
  144|  21.5k|  (void)u.has_port();
  145|  21.5k|  (void)u.has_hash();
  146|  21.5k|  (void)u.has_search();
  147|  21.5k|  (void)u.get_components();
  148|  21.5k|  volatile bool is_valid = u.validate();
  149|  21.5k|  (void)is_valid;
  150|  21.5k|  (void)u.to_diagram();
  151|  21.5k|}

