_ZN3ada4idna13utf8_to_utf32EPKcmPDi:
  151|    850|size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
  152|    850|  const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
  153|    850|  size_t pos = 0;
  154|    850|  const char32_t* start{utf32_output};
  155|  15.2k|  while (pos < len) {
  ------------------
  |  Branch (155:10): [True: 14.5k, False: 689]
  ------------------
  156|       |    // try to convert the next block of 16 ASCII bytes
  157|  14.5k|    if (pos + 16 <= len) {  // if it is safe to read 16 more
  ------------------
  |  Branch (157:9): [True: 7.88k, False: 6.69k]
  ------------------
  158|       |                            // bytes, check that they are ascii
  159|  7.88k|      uint64_t v1;
  160|  7.88k|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  161|  7.88k|      uint64_t v2;
  162|  7.88k|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  163|  7.88k|      uint64_t v{v1 | v2};
  164|  7.88k|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (164:11): [True: 731, False: 7.15k]
  ------------------
  165|    731|        size_t final_pos = pos + 16;
  166|  12.4k|        while (pos < final_pos) {
  ------------------
  |  Branch (166:16): [True: 11.6k, False: 731]
  ------------------
  167|  11.6k|          *utf32_output++ = char32_t(buf[pos]);
  168|  11.6k|          pos++;
  169|  11.6k|        }
  170|    731|        continue;
  171|    731|      }
  172|  7.88k|    }
  173|  13.8k|    uint8_t leading_byte = data[pos];  // leading byte
  174|  13.8k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (174:9): [True: 12.0k, False: 1.76k]
  ------------------
  175|       |      // converting one ASCII byte !!!
  176|  12.0k|      *utf32_output++ = char32_t(leading_byte);
  177|  12.0k|      pos++;
  178|  12.0k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (178:16): [True: 825, False: 937]
  ------------------
  179|       |      // We have a two-byte UTF-8
  180|    825|      if (pos + 1 >= len) {
  ------------------
  |  Branch (180:11): [True: 1, False: 824]
  ------------------
  181|      1|        return 0;
  182|      1|      }  // minimal bound checking
  183|    824|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (183:11): [True: 28, False: 796]
  ------------------
  184|     28|        return 0;
  185|     28|      }
  186|       |      // range check
  187|    796|      uint32_t code_point =
  188|    796|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  189|    796|      if (code_point < 0x80 || 0x7ff < code_point) {
  ------------------
  |  Branch (189:11): [True: 9, False: 787]
  |  Branch (189:32): [True: 0, False: 787]
  ------------------
  190|      9|        return 0;
  191|      9|      }
  192|    787|      *utf32_output++ = char32_t(code_point);
  193|    787|      pos += 2;
  194|    937|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (194:16): [True: 648, False: 289]
  ------------------
  195|       |      // We have a three-byte UTF-8
  196|    648|      if (pos + 2 >= len) {
  ------------------
  |  Branch (196:11): [True: 4, False: 644]
  ------------------
  197|      4|        return 0;
  198|      4|      }  // minimal bound checking
  199|       |
  200|    644|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (200:11): [True: 5, False: 639]
  ------------------
  201|      5|        return 0;
  202|      5|      }
  203|    639|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (203:11): [True: 6, False: 633]
  ------------------
  204|      6|        return 0;
  205|      6|      }
  206|       |      // range check
  207|    633|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  208|    633|                            (data[pos + 1] & 0b00111111) << 6 |
  209|    633|                            (data[pos + 2] & 0b00111111);
  210|    633|      if (code_point < 0x800 || 0xffff < code_point ||
  ------------------
  |  Branch (210:11): [True: 2, False: 631]
  |  Branch (210:33): [True: 0, False: 631]
  ------------------
  211|    631|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (211:12): [True: 174, False: 457]
  |  Branch (211:35): [True: 1, False: 173]
  ------------------
  212|      3|        return 0;
  213|      3|      }
  214|    630|      *utf32_output++ = char32_t(code_point);
  215|    630|      pos += 3;
  216|    630|    } else if ((leading_byte & 0b11111000) == 0b11110000) {  // 0b11110000
  ------------------
  |  Branch (216:16): [True: 210, False: 79]
  ------------------
  217|       |      // we have a 4-byte UTF-8 word.
  218|    210|      if (pos + 3 >= len) {
  ------------------
  |  Branch (218:11): [True: 3, False: 207]
  ------------------
  219|      3|        return 0;
  220|      3|      }  // minimal bound checking
  221|    207|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (221:11): [True: 11, False: 196]
  ------------------
  222|     11|        return 0;
  223|     11|      }
  224|    196|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (224:11): [True: 5, False: 191]
  ------------------
  225|      5|        return 0;
  226|      5|      }
  227|    191|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (227:11): [True: 5, False: 186]
  ------------------
  228|      5|        return 0;
  229|      5|      }
  230|       |
  231|       |      // range check
  232|    186|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  233|    186|                            (data[pos + 1] & 0b00111111) << 12 |
  234|    186|                            (data[pos + 2] & 0b00111111) << 6 |
  235|    186|                            (data[pos + 3] & 0b00111111);
  236|    186|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (236:11): [True: 1, False: 185]
  |  Branch (236:35): [True: 1, False: 184]
  ------------------
  237|      2|        return 0;
  238|      2|      }
  239|    184|      *utf32_output++ = char32_t(code_point);
  240|    184|      pos += 4;
  241|    184|    } else {
  242|     79|      return 0;
  243|     79|    }
  244|  13.8k|  }
  245|    689|  return utf32_output - start;
  246|    850|}
_ZN3ada4idna22utf32_length_from_utf8EPKcm:
  261|    850|size_t utf32_length_from_utf8(const char* buf, size_t len) {
  262|    850|  const int8_t* p = reinterpret_cast<const int8_t*>(buf);
  263|    850|  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|    850|    return c > -65;
  267|    850|  });
  268|    850|}
_ZN3ada4idna9ascii_mapEPcm:
 2837|  1.20k|void ascii_map(char* input, size_t length) {
 2838|  1.20k|  auto broadcast = [](uint8_t v) -> uint64_t {
 2839|  1.20k|    return 0x101010101010101ull * v;
 2840|  1.20k|  };
 2841|  1.20k|  uint64_t broadcast_80 = broadcast(0x80);
 2842|  1.20k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 2843|  1.20k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 2844|  1.20k|  size_t i = 0;
 2845|       |
 2846|  2.34k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (2846:10): [True: 1.14k, False: 1.20k]
  ------------------
 2847|  1.14k|    uint64_t word{};
 2848|  1.14k|    std::memcpy(&word, input + i, sizeof(word));
 2849|  1.14k|    word ^=
 2850|  1.14k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 2851|  1.14k|    std::memcpy(input + i, &word, sizeof(word));
 2852|  1.14k|  }
 2853|  1.20k|  if (i < length) {
  ------------------
  |  Branch (2853:7): [True: 1.12k, False: 74]
  ------------------
 2854|  1.12k|    uint64_t word{};
 2855|  1.12k|    std::memcpy(&word, input + i, length - i);
 2856|  1.12k|    word ^=
 2857|  1.12k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 2858|  1.12k|    std::memcpy(input + i, &word, length - i);
 2859|  1.12k|  }
 2860|  1.20k|}
_ZN3ada4idna3mapENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIDiS4_NS1_9allocatorIDiEEEE:
 2866|    961|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|    961|  out.clear();
 2880|    961|  out.reserve(input.size());
 2881|  27.5k|  for (char32_t x : input) {
  ------------------
  |  Branch (2881:19): [True: 27.5k, False: 860]
  ------------------
 2882|  27.5k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 2883|  27.5k|    if (status == IDNA_DISALLOWED) {
  ------------------
  |  Branch (2883:9): [True: 101, False: 27.4k]
  ------------------
 2884|    101|      return false;
 2885|    101|    }
 2886|  27.4k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (2886:9): [True: 23.9k, False: 3.46k]
  ------------------
 2887|  23.9k|      out.push_back(x);
 2888|  23.9k|      continue;
 2889|  23.9k|    }
 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|  3.46k|    const uint8_t* ptr = idna_utf8_mappings + status;
 2895|  7.69k|    while (*ptr != 0) {
  ------------------
  |  Branch (2895:12): [True: 4.23k, False: 3.46k]
  ------------------
 2896|  4.23k|      out.push_back(utf8_next(ptr));
 2897|  4.23k|    }
 2898|  3.46k|  }
 2899|    860|  return true;
 2900|    961|}
_ZN3ada4idna28compute_decomposition_lengthENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 7828|    831|    const std::u32string_view input) noexcept {
 7829|    831|  bool decomposition_needed{false};
 7830|    831|  size_t additional_elements{0};
 7831|  26.2k|  for (char32_t current_character : input) {
  ------------------
  |  Branch (7831:35): [True: 26.2k, False: 831]
  ------------------
 7832|  26.2k|    size_t decomposition_length{0};
 7833|       |
 7834|  26.2k|    if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (7834:9): [True: 885, False: 25.3k]
  ------------------
 7835|    885|        current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (7835:9): [True: 450, False: 435]
  ------------------
 7836|    450|      decomposition_length = 2;
 7837|    450|      if ((current_character - hangul_sbase) % hangul_tcount) {
  ------------------
  |  Branch (7837:11): [True: 413, False: 37]
  ------------------
 7838|    413|        decomposition_length = 3;
 7839|    413|      }
 7840|  25.7k|    } else if (current_character < 0x110000) {
  ------------------
  |  Branch (7840:16): [True: 25.7k, False: 0]
  ------------------
 7841|  25.7k|      const uint8_t di = decomposition_index[current_character >> 8];
 7842|  25.7k|      const uint16_t* const decomposition =
 7843|  25.7k|          decomposition_block[di] + (current_character % 256);
 7844|  25.7k|      decomposition_length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 7845|  25.7k|      if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (7845:11): [True: 402, False: 25.3k]
  |  Branch (7845:41): [True: 0, False: 402]
  ------------------
 7846|      0|        decomposition_length = 0;
 7847|      0|      }
 7848|  25.7k|    }
 7849|  26.2k|    if (decomposition_length != 0) {
  ------------------
  |  Branch (7849:9): [True: 852, False: 25.3k]
  ------------------
 7850|    852|      decomposition_needed = true;
 7851|    852|      additional_elements += decomposition_length - 1;
 7852|    852|    }
 7853|  26.2k|  }
 7854|    831|  return {decomposition_needed, additional_elements};
 7855|    831|}
_ZN3ada4idna9decomposeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEEm:
 7857|    244|void decompose(std::u32string& input, size_t additional_elements) {
 7858|    244|  input.resize(input.size() + additional_elements);
 7859|    244|  for (size_t descending_idx = input.size(),
 7860|    244|              input_count = descending_idx - additional_elements;
 7861|  8.02k|       input_count--;) {
  ------------------
  |  Branch (7861:8): [True: 7.78k, False: 244]
  ------------------
 7862|  7.78k|    if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (7862:9): [True: 632, False: 7.14k]
  ------------------
 7863|    632|        input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (7863:9): [True: 450, False: 182]
  ------------------
 7864|       |      // Hangul decomposition.
 7865|    450|      char32_t s_index = input[input_count] - hangul_sbase;
 7866|    450|      if (s_index % hangul_tcount != 0) {
  ------------------
  |  Branch (7866:11): [True: 413, False: 37]
  ------------------
 7867|    413|        input[--descending_idx] = hangul_tbase + s_index % hangul_tcount;
 7868|    413|      }
 7869|    450|      input[--descending_idx] =
 7870|    450|          hangul_vbase + (s_index % hangul_ncount) / hangul_tcount;
 7871|    450|      input[--descending_idx] = hangul_lbase + s_index / hangul_ncount;
 7872|  7.33k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (7872:16): [True: 7.33k, False: 0]
  ------------------
 7873|       |      // Check decomposition_data.
 7874|  7.33k|      const uint16_t* decomposition =
 7875|  7.33k|          decomposition_block[decomposition_index[input[input_count] >> 8]] +
 7876|  7.33k|          (input[input_count] % 256);
 7877|  7.33k|      uint16_t decomposition_length =
 7878|  7.33k|          (decomposition[1] >> 2) - (decomposition[0] >> 2);
 7879|  7.33k|      if (decomposition_length > 0 && (decomposition[0] & 1)) {
  ------------------
  |  Branch (7879:11): [True: 402, False: 6.92k]
  |  Branch (7879:39): [True: 0, False: 402]
  ------------------
 7880|      0|        decomposition_length = 0;
 7881|      0|      }
 7882|  7.33k|      if (decomposition_length > 0) {
  ------------------
  |  Branch (7882:11): [True: 402, False: 6.92k]
  ------------------
 7883|       |        // Non-recursive decomposition.
 7884|  1.42k|        while (decomposition_length-- > 0) {
  ------------------
  |  Branch (7884:16): [True: 1.01k, False: 402]
  ------------------
 7885|  1.01k|          input[--descending_idx] = decomposition_data[(decomposition[0] >> 2) +
 7886|  1.01k|                                                       decomposition_length];
 7887|  1.01k|        }
 7888|  6.92k|      } else {
 7889|       |        // No decomposition.
 7890|  6.92k|        input[--descending_idx] = input[input_count];
 7891|  6.92k|      }
 7892|  7.33k|    } else {
 7893|       |      // Non-Unicode character.
 7894|      0|      input[--descending_idx] = input[input_count];
 7895|      0|    }
 7896|  7.78k|  }
 7897|    244|}
_ZN3ada4idna7get_cccEDi:
 7899|  54.0k|uint8_t get_ccc(char32_t c) noexcept {
 7900|  54.0k|  return c < 0x110000 ? canonical_combining_class_block
  ------------------
  |  Branch (7900:10): [True: 54.0k, False: 0]
  ------------------
 7901|  54.0k|                            [canonical_combining_class_index[c >> 8]][c % 256]
 7902|  54.0k|                      : 0;
 7903|  54.0k|}
_ZN3ada4idna10sort_marksERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 7905|    831|void sort_marks(std::u32string& input) {
 7906|  27.6k|  for (size_t idx = 1; idx < input.size(); idx++) {
  ------------------
  |  Branch (7906:24): [True: 26.8k, False: 831]
  ------------------
 7907|  26.8k|    uint8_t ccc = get_ccc(input[idx]);
 7908|  26.8k|    if (ccc == 0) {
  ------------------
  |  Branch (7908:9): [True: 25.6k, False: 1.24k]
  ------------------
 7909|  25.6k|      continue;
 7910|  25.6k|    }  // Skip non-combining characters.
 7911|  1.24k|    auto current_character = input[idx];
 7912|  1.24k|    size_t back_idx = idx;
 7913|  1.69k|    while (back_idx != 0 && get_ccc(input[back_idx - 1]) > ccc) {
  ------------------
  |  Branch (7913:12): [True: 1.68k, False: 10]
  |  Branch (7913:29): [True: 445, False: 1.23k]
  ------------------
 7914|    445|      input[back_idx] = input[back_idx - 1];
 7915|    445|      back_idx--;
 7916|    445|    }
 7917|  1.24k|    input[back_idx] = current_character;
 7918|  1.24k|  }
 7919|    831|}
_ZN3ada4idna13decompose_nfcERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 7921|    831|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|    831|  auto [decomposition_needed, additional_elements] =
 7927|    831|      compute_decomposition_length(input);
 7928|    831|  if (decomposition_needed) {
  ------------------
  |  Branch (7928:7): [True: 244, False: 587]
  ------------------
 7929|    244|    decompose(input, additional_elements);
 7930|    244|  }
 7931|    831|  sort_marks(input);
 7932|    831|}
_ZN3ada4idna7composeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 7934|    831|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|    831|  size_t input_count{0};
 7940|    831|  size_t composition_count{0};
 7941|  26.4k|  for (; input_count < input.size(); input_count++, composition_count++) {
  ------------------
  |  Branch (7941:10): [True: 25.5k, False: 831]
  ------------------
 7942|  25.5k|    input[composition_count] = input[input_count];
 7943|  25.5k|    if (input[input_count] >= hangul_lbase &&
  ------------------
  |  Branch (7943:9): [True: 1.32k, False: 24.2k]
  ------------------
 7944|  1.32k|        input[input_count] < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (7944:9): [True: 454, False: 868]
  ------------------
 7945|    454|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (7945:11): [True: 454, False: 0]
  ------------------
 7946|    454|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (7946:11): [True: 453, False: 1]
  ------------------
 7947|    453|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (7947:11): [True: 450, False: 3]
  ------------------
 7948|    450|        input[composition_count] =
 7949|    450|            hangul_sbase +
 7950|    450|            ((input[input_count] - hangul_lbase) * hangul_vcount +
 7951|    450|             input[input_count + 1] - hangul_vbase) *
 7952|    450|                hangul_tcount;
 7953|    450|        input_count++;
 7954|    450|        if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (7954:13): [True: 448, False: 2]
  ------------------
 7955|    448|            input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (7955:13): [True: 416, False: 32]
  ------------------
 7956|    416|            input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (7956:13): [True: 413, False: 3]
  ------------------
 7957|    413|          input[composition_count] += input[++input_count] - hangul_tbase;
 7958|    413|        }
 7959|    450|      }
 7960|  25.1k|    } else if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (7960:16): [True: 301, False: 24.8k]
  ------------------
 7961|    301|               input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (7961:16): [True: 0, False: 301]
  ------------------
 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|  25.1k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (7968:16): [True: 25.1k, False: 0]
  ------------------
 7969|  25.1k|      const uint16_t* composition =
 7970|  25.1k|          &composition_block[composition_index[input[input_count] >> 8]]
 7971|  25.1k|                            [input[input_count] % 256];
 7972|  25.1k|      size_t initial_composition_count = composition_count;
 7973|  26.3k|      for (int32_t previous_ccc = -1; input_count + 1 < input.size();
  ------------------
  |  Branch (7973:39): [True: 25.5k, False: 808]
  ------------------
 7974|  25.5k|           input_count++) {
 7975|  25.5k|        uint8_t ccc = get_ccc(input[input_count + 1]);
 7976|       |
 7977|  25.5k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (7977:13): [True: 8.56k, False: 16.9k]
  |  Branch (7977:49): [True: 8.49k, False: 73]
  ------------------
 7978|       |          // Try finding a composition.
 7979|  8.49k|          int left = composition[0];
 7980|  8.49k|          int right = composition[1];
 7981|  26.5k|          while (left + 2 < right) {
  ------------------
  |  Branch (7981:18): [True: 18.1k, False: 8.49k]
  ------------------
 7982|       |            // mean without overflow
 7983|  18.1k|            int middle = left + (((right - left) >> 1) & ~1);
 7984|  18.1k|            if (composition_data[middle] <= input[input_count + 1]) {
  ------------------
  |  Branch (7984:17): [True: 1.91k, False: 16.1k]
  ------------------
 7985|  1.91k|              left = middle;
 7986|  1.91k|            }
 7987|  18.1k|            if (composition_data[middle] >= input[input_count + 1]) {
  ------------------
  |  Branch (7987:17): [True: 16.7k, False: 1.34k]
  ------------------
 7988|  16.7k|              right = middle;
 7989|  16.7k|            }
 7990|  18.1k|          }
 7991|  8.49k|          if (composition_data[left] == input[input_count + 1]) {
  ------------------
  |  Branch (7991:15): [True: 618, False: 7.87k]
  ------------------
 7992|    618|            input[initial_composition_count] = composition_data[left + 1];
 7993|    618|            composition =
 7994|    618|                &composition_block
 7995|    618|                    [composition_index[composition_data[left + 1] >> 8]]
 7996|    618|                    [composition_data[left + 1] % 256];
 7997|    618|            continue;
 7998|    618|          }
 7999|  8.49k|        }
 8000|       |
 8001|  24.9k|        if (ccc == 0) {
  ------------------
  |  Branch (8001:13): [True: 24.3k, False: 627]
  ------------------
 8002|  24.3k|          break;
 8003|  24.3k|        }  // Not a combining character.
 8004|    627|        previous_ccc = ccc;
 8005|    627|        input[++composition_count] = input[input_count + 1];
 8006|    627|      }
 8007|  25.1k|    }
 8008|  25.5k|  }
 8009|       |
 8010|    831|  if (composition_count < input_count) {
  ------------------
  |  Branch (8010:7): [True: 246, False: 585]
  ------------------
 8011|    246|    input.resize(composition_count);
 8012|    246|  }
 8013|    831|}
_ZN3ada4idna9normalizeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 8015|    831|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|    831|  decompose_nfc(input);
 8021|    831|  compose(input);
 8022|    831|}
_ZN3ada4idna17punycode_to_utf32ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIDiNS3_IDiEENS1_9allocatorIDiEEEE:
 8065|    306|bool punycode_to_utf32(std::string_view input, std::u32string &out) {
 8066|    306|  int32_t written_out{0};
 8067|    306|  out.reserve(out.size() + input.size());
 8068|    306|  uint32_t n = initial_n;
 8069|    306|  int32_t i = 0;
 8070|    306|  int32_t bias = initial_bias;
 8071|       |  // grab ascii content
 8072|    306|  size_t end_of_ascii = input.find_last_of('-');
 8073|    306|  if (end_of_ascii != std::string_view::npos) {
  ------------------
  |  Branch (8073:7): [True: 122, False: 184]
  ------------------
 8074|  1.78k|    for (uint8_t c : input.substr(0, end_of_ascii)) {
  ------------------
  |  Branch (8074:20): [True: 1.78k, False: 122]
  ------------------
 8075|  1.78k|      if (c >= 0x80) {
  ------------------
  |  Branch (8075:11): [True: 0, False: 1.78k]
  ------------------
 8076|      0|        return false;
 8077|      0|      }
 8078|  1.78k|      out.push_back(c);
 8079|  1.78k|      written_out++;
 8080|  1.78k|    }
 8081|    122|    input.remove_prefix(end_of_ascii + 1);
 8082|    122|  }
 8083|  3.81k|  while (!input.empty()) {
  ------------------
  |  Branch (8083:10): [True: 3.53k, False: 281]
  ------------------
 8084|  3.53k|    int32_t oldi = i;
 8085|  3.53k|    int32_t w = 1;
 8086|  5.95k|    for (int32_t k = base;; k += base) {
 8087|  5.95k|      if (input.empty()) {
  ------------------
  |  Branch (8087:11): [True: 9, False: 5.94k]
  ------------------
 8088|      9|        return false;
 8089|      9|      }
 8090|  5.94k|      uint8_t code_point = input.front();
 8091|  5.94k|      input.remove_prefix(1);
 8092|  5.94k|      int32_t digit = char_to_digit_value(code_point);
 8093|  5.94k|      if (digit < 0) {
  ------------------
  |  Branch (8093:11): [True: 8, False: 5.93k]
  ------------------
 8094|      8|        return false;
 8095|      8|      }
 8096|  5.93k|      if (digit > (0x7fffffff - i) / w) {
  ------------------
  |  Branch (8096:11): [True: 8, False: 5.92k]
  ------------------
 8097|      8|        return false;
 8098|      8|      }
 8099|  5.92k|      i = i + digit * w;
 8100|  5.92k|      int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (8100:19): [True: 1.09k, False: 4.82k]
  |  Branch (8100:38): [True: 3.94k, False: 886]
  ------------------
 8101|  5.92k|      if (digit < t) {
  ------------------
  |  Branch (8101:11): [True: 3.50k, False: 2.42k]
  ------------------
 8102|  3.50k|        break;
 8103|  3.50k|      }
 8104|  2.42k|      if (w > 0x7fffffff / (base - t)) {
  ------------------
  |  Branch (8104:11): [True: 0, False: 2.42k]
  ------------------
 8105|      0|        return false;
 8106|      0|      }
 8107|  2.42k|      w = w * (base - t);
 8108|  2.42k|    }
 8109|  3.50k|    bias = adapt(i - oldi, written_out + 1, oldi == 0);
 8110|  3.50k|    if (i / (written_out + 1) > int32_t(0x7fffffff - n)) {
  ------------------
  |  Branch (8110:9): [True: 0, False: 3.50k]
  ------------------
 8111|      0|      return false;
 8112|      0|    }
 8113|  3.50k|    n = n + i / (written_out + 1);
 8114|  3.50k|    i = i % (written_out + 1);
 8115|  3.50k|    if (n < 0x80) {
  ------------------
  |  Branch (8115:9): [True: 0, False: 3.50k]
  ------------------
 8116|      0|      return false;
 8117|      0|    }
 8118|  3.50k|    out.insert(out.begin() + i, n);
 8119|  3.50k|    written_out++;
 8120|  3.50k|    ++i;
 8121|  3.50k|  }
 8122|       |  // See https://github.com/whatwg/url/issues/803
 8123|       |  // Reject labels whose decoded form begins with "xn--" (double-encoded ACE).
 8124|    281|  if (out.size() >= 4 && out[0] == U'x' && out[1] == U'n' && out[2] == U'-' &&
  ------------------
  |  Branch (8124:7): [True: 219, False: 62]
  |  Branch (8124:26): [True: 14, False: 205]
  |  Branch (8124:44): [True: 8, False: 6]
  |  Branch (8124:62): [True: 2, False: 6]
  ------------------
 8125|      2|      out[3] == U'-') {
  ------------------
  |  Branch (8125:7): [True: 1, False: 1]
  ------------------
 8126|      1|    return false;
 8127|      1|  }
 8128|    280|  return true;
 8129|    281|}
_ZN3ada4idna17utf32_to_punycodeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIcNS3_IcEENS1_9allocatorIcEEEE:
 8209|    665|bool utf32_to_punycode(std::u32string_view input, std::string &out) {
 8210|    665|  out.reserve(input.size() + out.size());
 8211|    665|  uint32_t n = initial_n;
 8212|    665|  int32_t d = 0;
 8213|    665|  int32_t bias = initial_bias;
 8214|    665|  size_t h = 0;
 8215|       |  // first push the ascii content
 8216|  6.89k|  for (uint32_t c : input) {
  ------------------
  |  Branch (8216:19): [True: 6.89k, False: 665]
  ------------------
 8217|  6.89k|    if (c < 0x80) {
  ------------------
  |  Branch (8217:9): [True: 5.85k, False: 1.04k]
  ------------------
 8218|  5.85k|      ++h;
 8219|  5.85k|      out.push_back(char(c));
 8220|  5.85k|    }
 8221|  6.89k|    if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) {
  ------------------
  |  Branch (8221:9): [True: 0, False: 6.89k]
  |  Branch (8221:26): [True: 246, False: 6.64k]
  |  Branch (8221:41): [True: 0, False: 246]
  ------------------
 8222|      0|      return false;
 8223|      0|    }
 8224|  6.89k|  }
 8225|    665|  size_t b = h;
 8226|    665|  if (b > 0) {
  ------------------
  |  Branch (8226:7): [True: 571, False: 94]
  ------------------
 8227|    571|    out.push_back('-');
 8228|    571|  }
 8229|  1.56k|  while (h < input.size()) {
  ------------------
  |  Branch (8229:10): [True: 897, False: 665]
  ------------------
 8230|    897|    uint32_t m = 0x10FFFF;
 8231|  15.7k|    for (auto code_point : input) {
  ------------------
  |  Branch (8231:26): [True: 15.7k, False: 897]
  ------------------
 8232|  15.7k|      if (code_point >= n && code_point < m) m = code_point;
  ------------------
  |  Branch (8232:11): [True: 1.81k, False: 13.9k]
  |  Branch (8232:30): [True: 1.02k, False: 785]
  ------------------
 8233|  15.7k|    }
 8234|       |
 8235|    897|    if ((m - n) > (0x7fffffff - d) / (h + 1)) {
  ------------------
  |  Branch (8235:9): [True: 0, False: 897]
  ------------------
 8236|      0|      return false;
 8237|      0|    }
 8238|    897|    d = d + int32_t((m - n) * (h + 1));
 8239|    897|    n = m;
 8240|  15.7k|    for (auto c : input) {
  ------------------
  |  Branch (8240:17): [True: 15.7k, False: 897]
  ------------------
 8241|  15.7k|      if (c < n) {
  ------------------
  |  Branch (8241:11): [True: 13.9k, False: 1.81k]
  ------------------
 8242|  13.9k|        if (d == 0x7fffffff) {
  ------------------
  |  Branch (8242:13): [True: 0, False: 13.9k]
  ------------------
 8243|      0|          return false;
 8244|      0|        }
 8245|  13.9k|        ++d;
 8246|  13.9k|      }
 8247|  15.7k|      if (c == n) {
  ------------------
  |  Branch (8247:11): [True: 1.04k, False: 14.7k]
  ------------------
 8248|  1.04k|        int32_t q = d;
 8249|  3.26k|        for (int32_t k = base;; k += base) {
 8250|  3.26k|          int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (8250:23): [True: 1.49k, False: 1.76k]
  |  Branch (8250:42): [True: 1.61k, False: 158]
  ------------------
 8251|       |
 8252|  3.26k|          if (q < t) {
  ------------------
  |  Branch (8252:15): [True: 1.04k, False: 2.22k]
  ------------------
 8253|  1.04k|            break;
 8254|  1.04k|          }
 8255|  2.22k|          out.push_back(digit_to_char(t + ((q - t) % (base - t))));
 8256|  2.22k|          q = (q - t) / (base - t);
 8257|  2.22k|        }
 8258|  1.04k|        out.push_back(digit_to_char(q));
 8259|  1.04k|        bias = adapt(d, int32_t(h + 1), h == b);
 8260|  1.04k|        d = 0;
 8261|  1.04k|        ++h;
 8262|  1.04k|      }
 8263|  15.7k|    }
 8264|    897|    ++d;
 8265|    897|    ++n;
 8266|    897|  }
 8267|    665|  return true;
 8268|    665|}
_ZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9075|    993|bool is_label_valid(const std::u32string_view label) {
 9076|    993|  if (label.empty()) {
  ------------------
  |  Branch (9076:7): [True: 0, False: 993]
  ------------------
 9077|      0|    return true;
 9078|      0|  }
 9079|       |
 9080|       |  ///////////////
 9081|       |  // We have a normalization step which ensures that we are in NFC.
 9082|       |  // If we receive punycode, we normalize and check that the normalized
 9083|       |  // version matches the original.
 9084|       |  // --------------------------------------
 9085|       |  // The label must be in Unicode Normalization Form NFC.
 9086|       |
 9087|       |  // Current URL standard indicatest that CheckHyphens is set to false.
 9088|       |  // ---------------------------------------
 9089|       |  // If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character
 9090|       |  // in both the third and fourth positions. If CheckHyphens, the label must
 9091|       |  // neither begin nor end with a U+002D HYPHEN-MINUS character.
 9092|       |
 9093|       |  // This is not necessary because we segment the
 9094|       |  // labels by '.'.
 9095|       |  // ---------------------------------------
 9096|       |  // The label must not contain a U+002E ( . ) FULL STOP.
 9097|       |  // if (label.find('.') != std::string_view::npos) return false;
 9098|       |
 9099|       |  // The label must not begin with a combining mark, that is:
 9100|       |  // General_Category=Mark.
 9101|    993|  constexpr static uint32_t combining[] = {
 9102|    993|      0x300,   0x301,   0x302,   0x303,   0x304,   0x305,   0x306,   0x307,
 9103|    993|      0x308,   0x309,   0x30a,   0x30b,   0x30c,   0x30d,   0x30e,   0x30f,
 9104|    993|      0x310,   0x311,   0x312,   0x313,   0x314,   0x315,   0x316,   0x317,
 9105|    993|      0x318,   0x319,   0x31a,   0x31b,   0x31c,   0x31d,   0x31e,   0x31f,
 9106|    993|      0x320,   0x321,   0x322,   0x323,   0x324,   0x325,   0x326,   0x327,
 9107|    993|      0x328,   0x329,   0x32a,   0x32b,   0x32c,   0x32d,   0x32e,   0x32f,
 9108|    993|      0x330,   0x331,   0x332,   0x333,   0x334,   0x335,   0x336,   0x337,
 9109|    993|      0x338,   0x339,   0x33a,   0x33b,   0x33c,   0x33d,   0x33e,   0x33f,
 9110|    993|      0x340,   0x341,   0x342,   0x343,   0x344,   0x345,   0x346,   0x347,
 9111|    993|      0x348,   0x349,   0x34a,   0x34b,   0x34c,   0x34d,   0x34e,   0x34f,
 9112|    993|      0x350,   0x351,   0x352,   0x353,   0x354,   0x355,   0x356,   0x357,
 9113|    993|      0x358,   0x359,   0x35a,   0x35b,   0x35c,   0x35d,   0x35e,   0x35f,
 9114|    993|      0x360,   0x361,   0x362,   0x363,   0x364,   0x365,   0x366,   0x367,
 9115|    993|      0x368,   0x369,   0x36a,   0x36b,   0x36c,   0x36d,   0x36e,   0x36f,
 9116|    993|      0x483,   0x484,   0x485,   0x486,   0x487,   0x488,   0x489,   0x591,
 9117|    993|      0x592,   0x593,   0x594,   0x595,   0x596,   0x597,   0x598,   0x599,
 9118|    993|      0x59a,   0x59b,   0x59c,   0x59d,   0x59e,   0x59f,   0x5a0,   0x5a1,
 9119|    993|      0x5a2,   0x5a3,   0x5a4,   0x5a5,   0x5a6,   0x5a7,   0x5a8,   0x5a9,
 9120|    993|      0x5aa,   0x5ab,   0x5ac,   0x5ad,   0x5ae,   0x5af,   0x5b0,   0x5b1,
 9121|    993|      0x5b2,   0x5b3,   0x5b4,   0x5b5,   0x5b6,   0x5b7,   0x5b8,   0x5b9,
 9122|    993|      0x5ba,   0x5bb,   0x5bc,   0x5bd,   0x5bf,   0x5c1,   0x5c2,   0x5c4,
 9123|    993|      0x5c5,   0x5c7,   0x610,   0x611,   0x612,   0x613,   0x614,   0x615,
 9124|    993|      0x616,   0x617,   0x618,   0x619,   0x61a,   0x64b,   0x64c,   0x64d,
 9125|    993|      0x64e,   0x64f,   0x650,   0x651,   0x652,   0x653,   0x654,   0x655,
 9126|    993|      0x656,   0x657,   0x658,   0x659,   0x65a,   0x65b,   0x65c,   0x65d,
 9127|    993|      0x65e,   0x65f,   0x670,   0x6d6,   0x6d7,   0x6d8,   0x6d9,   0x6da,
 9128|    993|      0x6db,   0x6dc,   0x6df,   0x6e0,   0x6e1,   0x6e2,   0x6e3,   0x6e4,
 9129|    993|      0x6e7,   0x6e8,   0x6ea,   0x6eb,   0x6ec,   0x6ed,   0x711,   0x730,
 9130|    993|      0x731,   0x732,   0x733,   0x734,   0x735,   0x736,   0x737,   0x738,
 9131|    993|      0x739,   0x73a,   0x73b,   0x73c,   0x73d,   0x73e,   0x73f,   0x740,
 9132|    993|      0x741,   0x742,   0x743,   0x744,   0x745,   0x746,   0x747,   0x748,
 9133|    993|      0x749,   0x74a,   0x7a6,   0x7a7,   0x7a8,   0x7a9,   0x7aa,   0x7ab,
 9134|    993|      0x7ac,   0x7ad,   0x7ae,   0x7af,   0x7b0,   0x7eb,   0x7ec,   0x7ed,
 9135|    993|      0x7ee,   0x7ef,   0x7f0,   0x7f1,   0x7f2,   0x7f3,   0x7fd,   0x816,
 9136|    993|      0x817,   0x818,   0x819,   0x81b,   0x81c,   0x81d,   0x81e,   0x81f,
 9137|    993|      0x820,   0x821,   0x822,   0x823,   0x825,   0x826,   0x827,   0x829,
 9138|    993|      0x82a,   0x82b,   0x82c,   0x82d,   0x859,   0x85a,   0x85b,   0x8d3,
 9139|    993|      0x8d4,   0x8d5,   0x8d6,   0x8d7,   0x8d8,   0x8d9,   0x8da,   0x8db,
 9140|    993|      0x8dc,   0x8dd,   0x8de,   0x8df,   0x8e0,   0x8e1,   0x8e3,   0x8e4,
 9141|    993|      0x8e5,   0x8e6,   0x8e7,   0x8e8,   0x8e9,   0x8ea,   0x8eb,   0x8ec,
 9142|    993|      0x8ed,   0x8ee,   0x8ef,   0x8f0,   0x8f1,   0x8f2,   0x8f3,   0x8f4,
 9143|    993|      0x8f5,   0x8f6,   0x8f7,   0x8f8,   0x8f9,   0x8fa,   0x8fb,   0x8fc,
 9144|    993|      0x8fd,   0x8fe,   0x8ff,   0x900,   0x901,   0x902,   0x903,   0x93a,
 9145|    993|      0x93b,   0x93c,   0x93e,   0x93f,   0x940,   0x941,   0x942,   0x943,
 9146|    993|      0x944,   0x945,   0x946,   0x947,   0x948,   0x949,   0x94a,   0x94b,
 9147|    993|      0x94c,   0x94d,   0x94e,   0x94f,   0x951,   0x952,   0x953,   0x954,
 9148|    993|      0x955,   0x956,   0x957,   0x962,   0x963,   0x981,   0x982,   0x983,
 9149|    993|      0x9bc,   0x9be,   0x9bf,   0x9c0,   0x9c1,   0x9c2,   0x9c3,   0x9c4,
 9150|    993|      0x9c7,   0x9c8,   0x9cb,   0x9cc,   0x9cd,   0x9d7,   0x9e2,   0x9e3,
 9151|    993|      0x9fe,   0xa01,   0xa02,   0xa03,   0xa3c,   0xa3e,   0xa3f,   0xa40,
 9152|    993|      0xa41,   0xa42,   0xa47,   0xa48,   0xa4b,   0xa4c,   0xa4d,   0xa51,
 9153|    993|      0xa70,   0xa71,   0xa75,   0xa81,   0xa82,   0xa83,   0xabc,   0xabe,
 9154|    993|      0xabf,   0xac0,   0xac1,   0xac2,   0xac3,   0xac4,   0xac5,   0xac7,
 9155|    993|      0xac8,   0xac9,   0xacb,   0xacc,   0xacd,   0xae2,   0xae3,   0xafa,
 9156|    993|      0xafb,   0xafc,   0xafd,   0xafe,   0xaff,   0xb01,   0xb02,   0xb03,
 9157|    993|      0xb3c,   0xb3e,   0xb3f,   0xb40,   0xb41,   0xb42,   0xb43,   0xb44,
 9158|    993|      0xb47,   0xb48,   0xb4b,   0xb4c,   0xb4d,   0xb55,   0xb56,   0xb57,
 9159|    993|      0xb62,   0xb63,   0xb82,   0xbbe,   0xbbf,   0xbc0,   0xbc1,   0xbc2,
 9160|    993|      0xbc6,   0xbc7,   0xbc8,   0xbca,   0xbcb,   0xbcc,   0xbcd,   0xbd7,
 9161|    993|      0xc00,   0xc01,   0xc02,   0xc03,   0xc04,   0xc3e,   0xc3f,   0xc40,
 9162|    993|      0xc41,   0xc42,   0xc43,   0xc44,   0xc46,   0xc47,   0xc48,   0xc4a,
 9163|    993|      0xc4b,   0xc4c,   0xc4d,   0xc55,   0xc56,   0xc62,   0xc63,   0xc81,
 9164|    993|      0xc82,   0xc83,   0xcbc,   0xcbe,   0xcbf,   0xcc0,   0xcc1,   0xcc2,
 9165|    993|      0xcc3,   0xcc4,   0xcc6,   0xcc7,   0xcc8,   0xcca,   0xccb,   0xccc,
 9166|    993|      0xccd,   0xcd5,   0xcd6,   0xce2,   0xce3,   0xd00,   0xd01,   0xd02,
 9167|    993|      0xd03,   0xd3b,   0xd3c,   0xd3e,   0xd3f,   0xd40,   0xd41,   0xd42,
 9168|    993|      0xd43,   0xd44,   0xd46,   0xd47,   0xd48,   0xd4a,   0xd4b,   0xd4c,
 9169|    993|      0xd4d,   0xd57,   0xd62,   0xd63,   0xd81,   0xd82,   0xd83,   0xdca,
 9170|    993|      0xdcf,   0xdd0,   0xdd1,   0xdd2,   0xdd3,   0xdd4,   0xdd6,   0xdd8,
 9171|    993|      0xdd9,   0xdda,   0xddb,   0xddc,   0xddd,   0xdde,   0xddf,   0xdf2,
 9172|    993|      0xdf3,   0xe31,   0xe34,   0xe35,   0xe36,   0xe37,   0xe38,   0xe39,
 9173|    993|      0xe3a,   0xe47,   0xe48,   0xe49,   0xe4a,   0xe4b,   0xe4c,   0xe4d,
 9174|    993|      0xe4e,   0xeb1,   0xeb4,   0xeb5,   0xeb6,   0xeb7,   0xeb8,   0xeb9,
 9175|    993|      0xeba,   0xebb,   0xebc,   0xec8,   0xec9,   0xeca,   0xecb,   0xecc,
 9176|    993|      0xecd,   0xf18,   0xf19,   0xf35,   0xf37,   0xf39,   0xf3e,   0xf3f,
 9177|    993|      0xf71,   0xf72,   0xf73,   0xf74,   0xf75,   0xf76,   0xf77,   0xf78,
 9178|    993|      0xf79,   0xf7a,   0xf7b,   0xf7c,   0xf7d,   0xf7e,   0xf7f,   0xf80,
 9179|    993|      0xf81,   0xf82,   0xf83,   0xf84,   0xf86,   0xf87,   0xf8d,   0xf8e,
 9180|    993|      0xf8f,   0xf90,   0xf91,   0xf92,   0xf93,   0xf94,   0xf95,   0xf96,
 9181|    993|      0xf97,   0xf99,   0xf9a,   0xf9b,   0xf9c,   0xf9d,   0xf9e,   0xf9f,
 9182|    993|      0xfa0,   0xfa1,   0xfa2,   0xfa3,   0xfa4,   0xfa5,   0xfa6,   0xfa7,
 9183|    993|      0xfa8,   0xfa9,   0xfaa,   0xfab,   0xfac,   0xfad,   0xfae,   0xfaf,
 9184|    993|      0xfb0,   0xfb1,   0xfb2,   0xfb3,   0xfb4,   0xfb5,   0xfb6,   0xfb7,
 9185|    993|      0xfb8,   0xfb9,   0xfba,   0xfbb,   0xfbc,   0xfc6,   0x102b,  0x102c,
 9186|    993|      0x102d,  0x102e,  0x102f,  0x1030,  0x1031,  0x1032,  0x1033,  0x1034,
 9187|    993|      0x1035,  0x1036,  0x1037,  0x1038,  0x1039,  0x103a,  0x103b,  0x103c,
 9188|    993|      0x103d,  0x103e,  0x1056,  0x1057,  0x1058,  0x1059,  0x105e,  0x105f,
 9189|    993|      0x1060,  0x1062,  0x1063,  0x1064,  0x1067,  0x1068,  0x1069,  0x106a,
 9190|    993|      0x106b,  0x106c,  0x106d,  0x1071,  0x1072,  0x1073,  0x1074,  0x1082,
 9191|    993|      0x1083,  0x1084,  0x1085,  0x1086,  0x1087,  0x1088,  0x1089,  0x108a,
 9192|    993|      0x108b,  0x108c,  0x108d,  0x108f,  0x109a,  0x109b,  0x109c,  0x109d,
 9193|    993|      0x135d,  0x135e,  0x135f,  0x1712,  0x1713,  0x1714,  0x1732,  0x1733,
 9194|    993|      0x1734,  0x1752,  0x1753,  0x1772,  0x1773,  0x17b4,  0x17b5,  0x17b6,
 9195|    993|      0x17b7,  0x17b8,  0x17b9,  0x17ba,  0x17bb,  0x17bc,  0x17bd,  0x17be,
 9196|    993|      0x17bf,  0x17c0,  0x17c1,  0x17c2,  0x17c3,  0x17c4,  0x17c5,  0x17c6,
 9197|    993|      0x17c7,  0x17c8,  0x17c9,  0x17ca,  0x17cb,  0x17cc,  0x17cd,  0x17ce,
 9198|    993|      0x17cf,  0x17d0,  0x17d1,  0x17d2,  0x17d3,  0x17dd,  0x180b,  0x180c,
 9199|    993|      0x180d,  0x1885,  0x1886,  0x18a9,  0x1920,  0x1921,  0x1922,  0x1923,
 9200|    993|      0x1924,  0x1925,  0x1926,  0x1927,  0x1928,  0x1929,  0x192a,  0x192b,
 9201|    993|      0x1930,  0x1931,  0x1932,  0x1933,  0x1934,  0x1935,  0x1936,  0x1937,
 9202|    993|      0x1938,  0x1939,  0x193a,  0x193b,  0x1a17,  0x1a18,  0x1a19,  0x1a1a,
 9203|    993|      0x1a1b,  0x1a55,  0x1a56,  0x1a57,  0x1a58,  0x1a59,  0x1a5a,  0x1a5b,
 9204|    993|      0x1a5c,  0x1a5d,  0x1a5e,  0x1a60,  0x1a61,  0x1a62,  0x1a63,  0x1a64,
 9205|    993|      0x1a65,  0x1a66,  0x1a67,  0x1a68,  0x1a69,  0x1a6a,  0x1a6b,  0x1a6c,
 9206|    993|      0x1a6d,  0x1a6e,  0x1a6f,  0x1a70,  0x1a71,  0x1a72,  0x1a73,  0x1a74,
 9207|    993|      0x1a75,  0x1a76,  0x1a77,  0x1a78,  0x1a79,  0x1a7a,  0x1a7b,  0x1a7c,
 9208|    993|      0x1a7f,  0x1ab0,  0x1ab1,  0x1ab2,  0x1ab3,  0x1ab4,  0x1ab5,  0x1ab6,
 9209|    993|      0x1ab7,  0x1ab8,  0x1ab9,  0x1aba,  0x1abb,  0x1abc,  0x1abd,  0x1abe,
 9210|    993|      0x1abf,  0x1ac0,  0x1b00,  0x1b01,  0x1b02,  0x1b03,  0x1b04,  0x1b34,
 9211|    993|      0x1b35,  0x1b36,  0x1b37,  0x1b38,  0x1b39,  0x1b3a,  0x1b3b,  0x1b3c,
 9212|    993|      0x1b3d,  0x1b3e,  0x1b3f,  0x1b40,  0x1b41,  0x1b42,  0x1b43,  0x1b44,
 9213|    993|      0x1b6b,  0x1b6c,  0x1b6d,  0x1b6e,  0x1b6f,  0x1b70,  0x1b71,  0x1b72,
 9214|    993|      0x1b73,  0x1b80,  0x1b81,  0x1b82,  0x1ba1,  0x1ba2,  0x1ba3,  0x1ba4,
 9215|    993|      0x1ba5,  0x1ba6,  0x1ba7,  0x1ba8,  0x1ba9,  0x1baa,  0x1bab,  0x1bac,
 9216|    993|      0x1bad,  0x1be6,  0x1be7,  0x1be8,  0x1be9,  0x1bea,  0x1beb,  0x1bec,
 9217|    993|      0x1bed,  0x1bee,  0x1bef,  0x1bf0,  0x1bf1,  0x1bf2,  0x1bf3,  0x1c24,
 9218|    993|      0x1c25,  0x1c26,  0x1c27,  0x1c28,  0x1c29,  0x1c2a,  0x1c2b,  0x1c2c,
 9219|    993|      0x1c2d,  0x1c2e,  0x1c2f,  0x1c30,  0x1c31,  0x1c32,  0x1c33,  0x1c34,
 9220|    993|      0x1c35,  0x1c36,  0x1c37,  0x1cd0,  0x1cd1,  0x1cd2,  0x1cd4,  0x1cd5,
 9221|    993|      0x1cd6,  0x1cd7,  0x1cd8,  0x1cd9,  0x1cda,  0x1cdb,  0x1cdc,  0x1cdd,
 9222|    993|      0x1cde,  0x1cdf,  0x1ce0,  0x1ce1,  0x1ce2,  0x1ce3,  0x1ce4,  0x1ce5,
 9223|    993|      0x1ce6,  0x1ce7,  0x1ce8,  0x1ced,  0x1cf4,  0x1cf7,  0x1cf8,  0x1cf9,
 9224|    993|      0x1dc0,  0x1dc1,  0x1dc2,  0x1dc3,  0x1dc4,  0x1dc5,  0x1dc6,  0x1dc7,
 9225|    993|      0x1dc8,  0x1dc9,  0x1dca,  0x1dcb,  0x1dcc,  0x1dcd,  0x1dce,  0x1dcf,
 9226|    993|      0x1dd0,  0x1dd1,  0x1dd2,  0x1dd3,  0x1dd4,  0x1dd5,  0x1dd6,  0x1dd7,
 9227|    993|      0x1dd8,  0x1dd9,  0x1dda,  0x1ddb,  0x1ddc,  0x1ddd,  0x1dde,  0x1ddf,
 9228|    993|      0x1de0,  0x1de1,  0x1de2,  0x1de3,  0x1de4,  0x1de5,  0x1de6,  0x1de7,
 9229|    993|      0x1de8,  0x1de9,  0x1dea,  0x1deb,  0x1dec,  0x1ded,  0x1dee,  0x1def,
 9230|    993|      0x1df0,  0x1df1,  0x1df2,  0x1df3,  0x1df4,  0x1df5,  0x1df6,  0x1df7,
 9231|    993|      0x1df8,  0x1df9,  0x1dfb,  0x1dfc,  0x1dfd,  0x1dfe,  0x1dff,  0x20d0,
 9232|    993|      0x20d1,  0x20d2,  0x20d3,  0x20d4,  0x20d5,  0x20d6,  0x20d7,  0x20d8,
 9233|    993|      0x20d9,  0x20da,  0x20db,  0x20dc,  0x20dd,  0x20de,  0x20df,  0x20e0,
 9234|    993|      0x20e1,  0x20e2,  0x20e3,  0x20e4,  0x20e5,  0x20e6,  0x20e7,  0x20e8,
 9235|    993|      0x20e9,  0x20ea,  0x20eb,  0x20ec,  0x20ed,  0x20ee,  0x20ef,  0x20f0,
 9236|    993|      0x2cef,  0x2cf0,  0x2cf1,  0x2d7f,  0x2de0,  0x2de1,  0x2de2,  0x2de3,
 9237|    993|      0x2de4,  0x2de5,  0x2de6,  0x2de7,  0x2de8,  0x2de9,  0x2dea,  0x2deb,
 9238|    993|      0x2dec,  0x2ded,  0x2dee,  0x2def,  0x2df0,  0x2df1,  0x2df2,  0x2df3,
 9239|    993|      0x2df4,  0x2df5,  0x2df6,  0x2df7,  0x2df8,  0x2df9,  0x2dfa,  0x2dfb,
 9240|    993|      0x2dfc,  0x2dfd,  0x2dfe,  0x2dff,  0x302a,  0x302b,  0x302c,  0x302d,
 9241|    993|      0x302e,  0x302f,  0x3099,  0x309a,  0xa66f,  0xa670,  0xa671,  0xa672,
 9242|    993|      0xa674,  0xa675,  0xa676,  0xa677,  0xa678,  0xa679,  0xa67a,  0xa67b,
 9243|    993|      0xa67c,  0xa67d,  0xa69e,  0xa69f,  0xa6f0,  0xa6f1,  0xa802,  0xa806,
 9244|    993|      0xa80b,  0xa823,  0xa824,  0xa825,  0xa826,  0xa827,  0xa82c,  0xa880,
 9245|    993|      0xa881,  0xa8b4,  0xa8b5,  0xa8b6,  0xa8b7,  0xa8b8,  0xa8b9,  0xa8ba,
 9246|    993|      0xa8bb,  0xa8bc,  0xa8bd,  0xa8be,  0xa8bf,  0xa8c0,  0xa8c1,  0xa8c2,
 9247|    993|      0xa8c3,  0xa8c4,  0xa8c5,  0xa8e0,  0xa8e1,  0xa8e2,  0xa8e3,  0xa8e4,
 9248|    993|      0xa8e5,  0xa8e6,  0xa8e7,  0xa8e8,  0xa8e9,  0xa8ea,  0xa8eb,  0xa8ec,
 9249|    993|      0xa8ed,  0xa8ee,  0xa8ef,  0xa8f0,  0xa8f1,  0xa8ff,  0xa926,  0xa927,
 9250|    993|      0xa928,  0xa929,  0xa92a,  0xa92b,  0xa92c,  0xa92d,  0xa947,  0xa948,
 9251|    993|      0xa949,  0xa94a,  0xa94b,  0xa94c,  0xa94d,  0xa94e,  0xa94f,  0xa950,
 9252|    993|      0xa951,  0xa952,  0xa953,  0xa980,  0xa981,  0xa982,  0xa983,  0xa9b3,
 9253|    993|      0xa9b4,  0xa9b5,  0xa9b6,  0xa9b7,  0xa9b8,  0xa9b9,  0xa9ba,  0xa9bb,
 9254|    993|      0xa9bc,  0xa9bd,  0xa9be,  0xa9bf,  0xa9c0,  0xa9e5,  0xaa29,  0xaa2a,
 9255|    993|      0xaa2b,  0xaa2c,  0xaa2d,  0xaa2e,  0xaa2f,  0xaa30,  0xaa31,  0xaa32,
 9256|    993|      0xaa33,  0xaa34,  0xaa35,  0xaa36,  0xaa43,  0xaa4c,  0xaa4d,  0xaa7b,
 9257|    993|      0xaa7c,  0xaa7d,  0xaab0,  0xaab2,  0xaab3,  0xaab4,  0xaab7,  0xaab8,
 9258|    993|      0xaabe,  0xaabf,  0xaac1,  0xaaeb,  0xaaec,  0xaaed,  0xaaee,  0xaaef,
 9259|    993|      0xaaf5,  0xaaf6,  0xabe3,  0xabe4,  0xabe5,  0xabe6,  0xabe7,  0xabe8,
 9260|    993|      0xabe9,  0xabea,  0xabec,  0xabed,  0xfb1e,  0xfe00,  0xfe01,  0xfe02,
 9261|    993|      0xfe03,  0xfe04,  0xfe05,  0xfe06,  0xfe07,  0xfe08,  0xfe09,  0xfe0a,
 9262|    993|      0xfe0b,  0xfe0c,  0xfe0d,  0xfe0e,  0xfe0f,  0xfe20,  0xfe21,  0xfe22,
 9263|    993|      0xfe23,  0xfe24,  0xfe25,  0xfe26,  0xfe27,  0xfe28,  0xfe29,  0xfe2a,
 9264|    993|      0xfe2b,  0xfe2c,  0xfe2d,  0xfe2e,  0xfe2f,  0x101fd, 0x102e0, 0x10376,
 9265|    993|      0x10377, 0x10378, 0x10379, 0x1037a, 0x10a01, 0x10a02, 0x10a03, 0x10a05,
 9266|    993|      0x10a06, 0x10a0c, 0x10a0d, 0x10a0e, 0x10a0f, 0x10a38, 0x10a39, 0x10a3a,
 9267|    993|      0x10a3f, 0x10ae5, 0x10ae6, 0x10d24, 0x10d25, 0x10d26, 0x10d27, 0x10eab,
 9268|    993|      0x10eac, 0x10f46, 0x10f47, 0x10f48, 0x10f49, 0x10f4a, 0x10f4b, 0x10f4c,
 9269|    993|      0x10f4d, 0x10f4e, 0x10f4f, 0x10f50, 0x11000, 0x11001, 0x11002, 0x11038,
 9270|    993|      0x11039, 0x1103a, 0x1103b, 0x1103c, 0x1103d, 0x1103e, 0x1103f, 0x11040,
 9271|    993|      0x11041, 0x11042, 0x11043, 0x11044, 0x11045, 0x11046, 0x1107f, 0x11080,
 9272|    993|      0x11081, 0x11082, 0x110b0, 0x110b1, 0x110b2, 0x110b3, 0x110b4, 0x110b5,
 9273|    993|      0x110b6, 0x110b7, 0x110b8, 0x110b9, 0x110ba, 0x11100, 0x11101, 0x11102,
 9274|    993|      0x11127, 0x11128, 0x11129, 0x1112a, 0x1112b, 0x1112c, 0x1112d, 0x1112e,
 9275|    993|      0x1112f, 0x11130, 0x11131, 0x11132, 0x11133, 0x11134, 0x11145, 0x11146,
 9276|    993|      0x11173, 0x11180, 0x11181, 0x11182, 0x111b3, 0x111b4, 0x111b5, 0x111b6,
 9277|    993|      0x111b7, 0x111b8, 0x111b9, 0x111ba, 0x111bb, 0x111bc, 0x111bd, 0x111be,
 9278|    993|      0x111bf, 0x111c0, 0x111c9, 0x111ca, 0x111cb, 0x111cc, 0x111ce, 0x111cf,
 9279|    993|      0x1122c, 0x1122d, 0x1122e, 0x1122f, 0x11230, 0x11231, 0x11232, 0x11233,
 9280|    993|      0x11234, 0x11235, 0x11236, 0x11237, 0x1123e, 0x112df, 0x112e0, 0x112e1,
 9281|    993|      0x112e2, 0x112e3, 0x112e4, 0x112e5, 0x112e6, 0x112e7, 0x112e8, 0x112e9,
 9282|    993|      0x112ea, 0x11300, 0x11301, 0x11302, 0x11303, 0x1133b, 0x1133c, 0x1133e,
 9283|    993|      0x1133f, 0x11340, 0x11341, 0x11342, 0x11343, 0x11344, 0x11347, 0x11348,
 9284|    993|      0x1134b, 0x1134c, 0x1134d, 0x11357, 0x11362, 0x11363, 0x11366, 0x11367,
 9285|    993|      0x11368, 0x11369, 0x1136a, 0x1136b, 0x1136c, 0x11370, 0x11371, 0x11372,
 9286|    993|      0x11373, 0x11374, 0x11435, 0x11436, 0x11437, 0x11438, 0x11439, 0x1143a,
 9287|    993|      0x1143b, 0x1143c, 0x1143d, 0x1143e, 0x1143f, 0x11440, 0x11441, 0x11442,
 9288|    993|      0x11443, 0x11444, 0x11445, 0x11446, 0x1145e, 0x114b0, 0x114b1, 0x114b2,
 9289|    993|      0x114b3, 0x114b4, 0x114b5, 0x114b6, 0x114b7, 0x114b8, 0x114b9, 0x114ba,
 9290|    993|      0x114bb, 0x114bc, 0x114bd, 0x114be, 0x114bf, 0x114c0, 0x114c1, 0x114c2,
 9291|    993|      0x114c3, 0x115af, 0x115b0, 0x115b1, 0x115b2, 0x115b3, 0x115b4, 0x115b5,
 9292|    993|      0x115b8, 0x115b9, 0x115ba, 0x115bb, 0x115bc, 0x115bd, 0x115be, 0x115bf,
 9293|    993|      0x115c0, 0x115dc, 0x115dd, 0x11630, 0x11631, 0x11632, 0x11633, 0x11634,
 9294|    993|      0x11635, 0x11636, 0x11637, 0x11638, 0x11639, 0x1163a, 0x1163b, 0x1163c,
 9295|    993|      0x1163d, 0x1163e, 0x1163f, 0x11640, 0x116ab, 0x116ac, 0x116ad, 0x116ae,
 9296|    993|      0x116af, 0x116b0, 0x116b1, 0x116b2, 0x116b3, 0x116b4, 0x116b5, 0x116b6,
 9297|    993|      0x116b7, 0x1171d, 0x1171e, 0x1171f, 0x11720, 0x11721, 0x11722, 0x11723,
 9298|    993|      0x11724, 0x11725, 0x11726, 0x11727, 0x11728, 0x11729, 0x1172a, 0x1172b,
 9299|    993|      0x1182c, 0x1182d, 0x1182e, 0x1182f, 0x11830, 0x11831, 0x11832, 0x11833,
 9300|    993|      0x11834, 0x11835, 0x11836, 0x11837, 0x11838, 0x11839, 0x1183a, 0x11930,
 9301|    993|      0x11931, 0x11932, 0x11933, 0x11934, 0x11935, 0x11937, 0x11938, 0x1193b,
 9302|    993|      0x1193c, 0x1193d, 0x1193e, 0x11940, 0x11942, 0x11943, 0x119d1, 0x119d2,
 9303|    993|      0x119d3, 0x119d4, 0x119d5, 0x119d6, 0x119d7, 0x119da, 0x119db, 0x119dc,
 9304|    993|      0x119dd, 0x119de, 0x119df, 0x119e0, 0x119e4, 0x11a01, 0x11a02, 0x11a03,
 9305|    993|      0x11a04, 0x11a05, 0x11a06, 0x11a07, 0x11a08, 0x11a09, 0x11a0a, 0x11a33,
 9306|    993|      0x11a34, 0x11a35, 0x11a36, 0x11a37, 0x11a38, 0x11a39, 0x11a3b, 0x11a3c,
 9307|    993|      0x11a3d, 0x11a3e, 0x11a47, 0x11a51, 0x11a52, 0x11a53, 0x11a54, 0x11a55,
 9308|    993|      0x11a56, 0x11a57, 0x11a58, 0x11a59, 0x11a5a, 0x11a5b, 0x11a8a, 0x11a8b,
 9309|    993|      0x11a8c, 0x11a8d, 0x11a8e, 0x11a8f, 0x11a90, 0x11a91, 0x11a92, 0x11a93,
 9310|    993|      0x11a94, 0x11a95, 0x11a96, 0x11a97, 0x11a98, 0x11a99, 0x11c2f, 0x11c30,
 9311|    993|      0x11c31, 0x11c32, 0x11c33, 0x11c34, 0x11c35, 0x11c36, 0x11c38, 0x11c39,
 9312|    993|      0x11c3a, 0x11c3b, 0x11c3c, 0x11c3d, 0x11c3e, 0x11c3f, 0x11c92, 0x11c93,
 9313|    993|      0x11c94, 0x11c95, 0x11c96, 0x11c97, 0x11c98, 0x11c99, 0x11c9a, 0x11c9b,
 9314|    993|      0x11c9c, 0x11c9d, 0x11c9e, 0x11c9f, 0x11ca0, 0x11ca1, 0x11ca2, 0x11ca3,
 9315|    993|      0x11ca4, 0x11ca5, 0x11ca6, 0x11ca7, 0x11ca9, 0x11caa, 0x11cab, 0x11cac,
 9316|    993|      0x11cad, 0x11cae, 0x11caf, 0x11cb0, 0x11cb1, 0x11cb2, 0x11cb3, 0x11cb4,
 9317|    993|      0x11cb5, 0x11cb6, 0x11d31, 0x11d32, 0x11d33, 0x11d34, 0x11d35, 0x11d36,
 9318|    993|      0x11d3a, 0x11d3c, 0x11d3d, 0x11d3f, 0x11d40, 0x11d41, 0x11d42, 0x11d43,
 9319|    993|      0x11d44, 0x11d45, 0x11d47, 0x11d8a, 0x11d8b, 0x11d8c, 0x11d8d, 0x11d8e,
 9320|    993|      0x11d90, 0x11d91, 0x11d93, 0x11d94, 0x11d95, 0x11d96, 0x11d97, 0x11ef3,
 9321|    993|      0x11ef4, 0x11ef5, 0x11ef6, 0x16af0, 0x16af1, 0x16af2, 0x16af3, 0x16af4,
 9322|    993|      0x16b30, 0x16b31, 0x16b32, 0x16b33, 0x16b34, 0x16b35, 0x16b36, 0x16f4f,
 9323|    993|      0x16f51, 0x16f52, 0x16f53, 0x16f54, 0x16f55, 0x16f56, 0x16f57, 0x16f58,
 9324|    993|      0x16f59, 0x16f5a, 0x16f5b, 0x16f5c, 0x16f5d, 0x16f5e, 0x16f5f, 0x16f60,
 9325|    993|      0x16f61, 0x16f62, 0x16f63, 0x16f64, 0x16f65, 0x16f66, 0x16f67, 0x16f68,
 9326|    993|      0x16f69, 0x16f6a, 0x16f6b, 0x16f6c, 0x16f6d, 0x16f6e, 0x16f6f, 0x16f70,
 9327|    993|      0x16f71, 0x16f72, 0x16f73, 0x16f74, 0x16f75, 0x16f76, 0x16f77, 0x16f78,
 9328|    993|      0x16f79, 0x16f7a, 0x16f7b, 0x16f7c, 0x16f7d, 0x16f7e, 0x16f7f, 0x16f80,
 9329|    993|      0x16f81, 0x16f82, 0x16f83, 0x16f84, 0x16f85, 0x16f86, 0x16f87, 0x16f8f,
 9330|    993|      0x16f90, 0x16f91, 0x16f92, 0x16fe4, 0x16ff0, 0x16ff1, 0x1bc9d, 0x1bc9e,
 9331|    993|      0x1d165, 0x1d166, 0x1d167, 0x1d168, 0x1d169, 0x1d16d, 0x1d16e, 0x1d16f,
 9332|    993|      0x1d170, 0x1d171, 0x1d172, 0x1d17b, 0x1d17c, 0x1d17d, 0x1d17e, 0x1d17f,
 9333|    993|      0x1d180, 0x1d181, 0x1d182, 0x1d185, 0x1d186, 0x1d187, 0x1d188, 0x1d189,
 9334|    993|      0x1d18a, 0x1d18b, 0x1d1aa, 0x1d1ab, 0x1d1ac, 0x1d1ad, 0x1d242, 0x1d243,
 9335|    993|      0x1d244, 0x1da00, 0x1da01, 0x1da02, 0x1da03, 0x1da04, 0x1da05, 0x1da06,
 9336|    993|      0x1da07, 0x1da08, 0x1da09, 0x1da0a, 0x1da0b, 0x1da0c, 0x1da0d, 0x1da0e,
 9337|    993|      0x1da0f, 0x1da10, 0x1da11, 0x1da12, 0x1da13, 0x1da14, 0x1da15, 0x1da16,
 9338|    993|      0x1da17, 0x1da18, 0x1da19, 0x1da1a, 0x1da1b, 0x1da1c, 0x1da1d, 0x1da1e,
 9339|    993|      0x1da1f, 0x1da20, 0x1da21, 0x1da22, 0x1da23, 0x1da24, 0x1da25, 0x1da26,
 9340|    993|      0x1da27, 0x1da28, 0x1da29, 0x1da2a, 0x1da2b, 0x1da2c, 0x1da2d, 0x1da2e,
 9341|    993|      0x1da2f, 0x1da30, 0x1da31, 0x1da32, 0x1da33, 0x1da34, 0x1da35, 0x1da36,
 9342|    993|      0x1da3b, 0x1da3c, 0x1da3d, 0x1da3e, 0x1da3f, 0x1da40, 0x1da41, 0x1da42,
 9343|    993|      0x1da43, 0x1da44, 0x1da45, 0x1da46, 0x1da47, 0x1da48, 0x1da49, 0x1da4a,
 9344|    993|      0x1da4b, 0x1da4c, 0x1da4d, 0x1da4e, 0x1da4f, 0x1da50, 0x1da51, 0x1da52,
 9345|    993|      0x1da53, 0x1da54, 0x1da55, 0x1da56, 0x1da57, 0x1da58, 0x1da59, 0x1da5a,
 9346|    993|      0x1da5b, 0x1da5c, 0x1da5d, 0x1da5e, 0x1da5f, 0x1da60, 0x1da61, 0x1da62,
 9347|    993|      0x1da63, 0x1da64, 0x1da65, 0x1da66, 0x1da67, 0x1da68, 0x1da69, 0x1da6a,
 9348|    993|      0x1da6b, 0x1da6c, 0x1da75, 0x1da84, 0x1da9b, 0x1da9c, 0x1da9d, 0x1da9e,
 9349|    993|      0x1da9f, 0x1daa1, 0x1daa2, 0x1daa3, 0x1daa4, 0x1daa5, 0x1daa6, 0x1daa7,
 9350|    993|      0x1daa8, 0x1daa9, 0x1daaa, 0x1daab, 0x1daac, 0x1daad, 0x1daae, 0x1daaf,
 9351|    993|      0x1e000, 0x1e001, 0x1e002, 0x1e003, 0x1e004, 0x1e005, 0x1e006, 0x1e008,
 9352|    993|      0x1e009, 0x1e00a, 0x1e00b, 0x1e00c, 0x1e00d, 0x1e00e, 0x1e00f, 0x1e010,
 9353|    993|      0x1e011, 0x1e012, 0x1e013, 0x1e014, 0x1e015, 0x1e016, 0x1e017, 0x1e018,
 9354|    993|      0x1e01b, 0x1e01c, 0x1e01d, 0x1e01e, 0x1e01f, 0x1e020, 0x1e021, 0x1e023,
 9355|    993|      0x1e024, 0x1e026, 0x1e027, 0x1e028, 0x1e029, 0x1e02a, 0x1e130, 0x1e131,
 9356|    993|      0x1e132, 0x1e133, 0x1e134, 0x1e135, 0x1e136, 0x1e2ec, 0x1e2ed, 0x1e2ee,
 9357|    993|      0x1e2ef, 0x1e8d0, 0x1e8d1, 0x1e8d2, 0x1e8d3, 0x1e8d4, 0x1e8d5, 0x1e8d6,
 9358|    993|      0x1e944, 0x1e945, 0x1e946, 0x1e947, 0x1e948, 0x1e949, 0x1e94a, 0xe0100,
 9359|    993|      0xe0101, 0xe0102, 0xe0103, 0xe0104, 0xe0105, 0xe0106, 0xe0107, 0xe0108,
 9360|    993|      0xe0109, 0xe010a, 0xe010b, 0xe010c, 0xe010d, 0xe010e, 0xe010f, 0xe0110,
 9361|    993|      0xe0111, 0xe0112, 0xe0113, 0xe0114, 0xe0115, 0xe0116, 0xe0117, 0xe0118,
 9362|    993|      0xe0119, 0xe011a, 0xe011b, 0xe011c, 0xe011d, 0xe011e, 0xe011f, 0xe0120,
 9363|    993|      0xe0121, 0xe0122, 0xe0123, 0xe0124, 0xe0125, 0xe0126, 0xe0127, 0xe0128,
 9364|    993|      0xe0129, 0xe012a, 0xe012b, 0xe012c, 0xe012d, 0xe012e, 0xe012f, 0xe0130,
 9365|    993|      0xe0131, 0xe0132, 0xe0133, 0xe0134, 0xe0135, 0xe0136, 0xe0137, 0xe0138,
 9366|    993|      0xe0139, 0xe013a, 0xe013b, 0xe013c, 0xe013d, 0xe013e, 0xe013f, 0xe0140,
 9367|    993|      0xe0141, 0xe0142, 0xe0143, 0xe0144, 0xe0145, 0xe0146, 0xe0147, 0xe0148,
 9368|    993|      0xe0149, 0xe014a, 0xe014b, 0xe014c, 0xe014d, 0xe014e, 0xe014f, 0xe0150,
 9369|    993|      0xe0151, 0xe0152, 0xe0153, 0xe0154, 0xe0155, 0xe0156, 0xe0157, 0xe0158,
 9370|    993|      0xe0159, 0xe015a, 0xe015b, 0xe015c, 0xe015d, 0xe015e, 0xe015f, 0xe0160,
 9371|    993|      0xe0161, 0xe0162, 0xe0163, 0xe0164, 0xe0165, 0xe0166, 0xe0167, 0xe0168,
 9372|    993|      0xe0169, 0xe016a, 0xe016b, 0xe016c, 0xe016d, 0xe016e, 0xe016f, 0xe0170,
 9373|    993|      0xe0171, 0xe0172, 0xe0173, 0xe0174, 0xe0175, 0xe0176, 0xe0177, 0xe0178,
 9374|    993|      0xe0179, 0xe017a, 0xe017b, 0xe017c, 0xe017d, 0xe017e, 0xe017f, 0xe0180,
 9375|    993|      0xe0181, 0xe0182, 0xe0183, 0xe0184, 0xe0185, 0xe0186, 0xe0187, 0xe0188,
 9376|    993|      0xe0189, 0xe018a, 0xe018b, 0xe018c, 0xe018d, 0xe018e, 0xe018f, 0xe0190,
 9377|    993|      0xe0191, 0xe0192, 0xe0193, 0xe0194, 0xe0195, 0xe0196, 0xe0197, 0xe0198,
 9378|    993|      0xe0199, 0xe019a, 0xe019b, 0xe019c, 0xe019d, 0xe019e, 0xe019f, 0xe01a0,
 9379|    993|      0xe01a1, 0xe01a2, 0xe01a3, 0xe01a4, 0xe01a5, 0xe01a6, 0xe01a7, 0xe01a8,
 9380|    993|      0xe01a9, 0xe01aa, 0xe01ab, 0xe01ac, 0xe01ad, 0xe01ae, 0xe01af, 0xe01b0,
 9381|    993|      0xe01b1, 0xe01b2, 0xe01b3, 0xe01b4, 0xe01b5, 0xe01b6, 0xe01b7, 0xe01b8,
 9382|    993|      0xe01b9, 0xe01ba, 0xe01bb, 0xe01bc, 0xe01bd, 0xe01be, 0xe01bf, 0xe01c0,
 9383|    993|      0xe01c1, 0xe01c2, 0xe01c3, 0xe01c4, 0xe01c5, 0xe01c6, 0xe01c7, 0xe01c8,
 9384|    993|      0xe01c9, 0xe01ca, 0xe01cb, 0xe01cc, 0xe01cd, 0xe01ce, 0xe01cf, 0xe01d0,
 9385|    993|      0xe01d1, 0xe01d2, 0xe01d3, 0xe01d4, 0xe01d5, 0xe01d6, 0xe01d7, 0xe01d8,
 9386|    993|      0xe01d9, 0xe01da, 0xe01db, 0xe01dc, 0xe01dd, 0xe01de, 0xe01df, 0xe01e0,
 9387|    993|      0xe01e1, 0xe01e2, 0xe01e3, 0xe01e4, 0xe01e5, 0xe01e6, 0xe01e7, 0xe01e8,
 9388|    993|      0xe01e9, 0xe01ea, 0xe01eb, 0xe01ec, 0xe01ed, 0xe01ee, 0xe01ef};
 9389|    993|  if (std::ranges::binary_search(combining, label.front())) {
  ------------------
  |  Branch (9389:7): [True: 6, False: 987]
  ------------------
 9390|      6|    return false;
 9391|      6|  }
 9392|       |  // We verify this next step as part of the mapping:
 9393|       |  // ---------------------------------------------
 9394|       |  // Each code point in the label must only have certain status values
 9395|       |  // according to Section 5, IDNA Mapping Table:
 9396|       |  // - For Transitional Processing, each value must be valid.
 9397|       |  // - For Nontransitional Processing, each value must be either valid or
 9398|       |  // deviation.
 9399|       |
 9400|       |  // If CheckJoiners, the label must satisfy the ContextJ rules from Appendix
 9401|       |  // A, in The Unicode Code Points and Internationalized Domain Names for
 9402|       |  // Applications (IDNA) [IDNA2008].
 9403|    987|  constexpr static uint32_t virama[] = {
 9404|    987|      0x094D,  0x09CD,  0x0A4D,  0x0ACD,  0x0B4D,  0x0BCD,  0x0C4D,  0x0CCD,
 9405|    987|      0x0D3B,  0x0D3C,  0x0D4D,  0x0DCA,  0x0E3A,  0x0EBA,  0x0F84,  0x1039,
 9406|    987|      0x103A,  0x1714,  0x1734,  0x17D2,  0x1A60,  0x1B44,  0x1BAA,  0x1BAB,
 9407|    987|      0x1BF2,  0x1BF3,  0x2D7F,  0xA806,  0xA82C,  0xA8C4,  0xA953,  0xA9C0,
 9408|    987|      0xAAF6,  0xABED,  0x10A3F, 0x11046, 0x1107F, 0x110B9, 0x11133, 0x11134,
 9409|    987|      0x111C0, 0x11235, 0x112EA, 0x1134D, 0x11442, 0x114C2, 0x115BF, 0x1163F,
 9410|    987|      0x116B6, 0x1172B, 0x11839, 0x1193D, 0x1193E, 0x119E0, 0x11A34, 0x11A47,
 9411|    987|      0x11A99, 0x11C3F, 0x11D44, 0x11D45, 0x11D97};
 9412|    987|  constexpr static uint32_t R[] = {
 9413|    987|      0x622, 0x623, 0x624, 0x625, 0x627, 0x629, 0x62f, 0x630, 0x631,
 9414|    987|      0x632, 0x648, 0x671, 0x672, 0x673, 0x675, 0x676, 0x677, 0x688,
 9415|    987|      0x689, 0x68a, 0x68b, 0x68c, 0x68d, 0x68e, 0x68f, 0x690, 0x691,
 9416|    987|      0x692, 0x693, 0x694, 0x695, 0x696, 0x697, 0x698, 0x699, 0x6c0,
 9417|    987|      0x6c3, 0x6c4, 0x6c5, 0x6c6, 0x6c7, 0x6c8, 0x6c9, 0x6ca, 0x6cb,
 9418|    987|      0x6cd, 0x6cf, 0x6d2, 0x6d3, 0x6d5, 0x6ee, 0x6ef, 0x710, 0x715,
 9419|    987|      0x716, 0x717, 0x718, 0x719, 0x71e, 0x728, 0x72a, 0x72c, 0x72f,
 9420|    987|      0x74d, 0x759, 0x75a, 0x75b, 0x854, 0x8aa, 0x8ab, 0x8ac};
 9421|    987|  constexpr static uint32_t L[] = {0xa872};
 9422|    987|  constexpr static uint32_t D[] = {
 9423|    987|      0x620,  0x626,  0x628,  0x62a,  0x62b,  0x62c,  0x62d,  0x62e,  0x633,
 9424|    987|      0x634,  0x635,  0x636,  0x637,  0x638,  0x639,  0x63a,  0x63b,  0x63c,
 9425|    987|      0x63d,  0x63e,  0x63f,  0x641,  0x642,  0x643,  0x644,  0x645,  0x646,
 9426|    987|      0x647,  0x649,  0x64a,  0x66e,  0x66f,  0x678,  0x679,  0x67a,  0x67b,
 9427|    987|      0x67c,  0x67d,  0x67e,  0x67f,  0x680,  0x681,  0x682,  0x683,  0x684,
 9428|    987|      0x685,  0x686,  0x687,  0x69a,  0x69b,  0x69c,  0x69d,  0x69e,  0x69f,
 9429|    987|      0x6a0,  0x6a1,  0x6a2,  0x6a3,  0x6a4,  0x6a5,  0x6a6,  0x6a7,  0x6a8,
 9430|    987|      0x6a9,  0x6aa,  0x6ab,  0x6ac,  0x6ad,  0x6ae,  0x6af,  0x6b0,  0x6b1,
 9431|    987|      0x6b2,  0x6b3,  0x6b4,  0x6b5,  0x6b6,  0x6b7,  0x6b8,  0x6b9,  0x6ba,
 9432|    987|      0x6bb,  0x6bc,  0x6bd,  0x6be,  0x6bf,  0x6c1,  0x6c2,  0x6cc,  0x6ce,
 9433|    987|      0x6d0,  0x6d1,  0x6fa,  0x6fb,  0x6fc,  0x6ff,  0x712,  0x713,  0x714,
 9434|    987|      0x71a,  0x71b,  0x71c,  0x71d,  0x71f,  0x720,  0x721,  0x722,  0x723,
 9435|    987|      0x724,  0x725,  0x726,  0x727,  0x729,  0x72b,  0x72d,  0x72e,  0x74e,
 9436|    987|      0x74f,  0x750,  0x751,  0x752,  0x753,  0x754,  0x755,  0x756,  0x757,
 9437|    987|      0x758,  0x75c,  0x75d,  0x75e,  0x75f,  0x760,  0x761,  0x762,  0x763,
 9438|    987|      0x764,  0x765,  0x766,  0x850,  0x851,  0x852,  0x853,  0x855,  0x8a0,
 9439|    987|      0x8a2,  0x8a3,  0x8a4,  0x8a5,  0x8a6,  0x8a7,  0x8a8,  0x8a9,  0x1807,
 9440|    987|      0x1820, 0x1821, 0x1822, 0x1823, 0x1824, 0x1825, 0x1826, 0x1827, 0x1828,
 9441|    987|      0x1829, 0x182a, 0x182b, 0x182c, 0x182d, 0x182e, 0x182f, 0x1830, 0x1831,
 9442|    987|      0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837, 0x1838, 0x1839, 0x183a,
 9443|    987|      0x183b, 0x183c, 0x183d, 0x183e, 0x183f, 0x1840, 0x1841, 0x1842, 0x1843,
 9444|    987|      0x1844, 0x1845, 0x1846, 0x1847, 0x1848, 0x1849, 0x184a, 0x184b, 0x184c,
 9445|    987|      0x184d, 0x184e, 0x184f, 0x1850, 0x1851, 0x1852, 0x1853, 0x1854, 0x1855,
 9446|    987|      0x1856, 0x1857, 0x1858, 0x1859, 0x185a, 0x185b, 0x185c, 0x185d, 0x185e,
 9447|    987|      0x185f, 0x1860, 0x1861, 0x1862, 0x1863, 0x1864, 0x1865, 0x1866, 0x1867,
 9448|    987|      0x1868, 0x1869, 0x186a, 0x186b, 0x186c, 0x186d, 0x186e, 0x186f, 0x1870,
 9449|    987|      0x1871, 0x1872, 0x1873, 0x1874, 0x1875, 0x1876, 0x1877, 0x1887, 0x1888,
 9450|    987|      0x1889, 0x188a, 0x188b, 0x188c, 0x188d, 0x188e, 0x188f, 0x1890, 0x1891,
 9451|    987|      0x1892, 0x1893, 0x1894, 0x1895, 0x1896, 0x1897, 0x1898, 0x1899, 0x189a,
 9452|    987|      0x189b, 0x189c, 0x189d, 0x189e, 0x189f, 0x18a0, 0x18a1, 0x18a2, 0x18a3,
 9453|    987|      0x18a4, 0x18a5, 0x18a6, 0x18a7, 0x18a8, 0x18aa, 0xa840, 0xa841, 0xa842,
 9454|    987|      0xa843, 0xa844, 0xa845, 0xa846, 0xa847, 0xa848, 0xa849, 0xa84a, 0xa84b,
 9455|    987|      0xa84c, 0xa84d, 0xa84e, 0xa84f, 0xa850, 0xa851, 0xa852, 0xa853, 0xa854,
 9456|    987|      0xa855, 0xa856, 0xa857, 0xa858, 0xa859, 0xa85a, 0xa85b, 0xa85c, 0xa85d,
 9457|    987|      0xa85e, 0xa85f, 0xa860, 0xa861, 0xa862, 0xa863, 0xa864, 0xa865, 0xa866,
 9458|    987|      0xa867, 0xa868, 0xa869, 0xa86a, 0xa86b, 0xa86c, 0xa86d, 0xa86e, 0xa86f,
 9459|    987|      0xa870, 0xa871};
 9460|       |
 9461|  14.3k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (9461:22): [True: 13.4k, False: 926]
  ------------------
 9462|  13.4k|    uint32_t c = label[i];
 9463|  13.4k|    if (c == 0x200c) {
  ------------------
  |  Branch (9463:9): [True: 53, False: 13.4k]
  ------------------
 9464|     53|      if (i > 0) {
  ------------------
  |  Branch (9464:11): [True: 52, False: 1]
  ------------------
 9465|     52|        if (std::ranges::binary_search(virama, label[i - 1])) {
  ------------------
  |  Branch (9465:13): [True: 1, False: 51]
  ------------------
 9466|      1|          return true;
 9467|      1|        }
 9468|     52|      }
 9469|     52|      if ((i == 0) || (i + 1 >= label.size())) {
  ------------------
  |  Branch (9469:11): [True: 1, False: 51]
  |  Branch (9469:23): [True: 1, False: 50]
  ------------------
 9470|      2|        return false;
 9471|      2|      }
 9472|       |      // we go backward looking for L or D
 9473|     50|      auto is_l_or_d = [](uint32_t code) {
 9474|     50|        return std::ranges::binary_search(L, code) ||
 9475|     50|               std::ranges::binary_search(D, code);
 9476|     50|      };
 9477|     50|      auto is_r_or_d = [](uint32_t code) {
 9478|     50|        return std::ranges::binary_search(R, code) ||
 9479|     50|               std::ranges::binary_search(D, code);
 9480|     50|      };
 9481|     50|      std::u32string_view before = label.substr(0, i);
 9482|     50|      std::u32string_view after = label.substr(i + 1);
 9483|     50|      return (std::find_if(before.begin(), before.end(), is_l_or_d) !=
  ------------------
  |  Branch (9483:14): [True: 27, False: 23]
  ------------------
 9484|     50|              before.end()) &&
 9485|     27|             (std::find_if(after.begin(), after.end(), is_r_or_d) !=
  ------------------
  |  Branch (9485:14): [True: 8, False: 19]
  ------------------
 9486|     27|              after.end());
 9487|  13.4k|    } else if (c == 0x200d) {
  ------------------
  |  Branch (9487:16): [True: 8, False: 13.3k]
  ------------------
 9488|      8|      if (i > 0) {
  ------------------
  |  Branch (9488:11): [True: 7, False: 1]
  ------------------
 9489|      7|        if (std::ranges::binary_search(virama, label[i - 1])) {
  ------------------
  |  Branch (9489:13): [True: 0, False: 7]
  ------------------
 9490|      0|          return true;
 9491|      0|        }
 9492|      7|      }
 9493|      8|      return false;
 9494|      8|    }
 9495|  13.4k|  }
 9496|       |
 9497|       |  // If CheckBidi, and if the domain name is a  Bidi domain name, then the label
 9498|       |  // must satisfy all six of the numbered conditions in [IDNA2008] RFC 5893,
 9499|       |  // Section 2.
 9500|       |
 9501|       |  // The following rule, consisting of six conditions, applies to labels
 9502|       |  // in Bidi domain names.  The requirements that this rule satisfies are
 9503|       |  // described in Section 3.  All of the conditions must be satisfied for
 9504|       |  // the rule to be satisfied.
 9505|       |  //
 9506|       |  //  1.  The first character must be a character with Bidi property L, R,
 9507|       |  //     or AL.  If it has the R or AL property, it is an RTL label; if it
 9508|       |  //     has the L property, it is an LTR label.
 9509|       |  //
 9510|       |  //  2.  In an RTL label, only characters with the Bidi properties R, AL,
 9511|       |  //      AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
 9512|       |  //
 9513|       |  //   3.  In an RTL label, the end of the label must be a character with
 9514|       |  //       Bidi property R, AL, EN, or AN, followed by zero or more
 9515|       |  //       characters with Bidi property NSM.
 9516|       |  //
 9517|       |  //   4.  In an RTL label, if an EN is present, no AN may be present, and
 9518|       |  //       vice versa.
 9519|       |  //
 9520|       |  //  5.  In an LTR label, only characters with the Bidi properties L, EN,
 9521|       |  //       ES, CS, ET, ON, BN, or NSM are allowed.
 9522|       |  //
 9523|       |  //   6.  In an LTR label, the end of the label must be a character with
 9524|       |  //       Bidi property L or EN, followed by zero or more characters with
 9525|       |  //       Bidi property NSM.
 9526|       |
 9527|    926|  size_t last_non_nsm_char = find_last_not_of_nsm(label);
 9528|    926|  if (last_non_nsm_char == std::u32string_view::npos) {
  ------------------
  |  Branch (9528:7): [True: 0, False: 926]
  ------------------
 9529|      0|    return false;
 9530|      0|  }
 9531|       |
 9532|       |  // A "Bidi domain name" is a domain name that contains at least one RTL label.
 9533|       |  // The following rule, consisting of six conditions, applies to labels in Bidi
 9534|       |  // domain names.
 9535|    926|  if (is_rtl_label(label)) {
  ------------------
  |  Branch (9535:7): [True: 183, False: 743]
  ------------------
 9536|       |    // The first character must be a character with Bidi property L, R,
 9537|       |    // or AL. If it has the R or AL property, it is an RTL label; if it
 9538|       |    // has the L property, it is an LTR label.
 9539|       |
 9540|    183|    if (find_direction(label[0]) == direction::L) {
  ------------------
  |  Branch (9540:9): [True: 75, False: 108]
  ------------------
 9541|       |      // Eval as LTR
 9542|       |
 9543|       |      // In an LTR label, only characters with the Bidi properties L, EN,
 9544|       |      // ES, CS, ET, ON, BN, or NSM are allowed.
 9545|  1.88k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (9545:26): [True: 1.88k, False: 0]
  ------------------
 9546|  1.88k|        const direction d = find_direction(label[i]);
 9547|  1.88k|        if (!(d == direction::L || d == direction::EN || d == direction::ES ||
  ------------------
  |  Branch (9547:15): [True: 854, False: 1.02k]
  |  Branch (9547:36): [True: 359, False: 668]
  |  Branch (9547:58): [True: 123, False: 545]
  ------------------
 9548|    545|              d == direction::CS || d == direction::ET || d == direction::ON ||
  ------------------
  |  Branch (9548:15): [True: 119, False: 426]
  |  Branch (9548:37): [True: 44, False: 382]
  |  Branch (9548:59): [True: 121, False: 261]
  ------------------
 9549|    261|              d == direction::BN || d == direction::NSM)) {
  ------------------
  |  Branch (9549:15): [True: 178, False: 83]
  |  Branch (9549:37): [True: 8, False: 75]
  ------------------
 9550|     75|          return false;
 9551|     75|        }
 9552|  1.88k|      }
 9553|       |
 9554|      0|      const direction last_dir = find_direction(label[last_non_nsm_char]);
 9555|      0|      if (!(last_dir == direction::L || last_dir == direction::EN)) {
  ------------------
  |  Branch (9555:13): [True: 0, False: 0]
  |  Branch (9555:41): [True: 0, False: 0]
  ------------------
 9556|      0|        return false;
 9557|      0|      }
 9558|       |
 9559|      0|      return true;
 9560|       |
 9561|    108|    } else {
 9562|       |      // Eval as RTL
 9563|       |
 9564|       |      // The first character must be R or AL; a leading AN (or any other
 9565|       |      // direction) does not start a valid RTL label.
 9566|    108|      const direction first_dir = find_direction(label[0]);
 9567|    108|      if (first_dir != direction::R && first_dir != direction::AL) {
  ------------------
  |  Branch (9567:11): [True: 85, False: 23]
  |  Branch (9567:40): [True: 28, False: 57]
  ------------------
 9568|     28|        return false;
 9569|     28|      }
 9570|       |
 9571|     80|      bool has_an = false;
 9572|     80|      bool has_en = false;
 9573|  1.16k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (9573:26): [True: 1.11k, False: 50]
  ------------------
 9574|  1.11k|        const direction d = find_direction(label[i]);
 9575|       |
 9576|       |        // In an RTL label, if an EN is present, no AN may be present, and vice
 9577|       |        // versa.
 9578|  1.11k|        if ((d == direction::EN && ((has_en = true) && has_an)) ||
  ------------------
  |  Branch (9578:14): [True: 152, False: 958]
  |  Branch (9578:37): [True: 152, False: 0]
  |  Branch (9578:56): [True: 1, False: 151]
  ------------------
 9579|  1.10k|            (d == direction::AN && ((has_an = true) && has_en))) {
  ------------------
  |  Branch (9579:14): [True: 10, False: 1.09k]
  |  Branch (9579:37): [True: 10, False: 0]
  |  Branch (9579:56): [True: 0, False: 10]
  ------------------
 9580|      1|          return false;
 9581|      1|        }
 9582|       |
 9583|  1.10k|        if (!(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (9583:15): [True: 65, False: 1.04k]
  |  Branch (9583:36): [True: 251, False: 793]
  |  Branch (9583:58): [True: 10, False: 783]
  ------------------
 9584|    783|              d == direction::EN || d == direction::ES || d == direction::CS ||
  ------------------
  |  Branch (9584:15): [True: 151, False: 632]
  |  Branch (9584:37): [True: 77, False: 555]
  |  Branch (9584:59): [True: 77, False: 478]
  ------------------
 9585|    478|              d == direction::ET || d == direction::ON || d == direction::BN ||
  ------------------
  |  Branch (9585:15): [True: 135, False: 343]
  |  Branch (9585:37): [True: 176, False: 167]
  |  Branch (9585:59): [True: 50, False: 117]
  ------------------
 9586|    117|              d == direction::NSM)) {
  ------------------
  |  Branch (9586:15): [True: 102, False: 15]
  ------------------
 9587|     15|          return false;
 9588|     15|        }
 9589|       |
 9590|  1.09k|        if (i == last_non_nsm_char &&
  ------------------
  |  Branch (9590:13): [True: 64, False: 1.03k]
  ------------------
 9591|     64|            !(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (9591:15): [True: 5, False: 59]
  |  Branch (9591:36): [True: 22, False: 37]
  |  Branch (9591:58): [True: 0, False: 37]
  ------------------
 9592|     37|              d == direction::EN)) {
  ------------------
  |  Branch (9592:15): [True: 23, False: 14]
  ------------------
 9593|     14|          return false;
 9594|     14|        }
 9595|  1.09k|      }
 9596|       |
 9597|     50|      return true;
 9598|     80|    }
 9599|    183|  }
 9600|       |
 9601|    743|  return true;
 9602|    926|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9728|  1.48k|std::string to_ascii(std::string_view ut8_string) {
 9729|  1.48k|  if (is_ascii(ut8_string)) {
  ------------------
  |  Branch (9729:7): [True: 633, False: 850]
  ------------------
 9730|    633|    return from_ascii_to_ascii(ut8_string);
 9731|    633|  }
 9732|    850|  static const std::string error = "";
 9733|       |  // We convert to UTF-32
 9734|       |
 9735|       |#ifdef ADA_USE_SIMDUTF
 9736|       |  size_t utf32_length =
 9737|       |      simdutf::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 9738|       |  std::u32string utf32(utf32_length, '\0');
 9739|       |  size_t actual_utf32_length = simdutf::convert_utf8_to_utf32(
 9740|       |      ut8_string.data(), ut8_string.size(), utf32.data());
 9741|       |#else
 9742|    850|  size_t utf32_length =
 9743|    850|      ada::idna::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 9744|    850|  std::u32string utf32(utf32_length, '\0');
 9745|    850|  size_t actual_utf32_length = ada::idna::utf8_to_utf32(
 9746|    850|      ut8_string.data(), ut8_string.size(), utf32.data());
 9747|    850|#endif
 9748|    850|  if (actual_utf32_length == 0) {
  ------------------
  |  Branch (9748:7): [True: 161, False: 689]
  ------------------
 9749|    161|    return error;
 9750|    161|  }
 9751|       |  // mapping: use the two-argument overload to avoid an extra heap allocation
 9752|       |  // that the single-argument overload (which returns by value) would incur.
 9753|    689|  std::u32string tmp_buffer;
 9754|    689|  std::u32string post_map;
 9755|    689|  if (!ada::idna::map(utf32, tmp_buffer)) {
  ------------------
  |  Branch (9755:7): [True: 45, False: 644]
  ------------------
 9756|     45|    return error;
 9757|     45|  }
 9758|    644|  utf32 = std::move(tmp_buffer);
 9759|    644|  normalize(utf32);
 9760|    644|  std::string out;
 9761|    644|  out.reserve(ut8_string.size());
 9762|    644|  size_t label_start = 0;
 9763|       |
 9764|  2.74k|  while (label_start != utf32.size()) {
  ------------------
  |  Branch (9764:10): [True: 2.29k, False: 442]
  ------------------
 9765|  2.29k|    size_t loc_dot = utf32.find('.', label_start);
 9766|  2.29k|    bool is_last_label = (loc_dot == std::string_view::npos);
 9767|  2.29k|    size_t label_size =
 9768|  2.29k|        is_last_label ? utf32.size() - label_start : loc_dot - label_start;
  ------------------
  |  Branch (9768:9): [True: 435, False: 1.86k]
  ------------------
 9769|  2.29k|    size_t label_size_with_dot = is_last_label ? label_size : label_size + 1;
  ------------------
  |  Branch (9769:34): [True: 435, False: 1.86k]
  ------------------
 9770|  2.29k|    std::u32string_view label_view(utf32.data() + label_start, label_size);
 9771|  2.29k|    label_start += label_size_with_dot;
 9772|  2.29k|    if (label_size == 0) {
  ------------------
  |  Branch (9772:9): [True: 157, False: 2.14k]
  ------------------
 9773|       |      // empty label? Nothing to do.
 9774|  2.14k|    } else if (label_view.starts_with(U"xn--")) {
  ------------------
  |  Branch (9774:16): [True: 72, False: 2.07k]
  ------------------
 9775|       |      // we do not need to check, e.g., Xn-- because mapping goes to lower case
 9776|       |      // Validate first, then bulk-copy with a single resize to avoid per-char
 9777|       |      // capacity checks in operator+=.
 9778|  1.94k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (9778:23): [True: 1.94k, False: 69]
  ------------------
 9779|  1.94k|        if (c >= 0x80) {
  ------------------
  |  Branch (9779:13): [True: 3, False: 1.94k]
  ------------------
 9780|      3|          return error;
 9781|      3|        }
 9782|  1.94k|      }
 9783|     69|      size_t label_out_start = out.size();
 9784|     69|      out.resize(label_out_start + label_size);
 9785|     69|      char* dest = out.data() + label_out_start;
 9786|  1.88k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (9786:23): [True: 1.88k, False: 69]
  ------------------
 9787|  1.88k|        *dest++ = static_cast<char>(c);
 9788|  1.88k|      }
 9789|     69|      std::string_view puny_segment_ascii(out.data() + label_out_start + 4,
 9790|     69|                                          label_size - 4);
 9791|     69|      tmp_buffer.clear();
 9792|     69|      bool is_ok = ada::idna::punycode_to_utf32(puny_segment_ascii, tmp_buffer);
 9793|     69|      if (!is_ok) {
  ------------------
  |  Branch (9793:11): [True: 6, False: 63]
  ------------------
 9794|      6|        return error;
 9795|      6|      }
 9796|       |      // If the input is just ASCII, it should not have been encoded
 9797|       |      // as punycode.
 9798|       |      // https://github.com/whatwg/url/issues/760
 9799|     63|      if (is_ascii(tmp_buffer)) {
  ------------------
  |  Branch (9799:11): [True: 4, False: 59]
  ------------------
 9800|      4|        return error;
 9801|      4|      }
 9802|     59|      if (!ada::idna::map(tmp_buffer, post_map)) {
  ------------------
  |  Branch (9802:11): [True: 8, False: 51]
  ------------------
 9803|      8|        return error;
 9804|      8|      }
 9805|     51|      if (tmp_buffer != post_map) {
  ------------------
  |  Branch (9805:11): [True: 8, False: 43]
  ------------------
 9806|      8|        return error;
 9807|      8|      }
 9808|     43|      normalize(post_map);
 9809|     43|      if (post_map != tmp_buffer) {
  ------------------
  |  Branch (9809:11): [True: 3, False: 40]
  ------------------
 9810|      3|        return error;
 9811|      3|      }
 9812|     40|      if (post_map.empty()) {
  ------------------
  |  Branch (9812:11): [True: 0, False: 40]
  ------------------
 9813|      0|        return error;
 9814|      0|      }
 9815|     40|      if (!is_label_valid(post_map)) {
  ------------------
  |  Branch (9815:11): [True: 4, False: 36]
  ------------------
 9816|      4|        return error;
 9817|      4|      }
 9818|  2.07k|    } else {
 9819|       |      // The fast path here is an ascii label.
 9820|  2.07k|      if (is_ascii(label_view)) {
  ------------------
  |  Branch (9820:11): [True: 1.23k, False: 831]
  ------------------
 9821|       |        // no validation needed; bulk-copy with single resize.
 9822|  1.23k|        size_t old_size = out.size();
 9823|  1.23k|        out.resize(old_size + label_size);
 9824|  1.23k|        char* dest = out.data() + old_size;
 9825|  5.93k|        for (char32_t c : label_view) {
  ------------------
  |  Branch (9825:25): [True: 5.93k, False: 1.23k]
  ------------------
 9826|  5.93k|          *dest++ = static_cast<char>(c);
 9827|  5.93k|        }
 9828|  1.23k|      } else {
 9829|       |        // slow path.
 9830|       |        // first check validity.
 9831|    831|        if (!is_label_valid(label_view)) {
  ------------------
  |  Branch (9831:13): [True: 166, False: 665]
  ------------------
 9832|    166|          return error;
 9833|    166|        }
 9834|       |        // It is valid! So now we must encode it as punycode...
 9835|    665|        out.append("xn--");
 9836|    665|        bool is_ok = ada::idna::utf32_to_punycode(label_view, out);
 9837|    665|        if (!is_ok) {
  ------------------
  |  Branch (9837:13): [True: 0, False: 665]
  ------------------
 9838|      0|          return error;
 9839|      0|        }
 9840|    665|      }
 9841|  2.07k|    }
 9842|  2.09k|    if (!is_last_label) {
  ------------------
  |  Branch (9842:9): [True: 1.70k, False: 390]
  ------------------
 9843|  1.70k|      out.push_back('.');
 9844|  1.70k|    }
 9845|  2.09k|  }
 9846|    442|  return out;
 9847|    644|}
_ZN3ada7unicode14percent_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
11017|  1.59k|std::string percent_decode(const std::string_view input, size_t first_percent) {
11018|       |  // next line is for safety only, we expect users to avoid calling
11019|       |  // percent_decode when first_percent is outside the range.
11020|  1.59k|  if (first_percent == std::string_view::npos) {
  ------------------
  |  Branch (11020:7): [True: 0, False: 1.59k]
  ------------------
11021|      0|    return std::string(input);
11022|      0|  }
11023|  1.59k|  std::string dest;
11024|  1.59k|  dest.reserve(input.length());
11025|  1.59k|  dest.append(input.substr(0, first_percent));
11026|  1.59k|  const char* pointer = input.data() + first_percent;
11027|  1.59k|  const char* end = input.data() + input.size();
11028|       |  // Optimization opportunity: if the following code gets
11029|       |  // called often, it can be optimized quite a bit.
11030|  28.0k|  while (pointer < end) {
  ------------------
  |  Branch (11030:10): [True: 26.4k, False: 1.59k]
  ------------------
11031|  26.4k|    const char ch = pointer[0];
11032|  26.4k|    size_t remaining = end - pointer - 1;
11033|  26.4k|    if (ch != '%' || remaining < 2 ||
  ------------------
  |  Branch (11033:9): [True: 16.0k, False: 10.3k]
  |  Branch (11033:22): [True: 97, False: 10.2k]
  ------------------
11034|  10.2k|        (  // ch == '%' && // It is unnecessary to check that ch == '%'.
11035|  10.2k|            (!is_ascii_hex_digit(pointer[1]) ||
  ------------------
  |  Branch (11035:14): [True: 810, False: 9.42k]
  ------------------
11036|  17.3k|             !is_ascii_hex_digit(pointer[2])))) {
  ------------------
  |  Branch (11036:14): [True: 415, False: 9.01k]
  ------------------
11037|  17.3k|      dest += ch;
11038|  17.3k|      pointer++;
11039|  17.3k|    } else {
11040|  9.01k|      unsigned a = convert_hex_to_binary(pointer[1]);
11041|  9.01k|      unsigned b = convert_hex_to_binary(pointer[2]);
11042|  9.01k|      char c = static_cast<char>(a * 16 + b);
11043|  9.01k|      dest += c;
11044|  9.01k|      pointer += 3;
11045|  9.01k|    }
11046|  26.4k|  }
11047|  1.59k|  return dest;
11048|  1.59k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
11051|  23.3k|                           const uint8_t character_set[]) {
11052|  23.3k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11053|  23.3k|    return character_sets::bit_at(character_set, c);
11054|  23.3k|  });
11055|       |  // Optimization: Don't iterate if percent encode is not required
11056|  23.3k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11056:7): [True: 14.9k, False: 8.40k]
  ------------------
11057|  14.9k|    return std::string(input);
11058|  14.9k|  }
11059|       |
11060|  8.40k|  std::string result;
11061|  8.40k|  result.reserve(input.length());  // in the worst case, percent encoding might
11062|       |                                   // produce 3 characters.
11063|  8.40k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
11064|       |
11065|   116k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11065:10): [True: 108k, False: 8.40k]
  ------------------
11066|   108k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11066:9): [True: 64.0k, False: 44.1k]
  ------------------
11067|  64.0k|      result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11068|  64.0k|    } else {
11069|  44.1k|      result += *pointer;
11070|  44.1k|    }
11071|   108k|  }
11072|       |
11073|  8.40k|  return result;
11074|  23.3k|}
_ZN3ada7unicode8to_asciiERNSt3__18optionalINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEEENS1_17basic_string_viewIcS5_EEm:
11112|  1.48k|              size_t first_percent) {
11113|  1.48k|  std::string percent_decoded_buffer;
11114|  1.48k|  std::string_view input = plain;
11115|  1.48k|  if (first_percent != std::string_view::npos) {
  ------------------
  |  Branch (11115:7): [True: 304, False: 1.17k]
  ------------------
11116|    304|    percent_decoded_buffer = unicode::percent_decode(plain, first_percent);
11117|    304|    input = percent_decoded_buffer;
11118|    304|  }
11119|       |  // input is a non-empty UTF-8 string, must be percent decoded
11120|  1.48k|  std::string idna_ascii = ada::idna::to_ascii(input);
11121|  1.48k|  if (idna_ascii.empty() || contains_forbidden_domain_code_point(
  ------------------
  |  Branch (11121:7): [True: 545, False: 938]
  |  Branch (11121:29): [True: 258, False: 680]
  ------------------
11122|    938|                                idna_ascii.data(), idna_ascii.size())) {
11123|    803|    return false;
11124|    803|  }
11125|    680|  out = std::move(idna_ascii);
11126|    680|  return true;
11127|  1.48k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhm:
11130|  19.1k|                           const uint8_t character_set[], size_t index) {
11131|  19.1k|  std::string out;
11132|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
11133|  19.1k|  out.append(input.data(), index);
11134|  19.1k|  auto pointer = input.begin() + index;
11135|   121k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11135:10): [True: 102k, False: 19.1k]
  ------------------
11136|   102k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11136:9): [True: 38.4k, False: 64.2k]
  ------------------
11137|  38.4k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11138|  64.2k|    } else {
11139|  64.2k|      out += *pointer;
11140|  64.2k|    }
11141|   102k|  }
11142|  19.1k|  return out;
11143|  19.1k|}
_ZN3ada11serializers36find_longest_sequence_of_ipv6_piecesERKNSt3__15arrayItLm8EEERmS6_:
11156|  6.39k|    size_t& compress_length) noexcept {
11157|  53.6k|  for (size_t i = 0; i < 8; i++) {
  ------------------
  |  Branch (11157:22): [True: 47.7k, False: 5.92k]
  ------------------
11158|  47.7k|    if (address[i] == 0) {
  ------------------
  |  Branch (11158:9): [True: 1.18k, False: 46.5k]
  ------------------
11159|  1.18k|      size_t next = i + 1;
11160|  3.97k|      while (next != 8 && address[next] == 0) ++next;
  ------------------
  |  Branch (11160:14): [True: 3.46k, False: 510]
  |  Branch (11160:27): [True: 2.79k, False: 675]
  ------------------
11161|  1.18k|      const size_t count = next - i;
11162|  1.18k|      if (compress_length < count) {
  ------------------
  |  Branch (11162:11): [True: 1.10k, False: 85]
  ------------------
11163|  1.10k|        compress_length = count;
11164|  1.10k|        compress = i;
11165|  1.10k|        if (next == 8) break;
  ------------------
  |  Branch (11165:13): [True: 468, False: 632]
  ------------------
11166|    632|        i = next;
11167|    632|      }
11168|  1.18k|    }
11169|  47.7k|  }
11170|  6.39k|}
_ZN3ada11serializers4ipv6ERKNSt3__15arrayItLm8EEE:
11172|  3.20k|std::string ipv6(const std::array<uint16_t, 8>& address) {
11173|  3.20k|  size_t compress_length = 0;  // The length of a long sequence of zeros.
11174|  3.20k|  size_t compress = 0;         // The start of a long sequence of zeros.
11175|  3.20k|  find_longest_sequence_of_ipv6_pieces(address, compress, compress_length);
11176|       |
11177|  3.20k|  if (compress_length <= 1) {
  ------------------
  |  Branch (11177:7): [True: 2.89k, False: 301]
  ------------------
11178|       |    // Optimization opportunity: Find a faster way then snprintf for imploding
11179|       |    // and return here.
11180|  2.89k|    compress = compress_length = 8;
11181|  2.89k|  }
11182|       |
11183|  3.20k|  std::string output(4 * 8 + 7 + 2, '\0');
11184|  3.20k|  size_t piece_index = 0;
11185|  3.20k|  char* point = output.data();
11186|  3.20k|  char* point_end = output.data() + output.size();
11187|  3.20k|  *point++ = '[';
11188|  24.1k|  while (true) {
  ------------------
  |  Branch (11188:10): [True: 24.1k, Folded]
  ------------------
11189|  24.1k|    if (piece_index == compress) {
  ------------------
  |  Branch (11189:9): [True: 301, False: 23.8k]
  ------------------
11190|    301|      *point++ = ':';
11191|       |      // If we skip a value initially, we need to write '::', otherwise
11192|       |      // a single ':' will do since it follows a previous ':'.
11193|    301|      if (piece_index == 0) {
  ------------------
  |  Branch (11193:11): [True: 158, False: 143]
  ------------------
11194|    158|        *point++ = ':';
11195|    158|      }
11196|    301|      piece_index += compress_length;
11197|    301|      if (piece_index == 8) {
  ------------------
  |  Branch (11197:11): [True: 216, False: 85]
  ------------------
11198|    216|        break;
11199|    216|      }
11200|    301|    }
11201|  23.9k|    point = std::to_chars(point, point_end, address[piece_index], 16).ptr;
11202|  23.9k|    piece_index++;
11203|  23.9k|    if (piece_index == 8) {
  ------------------
  |  Branch (11203:9): [True: 2.98k, False: 20.9k]
  ------------------
11204|  2.98k|      break;
11205|  2.98k|    }
11206|  20.9k|    *point++ = ':';
11207|  20.9k|  }
11208|  3.20k|  *point++ = ']';
11209|  3.20k|  output.resize(point - output.data());
11210|  3.20k|  return output;
11211|  3.20k|}
_ZN3ada11serializers4ipv4Em:
11213|  3.55k|std::string ipv4(const uint64_t address) {
11214|  3.55k|  std::string output(15, '\0');
11215|  3.55k|  char* point = output.data();
11216|  3.55k|  char* point_end = output.data() + output.size();
11217|  3.55k|  point = std::to_chars(point, point_end, uint8_t(address >> 24)).ptr;
11218|  14.2k|  for (int i = 2; i >= 0; i--) {
  ------------------
  |  Branch (11218:19): [True: 10.6k, False: 3.55k]
  ------------------
11219|  10.6k|    *point++ = '.';
11220|  10.6k|    point = std::to_chars(point, point_end, uint8_t(address >> (i * 8))).ptr;
11221|  10.6k|  }
11222|  3.55k|  output.resize(point - output.data());
11223|  3.55k|  return output;
11224|  3.55k|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
11496|  5.68k|    std::string_view input, const result_type* base_url) {
11497|  5.68k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
11498|  5.68k|  if (!u.is_valid) {
  ------------------
  |  Branch (11498:7): [True: 4.83k, False: 856]
  ------------------
11499|  4.83k|    return tl::unexpected(errors::type_error);
11500|  4.83k|  }
11501|    856|  return u;
11502|  5.68k|}
_ZN3ada20get_max_input_lengthEv:
11245|  5.68k|uint32_t get_max_input_length() {
11246|  5.68k|  return max_input_length_.load(std::memory_order_relaxed);
11247|  5.68k|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12404|  5.68k|void trim_c0_whitespace(std::string_view& input) noexcept {
12405|  5.68k|  while (!input.empty() &&
  ------------------
  |  Branch (12405:10): [True: 5.68k, False: 0]
  ------------------
12406|  5.68k|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (12406:10): [True: 0, False: 5.68k]
  ------------------
12407|      0|    input.remove_prefix(1);
12408|      0|  }
12409|  5.68k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (12409:10): [True: 5.68k, False: 0]
  |  Branch (12409:28): [True: 0, False: 5.68k]
  ------------------
12410|      0|    input.remove_suffix(1);
12411|      0|  }
12412|  5.68k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
13654|  5.68k|                           const result_type* base_url) {
13655|       |  // We can specialize the implementation per type.
13656|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
13657|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
13658|       |  // something else } is free (at runtime). This means that ada::url_aggregator
13659|       |  // and ada::url **do not have to support the exact same API**.
13660|  5.68k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
13661|  5.68k|  constexpr bool result_type_is_ada_url_aggregator =
13662|  5.68k|      std::is_same_v<url_aggregator, result_type>;
13663|  5.68k|  static_assert(result_type_is_ada_url ||
13664|  5.68k|                result_type_is_ada_url_aggregator);  // We don't support
13665|       |                                                     // anything else for now.
13666|       |
13667|  5.68k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
13668|  5.68k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
13669|  5.68k|          ")");
13670|       |
13671|  5.68k|  state state = state::SCHEME_START;
13672|  5.68k|  result_type url{};
13673|       |
13674|  5.68k|  const uint32_t max_input_length = ada::get_max_input_length();
13675|       |
13676|       |  // We refuse to parse URL strings that exceed the maximum input length.
13677|       |  // By default, this is 4GB but can be configured via
13678|       |  // ada::set_max_input_length().
13679|  5.68k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (13679:7): [True: 0, False: 5.68k]
  ------------------
13680|      0|    url.is_valid = false;
13681|      0|  }
13682|       |  // Going forward, user_input.size() is in [0,
13683|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
13684|       |  // base, or the optional_url was invalid, we must return.
13685|  5.68k|  if (base_url != nullptr) {
  ------------------
  |  Branch (13685:7): [True: 0, False: 5.68k]
  ------------------
13686|      0|    url.is_valid &= base_url->is_valid;
13687|      0|  }
13688|  5.68k|  if (!url.is_valid) {
  ------------------
  |  Branch (13688:7): [True: 0, False: 5.68k]
  ------------------
13689|      0|    return url;
13690|      0|  }
13691|  5.68k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
13692|       |    // Most of the time, we just need user_input.size().
13693|       |    // In some instances, we may need a bit more.
13694|       |    ///////////////////////////
13695|       |    // This is *very* important. This line should *not* be removed
13696|       |    // hastily. There are principled reasons why reserve is important
13697|       |    // for performance. If you have a benchmark with small inputs,
13698|       |    // it may not matter, but in other instances, it could.
13699|       |    ////
13700|       |    // This rounds up to the next power of two.
13701|       |    // We know that user_input.size() is in [0,
13702|       |    // std::numeric_limits<uint32_t>::max).
13703|  5.68k|    uint32_t reserve_capacity =
13704|  5.68k|        (0xFFFFFFFF >>
13705|  5.68k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
13706|  5.68k|        1;
13707|  5.68k|    url.reserve(reserve_capacity);
13708|  5.68k|  }
13709|  5.68k|  std::string tmp_buffer;
13710|  5.68k|  std::string_view url_data;
13711|  5.68k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (13711:7): [True: 194, False: 5.49k]
  ------------------
13712|    194|    tmp_buffer = user_input;
13713|       |    // Optimization opportunity: Instead of copying and then pruning, we could
13714|       |    // just directly build the string from user_input.
13715|    194|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
13716|    194|    url_data = tmp_buffer;
13717|  5.49k|  } else [[likely]] {
13718|  5.49k|    url_data = user_input;
13719|  5.49k|  }
13720|       |
13721|       |  // Leading and trailing control characters are uncommon and easy to deal with
13722|       |  // (no performance concern).
13723|  5.68k|  helpers::trim_c0_whitespace(url_data);
13724|       |
13725|       |  // Optimization opportunity. Most websites do not have fragment.
13726|  5.68k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
13727|       |  // We add it last so that an implementation like ada::url_aggregator
13728|       |  // can append it last to its internal buffer, thus improving performance.
13729|       |
13730|       |  // Here url_data no longer has its fragment.
13731|       |  // We are going to access the data from url_data (it is immutable).
13732|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
13733|       |  // The input_position variable should range from 0 to input_size.
13734|       |  // It is illegal to access url_data at input_size.
13735|  5.68k|  size_t input_position = 0;
13736|  5.68k|  const size_t input_size = url_data.size();
13737|       |  // Keep running the following state machine by switching on state.
13738|       |  // If after a run pointer points to the EOF code point, go to the next step.
13739|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
13740|       |  // We never decrement input_position.
13741|  31.1k|  while (input_position <= input_size) {
  ------------------
  |  Branch (13741:10): [True: 30.6k, False: 533]
  ------------------
13742|  30.6k|    ada_log("In parsing at ", input_position, " out of ", input_size,
13743|  30.6k|            " in state ", ada::to_string(state));
13744|  30.6k|    switch (state) {
13745|  5.68k|      case state::SCHEME_START: {
  ------------------
  |  Branch (13745:7): [True: 5.68k, False: 24.9k]
  ------------------
13746|  5.68k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13747|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13748|       |        // state to scheme state.
13749|  5.68k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13749:13): [True: 5.68k, False: 0]
  ------------------
13750|  5.68k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13750:13): [True: 5.68k, False: 0]
  ------------------
13751|  5.68k|          state = state::SCHEME;
13752|  5.68k|          input_position++;
13753|  5.68k|        } else {
13754|       |          // Otherwise, if state override is not given, set state to no scheme
13755|       |          // state and decrease pointer by 1.
13756|      0|          state = state::NO_SCHEME;
13757|      0|        }
13758|  5.68k|        break;
13759|      0|      }
13760|  5.68k|      case state::SCHEME: {
  ------------------
  |  Branch (13760:7): [True: 5.68k, False: 24.9k]
  ------------------
13761|  5.68k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13762|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13763|       |        // append c, lowercased, to buffer.
13764|  22.7k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13764:16): [True: 22.7k, False: 0]
  ------------------
13765|  22.7k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13765:16): [True: 17.0k, False: 5.68k]
  ------------------
13766|  17.0k|          input_position++;
13767|  17.0k|        }
13768|       |        // Otherwise, if c is U+003A (:), then:
13769|  5.68k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13769:13): [True: 5.68k, False: 0]
  ------------------
13770|  5.68k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13770:13): [True: 5.68k, False: 0]
  ------------------
13771|  5.68k|          ada_log("SCHEME the scheme should be ",
13772|  5.68k|                  url_data.substr(0, input_position));
13773|       |          if constexpr (result_type_is_ada_url) {
13774|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
13775|       |              return url;
13776|       |            }
13777|  5.68k|          } else {
13778|       |            // we pass the colon along instead of painfully adding it back.
13779|  5.68k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (13779:17): [True: 0, False: 5.68k]
  ------------------
13780|  5.68k|                    url_data.substr(0, input_position + 1))) {
13781|      0|              return url;
13782|      0|            }
13783|  5.68k|          }
13784|  5.68k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13785|       |
13786|       |          // If url's scheme is "file", then:
13787|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13788|  5.68k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13788:15): [True: 0, False: 5.68k]
  ------------------
13789|       |            // Set state to file state.
13790|      0|            state = state::FILE;
13791|      0|          }
13792|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13793|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13794|       |          // != nullptr is false.
13795|  5.68k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13795:20): [True: 5.68k, False: 0]
  |  Branch (13795:40): [True: 0, False: 5.68k]
  ------------------
13796|      0|                   base_url->type == url.type) {
  ------------------
  |  Branch (13796:20): [True: 0, False: 0]
  ------------------
13797|       |            // Set state to special relative or authority state.
13798|      0|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13799|      0|          }
13800|       |          // Otherwise, if url is special, set state to special authority
13801|       |          // slashes state.
13802|  5.68k|          else if (url.is_special()) {
  ------------------
  |  Branch (13802:20): [True: 5.68k, False: 0]
  ------------------
13803|  5.68k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13804|  5.68k|          }
13805|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13806|       |          // path or authority state and increase pointer by 1.
13807|      0|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13807:20): [True: 0, False: 0]
  ------------------
13808|      0|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13808:20): [True: 0, False: 0]
  ------------------
13809|      0|            state = state::PATH_OR_AUTHORITY;
13810|      0|            input_position++;
13811|      0|          }
13812|       |          // Otherwise, set url's path to the empty string and set state to
13813|       |          // opaque path state.
13814|      0|          else {
13815|      0|            state = state::OPAQUE_PATH;
13816|      0|          }
13817|  5.68k|        }
13818|       |        // Otherwise, if state override is not given, set buffer to the empty
13819|       |        // string, state to no scheme state, and start over (from the first code
13820|       |        // point in input).
13821|      0|        else {
13822|      0|          state = state::NO_SCHEME;
13823|      0|          input_position = 0;
13824|      0|          break;
13825|      0|        }
13826|  5.68k|        input_position++;
13827|  5.68k|        break;
13828|  5.68k|      }
13829|      0|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13829:7): [True: 0, False: 30.6k]
  ------------------
13830|      0|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13831|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13832|       |        // validation error, return failure.
13833|      0|        if (base_url == nullptr ||
  ------------------
  |  Branch (13833:13): [True: 0, False: 0]
  ------------------
13834|      0|            (base_url->has_opaque_path && !fragment.has_value())) {
  ------------------
  |  Branch (13834:14): [True: 0, False: 0]
  |  Branch (13834:43): [True: 0, False: 0]
  ------------------
13835|      0|          ada_log("NO_SCHEME validation error");
13836|      0|          url.is_valid = false;
13837|      0|          return url;
13838|      0|        }
13839|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13840|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13841|       |        // query to base's query, and set state to fragment state.
13842|      0|        else if (base_url->has_opaque_path && fragment.has_value() &&
  ------------------
  |  Branch (13842:18): [True: 0, False: 0]
  |  Branch (13842:47): [True: 0, False: 0]
  ------------------
13843|      0|                 input_position == input_size) {
  ------------------
  |  Branch (13843:18): [True: 0, False: 0]
  ------------------
13844|      0|          ada_log("NO_SCHEME opaque base with fragment");
13845|      0|          url.copy_scheme(*base_url);
13846|      0|          url.has_opaque_path = base_url->has_opaque_path;
13847|       |
13848|       |          if constexpr (result_type_is_ada_url) {
13849|       |            url.path = base_url->path;
13850|       |            url.query = base_url->query;
13851|      0|          } else {
13852|      0|            url.update_base_pathname(base_url->get_pathname());
13853|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13853:17): [True: 0, False: 0]
  ------------------
13854|       |              // get_search() returns "" for an empty query string (URL ends
13855|       |              // with '?'). update_base_search("") would incorrectly clear the
13856|       |              // query, so pass "?" to preserve the empty query distinction.
13857|      0|              auto s = base_url->get_search();
13858|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13858:38): [True: 0, False: 0]
  ------------------
13859|      0|            }
13860|      0|          }
13861|      0|          url.update_unencoded_base_hash(*fragment);
13862|      0|          return url;
13863|      0|        }
13864|       |        // Otherwise, if base's scheme is not "file", set state to relative
13865|       |        // state and decrease pointer by 1.
13866|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13867|      0|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13867:18): [True: 0, False: 0]
  ------------------
13868|      0|          ada_log("NO_SCHEME non-file relative path");
13869|      0|          state = state::RELATIVE_SCHEME;
13870|      0|        }
13871|       |        // Otherwise, set state to file state and decrease pointer by 1.
13872|      0|        else {
13873|      0|          ada_log("NO_SCHEME file base type");
13874|      0|          state = state::FILE;
13875|      0|        }
13876|      0|        break;
13877|      0|      }
13878|  5.68k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13878:7): [True: 5.68k, False: 24.9k]
  ------------------
13879|  5.68k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13880|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13881|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13882|       |        // worry about AUTHORITY.
13883|       |        // TODO: Instead of just collecting a bool, collect the location of the
13884|       |        // '@' and do something useful with it.
13885|       |        // TODO: We could do various processing early on, using a single pass
13886|       |        // over the string to collect information about it, e.g., telling us
13887|       |        // whether there is a @ and if so, where (or how many).
13888|       |
13889|       |        // Check if url data contains an @.
13890|  5.68k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13890:13): [True: 5.50k, False: 185]
  ------------------
13891|  5.50k|          state = state::HOST;
13892|  5.50k|          break;
13893|  5.50k|        }
13894|    185|        bool at_sign_seen{false};
13895|    185|        bool password_token_seen{false};
13896|       |        /**
13897|       |         * We expect something of the sort...
13898|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13899|       |         * --------^
13900|       |         */
13901|  1.14k|        do {
13902|  1.14k|          std::string_view view = url_data.substr(input_position);
13903|       |          // The delimiters are @, /, ? \\.
13904|  1.14k|          size_t location =
13905|  1.14k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13905:15): [True: 1.14k, False: 0]
  ------------------
13906|  1.14k|                               : helpers::find_authority_delimiter(view);
13907|  1.14k|          std::string_view authority_view = view.substr(0, location);
13908|  1.14k|          size_t end_of_authority = input_position + authority_view.size();
13909|       |          // If c is U+0040 (@), then:
13910|  1.14k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13910:15): [True: 1.13k, False: 10]
  ------------------
13911|  1.13k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13911:15): [True: 960, False: 175]
  ------------------
13912|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13913|    960|            if (at_sign_seen) {
  ------------------
  |  Branch (13913:17): [True: 798, False: 162]
  ------------------
13914|    798|              if (password_token_seen) {
  ------------------
  |  Branch (13914:19): [True: 419, False: 379]
  ------------------
13915|       |                if constexpr (result_type_is_ada_url) {
13916|       |                  url.password += "%40";
13917|    419|                } else {
13918|    419|                  url.append_base_password("%40");
13919|    419|                }
13920|    419|              } else {
13921|       |                if constexpr (result_type_is_ada_url) {
13922|       |                  url.username += "%40";
13923|    379|                } else {
13924|    379|                  url.append_base_username("%40");
13925|    379|                }
13926|    379|              }
13927|    798|            }
13928|       |
13929|    960|            at_sign_seen = true;
13930|       |
13931|    960|            if (!password_token_seen) {
  ------------------
  |  Branch (13931:17): [True: 541, False: 419]
  ------------------
13932|    541|              size_t password_token_location = authority_view.find(':');
13933|    541|              password_token_seen =
13934|    541|                  password_token_location != std::string_view::npos;
13935|       |
13936|    541|              if constexpr (store_values) {
13937|    541|                if (!password_token_seen) {
  ------------------
  |  Branch (13937:21): [True: 458, False: 83]
  ------------------
13938|       |                  if constexpr (result_type_is_ada_url) {
13939|       |                    url.username += unicode::percent_encode(
13940|       |                        authority_view,
13941|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13942|    458|                  } else {
13943|    458|                    url.append_base_username(unicode::percent_encode(
13944|    458|                        authority_view,
13945|    458|                        character_sets::USERINFO_PERCENT_ENCODE));
13946|    458|                  }
13947|    458|                } else {
13948|       |                  if constexpr (result_type_is_ada_url) {
13949|       |                    url.username += unicode::percent_encode(
13950|       |                        authority_view.substr(0, password_token_location),
13951|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13952|       |                    url.password += unicode::percent_encode(
13953|       |                        authority_view.substr(password_token_location + 1),
13954|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13955|     83|                  } else {
13956|     83|                    url.append_base_username(unicode::percent_encode(
13957|     83|                        authority_view.substr(0, password_token_location),
13958|     83|                        character_sets::USERINFO_PERCENT_ENCODE));
13959|     83|                    url.append_base_password(unicode::percent_encode(
13960|     83|                        authority_view.substr(password_token_location + 1),
13961|     83|                        character_sets::USERINFO_PERCENT_ENCODE));
13962|     83|                  }
13963|     83|                }
13964|    541|              }
13965|    541|            } else if constexpr (store_values) {
13966|       |              if constexpr (result_type_is_ada_url) {
13967|       |                url.password += unicode::percent_encode(
13968|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13969|    419|              } else {
13970|    419|                url.append_base_password(unicode::percent_encode(
13971|    419|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13972|    419|              }
13973|    419|            }
13974|    960|          }
13975|       |          // Otherwise, if one of the following is true:
13976|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13977|       |          // - url is special and c is U+005C (\)
13978|    185|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13978:20): [True: 10, False: 175]
  ------------------
13979|    175|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13979:20): [True: 164, False: 11]
  ------------------
13980|     11|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13980:20): [True: 3, False: 8]
  ------------------
13981|    185|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13981:21): [True: 8, False: 0]
  |  Branch (13981:41): [True: 8, False: 0]
  ------------------
13982|       |            // If atSignSeen is true and authority_view is the empty string,
13983|       |            // validation error, return failure.
13984|    185|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13984:17): [True: 162, False: 23]
  |  Branch (13984:33): [True: 3, False: 159]
  ------------------
13985|      3|              url.is_valid = false;
13986|      3|              return url;
13987|      3|            }
13988|    182|            state = state::HOST;
13989|    182|            break;
13990|    185|          }
13991|    960|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13991:15): [True: 0, False: 960]
  ------------------
13992|      0|            if constexpr (store_values) {
13993|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13993:19): [True: 0, False: 0]
  ------------------
13994|      0|                url.update_unencoded_base_hash(*fragment);
13995|      0|              }
13996|      0|            }
13997|      0|            return url;
13998|      0|          }
13999|    960|          input_position = end_of_authority + 1;
14000|    960|        } while (true);
  ------------------
  |  Branch (14000:18): [True: 960, Folded]
  ------------------
14001|       |
14002|    182|        break;
14003|    185|      }
14004|    182|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (14004:7): [True: 0, False: 30.6k]
  ------------------
14005|      0|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
14006|      0|                helpers::substring(url_data, input_position));
14007|       |
14008|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
14009|       |        // then set state to special authority ignore slashes state and increase
14010|       |        // pointer by 1.
14011|      0|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (14011:13): [True: 0, False: 0]
  ------------------
14012|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
14013|      0|          input_position += 2;
14014|      0|        } else {
14015|       |          // Otherwise, validation error, set state to relative state and
14016|       |          // decrease pointer by 1.
14017|      0|          state = state::RELATIVE_SCHEME;
14018|      0|        }
14019|       |
14020|      0|        break;
14021|    185|      }
14022|      0|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (14022:7): [True: 0, False: 30.6k]
  ------------------
14023|      0|        ada_log("PATH_OR_AUTHORITY ",
14024|      0|                helpers::substring(url_data, input_position));
14025|       |
14026|       |        // If c is U+002F (/), then set state to authority state.
14027|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14027:13): [True: 0, False: 0]
  ------------------
14028|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14028:13): [True: 0, False: 0]
  ------------------
14029|      0|          state = state::AUTHORITY;
14030|      0|          input_position++;
14031|      0|        } else {
14032|       |          // Otherwise, set state to path state, and decrease pointer by 1.
14033|      0|          state = state::PATH;
14034|      0|        }
14035|       |
14036|      0|        break;
14037|    185|      }
14038|      0|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (14038:7): [True: 0, False: 30.6k]
  ------------------
14039|      0|        ada_log("RELATIVE_SCHEME ",
14040|      0|                helpers::substring(url_data, input_position));
14041|       |
14042|       |        // Set url's scheme to base's scheme.
14043|      0|        url.copy_scheme(*base_url);
14044|       |
14045|       |        // If c is U+002F (/), then set state to relative slash state.
14046|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14046:13): [True: 0, False: 0]
  ------------------
14047|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
14048|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14048:13): [True: 0, False: 0]
  ------------------
14049|      0|          ada_log(
14050|      0|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
14051|      0|              "slash state");
14052|      0|          state = state::RELATIVE_SLASH;
14053|      0|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14053:20): [True: 0, False: 0]
  |  Branch (14053:40): [True: 0, False: 0]
  ------------------
14054|      0|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14054:20): [True: 0, False: 0]
  ------------------
14055|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
14056|       |          // set state to relative slash state.
14057|      0|          ada_log(
14058|      0|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
14059|      0|              "error, set state to relative slash state");
14060|      0|          state = state::RELATIVE_SLASH;
14061|      0|        } else {
14062|      0|          ada_log("RELATIVE_SCHEME otherwise");
14063|       |          // Set url's username to base's username, url's password to base's
14064|       |          // password, url's host to base's host, url's port to base's port,
14065|       |          // url's path to a clone of base's path, and url's query to base's
14066|       |          // query.
14067|       |          if constexpr (result_type_is_ada_url) {
14068|       |            url.username = base_url->username;
14069|       |            url.password = base_url->password;
14070|       |            url.host = base_url->host;
14071|       |            url.port = base_url->port;
14072|       |            // cloning the base path includes cloning the has_opaque_path flag
14073|       |            url.has_opaque_path = base_url->has_opaque_path;
14074|       |            url.path = base_url->path;
14075|       |            url.query = base_url->query;
14076|      0|          } else {
14077|      0|            url.update_base_authority(base_url->get_href(),
14078|      0|                                      base_url->get_components());
14079|      0|            url.update_host_to_base_host(base_url->get_hostname());
14080|      0|            url.update_base_port(base_url->retrieve_base_port());
14081|       |            // cloning the base path includes cloning the has_opaque_path flag
14082|      0|            url.has_opaque_path = base_url->has_opaque_path;
14083|      0|            url.update_base_pathname(base_url->get_pathname());
14084|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (14084:17): [True: 0, False: 0]
  ------------------
14085|       |              // get_search() returns "" for an empty query string (URL ends
14086|       |              // with '?'). update_base_search("") would incorrectly clear the
14087|       |              // query, so pass "?" to preserve the empty query distinction.
14088|      0|              auto s = base_url->get_search();
14089|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (14089:38): [True: 0, False: 0]
  ------------------
14090|      0|            }
14091|      0|          }
14092|       |
14093|      0|          url.has_opaque_path = base_url->has_opaque_path;
14094|       |
14095|       |          // If c is U+003F (?), then set url's query to the empty string, and
14096|       |          // state to query state.
14097|      0|          if ((input_position != input_size) &&
  ------------------
  |  Branch (14097:15): [True: 0, False: 0]
  ------------------
14098|      0|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14098:15): [True: 0, False: 0]
  ------------------
14099|      0|            state = state::QUERY;
14100|      0|          }
14101|       |          // Otherwise, if c is not the EOF code point:
14102|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (14102:20): [True: 0, False: 0]
  ------------------
14103|       |            // Set url's query to null.
14104|      0|            url.clear_search();
14105|       |            if constexpr (result_type_is_ada_url) {
14106|       |              // Shorten url's path.
14107|       |              helpers::shorten_path(url.path, url.type);
14108|      0|            } else {
14109|      0|              std::string_view path = url.get_pathname();
14110|      0|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (14110:19): [True: 0, False: 0]
  ------------------
14111|      0|                url.update_base_pathname(std::move(std::string(path)));
14112|      0|              }
14113|      0|            }
14114|       |            // Set state to path state and decrease pointer by 1.
14115|      0|            state = state::PATH;
14116|      0|            break;
14117|      0|          }
14118|      0|        }
14119|      0|        input_position++;
14120|      0|        break;
14121|      0|      }
14122|      0|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (14122:7): [True: 0, False: 30.6k]
  ------------------
14123|      0|        ada_log("RELATIVE_SLASH ",
14124|      0|                helpers::substring(url_data, input_position));
14125|       |
14126|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
14127|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
14128|      0|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (14128:13): [True: 0, False: 0]
  |  Branch (14128:33): [True: 0, False: 0]
  ------------------
14129|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14129:14): [True: 0, False: 0]
  ------------------
14130|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14130:14): [True: 0, False: 0]
  ------------------
14131|       |          // Set state to special authority ignore slashes state.
14132|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
14133|      0|        }
14134|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
14135|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14135:18): [True: 0, False: 0]
  ------------------
14136|      0|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (14136:18): [True: 0, False: 0]
  ------------------
14137|      0|          state = state::AUTHORITY;
14138|      0|        }
14139|       |        // Otherwise, set
14140|       |        // - url's username to base's username,
14141|       |        // - url's password to base's password,
14142|       |        // - url's host to base's host,
14143|       |        // - url's port to base's port,
14144|       |        // - state to path state, and then, decrease pointer by 1.
14145|      0|        else {
14146|       |          if constexpr (result_type_is_ada_url) {
14147|       |            url.username = base_url->username;
14148|       |            url.password = base_url->password;
14149|       |            url.host = base_url->host;
14150|       |            url.port = base_url->port;
14151|      0|          } else {
14152|      0|            url.update_base_authority(base_url->get_href(),
14153|      0|                                      base_url->get_components());
14154|      0|            url.update_host_to_base_host(base_url->get_hostname());
14155|      0|            url.update_base_port(base_url->retrieve_base_port());
14156|      0|          }
14157|      0|          state = state::PATH;
14158|      0|          break;
14159|      0|        }
14160|       |
14161|      0|        input_position++;
14162|      0|        break;
14163|      0|      }
14164|  5.68k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (14164:7): [True: 5.68k, False: 24.9k]
  ------------------
14165|  5.68k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
14166|  5.68k|                helpers::substring(url_data, input_position));
14167|       |
14168|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
14169|       |        // then set state to special authority ignore slashes state and increase
14170|       |        // pointer by 1.
14171|  5.68k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (14171:13): [True: 5.68k, False: 0]
  ------------------
14172|  5.68k|          input_position += 2;
14173|  5.68k|        }
14174|       |
14175|  5.68k|        [[fallthrough]];
14176|  5.68k|      }
14177|  5.68k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (14177:7): [True: 0, False: 30.6k]
  ------------------
14178|  5.68k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
14179|  5.68k|                helpers::substring(url_data, input_position));
14180|       |
14181|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
14182|       |        // authority state and decrease pointer by 1.
14183|  5.92k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (14183:16): [True: 5.92k, False: 1]
  ------------------
14184|  5.92k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (14184:17): [True: 121, False: 5.80k]
  ------------------
14185|  5.80k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (14185:17): [True: 119, False: 5.68k]
  ------------------
14186|    240|          input_position++;
14187|    240|        }
14188|  5.68k|        state = state::AUTHORITY;
14189|       |
14190|  5.68k|        break;
14191|  5.68k|      }
14192|    282|      case state::QUERY: {
  ------------------
  |  Branch (14192:7): [True: 282, False: 30.3k]
  ------------------
14193|    282|        ada_log("QUERY ", helpers::substring(url_data, input_position));
14194|    282|        if constexpr (store_values) {
14195|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
14196|       |          // if url is special; otherwise the query percent-encode set.
14197|    282|          const uint8_t* query_percent_encode_set =
14198|    282|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (14198:15): [True: 282, False: 0]
  ------------------
14199|    282|                               : character_sets::QUERY_PERCENT_ENCODE;
14200|       |
14201|       |          // Percent-encode after encoding, with encoding, buffer, and
14202|       |          // queryPercentEncodeSet, and append the result to url's query.
14203|    282|          url.update_base_search(url_data.substr(input_position),
14204|    282|                                 query_percent_encode_set);
14205|    282|          ada_log("QUERY update_base_search completed ");
14206|    282|          if (fragment.has_value()) {
  ------------------
  |  Branch (14206:15): [True: 7, False: 275]
  ------------------
14207|      7|            url.update_unencoded_base_hash(*fragment);
14208|      7|          }
14209|    282|        }
14210|    282|        return url;
14211|  5.68k|      }
14212|  5.68k|      case state::HOST: {
  ------------------
  |  Branch (14212:7): [True: 5.68k, False: 24.9k]
  ------------------
14213|  5.68k|        ada_log("HOST ", helpers::substring(url_data, input_position));
14214|       |
14215|  5.68k|        std::string_view host_view = url_data.substr(input_position);
14216|  5.68k|        auto [location, found_colon] =
14217|  5.68k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
14218|  5.68k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (14218:26): [True: 5.68k, False: 0]
  ------------------
14219|  5.68k|                             ? input_position + location
14220|  5.68k|                             : input_size;
14221|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
14222|       |        // Note: the 'found_colon' value is true if and only if a colon was
14223|       |        // encountered while not inside brackets.
14224|  5.68k|        if (found_colon) {
  ------------------
  |  Branch (14224:13): [True: 579, False: 5.10k]
  ------------------
14225|       |          // If buffer is the empty string, validation error, return failure.
14226|       |          // Let host be the result of host parsing buffer with url is not
14227|       |          // special.
14228|    579|          ada_log("HOST parsing ", host_view);
14229|    579|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14229:15): [True: 238, False: 341]
  ------------------
14230|    238|            return url;
14231|    238|          }
14232|    341|          ada_log("HOST parsing results in ", url.get_hostname());
14233|       |          // Set url's host to host, buffer to the empty string, and state to
14234|       |          // port state.
14235|    341|          state = state::PORT;
14236|    341|          input_position++;
14237|    341|        }
14238|       |        // Otherwise, if one of the following is true:
14239|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
14240|       |        // - url is special and c is U+005C (\)
14241|       |        // The get_host_delimiter_location function either brings us to
14242|       |        // the colon outside of the bracket, or to one of those characters.
14243|  5.10k|        else {
14244|       |          // If url is special and host_view is the empty string, validation
14245|       |          // error, return failure.
14246|  5.10k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (14246:15): [True: 2, False: 5.10k]
  |  Branch (14246:36): [True: 2, False: 0]
  ------------------
14247|      2|            url.is_valid = false;
14248|      2|            return url;
14249|      2|          }
14250|  5.10k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
14251|       |          // Let host be the result of host parsing host_view with url is not
14252|       |          // special.
14253|  5.10k|          if (host_view.empty()) {
  ------------------
  |  Branch (14253:15): [True: 0, False: 5.10k]
  ------------------
14254|      0|            url.update_base_hostname("");
14255|  5.10k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (14255:22): [True: 4.32k, False: 778]
  ------------------
14256|  4.32k|            return url;
14257|  4.32k|          }
14258|    778|          ada_log("HOST parsing results in ", url.get_hostname(),
14259|    778|                  " href=", url.get_href());
14260|       |
14261|       |          // Set url's host to host, and state to path start state.
14262|    778|          state = state::PATH_START;
14263|    778|        }
14264|       |
14265|  1.11k|        break;
14266|  5.68k|      }
14267|  1.11k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (14267:7): [True: 0, False: 30.6k]
  ------------------
14268|      0|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
14269|       |        // Opaque path, query, and fragment are structurally always valid:
14270|       |        // the parser would just percent-encode whatever is there. When we
14271|       |        // are not storing values (can_parse), we can return immediately.
14272|       |        // We must set has_opaque_path = true before returning so that when
14273|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
14274|       |        // correctly rejects relative inputs against an opaque-path base
14275|       |        // (e.g. can_parse("", &"W:") must return false).
14276|       |        if constexpr (!store_values) {
14277|       |          url.has_opaque_path = true;
14278|       |          return url;
14279|       |        }
14280|      0|        std::string_view view = url_data.substr(input_position);
14281|       |        // If c is U+003F (?), then set url's query to the empty string and
14282|       |        // state to query state.
14283|      0|        size_t location = view.find('?');
14284|      0|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (14284:13): [True: 0, False: 0]
  ------------------
14285|      0|          view.remove_suffix(view.size() - location);
14286|      0|          state = state::QUERY;
14287|      0|          input_position += location + 1;
14288|      0|        } else {
14289|      0|          input_position = input_size + 1;
14290|      0|        }
14291|      0|        url.has_opaque_path = true;
14292|       |
14293|       |        // This is a really unlikely scenario in real world. We should not seek
14294|       |        // to optimize it.
14295|      0|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (14295:13): [True: 0, False: 0]
  ------------------
14296|      0|          std::string modified_view =
14297|      0|              std::string(view.substr(0, view.size() - 1)) + "%20";
14298|      0|          url.update_base_pathname(unicode::percent_encode(
14299|      0|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14300|      0|        } else {
14301|      0|          url.update_base_pathname(unicode::percent_encode(
14302|      0|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
14303|      0|        }
14304|      0|        break;
14305|  5.68k|      }
14306|    341|      case state::PORT: {
  ------------------
  |  Branch (14306:7): [True: 341, False: 30.3k]
  ------------------
14307|    341|        ada_log("PORT ", helpers::substring(url_data, input_position));
14308|    341|        std::string_view port_view = url_data.substr(input_position);
14309|    341|        input_position += url.parse_port(port_view, true);
14310|    341|        if (!url.is_valid) {
  ------------------
  |  Branch (14310:13): [True: 263, False: 78]
  ------------------
14311|    263|          return url;
14312|    263|        }
14313|     78|        state = state::PATH_START;
14314|     78|        [[fallthrough]];
14315|     78|      }
14316|    856|      case state::PATH_START: {
  ------------------
  |  Branch (14316:7): [True: 778, False: 29.8k]
  ------------------
14317|    856|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
14318|       |        // Path, query, and fragment are structurally always valid: the
14319|       |        // parser would just percent-encode whatever is there. When we are
14320|       |        // not storing values (can_parse), we can return immediately since
14321|       |        // no subsequent state can invalidate the URL.
14322|       |        if constexpr (!store_values) {
14323|       |          return url;
14324|       |        }
14325|       |
14326|       |        // If url is special, then:
14327|    856|        if (url.is_special()) {
  ------------------
  |  Branch (14327:13): [True: 856, False: 0]
  ------------------
14328|       |          // Set state to path state.
14329|    856|          state = state::PATH;
14330|       |
14331|       |          // Optimization: Avoiding going into PATH state improves the
14332|       |          // performance of urls ending with /.
14333|    856|          if (input_position == input_size) {
  ------------------
  |  Branch (14333:15): [True: 41, False: 815]
  ------------------
14334|     41|            if constexpr (store_values) {
14335|     41|              url.update_base_pathname("/");
14336|     41|              if (fragment.has_value()) {
  ------------------
  |  Branch (14336:19): [True: 41, False: 0]
  ------------------
14337|     41|                url.update_unencoded_base_hash(*fragment);
14338|     41|              }
14339|     41|            }
14340|     41|            return url;
14341|     41|          }
14342|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
14343|       |          // by 1. We know that (input_position == input_size) is impossible
14344|       |          // here, because of the previous if-check.
14345|    815|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (14345:15): [True: 102, False: 713]
  ------------------
14346|    102|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (14346:15): [True: 43, False: 59]
  ------------------
14347|     43|            break;
14348|     43|          }
14349|    815|        }
14350|       |        // Otherwise, if state override is not given and c is U+003F (?),
14351|       |        // set url's query to the empty string and state to query state.
14352|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (14352:18): [True: 0, False: 0]
  ------------------
14353|      0|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (14353:18): [True: 0, False: 0]
  ------------------
14354|      0|          state = state::QUERY;
14355|      0|        }
14356|       |        // Otherwise, if c is not the EOF code point:
14357|      0|        else if (input_position != input_size) {
  ------------------
  |  Branch (14357:18): [True: 0, False: 0]
  ------------------
14358|       |          // Set state to path state.
14359|      0|          state = state::PATH;
14360|       |
14361|       |          // If c is not U+002F (/), then decrease pointer by 1.
14362|      0|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (14362:15): [True: 0, False: 0]
  ------------------
14363|      0|            break;
14364|      0|          }
14365|      0|        }
14366|       |
14367|    772|        input_position++;
14368|    772|        break;
14369|    856|      }
14370|    815|      case state::PATH: {
  ------------------
  |  Branch (14370:7): [True: 815, False: 29.8k]
  ------------------
14371|    815|        ada_log("PATH ", helpers::substring(url_data, input_position));
14372|       |        // Path, query, and fragment are structurally always valid: the
14373|       |        // parser would just percent-encode whatever is there. When we are
14374|       |        // not storing values (can_parse), we can return immediately since
14375|       |        // no subsequent state can invalidate the URL.
14376|       |        if constexpr (!store_values) {
14377|       |          return url;
14378|       |        }
14379|    815|        std::string_view view = url_data.substr(input_position);
14380|       |
14381|       |        // Most time, we do not need percent encoding.
14382|       |        // Furthermore, we can immediately locate the '?'.
14383|    815|        size_t locofquestionmark = view.find('?');
14384|    815|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (14384:13): [True: 282, False: 533]
  ------------------
14385|    282|          state = state::QUERY;
14386|    282|          view.remove_suffix(view.size() - locofquestionmark);
14387|    282|          input_position += locofquestionmark + 1;
14388|    533|        } else {
14389|    533|          input_position = input_size + 1;
14390|    533|        }
14391|    815|        if constexpr (store_values) {
14392|       |          if constexpr (result_type_is_ada_url) {
14393|       |            helpers::parse_prepared_path(view, url.type, url.path);
14394|    815|          } else {
14395|    815|            url.consume_prepared_path(view);
14396|    815|            ADA_ASSERT_TRUE(url.validate());
14397|    815|          }
14398|    815|        }
14399|    815|        break;
14400|    856|      }
14401|      0|      case state::FILE_SLASH: {
  ------------------
  |  Branch (14401:7): [True: 0, False: 30.6k]
  ------------------
14402|      0|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
14403|       |
14404|       |        // If c is U+002F (/) or U+005C (\), then:
14405|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (14405:13): [True: 0, False: 0]
  ------------------
14406|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14406:14): [True: 0, False: 0]
  ------------------
14407|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14407:14): [True: 0, False: 0]
  ------------------
14408|      0|          ada_log("FILE_SLASH c is U+002F or U+005C");
14409|       |          // Set state to file host state.
14410|      0|          state = state::FILE_HOST;
14411|      0|          input_position++;
14412|      0|        } else {
14413|      0|          ada_log("FILE_SLASH otherwise");
14414|       |          // If base is non-null and base's scheme is "file", then:
14415|       |          // Note: it is unsafe to do base_url->scheme unless you know that
14416|       |          // base_url_has_value() is true.
14417|      0|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14417:15): [True: 0, False: 0]
  |  Branch (14417:38): [True: 0, False: 0]
  ------------------
14418|       |            // Set url's host to base's host.
14419|       |            if constexpr (result_type_is_ada_url) {
14420|       |              url.host = base_url->host;
14421|      0|            } else {
14422|      0|              url.update_host_to_base_host(base_url->get_host());
14423|      0|            }
14424|       |            // If the code point substring from pointer to the end of input does
14425|       |            // not start with a Windows drive letter and base's path[0] is a
14426|       |            // normalized Windows drive letter, then append base's path[0] to
14427|       |            // url's path.
14428|      0|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (14428:17): [True: 0, False: 0]
  ------------------
14429|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (14429:19): [True: 0, False: 0]
  ------------------
14430|      0|                      url_data.substr(input_position))) {
14431|      0|                std::string_view first_base_url_path =
14432|      0|                    base_url->get_pathname().substr(1);
14433|      0|                size_t loc = first_base_url_path.find('/');
14434|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (14434:21): [True: 0, False: 0]
  ------------------
14435|      0|                  helpers::resize(first_base_url_path, loc);
14436|      0|                }
14437|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (14437:21): [True: 0, False: 0]
  ------------------
14438|      0|                        first_base_url_path)) {
14439|       |                  if constexpr (result_type_is_ada_url) {
14440|       |                    url.path += '/';
14441|       |                    url.path += first_base_url_path;
14442|      0|                  } else {
14443|      0|                    url.append_base_pathname(
14444|      0|                        helpers::concat("/", first_base_url_path));
14445|      0|                  }
14446|      0|                }
14447|      0|              }
14448|      0|            }
14449|      0|          }
14450|       |
14451|       |          // Set state to path state, and decrease pointer by 1.
14452|      0|          state = state::PATH;
14453|      0|        }
14454|       |
14455|      0|        break;
14456|    856|      }
14457|      0|      case state::FILE_HOST: {
  ------------------
  |  Branch (14457:7): [True: 0, False: 30.6k]
  ------------------
14458|      0|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
14459|      0|        std::string_view view = url_data.substr(input_position);
14460|       |
14461|      0|        size_t location = view.find_first_of("/\\?");
14462|      0|        std::string_view file_host_buffer = view.substr(
14463|      0|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (14463:16): [True: 0, False: 0]
  ------------------
14464|       |
14465|      0|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (14465:13): [True: 0, False: 0]
  ------------------
14466|      0|          state = state::PATH;
14467|      0|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (14467:20): [True: 0, False: 0]
  ------------------
14468|       |          // Set url's host to the empty string.
14469|       |          if constexpr (result_type_is_ada_url) {
14470|       |            url.host = "";
14471|      0|          } else {
14472|      0|            url.update_base_hostname("");
14473|      0|          }
14474|       |          // Set state to path start state.
14475|      0|          state = state::PATH_START;
14476|      0|        } else {
14477|      0|          size_t consumed_bytes = file_host_buffer.size();
14478|      0|          input_position += consumed_bytes;
14479|       |          // Let host be the result of host parsing buffer with url is not
14480|       |          // special.
14481|      0|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (14481:15): [True: 0, False: 0]
  ------------------
14482|      0|            return url;
14483|      0|          }
14484|       |
14485|       |          if constexpr (result_type_is_ada_url) {
14486|       |            // If host is "localhost", then set host to the empty string.
14487|       |            if (url.host.has_value() && url.host.value() == "localhost") {
14488|       |              url.host = "";
14489|       |            }
14490|      0|          } else {
14491|      0|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (14491:17): [True: 0, False: 0]
  ------------------
14492|      0|              url.update_base_hostname("");
14493|      0|            }
14494|      0|          }
14495|       |
14496|       |          // Set buffer to the empty string and state to path start state.
14497|      0|          state = state::PATH_START;
14498|      0|        }
14499|       |
14500|      0|        break;
14501|      0|      }
14502|      0|      case state::FILE: {
  ------------------
  |  Branch (14502:7): [True: 0, False: 30.6k]
  ------------------
14503|      0|        ada_log("FILE ", helpers::substring(url_data, input_position));
14504|      0|        std::string_view file_view = url_data.substr(input_position);
14505|       |
14506|      0|        url.set_protocol_as_file();
14507|       |        if constexpr (result_type_is_ada_url) {
14508|       |          // Set url's host to the empty string.
14509|       |          url.host = "";
14510|      0|        } else {
14511|      0|          url.update_base_hostname("");
14512|      0|        }
14513|       |        // If c is U+002F (/) or U+005C (\), then:
14514|      0|        if (input_position != input_size &&
  ------------------
  |  Branch (14514:13): [True: 0, False: 0]
  ------------------
14515|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (14515:14): [True: 0, False: 0]
  ------------------
14516|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (14516:14): [True: 0, False: 0]
  ------------------
14517|      0|          ada_log("FILE c is U+002F or U+005C");
14518|       |          // Set state to file slash state.
14519|      0|          state = state::FILE_SLASH;
14520|      0|        }
14521|       |        // Otherwise, if base is non-null and base's scheme is "file":
14522|      0|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (14522:18): [True: 0, False: 0]
  |  Branch (14522:41): [True: 0, False: 0]
  ------------------
14523|       |          // Set url's host to base's host, url's path to a clone of base's
14524|       |          // path, and url's query to base's query.
14525|      0|          ada_log("FILE base non-null");
14526|       |          if constexpr (result_type_is_ada_url) {
14527|       |            url.host = base_url->host;
14528|       |            url.path = base_url->path;
14529|       |            url.query = base_url->query;
14530|      0|          } else {
14531|      0|            url.update_host_to_base_host(base_url->get_hostname());
14532|      0|            url.update_base_pathname(base_url->get_pathname());
14533|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (14533:17): [True: 0, False: 0]
  ------------------
14534|       |              // get_search() returns "" for an empty query string (URL ends
14535|       |              // with '?'). update_base_search("") would incorrectly clear the
14536|       |              // query, so pass "?" to preserve the empty query distinction.
14537|      0|              auto s = base_url->get_search();
14538|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (14538:38): [True: 0, False: 0]
  ------------------
14539|      0|            }
14540|      0|          }
14541|      0|          url.has_opaque_path = base_url->has_opaque_path;
14542|       |
14543|       |          // If c is U+003F (?), then set url's query to the empty string and
14544|       |          // state to query state.
14545|      0|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (14545:15): [True: 0, False: 0]
  |  Branch (14545:47): [True: 0, False: 0]
  ------------------
14546|      0|            state = state::QUERY;
14547|      0|          }
14548|       |          // Otherwise, if c is not the EOF code point:
14549|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (14549:20): [True: 0, False: 0]
  ------------------
14550|       |            // Set url's query to null.
14551|      0|            url.clear_search();
14552|       |            // If the code point substring from pointer to the end of input does
14553|       |            // not start with a Windows drive letter, then shorten url's path.
14554|      0|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (14554:17): [True: 0, False: 0]
  ------------------
14555|       |              if constexpr (result_type_is_ada_url) {
14556|       |                helpers::shorten_path(url.path, url.type);
14557|      0|              } else {
14558|      0|                std::string_view path = url.get_pathname();
14559|      0|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (14559:21): [True: 0, False: 0]
  ------------------
14560|      0|                  url.update_base_pathname(std::move(std::string(path)));
14561|      0|                }
14562|      0|              }
14563|      0|            }
14564|       |            // Otherwise:
14565|      0|            else {
14566|       |              // Set url's path to an empty list.
14567|      0|              url.clear_pathname();
14568|      0|              url.has_opaque_path = true;
14569|      0|            }
14570|       |
14571|       |            // Set state to path state and decrease pointer by 1.
14572|      0|            state = state::PATH;
14573|      0|            break;
14574|      0|          }
14575|      0|        }
14576|       |        // Otherwise, set state to path state, and decrease pointer by 1.
14577|      0|        else {
14578|      0|          ada_log("FILE go to path");
14579|      0|          state = state::PATH;
14580|      0|          break;
14581|      0|        }
14582|       |
14583|      0|        input_position++;
14584|      0|        break;
14585|      0|      }
14586|      0|      default:
  ------------------
  |  Branch (14586:7): [True: 0, False: 30.6k]
  ------------------
14587|      0|        unreachable();
14588|  30.6k|    }
14589|  30.6k|  }
14590|    533|  if constexpr (store_values) {
14591|    533|    if (fragment.has_value()) {
  ------------------
  |  Branch (14591:9): [True: 20, False: 513]
  ------------------
14592|     20|      url.update_unencoded_base_hash(*fragment);
14593|     20|    }
14594|    533|  }
14595|       |  // Check the resulting (normalized) URL size against the maximum input length.
14596|       |  // Normalization (percent-encoding, IDNA, etc.) can expand the URL beyond the
14597|       |  // original input size.
14598|    533|  if constexpr (store_values) {
14599|    533|    if (url.is_valid) {
  ------------------
  |  Branch (14599:9): [True: 533, False: 0]
  ------------------
14600|    533|      if constexpr (result_type_is_ada_url_aggregator) {
14601|    533|        if (url.buffer.size() > max_input_length) {
  ------------------
  |  Branch (14601:13): [True: 0, False: 533]
  ------------------
14602|      0|          url.is_valid = false;
14603|      0|        }
14604|       |      } else {
14605|       |        if (url.get_href_size() > max_input_length) {
14606|       |          url.is_valid = false;
14607|       |        }
14608|       |      }
14609|    533|    }
14610|    533|  }
14611|    533|  return url;
14612|  5.68k|}
_ZNK3ada14url_aggregator12get_hostnameEv:
15498|  2.98k|    ada_lifetime_bound {
15499|  2.98k|  ada_log("url_aggregator::get_hostname");
15500|       |  // Technically, we should check if there is a hostname, but
15501|       |  // the code below works even if there isn't.
15502|       |  // if(!has_hostname()) { return ""; }
15503|  2.98k|  size_t start = components.host_start;
15504|       |  // So host_start is not where the host begins.
15505|  2.98k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (15505:7): [True: 1.55k, False: 1.42k]
  ------------------
15506|  1.55k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (15506:7): [True: 216, False: 1.33k]
  ------------------
15507|    216|    start++;
15508|    216|  }
15509|  2.98k|  return helpers::substring(buffer, start, components.host_end);
15510|  2.98k|}
_ZN3ada14url_aggregator10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
15643|    821|bool url_aggregator::parse_ipv4(std::string_view input, bool in_place) {
15644|    821|  ada_log("parse_ipv4 ", input, " [", input.size(),
15645|    821|          " bytes], overlaps with buffer: ",
15646|    821|          helpers::overlaps(input, buffer) ? "yes" : "no");
15647|    821|  ADA_ASSERT_TRUE(validate());
15648|    821|  const bool trailing_dot = (input.back() == '.');
15649|    821|  if (trailing_dot) {
  ------------------
  |  Branch (15649:7): [True: 82, False: 739]
  ------------------
15650|     82|    input.remove_suffix(1);
15651|     82|  }
15652|    821|  size_t digit_count{0};
15653|    821|  int pure_decimal_count = 0;  // entries that are decimal
15654|    821|  uint64_t ipv4{0};
15655|       |  // we could unroll for better performance?
15656|  1.09k|  for (; (digit_count < 4) && !(input.empty()); digit_count++) {
  ------------------
  |  Branch (15656:10): [True: 1.08k, False: 11]
  |  Branch (15656:31): [True: 1.08k, False: 0]
  ------------------
15657|  1.08k|    uint32_t
15658|  1.08k|        segment_result{};  // If any number exceeds 32 bits, we have an error.
15659|  1.08k|    bool is_hex = checkers::has_hex_prefix(input);
15660|  1.08k|    if (is_hex && ((input.length() == 2) ||
  ------------------
  |  Branch (15660:9): [True: 222, False: 858]
  |  Branch (15660:20): [True: 8, False: 214]
  ------------------
15661|    214|                   ((input.length() > 2) && (input[2] == '.')))) {
  ------------------
  |  Branch (15661:21): [True: 214, False: 0]
  |  Branch (15661:45): [True: 3, False: 211]
  ------------------
15662|       |      // special case
15663|     11|      segment_result = 0;
15664|     11|      input.remove_prefix(2);
15665|  1.06k|    } else {
15666|  1.06k|      std::from_chars_result r{};
15667|  1.06k|      if (is_hex) {
  ------------------
  |  Branch (15667:11): [True: 211, False: 858]
  ------------------
15668|    211|        ada_log("parse_ipv4 trying to parse hex number");
15669|    211|        r = std::from_chars(input.data() + 2, input.data() + input.size(),
15670|    211|                            segment_result, 16);
15671|    858|      } else if ((input.length() >= 2) && input[0] == '0' &&
  ------------------
  |  Branch (15671:18): [True: 771, False: 87]
  |  Branch (15671:43): [True: 203, False: 568]
  ------------------
15672|    203|                 checkers::is_digit(input[1])) {
  ------------------
  |  Branch (15672:18): [True: 126, False: 77]
  ------------------
15673|    126|        ada_log("parse_ipv4 trying to parse octal number");
15674|    126|        r = std::from_chars(input.data() + 1, input.data() + input.size(),
15675|    126|                            segment_result, 8);
15676|    732|      } else {
15677|    732|        ada_log("parse_ipv4 trying to parse decimal number");
15678|    732|        pure_decimal_count++;
15679|    732|        r = std::from_chars(input.data(), input.data() + input.size(),
15680|    732|                            segment_result, 10);
15681|    732|      }
15682|  1.06k|      if (r.ec != std::errc()) {
  ------------------
  |  Branch (15682:11): [True: 375, False: 694]
  ------------------
15683|    375|        ada_log("parse_ipv4 parsing failed");
15684|    375|        return is_valid = false;
15685|    375|      }
15686|    694|      ada_log("parse_ipv4 parsed ", segment_result);
15687|    694|      input.remove_prefix(r.ptr - input.data());
15688|    694|    }
15689|    705|    if (input.empty()) {
  ------------------
  |  Branch (15689:9): [True: 340, False: 365]
  ------------------
15690|       |      // We have the last value.
15691|       |      // At this stage, ipv4 contains digit_count*8 bits.
15692|       |      // So we have 32-digit_count*8 bits left.
15693|    340|      if (segment_result >= (uint64_t(1) << (32 - digit_count * 8))) {
  ------------------
  |  Branch (15693:11): [True: 15, False: 325]
  ------------------
15694|     15|        return is_valid = false;
15695|     15|      }
15696|    325|      ipv4 <<= (32 - digit_count * 8);
15697|    325|      ipv4 |= segment_result;
15698|    325|      goto final;
15699|    365|    } else {
15700|       |      // There is more, so that the value must no be larger than 255
15701|       |      // and we must have a '.'.
15702|    365|      if ((segment_result > 255) || (input[0] != '.')) {
  ------------------
  |  Branch (15702:11): [True: 47, False: 318]
  |  Branch (15702:37): [True: 48, False: 270]
  ------------------
15703|     95|        return is_valid = false;
15704|     95|      }
15705|    270|      ipv4 <<= 8;
15706|    270|      ipv4 |= segment_result;
15707|    270|      input.remove_prefix(1);  // remove '.'
15708|    270|    }
15709|    705|  }
15710|     11|  if ((digit_count != 4) || (!input.empty())) {
  ------------------
  |  Branch (15710:7): [True: 0, False: 11]
  |  Branch (15710:29): [True: 11, False: 0]
  ------------------
15711|     11|    ada_log("parse_ipv4 found invalid (more than 4 numbers or empty) ");
15712|     11|    return is_valid = false;
15713|     11|  }
15714|    325|final:
15715|    325|  ada_log("url_aggregator::parse_ipv4 completed ", get_href(),
15716|    325|          " host: ", get_host());
15717|       |
15718|       |  // We could also check r.ptr to see where the parsing ended.
15719|    325|  if (in_place && pure_decimal_count == 4 && !trailing_dot) {
  ------------------
  |  Branch (15719:7): [True: 316, False: 9]
  |  Branch (15719:19): [True: 0, False: 316]
  |  Branch (15719:46): [True: 0, False: 0]
  ------------------
15720|      0|    ada_log(
15721|      0|        "url_aggregator::parse_ipv4 completed and was already correct in the "
15722|      0|        "buffer");
15723|       |    // The original input was already all decimal and we validated it. So we
15724|       |    // don't need to do anything.
15725|    325|  } else {
15726|    325|    ada_log("url_aggregator::parse_ipv4 completed and we need to update it");
15727|       |    // Optimization opportunity: Get rid of unnecessary string return in ipv4
15728|       |    // serializer.
15729|       |    // TODO: This is likely a bug because it goes back update_base_hostname, not
15730|       |    // what we want to do.
15731|    325|    update_base_hostname(
15732|    325|        ada::serializers::ipv4(ipv4));  // We have to reserialize the address.
15733|    325|  }
15734|    325|  host_type = IPV4;
15735|    325|  ADA_ASSERT_TRUE(validate());
15736|    325|  return true;
15737|     11|}
_ZN3ada14url_aggregator10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15739|  3.25k|bool url_aggregator::parse_ipv6(std::string_view input) {
15740|       |  // TODO: Implement in_place optimization: we know that input points
15741|       |  // in the buffer, so we can just check whether the buffer is already
15742|       |  // well formatted.
15743|       |  // TODO: Find a way to merge parse_ipv6 with url.cpp implementation.
15744|  3.25k|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
15745|  3.25k|  ADA_ASSERT_TRUE(validate());
15746|  3.25k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15747|  3.25k|  if (input.empty()) {
  ------------------
  |  Branch (15747:7): [True: 1, False: 3.25k]
  ------------------
15748|      1|    return is_valid = false;
15749|      1|  }
15750|       |  // Let address be a new IPv6 address whose IPv6 pieces are all 0.
15751|  3.25k|  std::array<uint16_t, 8> address{};
15752|       |
15753|       |  // Let pieceIndex be 0.
15754|  3.25k|  int piece_index = 0;
15755|       |
15756|       |  // Let compress be null.
15757|  3.25k|  std::optional<int> compress{};
15758|       |
15759|       |  // Let pointer be a pointer for input.
15760|  3.25k|  std::string_view::iterator pointer = input.begin();
15761|       |
15762|       |  // If c is U+003A (:), then:
15763|  3.25k|  if (input[0] == ':') {
  ------------------
  |  Branch (15763:7): [True: 5, False: 3.24k]
  ------------------
15764|       |    // If remaining does not start with U+003A (:), validation error, return
15765|       |    // failure.
15766|      5|    if (input.size() == 1 || input[1] != ':') {
  ------------------
  |  Branch (15766:9): [True: 1, False: 4]
  |  Branch (15766:30): [True: 1, False: 3]
  ------------------
15767|      2|      ada_log("parse_ipv6 starts with : but the rest does not start with :");
15768|      2|      return is_valid = false;
15769|      2|    }
15770|       |
15771|       |    // Increase pointer by 2.
15772|      3|    pointer += 2;
15773|       |
15774|       |    // Increase pieceIndex by 1 and then set compress to pieceIndex.
15775|      3|    compress = ++piece_index;
15776|      3|  }
15777|       |
15778|       |  // While c is not the EOF code point:
15779|  3.39k|  while (pointer != input.end()) {
  ------------------
  |  Branch (15779:10): [True: 3.38k, False: 6]
  ------------------
15780|       |    // If pieceIndex is 8, validation error, return failure.
15781|  3.38k|    if (piece_index == 8) {
  ------------------
  |  Branch (15781:9): [True: 2, False: 3.38k]
  ------------------
15782|      2|      ada_log("parse_ipv6 piece_index == 8");
15783|      2|      return is_valid = false;
15784|      2|    }
15785|       |
15786|       |    // If c is U+003A (:), then:
15787|  3.38k|    if (*pointer == ':') {
  ------------------
  |  Branch (15787:9): [True: 21, False: 3.36k]
  ------------------
15788|       |      // If compress is non-null, validation error, return failure.
15789|     21|      if (compress.has_value()) {
  ------------------
  |  Branch (15789:11): [True: 2, False: 19]
  ------------------
15790|      2|        ada_log("parse_ipv6 compress is non-null");
15791|      2|        return is_valid = false;
15792|      2|      }
15793|       |
15794|       |      // Increase pointer and pieceIndex by 1, set compress to pieceIndex, and
15795|       |      // then continue.
15796|     19|      pointer++;
15797|     19|      compress = ++piece_index;
15798|     19|      continue;
15799|     21|    }
15800|       |
15801|       |    // Let value and length be 0.
15802|  3.36k|    uint16_t value = 0, length = 0;
15803|       |
15804|       |    // While length is less than 4 and c is an ASCII hex digit,
15805|       |    // set value to value times 0x10 + c interpreted as hexadecimal number, and
15806|       |    // increase pointer and length by 1.
15807|  3.78k|    while (length < 4 && pointer != input.end() &&
  ------------------
  |  Branch (15807:12): [True: 3.73k, False: 49]
  |  Branch (15807:26): [True: 3.73k, False: 4]
  ------------------
15808|  3.73k|           unicode::is_ascii_hex_digit(*pointer)) {
  ------------------
  |  Branch (15808:12): [True: 423, False: 3.30k]
  ------------------
15809|       |      // https://stackoverflow.com/questions/39060852/why-does-the-addition-of-two-shorts-return-an-int
15810|    423|      value = uint16_t(value * 0x10 + unicode::convert_hex_to_binary(*pointer));
15811|    423|      pointer++;
15812|    423|      length++;
15813|    423|    }
15814|       |
15815|       |    // If c is U+002E (.), then:
15816|  3.36k|    if (pointer != input.end() && *pointer == '.') {
  ------------------
  |  Branch (15816:9): [True: 3.35k, False: 4]
  |  Branch (15816:35): [True: 27, False: 3.33k]
  ------------------
15817|       |      // If length is 0, validation error, return failure.
15818|     27|      if (length == 0) {
  ------------------
  |  Branch (15818:11): [True: 1, False: 26]
  ------------------
15819|      1|        ada_log("parse_ipv6 length is 0");
15820|      1|        return is_valid = false;
15821|      1|      }
15822|       |
15823|       |      // Decrease pointer by length.
15824|     26|      pointer -= length;
15825|       |
15826|       |      // If pieceIndex is greater than 6, validation error, return failure.
15827|     26|      if (piece_index > 6) {
  ------------------
  |  Branch (15827:11): [True: 1, False: 25]
  ------------------
15828|      1|        ada_log("parse_ipv6 piece_index > 6");
15829|      1|        return is_valid = false;
15830|      1|      }
15831|       |
15832|       |      // Let numbersSeen be 0.
15833|     25|      int numbers_seen = 0;
15834|       |
15835|       |      // While c is not the EOF code point:
15836|     80|      while (pointer != input.end()) {
  ------------------
  |  Branch (15836:14): [True: 69, False: 11]
  ------------------
15837|       |        // Let ipv4Piece be null.
15838|     69|        std::optional<uint16_t> ipv4_piece{};
15839|       |
15840|       |        // If numbersSeen is greater than 0, then:
15841|     69|        if (numbers_seen > 0) {
  ------------------
  |  Branch (15841:13): [True: 44, False: 25]
  ------------------
15842|       |          // If c is a U+002E (.) and numbersSeen is less than 4, then increase
15843|       |          // pointer by 1.
15844|     44|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (15844:15): [True: 42, False: 2]
  |  Branch (15844:34): [True: 40, False: 2]
  ------------------
15845|     40|            pointer++;
15846|     40|          } else {
15847|       |            // Otherwise, validation error, return failure.
15848|      4|            ada_log("parse_ipv6 Otherwise, validation error, return failure");
15849|      4|            return is_valid = false;
15850|      4|          }
15851|     44|        }
15852|       |
15853|       |        // If c is not an ASCII digit, validation error, return failure.
15854|     65|        if (pointer == input.end() || !checkers::is_digit(*pointer)) {
  ------------------
  |  Branch (15854:13): [True: 0, False: 65]
  |  Branch (15854:39): [True: 7, False: 58]
  ------------------
15855|      7|          ada_log(
15856|      7|              "parse_ipv6 If c is not an ASCII digit, validation error, return "
15857|      7|              "failure");
15858|      7|          return is_valid = false;
15859|      7|        }
15860|       |
15861|       |        // While c is an ASCII digit:
15862|    161|        while (pointer != input.end() && checkers::is_digit(*pointer)) {
  ------------------
  |  Branch (15862:16): [True: 150, False: 11]
  |  Branch (15862:42): [True: 106, False: 44]
  ------------------
15863|       |          // Let number be c interpreted as decimal number.
15864|    106|          int number = *pointer - '0';
15865|       |
15866|       |          // If ipv4Piece is null, then set ipv4Piece to number.
15867|    106|          if (!ipv4_piece.has_value()) {
  ------------------
  |  Branch (15867:15): [True: 58, False: 48]
  ------------------
15868|     58|            ipv4_piece = number;
15869|     58|          }
15870|       |          // Otherwise, if ipv4Piece is 0, validation error, return failure.
15871|     48|          else if (ipv4_piece == 0) {
  ------------------
  |  Branch (15871:20): [True: 1, False: 47]
  ------------------
15872|      1|            ada_log("parse_ipv6 if ipv4Piece is 0, validation error");
15873|      1|            return is_valid = false;
15874|      1|          }
15875|       |          // Otherwise, set ipv4Piece to ipv4Piece times 10 + number.
15876|     47|          else {
15877|     47|            ipv4_piece = *ipv4_piece * 10 + number;
15878|     47|          }
15879|       |
15880|       |          // If ipv4Piece is greater than 255, validation error, return failure.
15881|    105|          if (ipv4_piece > 255) {
  ------------------
  |  Branch (15881:15): [True: 2, False: 103]
  ------------------
15882|      2|            ada_log("parse_ipv6 ipv4_piece > 255");
15883|      2|            return is_valid = false;
15884|      2|          }
15885|       |
15886|       |          // Increase pointer by 1.
15887|    103|          pointer++;
15888|    103|        }
15889|       |
15890|       |        // Set address[pieceIndex] to address[pieceIndex] times 0x100 +
15891|       |        // ipv4Piece.
15892|       |        // https://stackoverflow.com/questions/39060852/why-does-the-addition-of-two-shorts-return-an-int
15893|     55|        if (!ipv4_piece.has_value()) {
  ------------------
  |  Branch (15893:13): [True: 0, False: 55]
  ------------------
15894|      0|          return is_valid = false;
15895|      0|        }
15896|     55|        address[piece_index] =
15897|     55|            uint16_t(address[piece_index] * 0x100 + *ipv4_piece);
15898|       |
15899|       |        // Increase numbersSeen by 1.
15900|     55|        numbers_seen++;
15901|       |
15902|       |        // If numbersSeen is 2 or 4, then increase pieceIndex by 1.
15903|     55|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (15903:13): [True: 16, False: 39]
  |  Branch (15903:34): [True: 6, False: 33]
  ------------------
15904|     22|          piece_index++;
15905|     22|        }
15906|     55|      }
15907|       |
15908|       |      // If numbersSeen is not 4, validation error, return failure.
15909|     11|      if (numbers_seen != 4) {
  ------------------
  |  Branch (15909:11): [True: 7, False: 4]
  ------------------
15910|      7|        return is_valid = false;
15911|      7|      }
15912|       |
15913|       |      // Break.
15914|      4|      break;
15915|     11|    }
15916|       |    // Otherwise, if c is U+003A (:):
15917|  3.33k|    else if ((pointer != input.end()) && (*pointer == ':')) {
  ------------------
  |  Branch (15917:14): [True: 3.33k, False: 4]
  |  Branch (15917:42): [True: 119, False: 3.21k]
  ------------------
15918|       |      // Increase pointer by 1.
15919|    119|      pointer++;
15920|       |
15921|       |      // If c is the EOF code point, validation error, return failure.
15922|    119|      if (pointer == input.end()) {
  ------------------
  |  Branch (15922:11): [True: 1, False: 118]
  ------------------
15923|      1|        ada_log(
15924|      1|            "parse_ipv6 If c is the EOF code point, validation error, return "
15925|      1|            "failure");
15926|      1|        return is_valid = false;
15927|      1|      }
15928|    119|    }
15929|       |    // Otherwise, if c is not the EOF code point, validation error, return
15930|       |    // failure.
15931|  3.21k|    else if (pointer != input.end()) {
  ------------------
  |  Branch (15931:14): [True: 3.21k, False: 4]
  ------------------
15932|  3.21k|      ada_log(
15933|  3.21k|          "parse_ipv6 Otherwise, if c is not the EOF code point, validation "
15934|  3.21k|          "error, return failure");
15935|  3.21k|      return is_valid = false;
15936|  3.21k|    }
15937|       |
15938|       |    // Set address[pieceIndex] to value.
15939|    122|    address[piece_index] = value;
15940|       |
15941|       |    // Increase pieceIndex by 1.
15942|    122|    piece_index++;
15943|    122|  }
15944|       |
15945|       |  // If compress is non-null, then:
15946|     10|  if (compress.has_value()) {
  ------------------
  |  Branch (15946:7): [True: 7, False: 3]
  ------------------
15947|       |    // Let swaps be pieceIndex - compress.
15948|      7|    int swaps = piece_index - *compress;
15949|       |
15950|       |    // Set pieceIndex to 7.
15951|      7|    piece_index = 7;
15952|       |
15953|       |    // While pieceIndex is not 0 and swaps is greater than 0,
15954|       |    // swap address[pieceIndex] with address[compress + swaps - 1], and then
15955|       |    // decrease both pieceIndex and swaps by 1.
15956|     22|    while (piece_index != 0 && swaps > 0) {
  ------------------
  |  Branch (15956:12): [True: 22, False: 0]
  |  Branch (15956:32): [True: 15, False: 7]
  ------------------
15957|     15|      std::swap(address[piece_index], address[*compress + swaps - 1]);
15958|     15|      piece_index--;
15959|     15|      swaps--;
15960|     15|    }
15961|      7|  }
15962|       |  // Otherwise, if compress is null and pieceIndex is not 8, validation error,
15963|       |  // return failure.
15964|      3|  else if (piece_index != 8) {
  ------------------
  |  Branch (15964:12): [True: 2, False: 1]
  ------------------
15965|      2|    ada_log(
15966|      2|        "parse_ipv6 if compress is null and pieceIndex is not 8, validation "
15967|      2|        "error, return failure");
15968|      2|    return is_valid = false;
15969|      2|  }
15970|       |  // TODO: Optimization opportunity: Get rid of unnecessary string creation.
15971|       |  // TODO: This is likely a bug because it goes back update_base_hostname, not
15972|       |  // what we want to do.
15973|      8|  update_base_hostname(ada::serializers::ipv6(address));
15974|      8|  ada_log("parse_ipv6 ", get_hostname());
15975|      8|  ADA_ASSERT_TRUE(validate());
15976|      8|  host_type = IPV6;
15977|      8|  return true;
15978|     10|}
serializers.cc:_ZZN3ada4idna22utf32_length_from_utf8EPKcmENK3$_0clEa:
  263|  32.0k|  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|  32.0k|    return c > -65;
  267|  32.0k|  });
serializers.cc:_ZZN3ada4idna9ascii_mapEPcmENK3$_0clEh:
 2838|  3.60k|  auto broadcast = [](uint8_t v) -> uint64_t {
 2839|  3.60k|    return 0x101010101010101ull * v;
 2840|  3.60k|  };
serializers.cc:_ZN3ada4idnaL11idna_lookupEj:
 2786|  27.5k|static uint16_t idna_lookup(uint32_t cp) noexcept {
 2787|       |  // -- Two-level table covers the full active code-point range ---------------
 2788|  27.5k|  if (cp < IDNA_LOW_RANGE_END) {
  ------------------
  |  Branch (2788:7): [True: 27.4k, False: 76]
  ------------------
 2789|  27.4k|    uint16_t ref = idna_stage1[cp >> IDNA_BLOCK_BITS];
 2790|  27.4k|    if (ref & IDNA_BOOL_FLAG) {
  ------------------
  |  Branch (2790:9): [True: 16.8k, False: 10.5k]
  ------------------
 2791|       |      // Boolean block: one bit per code point, 1 = VALID, 0 = DISALLOWED.
 2792|  16.8k|      uint32_t bit_idx =
 2793|  16.8k|          static_cast<uint32_t>(ref & ~IDNA_BOOL_FLAG) * IDNA_BLOCK_SIZE +
 2794|  16.8k|          (cp & IDNA_BLOCK_MASK);
 2795|  16.8k|      bool is_valid = (idna_bool_blocks[bit_idx >> 6] >> (bit_idx & 63u)) & 1u;
 2796|  16.8k|      return is_valid ? IDNA_VALID : IDNA_DISALLOWED;
  ------------------
  |  Branch (2796:14): [True: 16.8k, False: 7]
  ------------------
 2797|  16.8k|    }
 2798|  10.5k|    return idna_stage2[ref + (cp & IDNA_BLOCK_MASK)];
 2799|  27.4k|  }
 2800|       |
 2801|       |  // -- Variation selectors supplement (U+E0100-U+E01EF): all ignored ---------
 2802|       |  // Everything else above IDNA_LOW_RANGE_END is disallowed.
 2803|     76|  if (cp >= IDNA_HIGH_IGNORED_START && cp < IDNA_HIGH_IGNORED_END) {
  ------------------
  |  Branch (2803:7): [True: 46, False: 30]
  |  Branch (2803:40): [True: 13, False: 33]
  ------------------
 2804|     13|    return IDNA_IGNORED;
 2805|     13|  }
 2806|       |
 2807|     63|  return IDNA_DISALLOWED;
 2808|     76|}
serializers.cc:_ZN3ada4idnaL9utf8_nextERPKh:
 2813|  4.23k|static char32_t utf8_next(const uint8_t*& ptr) noexcept {
 2814|  4.23k|  uint8_t b0 = *ptr++;
 2815|  4.23k|  if (b0 < 0x80u) return static_cast<char32_t>(b0);
  ------------------
  |  Branch (2815:7): [True: 3.04k, False: 1.18k]
  ------------------
 2816|  1.18k|  if (b0 < 0xE0u) {
  ------------------
  |  Branch (2816:7): [True: 774, False: 410]
  ------------------
 2817|    774|    uint32_t cp = (b0 & 0x1Fu) << 6;
 2818|    774|    cp |= (*ptr++ & 0x3Fu);
 2819|    774|    return static_cast<char32_t>(cp);
 2820|    774|  }
 2821|    410|  if (b0 < 0xF0u) {
  ------------------
  |  Branch (2821:7): [True: 207, False: 203]
  ------------------
 2822|    207|    uint32_t cp = (b0 & 0x0Fu) << 12;
 2823|    207|    cp |= ((*ptr++ & 0x3Fu) << 6);
 2824|    207|    cp |= (*ptr++ & 0x3Fu);
 2825|    207|    return static_cast<char32_t>(cp);
 2826|    207|  }
 2827|       |  // 4-byte sequence
 2828|    203|  uint32_t cp = (b0 & 0x07u) << 18;
 2829|    203|  cp |= ((*ptr++ & 0x3Fu) << 12);
 2830|    203|  cp |= ((*ptr++ & 0x3Fu) << 6);
 2831|    203|  cp |= (*ptr++ & 0x3Fu);
 2832|    203|  return static_cast<char32_t>(cp);
 2833|    410|}
serializers.cc:_ZN3ada4idnaL19char_to_digit_valueEc:
 8040|  5.94k|static constexpr int32_t char_to_digit_value(char value) {
 8041|  5.94k|  if (value >= 'a' && value <= 'z') return value - 'a';
  ------------------
  |  Branch (8041:7): [True: 4.97k, False: 969]
  |  Branch (8041:23): [True: 4.97k, False: 1]
  ------------------
 8042|    970|  if (value >= '0' && value <= '9') return value - '0' + 26;
  ------------------
  |  Branch (8042:7): [True: 965, False: 5]
  |  Branch (8042:23): [True: 962, False: 3]
  ------------------
 8043|      8|  return -1;
 8044|    970|}
serializers.cc:_ZN3ada4idnaL5adaptEiib:
 8050|  4.54k|static constexpr int32_t adapt(int32_t d, int32_t n, bool firsttime) {
 8051|  4.54k|  if (firsttime) {
  ------------------
  |  Branch (8051:7): [True: 957, False: 3.59k]
  ------------------
 8052|    957|    d = d / damp;
 8053|  3.59k|  } else {
 8054|  3.59k|    d = d / 2;
 8055|  3.59k|  }
 8056|  4.54k|  d += d / n;
 8057|  4.54k|  int32_t k = 0;
 8058|  5.50k|  while (d > ((base - tmin) * tmax) / 2) {
  ------------------
  |  Branch (8058:10): [True: 955, False: 4.54k]
  ------------------
 8059|    955|    d /= base - tmin;
 8060|    955|    k += base;
 8061|    955|  }
 8062|  4.54k|  return k + (((base - tmin + 1) * d) / (d + skew));
 8063|  4.54k|}
serializers.cc:_ZN3ada4idnaL13digit_to_charEi:
 8046|  3.26k|static constexpr char digit_to_char(int32_t digit) {
 8047|  3.26k|  return digit < 26 ? char(digit + 97) : char(digit + 22);
  ------------------
  |  Branch (8047:10): [True: 2.17k, False: 1.09k]
  ------------------
 8048|  3.26k|}
serializers.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_0clEj:
 9473|    913|      auto is_l_or_d = [](uint32_t code) {
 9474|    913|        return std::ranges::binary_search(L, code) ||
  ------------------
  |  Branch (9474:16): [True: 0, False: 913]
  ------------------
 9475|    913|               std::ranges::binary_search(D, code);
  ------------------
  |  Branch (9475:16): [True: 27, False: 886]
  ------------------
 9476|    913|      };
serializers.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_1clEj:
 9477|    705|      auto is_r_or_d = [](uint32_t code) {
 9478|    705|        return std::ranges::binary_search(R, code) ||
  ------------------
  |  Branch (9478:16): [True: 1, False: 704]
  ------------------
 9479|    704|               std::ranges::binary_search(D, code);
  ------------------
  |  Branch (9479:16): [True: 7, False: 697]
  ------------------
 9480|    705|      };
serializers.cc:_ZN3ada4idnaL20find_last_not_of_nsmENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9055|    926|    const std::u32string_view label) noexcept {
 9056|    970|  for (int i = static_cast<int>(label.size() - 1); i >= 0; i--)
  ------------------
  |  Branch (9056:52): [True: 970, False: 0]
  ------------------
 9057|    970|    if (find_direction(label[i]) != direction::NSM) return i;
  ------------------
  |  Branch (9057:9): [True: 926, False: 44]
  ------------------
 9058|       |
 9059|      0|  return std::u32string_view::npos;
 9060|    926|}
serializers.cc:_ZN3ada4idnaL12is_rtl_labelENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9064|    926|inline static bool is_rtl_label(const std::u32string_view label) noexcept {
 9065|    926|  const size_t mask =
 9066|    926|      (1u << direction::R) | (1u << direction::AL) | (1u << direction::AN);
 9067|       |
 9068|    926|  size_t directions = 0;
 9069|  13.2k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (9069:22): [True: 12.3k, False: 926]
  ------------------
 9070|  12.3k|    directions |= 1u << find_direction(label[i]);
 9071|  12.3k|  }
 9072|    926|  return (directions & mask) != 0;
 9073|    926|}
serializers.cc:_ZN3ada4idnaL14find_directionEj:
 9040|  16.5k|inline static direction find_direction(uint32_t code_point) noexcept {
 9041|  16.5k|  const auto it = std::ranges::lower_bound(
 9042|  16.5k|      dir_table, code_point, {},
 9043|  16.5k|      &directions::final_code);  // project to the upper bound of each range
 9044|       |
 9045|       |  // Safety check - almost always false, but cheap.
 9046|  16.5k|  if (it == std::ranges::end(dir_table)) {
  ------------------
  |  Branch (9046:7): [True: 0, False: 16.5k]
  ------------------
 9047|      0|    return direction::NONE;
 9048|      0|  }
 9049|       |
 9050|       |  // Invariant: it->final_code >= code_point. Confirm it's also in range.
 9051|  16.5k|  return code_point >= it->start_code ? it->direct : direction::NONE;
  ------------------
  |  Branch (9051:10): [True: 16.4k, False: 106]
  ------------------
 9052|  16.5k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9628|  1.48k|bool constexpr is_ascii(std::string_view view) {
 9629|  23.2k|  for (uint8_t c : view) {
  ------------------
  |  Branch (9629:18): [True: 23.2k, False: 633]
  ------------------
 9630|  23.2k|    if (c >= 0x80) {
  ------------------
  |  Branch (9630:9): [True: 850, False: 22.3k]
  ------------------
 9631|    850|      return false;
 9632|    850|    }
 9633|  23.2k|  }
 9634|    633|  return true;
 9635|  1.48k|}
serializers.cc:_ZN3ada4idnaL19from_ascii_to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9661|    633|static std::string from_ascii_to_ascii(std::string_view ut8_string) {
 9662|    633|  static const std::string error = "";
 9663|    633|  std::string out;
 9664|    633|  out.reserve(ut8_string.size());
 9665|    633|  std::u32string tmp_buffer;
 9666|    633|  std::u32string post_map;
 9667|    633|  size_t label_start = 0;
 9668|       |
 9669|  1.82k|  while (label_start != ut8_string.size()) {
  ------------------
  |  Branch (9669:10): [True: 1.33k, False: 497]
  ------------------
 9670|  1.33k|    size_t loc_dot = ut8_string.find('.', label_start);
 9671|  1.33k|    bool is_last_label = (loc_dot == std::string_view::npos);
 9672|  1.33k|    size_t label_size =
 9673|  1.33k|        is_last_label ? ut8_string.size() - label_start : loc_dot - label_start;
  ------------------
  |  Branch (9673:9): [True: 540, False: 791]
  ------------------
 9674|  1.33k|    size_t label_size_with_dot = is_last_label ? label_size : label_size + 1;
  ------------------
  |  Branch (9674:34): [True: 540, False: 791]
  ------------------
 9675|  1.33k|    std::string_view label_view(ut8_string.data() + label_start, label_size);
 9676|  1.33k|    label_start += label_size_with_dot;
 9677|  1.33k|    if (label_size == 0) {
  ------------------
  |  Branch (9677:9): [True: 129, False: 1.20k]
  ------------------
 9678|       |      // empty label? Nothing to do.
 9679|  1.20k|    } else {
 9680|       |      // Append the label to out and lowercase it in-place, avoiding a separate
 9681|       |      // copy of the entire input string.
 9682|  1.20k|      size_t label_out_start = out.size();
 9683|  1.20k|      out.append(label_view);
 9684|  1.20k|      ascii_map(out.data() + label_out_start, label_size);
 9685|  1.20k|      std::string_view mapped_label(out.data() + label_out_start, label_size);
 9686|  1.20k|      if (mapped_label.starts_with("xn--")) {
  ------------------
  |  Branch (9686:11): [True: 237, False: 965]
  ------------------
 9687|       |        // The xn-- part is the expensive game.
 9688|    237|        std::string_view puny_segment_ascii(out.data() + label_out_start + 4,
 9689|    237|                                            label_size - 4);
 9690|    237|        tmp_buffer.clear();
 9691|    237|        bool is_ok =
 9692|    237|            ada::idna::punycode_to_utf32(puny_segment_ascii, tmp_buffer);
 9693|    237|        if (!is_ok) {
  ------------------
  |  Branch (9693:13): [True: 20, False: 217]
  ------------------
 9694|     20|          return error;
 9695|     20|        }
 9696|       |        // If the input is just ASCII, it should not have been encoded
 9697|       |        // as punycode.
 9698|       |        // https://github.com/whatwg/url/issues/760
 9699|    217|        if (is_ascii(tmp_buffer)) {
  ------------------
  |  Branch (9699:13): [True: 4, False: 213]
  ------------------
 9700|      4|          return error;
 9701|      4|        }
 9702|    213|        if (!ada::idna::map(tmp_buffer, post_map)) {
  ------------------
  |  Branch (9702:13): [True: 48, False: 165]
  ------------------
 9703|     48|          return error;
 9704|     48|        }
 9705|    165|        if (tmp_buffer != post_map) {
  ------------------
  |  Branch (9705:13): [True: 21, False: 144]
  ------------------
 9706|     21|          return error;
 9707|     21|        }
 9708|    144|        normalize(post_map);
 9709|    144|        if (post_map != tmp_buffer) {
  ------------------
  |  Branch (9709:13): [True: 22, False: 122]
  ------------------
 9710|     22|          return error;
 9711|     22|        }
 9712|    122|        if (post_map.empty()) {
  ------------------
  |  Branch (9712:13): [True: 0, False: 122]
  ------------------
 9713|      0|          return error;
 9714|      0|        }
 9715|    122|        if (!is_label_valid(post_map)) {
  ------------------
  |  Branch (9715:13): [True: 21, False: 101]
  ------------------
 9716|     21|          return error;
 9717|     21|        }
 9718|    122|      }
 9719|  1.20k|    }
 9720|  1.19k|    if (!is_last_label) {
  ------------------
  |  Branch (9720:9): [True: 740, False: 455]
  ------------------
 9721|    740|      out.push_back('.');
 9722|    740|    }
 9723|  1.19k|  }
 9724|    497|  return out;
 9725|    633|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 9619|  2.35k|bool constexpr is_ascii(std::u32string_view view) {
 9620|  12.9k|  for (uint32_t c : view) {
  ------------------
  |  Branch (9620:19): [True: 12.9k, False: 1.24k]
  ------------------
 9621|  12.9k|    if (c >= 0x80) {
  ------------------
  |  Branch (9621:9): [True: 1.10k, False: 11.8k]
  ------------------
 9622|  1.10k|      return false;
 9623|  1.10k|    }
 9624|  12.9k|  }
 9625|  1.24k|  return true;
 9626|  2.35k|}
_ZN3ada7unicode18is_ascii_hex_digitEc:
10923|  23.4k|ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept {
10924|  23.4k|  return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
  ------------------
  |  Branch (10924:11): [True: 22.4k, False: 902]
  |  Branch (10924:23): [True: 10.2k, False: 12.2k]
  |  Branch (10924:37): [True: 12.0k, False: 1.08k]
  |  Branch (10924:49): [True: 7.78k, False: 4.31k]
  ------------------
10925|  5.40k|         (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (10925:11): [True: 1.05k, False: 4.35k]
  |  Branch (10925:23): [True: 870, False: 180]
  ------------------
10926|  23.4k|}
_ZN3ada7unicode21convert_hex_to_binaryEc:
11013|  18.4k|unsigned constexpr convert_hex_to_binary(const char c) noexcept {
11014|  18.4k|  return hex_to_binary_table[c - '0'];
11015|  18.4k|}
serializers.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
11052|  84.6k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11053|  84.6k|    return character_sets::bit_at(character_set, c);
11054|  84.6k|  });
_ZN3ada7unicode36contains_forbidden_domain_code_pointEPKcm:
10850|  4.13k|    const char* input, size_t length) noexcept {
10851|  4.13k|  size_t i = 0;
10852|  4.13k|  uint8_t accumulator{};
10853|  34.0k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (10853:10): [True: 29.9k, False: 4.13k]
  ------------------
10854|  29.9k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
10855|  29.9k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 1])];
10856|  29.9k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 2])];
10857|  29.9k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 3])];
10858|  29.9k|  }
10859|  9.12k|  for (; i < length; i++) {
  ------------------
  |  Branch (10859:10): [True: 4.99k, False: 4.13k]
  ------------------
10860|  4.99k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
10861|  4.99k|  }
10862|  4.13k|  return accumulator;
10863|  4.13k|}
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10699|  8.87k|    std::string_view user_input) noexcept {
10700|       |  // first check for short strings in which case we do it naively.
10701|  8.87k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (10701:7): [True: 1.52k, False: 7.35k]
  ------------------
10702|  1.52k|    return std::ranges::any_of(user_input, is_tabs_or_newline);
10703|  1.52k|  }
10704|       |  // fast path for long strings (expected to be common)
10705|  7.35k|  size_t i = 0;
10706|  7.35k|  const __m128i mask1 = _mm_set1_epi8('\r');
10707|  7.35k|  const __m128i mask2 = _mm_set1_epi8('\n');
10708|  7.35k|  const __m128i mask3 = _mm_set1_epi8('\t');
10709|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
10710|  7.35k|  __m128i running{0};
10711|  27.2k|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (10711:10): [True: 19.9k, False: 7.35k]
  ------------------
10712|  19.9k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
10713|  19.9k|    running = _mm_or_si128(
10714|  19.9k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
10715|  19.9k|                                           _mm_cmpeq_epi8(word, mask2))),
10716|  19.9k|        _mm_cmpeq_epi8(word, mask3));
10717|  19.9k|  }
10718|  7.35k|  if (i < user_input.size()) {
  ------------------
  |  Branch (10718:7): [True: 6.93k, False: 413]
  ------------------
10719|  6.93k|    __m128i word = _mm_loadu_si128(
10720|  6.93k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
10721|  6.93k|    running = _mm_or_si128(
10722|  6.93k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
10723|  6.93k|                                           _mm_cmpeq_epi8(word, mask2))),
10724|  6.93k|        _mm_cmpeq_epi8(word, mask3));
10725|  6.93k|  }
10726|  7.35k|  return _mm_movemask_epi8(running) != 0;
10727|  8.87k|}
_ZN3ada7unicode18is_tabs_or_newlineEc:
10594|  8.21k|constexpr bool is_tabs_or_newline(char c) noexcept {
10595|  8.21k|  return c == '\r' || c == '\n' || c == '\t';
  ------------------
  |  Branch (10595:10): [True: 34, False: 8.18k]
  |  Branch (10595:23): [True: 6, False: 8.17k]
  |  Branch (10595:36): [True: 7, False: 8.16k]
  ------------------
10596|  8.21k|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
11734|    194|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
11735|       |  // if this ever becomes a performance issue, we could use an approach similar
11736|       |  // to has_tabs_or_newline
11737|    194|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
11738|    194|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
10944|  10.7k|    const char c) noexcept {
10945|  10.7k|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (10945:10): [True: 303, False: 10.4k]
  |  Branch (10945:23): [True: 160, False: 10.2k]
  |  Branch (10945:36): [True: 275, False: 9.97k]
  ------------------
10946|  10.7k|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
10939|  11.3k|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
10940|  11.3k|  return (unsigned char)c <= ' ';
10941|  11.3k|}
_ZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10952|  2.61k|    std::string_view input) noexcept {
10953|       |  // This will catch most cases:
10954|       |  // The length must be 2,4 or 6.
10955|       |  // We divide by two and require
10956|       |  // that the result be between 1 and 3 inclusively.
10957|  2.61k|  uint64_t half_length = uint64_t(input.size()) / 2;
10958|  2.61k|  if (half_length - 1 > 2) {
  ------------------
  |  Branch (10958:7): [True: 1.95k, False: 658]
  ------------------
10959|  1.95k|    return false;
10960|  1.95k|  }
10961|       |  // We have a string of length 2, 4 or 6.
10962|       |  // We now check the first character:
10963|    658|  if ((input[0] != '.') && (input[0] != '%')) {
  ------------------
  |  Branch (10963:7): [True: 405, False: 253]
  |  Branch (10963:28): [True: 284, False: 121]
  ------------------
10964|    284|    return false;
10965|    284|  }
10966|       |  // We are unlikely the get beyond this point.
10967|    374|  int hash_value = (input.size() + (unsigned)(input[0])) & 3;
10968|    374|  const std::string_view target = table_is_double_dot_path_segment[hash_value];
10969|    374|  if (target.size() != input.size()) {
  ------------------
  |  Branch (10969:7): [True: 160, False: 214]
  ------------------
10970|    160|    return false;
10971|    160|  }
10972|       |  // We almost never get here.
10973|       |  // Optimizing the rest is relatively unimportant.
10974|    214|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
10975|    214|    uint16_t A, B;
10976|    214|    memcpy(&A, a.data(), sizeof(A));
10977|    214|    memcpy(&B, b.data(), sizeof(B));
10978|    214|    return A == B;
10979|    214|  };
10980|    214|  if (!prefix_equal_unsafe(input, target)) {
  ------------------
  |  Branch (10980:7): [True: 47, False: 167]
  ------------------
10981|     47|    return false;
10982|     47|  }
10983|    290|  for (size_t i = 2; i < input.size(); i++) {
  ------------------
  |  Branch (10983:22): [True: 147, False: 143]
  ------------------
10984|    147|    char c = input[i];
10985|    147|    if ((uint8_t((c | 0x20) - 0x61) <= 25 ? (c | 0x20) : c) != target[i]) {
  ------------------
  |  Branch (10985:9): [True: 24, False: 123]
  |  Branch (10985:10): [True: 62, False: 85]
  ------------------
10986|     24|      return false;
10987|     24|    }
10988|    147|  }
10989|    143|  return true;
10990|       |  // The above code might be a bit better than the code below. Compilers
10991|       |  // are not stupid and may use the fact that these strings have length 2,4 and
10992|       |  // 6 and other tricks.
10993|       |  // return input == ".." ||
10994|       |  //  input == ".%2e" || input == ".%2E" ||
10995|       |  //  input == "%2e." || input == "%2E." ||
10996|       |  //  input == "%2e%2e" || input == "%2E%2E" || input == "%2E%2e" || input ==
10997|       |  //  "%2e%2E";
10998|    167|}
_ZZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_S5_E_clES5_S5_:
10974|    214|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
10975|    214|    uint16_t A, B;
10976|    214|    memcpy(&A, a.data(), sizeof(A));
10977|    214|    memcpy(&B, b.data(), sizeof(B));
10978|    214|    return A == B;
10979|    214|  };
_ZN3ada7unicode26is_single_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
11001|  4.94k|    std::string_view input) noexcept {
11002|  4.94k|  return input == "." || input == "%2e" || input == "%2E";
  ------------------
  |  Branch (11002:10): [True: 118, False: 4.82k]
  |  Branch (11002:26): [True: 6, False: 4.82k]
  |  Branch (11002:44): [True: 0, False: 4.82k]
  ------------------
11003|  4.94k|}
_ZN3ada8checkers17verify_dns_lengthENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
  112|  3.19k|    std::string_view input) noexcept {
  113|  3.19k|  if (input.empty()) return false;
  ------------------
  |  Branch (113:7): [True: 633, False: 2.55k]
  ------------------
  114|  2.55k|  if (input.back() == '.') {
  ------------------
  |  Branch (114:7): [True: 272, False: 2.28k]
  ------------------
  115|    272|    if (input.size() > 254) return false;
  ------------------
  |  Branch (115:9): [True: 0, False: 272]
  ------------------
  116|  2.28k|  } else if (input.size() > 253)
  ------------------
  |  Branch (116:14): [True: 0, False: 2.28k]
  ------------------
  117|      0|    return false;
  118|       |
  119|  2.55k|  size_t start = 0;
  120|  10.5k|  while (start < input.size()) {
  ------------------
  |  Branch (120:10): [True: 8.36k, False: 2.17k]
  ------------------
  121|  8.36k|    auto dot_location = input.find('.', start);
  122|       |    // If not found, it's likely the end of the domain
  123|  8.36k|    if (dot_location == std::string_view::npos) dot_location = input.size();
  ------------------
  |  Branch (123:9): [True: 1.98k, False: 6.38k]
  ------------------
  124|       |
  125|  8.36k|    auto label_size = dot_location - start;
  126|  8.36k|    if (label_size > 63 || label_size == 0) return false;
  ------------------
  |  Branch (126:9): [True: 209, False: 8.15k]
  |  Branch (126:28): [True: 173, False: 7.98k]
  ------------------
  127|       |
  128|  7.98k|    start = dot_location + 1;
  129|  7.98k|  }
  130|       |
  131|  2.17k|  return true;
  132|  2.55k|}
_ZN3ada7unicode13is_alnum_plusEc:
10916|  22.7k|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
10917|  22.7k|  return is_alnum_plus_table[uint8_t(c)];
10918|       |  // A table is almost surely much faster than the
10919|       |  // following under most compilers: return
10920|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
10921|  22.7k|}
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
11674|  5.68k|    std::string_view& input) noexcept {
11675|       |  // compiles down to 20--30 instructions including a class to memchr (C
11676|       |  // function). this function should be quite fast.
11677|  5.68k|  size_t location_of_first = input.find('#');
11678|  5.68k|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (11678:7): [True: 5.57k, False: 108]
  ------------------
11679|  5.57k|    return std::nullopt;
11680|  5.57k|  }
11681|    108|  std::string_view hash = input;
11682|    108|  hash.remove_prefix(location_of_first + 1);
11683|    108|  input.remove_suffix(input.size() - location_of_first);
11684|    108|  return hash;
11685|  5.68k|}
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12612|  1.14k|find_authority_delimiter_special(std::string_view view) noexcept {
12613|       |  // performance note: we might be able to gain further performance
12614|       |  // with SIMD intrinsics.
12615|  7.89k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (12615:33): [True: 7.88k, False: 10]
  ------------------
12616|  7.88k|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (12616:9): [True: 1.13k, False: 6.74k]
  ------------------
12617|  1.13k|      return pos - view.begin();
12618|  1.13k|    }
12619|  7.88k|  }
12620|     10|  return size_t(view.size());
12621|  1.14k|}
_ZN3ada7helpers12shorten_pathERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_6scheme4typeE:
11687|    143|ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) {
11688|       |  // Let path be url's path.
11689|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
11690|       |  // Windows drive letter, then return.
11691|    143|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (11691:7): [True: 0, False: 143]
  ------------------
11692|      0|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (11692:7): [True: 0, False: 0]
  |  Branch (11692:54): [True: 0, False: 0]
  ------------------
11693|      0|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (11693:9): [True: 0, False: 0]
  ------------------
11694|      0|            helpers::substring(path, 1))) {
11695|      0|      return false;
11696|      0|    }
11697|      0|  }
11698|       |
11699|       |  // Remove path's last item, if any.
11700|    143|  size_t last_delimiter = path.rfind('/');
11701|    143|  if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (11701:7): [True: 92, False: 51]
  ------------------
11702|     92|    path.erase(last_delimiter);
11703|     92|    return true;
11704|     92|  }
11705|       |
11706|     51|  return false;
11707|    143|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12325|  5.68k|    const bool is_special, std::string_view& view) noexcept {
12326|       |  /**
12327|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
12328|       |   * compute a variable called insideBrackets but this variable is only used
12329|       |   * once, to check whether a ':' character was found outside brackets. Exact
12330|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
12331|       |   * It is conceptually simpler and arguably more efficient to just return a
12332|       |   * Boolean indicating whether ':' was found outside brackets.
12333|       |   */
12334|  5.68k|  const size_t view_size = view.size();
12335|  5.68k|  size_t location = 0;
12336|  5.68k|  bool found_colon = false;
12337|       |  /**
12338|       |   * Performance analysis:
12339|       |   *
12340|       |   * We are basically seeking the end of the hostname which can be indicated
12341|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
12342|       |   * (where '\\' is only applicable for special URLs). However, these must
12343|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
12344|       |   * '?' does not count.
12345|       |   *
12346|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
12347|       |   * the set of delimiters, and that we handle it first.
12348|       |   *
12349|       |   * So the trick is to have a fast function that locates the next delimiter.
12350|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
12351|       |   * function would be provided by the C++ standard library, but it seems that
12352|       |   * find_first_of is not very fast, so we are forced to roll our own.
12353|       |   *
12354|       |   * We do not break into two loops for speed, but for clarity.
12355|       |   */
12356|  5.68k|  if (is_special) {
  ------------------
  |  Branch (12356:7): [True: 5.68k, False: 0]
  ------------------
12357|       |    // We move to the next delimiter.
12358|  5.68k|    location = find_next_host_delimiter_special(view, location);
12359|       |    // Unless we find '[' then we are going only going to have to call
12360|       |    // find_next_host_delimiter_special once.
12361|  9.09k|    for (; location < view_size;
  ------------------
  |  Branch (12361:12): [True: 9.05k, False: 48]
  ------------------
12362|  9.05k|         location = find_next_host_delimiter_special(view, location)) {
12363|  9.05k|      if (view[location] == '[') {
  ------------------
  |  Branch (12363:11): [True: 3.45k, False: 5.59k]
  ------------------
12364|  3.45k|        location = view.find(']', location);
12365|  3.45k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (12365:13): [True: 37, False: 3.41k]
  ------------------
12366|       |          // performance: view.find might get translated to a memchr, which
12367|       |          // has no notion of std::string_view::npos, so the code does not
12368|       |          // reflect the assembly.
12369|     37|          location = view_size;
12370|     37|          break;
12371|     37|        }
12372|  5.59k|      } else {
12373|  5.59k|        found_colon = view[location] == ':';
12374|  5.59k|        break;
12375|  5.59k|      }
12376|  9.05k|    }
12377|  5.68k|  } else {
12378|       |    // We move to the next delimiter.
12379|      0|    location = find_next_host_delimiter(view, location);
12380|       |    // Unless we find '[' then we are going only going to have to call
12381|       |    // find_next_host_delimiter_special once.
12382|      0|    for (; location < view_size;
  ------------------
  |  Branch (12382:12): [True: 0, False: 0]
  ------------------
12383|      0|         location = find_next_host_delimiter(view, location)) {
12384|      0|      if (view[location] == '[') {
  ------------------
  |  Branch (12384:11): [True: 0, False: 0]
  ------------------
12385|      0|        location = view.find(']', location);
12386|      0|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (12386:13): [True: 0, False: 0]
  ------------------
12387|       |          // performance: view.find might get translated to a memchr, which
12388|       |          // has no notion of std::string_view::npos, so the code does not
12389|       |          // reflect the assembly.
12390|      0|          location = view_size;
12391|      0|          break;
12392|      0|        }
12393|      0|      } else {
12394|      0|        found_colon = view[location] == ':';
12395|      0|        break;
12396|      0|      }
12397|      0|    }
12398|      0|  }
12399|       |  // performance: remove_suffix may translate into a single instruction.
12400|  5.68k|  view.remove_suffix(view_size - location);
12401|  5.68k|  return {location, found_colon};
12402|  5.68k|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
11902|  9.09k|    std::string_view view, size_t location) noexcept {
11903|       |  // first check for short strings in which case we do it naively.
11904|  9.09k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (11904:7): [True: 4.11k, False: 4.98k]
  ------------------
11905|  15.5k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (11905:31): [True: 15.5k, False: 34]
  ------------------
11906|  15.5k|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (11906:11): [True: 132, False: 15.4k]
  |  Branch (11906:29): [True: 3.73k, False: 11.6k]
  |  Branch (11906:47): [True: 22, False: 11.6k]
  ------------------
11907|  11.6k|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (11907:11): [True: 6, False: 11.6k]
  |  Branch (11907:29): [True: 179, False: 11.4k]
  ------------------
11908|  4.07k|        return i;
11909|  4.07k|      }
11910|  15.5k|    }
11911|     34|    return size_t(view.size());
11912|  4.11k|  }
11913|       |  // fast path for long strings (expected to be common)
11914|  4.98k|  size_t i = location;
11915|  4.98k|  const __m128i mask1 = _mm_set1_epi8(':');
11916|  4.98k|  const __m128i mask2 = _mm_set1_epi8('/');
11917|  4.98k|  const __m128i mask3 = _mm_set1_epi8('\\');
11918|  4.98k|  const __m128i mask4 = _mm_set1_epi8('?');
11919|  4.98k|  const __m128i mask5 = _mm_set1_epi8('[');
11920|       |
11921|  7.38k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (11921:10): [True: 6.57k, False: 811]
  ------------------
11922|  6.57k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
11923|  6.57k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
11924|  6.57k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
11925|  6.57k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
11926|  6.57k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
11927|  6.57k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
11928|  6.57k|    __m128i m = _mm_or_si128(
11929|  6.57k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
11930|  6.57k|    int mask = _mm_movemask_epi8(m);
11931|  6.57k|    if (mask != 0) {
  ------------------
  |  Branch (11931:9): [True: 4.17k, False: 2.39k]
  ------------------
11932|  4.17k|      return i + trailing_zeroes(mask);
11933|  4.17k|    }
11934|  6.57k|  }
11935|    811|  if (i < view.size()) {
  ------------------
  |  Branch (11935:7): [True: 808, False: 3]
  ------------------
11936|    808|    __m128i word =
11937|    808|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
11938|    808|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
11939|    808|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
11940|    808|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
11941|    808|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
11942|    808|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
11943|    808|    __m128i m = _mm_or_si128(
11944|    808|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
11945|    808|    int mask = _mm_movemask_epi8(m);
11946|    808|    if (mask != 0) {
  ------------------
  |  Branch (11946:9): [True: 797, False: 11]
  ------------------
11947|    797|      return view.length() - 16 + trailing_zeroes(mask);
11948|    797|    }
11949|    808|  }
11950|     14|  return size_t(view.length());
11951|    811|}
_ZN3ada7helpers15trailing_zeroesEj:
11755|  4.97k|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
11756|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
11757|       |  unsigned long ret;
11758|       |  // Search the mask data from least significant bit (LSB)
11759|       |  // to the most significant bit (MSB) for a set bit (1).
11760|       |  _BitScanForward(&ret, input_num);
11761|       |  return (int)ret;
11762|       |#else   // ADA_REGULAR_VISUAL_STUDIO
11763|  4.97k|  return __builtin_ctzl(input_num);
11764|  4.97k|#endif  // ADA_REGULAR_VISUAL_STUDIO
11765|  4.97k|}
_ZN3ada7unicode30is_forbidden_domain_code_pointEc:
10845|  16.6k|    const char c) noexcept {
10846|  16.6k|  return is_forbidden_domain_code_point_table[uint8_t(c)];
10847|  16.6k|}
serializers.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
14693|  5.68k|    ada::url_components& components, uint32_t new_difference) {
14694|  5.68k|  components.username_end += new_difference;
14695|  5.68k|  components.host_start += new_difference;
14696|  5.68k|  components.host_end += new_difference;
14697|  5.68k|  components.pathname_start += new_difference;
14698|  5.68k|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (14698:7): [True: 0, False: 5.68k]
  ------------------
14699|      0|    components.search_start += new_difference;
14700|      0|  }
14701|  5.68k|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (14701:7): [True: 0, False: 5.68k]
  ------------------
14702|      0|    components.hash_start += new_difference;
14703|      0|  }
14704|  5.68k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15149|  5.68k|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
15150|  5.68k|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
15151|  5.68k|          " bytes]");
15152|  5.68k|  ADA_ASSERT_TRUE(validate());
15153|  5.68k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15154|  5.68k|  if (input.empty()) {
  ------------------
  |  Branch (15154:7): [True: 2, False: 5.67k]
  ------------------
15155|      2|    return is_valid = false;
15156|      2|  }  // technically unnecessary.
15157|       |  // If input starts with U+005B ([), then:
15158|  5.67k|  if (input[0] == '[') {
  ------------------
  |  Branch (15158:7): [True: 3.26k, False: 2.41k]
  ------------------
15159|       |    // If input does not end with U+005D (]), validation error, return failure.
15160|  3.26k|    if (input.back() != ']') {
  ------------------
  |  Branch (15160:9): [True: 16, False: 3.25k]
  ------------------
15161|     16|      return is_valid = false;
15162|     16|    }
15163|  3.25k|    ada_log("parse_host ipv6");
15164|       |
15165|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
15166|       |    // trailing U+005D (]) removed.
15167|  3.25k|    input.remove_prefix(1);
15168|  3.25k|    input.remove_suffix(1);
15169|  3.25k|    return parse_ipv6(input);
15170|  3.26k|  }
15171|       |
15172|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
15173|       |  // input.
15174|  2.41k|  if (!is_special()) {
  ------------------
  |  Branch (15174:7): [True: 0, False: 2.41k]
  ------------------
15175|      0|    return parse_opaque_host(input);
15176|      0|  }
15177|       |  // Let domain be the result of running UTF-8 decode without BOM on the
15178|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
15179|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
15180|       |  // which case we do not need to call the expensive 'to_ascii' if a few
15181|       |  // conditions are met: no '%' and no 'xn-' subsequence.
15182|       |
15183|       |  // Often, the input does not contain any forbidden code points, and no upper
15184|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
15185|       |  // optimize for such a common case.
15186|       |
15187|       |  // Fast path: try to parse as pure decimal IPv4(a.b.c.d) first.
15188|  2.41k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
15189|  2.41k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (15189:7): [True: 140, False: 2.27k]
  ------------------
15190|       |    // Fast path succeeded - input is pure decimal IPv4
15191|    140|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (15191:9): [True: 140, False: 0]
  |  Branch (15191:27): [True: 2, False: 138]
  ------------------
15192|      2|      update_base_hostname(input.substr(0, input.size() - 1));
15193|    138|    } else {
15194|    138|      update_base_hostname(input);
15195|    138|    }
15196|    140|    host_type = IPV4;
15197|    140|    is_valid = true;
15198|    140|    ada_log("parse_host fast path decimal ipv4");
15199|    140|    ADA_ASSERT_TRUE(validate());
15200|    140|    return true;
15201|    140|  }
15202|  2.27k|  uint8_t is_forbidden_or_upper =
15203|  2.27k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
15204|  2.27k|                                                             input.size());
15205|       |  // Minor optimization opportunity:
15206|       |  // contains_forbidden_domain_code_point_or_upper could be extend to check for
15207|       |  // the presence of characters that cannot appear in the ipv4 address and we
15208|       |  // could also check whether x and n and - are present, and so we could skip
15209|       |  // some of the checks below. However, the gains are likely to be small, and
15210|       |  // the code would be more complex.
15211|  2.27k|  if (is_forbidden_or_upper == 0 &&
  ------------------
  |  Branch (15211:7): [True: 925, False: 1.34k]
  ------------------
15212|    925|      input.find("xn-") == std::string_view::npos) {
  ------------------
  |  Branch (15212:7): [True: 787, False: 138]
  ------------------
15213|       |    // fast path
15214|    787|    update_base_hostname(input);
15215|       |
15216|       |    // Check for other IPv4 formats (hex, octal, etc.)
15217|    787|    if (checkers::is_ipv4(get_hostname())) {
  ------------------
  |  Branch (15217:9): [True: 587, False: 200]
  ------------------
15218|    587|      ada_log("parse_host fast path ipv4");
15219|    587|      return parse_ipv4(get_hostname(), true);
15220|    587|    }
15221|    200|    ada_log("parse_host fast path ", get_hostname());
15222|    200|    is_valid = true;
15223|    200|    return true;
15224|    787|  }
15225|       |  // We have encountered at least one forbidden code point or the input contains
15226|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
15227|       |  // conversion.
15228|       |
15229|  1.48k|  ada_log("parse_host calling to_ascii");
15230|  1.48k|  std::optional<std::string> host = std::string(get_hostname());
15231|  1.48k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
15232|  1.48k|  if (!is_valid) {
  ------------------
  |  Branch (15232:7): [True: 803, False: 680]
  ------------------
15233|    803|    ada_log("parse_host to_ascii returns false");
15234|    803|    return is_valid = false;
15235|    803|  }
15236|    680|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
15237|    680|          " bytes]");
15238|       |
15239|    680|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (15239:7): [True: 0, False: 680]
  ------------------
15240|    680|                          ada::unicode::is_forbidden_domain_code_point)) {
15241|      0|    return is_valid = false;
15242|      0|  }
15243|       |
15244|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
15245|       |  // asciiDomain.
15246|    680|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (15246:7): [True: 234, False: 446]
  ------------------
15247|    234|    ada_log("parse_host got ipv4 ", *host);
15248|    234|    return parse_ipv4(host.value(), false);
15249|    234|  }
15250|       |
15251|    446|  update_base_hostname(host.value());
15252|    446|  ADA_ASSERT_TRUE(validate());
15253|    446|  return true;
15254|    680|}
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
16176|    815|inline void url_aggregator::consume_prepared_path(std::string_view input) {
16177|    815|  ada_log("url_aggregator::consume_prepared_path ", input);
16178|       |  /***
16179|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
16180|       |   * unfortunate. This particular function is nearly identical, except that it
16181|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
16182|       |   * very common) merely appends to the buffer. This is the same trivial path as
16183|       |   * with helpers::parse_prepared_path, except that we have the additional check
16184|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
16185|       |   * modify it, and then insert it back into the buffer.
16186|       |   */
16187|    815|  uint8_t accumulator = checkers::path_signature(input);
16188|       |  // Let us first detect a trivial case.
16189|       |  // If it is special, we check that we have no dot, no %,  no \ and no
16190|       |  // character needing percent encoding. Otherwise, we check that we have no %,
16191|       |  // no dot, and no character needing percent encoding.
16192|    815|  constexpr uint8_t need_encoding = 1;
16193|    815|  constexpr uint8_t backslash_char = 2;
16194|    815|  constexpr uint8_t dot_char = 4;
16195|    815|  constexpr uint8_t percent_char = 8;
16196|    815|  bool special = type != ada::scheme::NOT_SPECIAL;
16197|    815|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (16197:39): [True: 0, False: 815]
  ------------------
16198|      0|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (16198:39): [True: 0, False: 0]
  ------------------
16199|    815|  bool trivial_path =
16200|    815|      (special ? (accumulator == 0)
  ------------------
  |  Branch (16200:7): [True: 381, False: 434]
  |  Branch (16200:8): [True: 815, False: 0]
  ------------------
16201|    815|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
16202|      0|                  0)) &&
16203|    381|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (16203:7): [True: 381, False: 0]
  ------------------
16204|    815|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (16204:7): [True: 181, False: 634]
  |  Branch (16204:34): [True: 181, False: 0]
  ------------------
16205|       |    // '4' means that we have at least one dot, but nothing that requires
16206|       |    // percent encoding or decoding. The only part that is not trivial is
16207|       |    // that we may have single dots and double dots path segments.
16208|       |    // If we have such segments, then we either have a path that begins
16209|       |    // with '.' (easy to check), or we have the sequence './'.
16210|       |    // Note: input cannot be empty, it must at least contain one character ('.')
16211|       |    // Note: we know that '\' is not present.
16212|    181|    if (input[0] != '.') {
  ------------------
  |  Branch (16212:9): [True: 125, False: 56]
  ------------------
16213|    125|      size_t slashdot = 0;
16214|    125|      bool dot_is_file = true;
16215|    373|      for (;;) {
16216|    373|        slashdot = input.find("/.", slashdot);
16217|    373|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (16217:13): [True: 125, False: 248]
  ------------------
16218|    125|          break;
16219|    248|        } else {  // uncommon
16220|       |          // only three cases matter: /./, /.. or a final /
16221|    248|          slashdot += 2;
16222|    248|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (16222:28): [True: 1, False: 247]
  |  Branch (16222:56): [True: 121, False: 126]
  ------------------
16223|    126|                           input[slashdot] == '/');
  ------------------
  |  Branch (16223:28): [True: 65, False: 61]
  ------------------
16224|    248|        }
16225|    373|      }
16226|    125|      trivial_path = dot_is_file;
16227|    125|    }
16228|    181|  }
16229|    815|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (16229:7): [True: 433, False: 382]
  |  Branch (16229:23): [True: 433, False: 0]
  ------------------
16230|    433|    ada_log("parse_path trivial");
16231|    433|    buffer += '/';
16232|    433|    buffer += input;
16233|    433|    return;
16234|    433|  }
16235|    382|  std::string path = std::string(get_pathname());
16236|       |  // We are going to need to look a bit at the path, but let us see if we can
16237|       |  // ignore percent encoding *and* backslashes *and* percent characters.
16238|       |  // Except for the trivial case, this is likely to capture 99% of paths out
16239|       |  // there.
16240|    382|  bool fast_path =
16241|    382|      (special &&
  ------------------
  |  Branch (16241:8): [True: 382, False: 0]
  ------------------
16242|    382|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (16242:8): [True: 129, False: 253]
  ------------------
16243|    129|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (16243:7): [True: 129, False: 0]
  ------------------
16244|    382|  if (fast_path) {
  ------------------
  |  Branch (16244:7): [True: 129, False: 253]
  ------------------
16245|    129|    ada_log("parse_prepared_path fast");
16246|       |    // Here we don't need to worry about \ or percent encoding.
16247|       |    // We also do not have a file protocol. We might have dots, however,
16248|       |    // but dots must as appear as '.', and they cannot be encoded because
16249|       |    // the symbol '%' is not present.
16250|    129|    size_t previous_location = 0;  // We start at 0.
16251|  1.38k|    do {
16252|  1.38k|      size_t new_location = input.find('/', previous_location);
16253|       |      // std::string_view path_view = input;
16254|       |      //  We process the last segment separately:
16255|  1.38k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (16255:11): [True: 129, False: 1.25k]
  ------------------
16256|    129|        std::string_view path_view = input.substr(previous_location);
16257|    129|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (16257:13): [True: 27, False: 102]
  ------------------
16258|       |          // e.g., if you receive ".." with an empty path, you go to "/".
16259|     27|          if (path.empty()) {
  ------------------
  |  Branch (16259:15): [True: 6, False: 21]
  ------------------
16260|      6|            path = '/';
16261|      6|            update_base_pathname(path);
16262|      6|            return;
16263|      6|          }
16264|       |          // Fast case where we have nothing to do:
16265|     21|          if (path.back() == '/') {
  ------------------
  |  Branch (16265:15): [True: 3, False: 18]
  ------------------
16266|      3|            update_base_pathname(path);
16267|      3|            return;
16268|      3|          }
16269|       |          // If you have the path "/joe/myfriend",
16270|       |          // then you delete 'myfriend'.
16271|     18|          path.resize(path.rfind('/') + 1);
16272|     18|          update_base_pathname(path);
16273|     18|          return;
16274|     21|        }
16275|    102|        path += '/';
16276|    102|        if (path_view != ".") {
  ------------------
  |  Branch (16276:13): [True: 99, False: 3]
  ------------------
16277|     99|          path.append(path_view);
16278|     99|        }
16279|    102|        update_base_pathname(path);
16280|    102|        return;
16281|  1.25k|      } else {
16282|       |        // This is a non-final segment.
16283|  1.25k|        std::string_view path_view =
16284|  1.25k|            input.substr(previous_location, new_location - previous_location);
16285|  1.25k|        previous_location = new_location + 1;
16286|  1.25k|        if (path_view == "..") {
  ------------------
  |  Branch (16286:13): [True: 90, False: 1.16k]
  ------------------
16287|     90|          size_t last_delimiter = path.rfind('/');
16288|     90|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (16288:15): [True: 72, False: 18]
  ------------------
16289|     72|            path.erase(last_delimiter);
16290|     72|          }
16291|  1.16k|        } else if (path_view != ".") {
  ------------------
  |  Branch (16291:20): [True: 1.09k, False: 65]
  ------------------
16292|  1.09k|          path += '/';
16293|  1.09k|          path.append(path_view);
16294|  1.09k|        }
16295|  1.25k|      }
16296|  1.38k|    } while (true);
  ------------------
  |  Branch (16296:14): [True: 1.25k, Folded]
  ------------------
16297|    253|  } else {
16298|    253|    ada_log("parse_path slow");
16299|       |    // we have reached the general case
16300|    253|    bool needs_percent_encoding = (accumulator & 1);
16301|    253|    std::string path_buffer_tmp;
16302|  2.61k|    do {
16303|  2.61k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (16303:26): [True: 2.61k, False: 0]
  |  Branch (16303:37): [True: 565, False: 2.05k]
  ------------------
16304|  2.61k|                            ? input.find_first_of("/\\")
16305|  2.61k|                            : input.find('/');
16306|  2.61k|      std::string_view path_view = input;
16307|  2.61k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (16307:11): [True: 2.36k, False: 253]
  ------------------
16308|  2.36k|        path_view.remove_suffix(path_view.size() - location);
16309|  2.36k|        input.remove_prefix(location + 1);
16310|  2.36k|      }
16311|       |      // path_buffer is either path_view or it might point at a percent encoded
16312|       |      // temporary string.
16313|  2.61k|      std::string_view path_buffer =
16314|  2.61k|          (needs_percent_encoding &&
  ------------------
  |  Branch (16314:12): [True: 2.26k, False: 350]
  ------------------
16315|  2.26k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (16315:12): [True: 465, False: 1.80k]
  ------------------
16316|  2.26k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
16317|  2.61k|              ? path_buffer_tmp
16318|  2.61k|              : path_view;
16319|  2.61k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (16319:11): [True: 143, False: 2.47k]
  ------------------
16320|    143|        helpers::shorten_path(path, type);
16321|    143|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (16321:13): [True: 6, False: 137]
  ------------------
16322|      6|          path += '/';
16323|      6|        }
16324|  2.47k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (16324:18): [True: 64, False: 2.41k]
  ------------------
16325|     64|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (16325:18): [True: 4, False: 60]
  ------------------
16326|      4|        path += '/';
16327|      4|      }
16328|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
16329|  2.47k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (16329:16): [True: 2.41k, False: 60]
  ------------------
16330|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
16331|       |        // Windows drive letter, then replace the second code point in
16332|       |        // path_buffer with U+003A (:).
16333|  2.41k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (16333:13): [True: 0, False: 2.41k]
  |  Branch (16333:48): [True: 0, False: 0]
  ------------------
16334|      0|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (16334:13): [True: 0, False: 0]
  ------------------
16335|      0|          path += '/';
16336|      0|          path += path_buffer[0];
16337|      0|          path += ':';
16338|      0|          path_buffer.remove_prefix(2);
16339|      0|          path.append(path_buffer);
16340|  2.41k|        } else {
16341|       |          // Append path_buffer to url's path.
16342|  2.41k|          path += '/';
16343|  2.41k|          path.append(path_buffer);
16344|  2.41k|        }
16345|  2.41k|      }
16346|  2.61k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (16346:11): [True: 253, False: 2.36k]
  ------------------
16347|    253|        update_base_pathname(path);
16348|    253|        return;
16349|    253|      }
16350|  2.61k|    } while (true);
  ------------------
  |  Branch (16350:14): [True: 2.36k, Folded]
  ------------------
16351|    253|  }
16352|    382|}
_ZN3ada7unicode14to_lower_asciiEPcm:
10602|  3.19k|constexpr bool to_lower_ascii(char* input, size_t length) noexcept {
10603|  3.19k|  uint64_t broadcast_80 = broadcast(0x80);
10604|  3.19k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
10605|  3.19k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
10606|  3.19k|  uint64_t non_ascii = 0;
10607|  3.19k|  size_t i = 0;
10608|       |
10609|  14.1k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (10609:10): [True: 10.9k, False: 3.19k]
  ------------------
10610|  10.9k|    uint64_t word{};
10611|  10.9k|    memcpy(&word, input + i, sizeof(word));
10612|  10.9k|    non_ascii |= (word & broadcast_80);
10613|  10.9k|    word ^=
10614|  10.9k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
10615|  10.9k|    memcpy(input + i, &word, sizeof(word));
10616|  10.9k|  }
10617|  3.19k|  if (i < length) {
  ------------------
  |  Branch (10617:7): [True: 2.14k, False: 1.04k]
  ------------------
10618|  2.14k|    uint64_t word{};
10619|  2.14k|    memcpy(&word, input + i, length - i);
10620|  2.14k|    non_ascii |= (word & broadcast_80);
10621|  2.14k|    word ^=
10622|  2.14k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
10623|  2.14k|    memcpy(input + i, &word, length - i);
10624|  2.14k|  }
10625|  3.19k|  return non_ascii == 0;
10626|  3.19k|}
_ZN3ada7unicode9broadcastEh:
10598|  9.57k|constexpr uint64_t broadcast(uint8_t v) noexcept {
10599|  9.57k|  return 0x101010101010101ull * v;
10600|  9.57k|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   12|  4.65k|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|  4.65k|  if (view.ends_with('.')) {
  ------------------
  |  Branch (20:7): [True: 394, False: 4.26k]
  ------------------
   21|    394|    view.remove_suffix(1);
   22|    394|    if (view.empty()) {
  ------------------
  |  Branch (22:9): [True: 13, False: 381]
  ------------------
   23|     13|      return false;
   24|     13|    }
   25|    394|  }
   26|  4.64k|  char last_char = view.back();
   27|  4.64k|  bool possible_ipv4 = (last_char >= '0' && last_char <= '9') ||
  ------------------
  |  Branch (27:25): [True: 3.89k, False: 755]
  |  Branch (27:45): [True: 3.14k, False: 750]
  ------------------
   28|  1.50k|                       (last_char >= 'a' && last_char <= 'f') ||
  ------------------
  |  Branch (28:25): [True: 710, False: 795]
  |  Branch (28:45): [True: 507, False: 203]
  ------------------
   29|    998|                       last_char == 'x';
  ------------------
  |  Branch (29:24): [True: 55, False: 943]
  ------------------
   30|  4.64k|  if (!possible_ipv4) {
  ------------------
  |  Branch (30:7): [True: 943, False: 3.70k]
  ------------------
   31|    943|    return false;
   32|    943|  }
   33|       |  // From the last character, find the last dot.
   34|  3.70k|  size_t last_dot = view.rfind('.');
   35|  3.70k|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (35:7): [True: 2.71k, False: 988]
  ------------------
   36|       |    // We have at least one dot.
   37|  2.71k|    view.remove_prefix(last_dot + 1);
   38|  2.71k|  }
   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|  3.70k|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (42:7): [True: 2.81k, False: 888]
  ------------------
   43|  2.81k|    return true;
   44|  2.81k|  }
   45|       |  // It could be hex (0x), but not if there is a single character.
   46|    888|  if (view.size() == 1) {
  ------------------
  |  Branch (46:7): [True: 186, False: 702]
  ------------------
   47|    186|    return false;
   48|    186|  }
   49|       |  // It must start with 0x.
   50|    702|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (50:7): [True: 296, False: 406]
  ------------------
   51|    296|    return false;
   52|    296|  }
   53|       |  // We must allow "0x".
   54|    406|  if (view.size() == 2) {
  ------------------
  |  Branch (54:7): [True: 42, False: 364]
  ------------------
   55|     42|    return true;
   56|     42|  }
   57|       |  // We have 0x followed by some characters, we need to check that they are
   58|       |  // hexadecimals.
   59|    364|  view.remove_prefix(2);
   60|    364|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   61|    406|}
_ZN3ada7unicode16is_lowercase_hexEc:
11005|  3.76k|ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept {
11006|  3.76k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (11006:11): [True: 3.75k, False: 14]
  |  Branch (11006:23): [True: 2.71k, False: 1.04k]
  |  Branch (11006:37): [True: 1.03k, False: 17]
  |  Branch (11006:49): [True: 1.02k, False: 16]
  ------------------
11007|  3.76k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   87|  4.00k|    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|  4.00k|  size_t i = 0;
   94|  4.00k|  uint8_t accumulator{};
   95|  16.9k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (95:10): [True: 12.9k, False: 4.00k]
  ------------------
   96|  12.9k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   97|  12.9k|                           path_signature_table[uint8_t(input[i + 1])] |
   98|  12.9k|                           path_signature_table[uint8_t(input[i + 2])] |
   99|  12.9k|                           path_signature_table[uint8_t(input[i + 3])] |
  100|  12.9k|                           path_signature_table[uint8_t(input[i + 4])] |
  101|  12.9k|                           path_signature_table[uint8_t(input[i + 5])] |
  102|  12.9k|                           path_signature_table[uint8_t(input[i + 6])] |
  103|  12.9k|                           path_signature_table[uint8_t(input[i + 7])]);
  104|  12.9k|  }
  105|  14.5k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (105:10): [True: 10.5k, False: 4.00k]
  ------------------
  106|  10.5k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
  107|  10.5k|  }
  108|  4.00k|  return accumulator;
  109|  4.00k|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
10886|  5.46k|                                              size_t length) noexcept {
10887|  5.46k|  size_t i = 0;
10888|  5.46k|  uint8_t accumulator{};
10889|  41.6k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (10889:10): [True: 36.1k, False: 5.46k]
  ------------------
10890|  36.1k|    accumulator |=
10891|  36.1k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
10892|  36.1k|    accumulator |=
10893|  36.1k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
10894|  36.1k|    accumulator |=
10895|  36.1k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
10896|  36.1k|    accumulator |=
10897|  36.1k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
10898|  36.1k|  }
10899|  12.3k|  for (; i < length; i++) {
  ------------------
  |  Branch (10899:10): [True: 6.87k, False: 5.46k]
  ------------------
10900|  6.87k|    accumulator |=
10901|  6.87k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
10902|  6.87k|  }
10903|  5.46k|  return accumulator;
10904|  5.46k|}
_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
11078|  19.5k|                    std::string& out) {
11079|  19.5k|  ada_log("percent_encode ", input, " to output string while ",
11080|  19.5k|          append ? "appending" : "overwriting");
11081|  19.5k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11082|  19.5k|    return character_sets::bit_at(character_set, c);
11083|  19.5k|  });
11084|  19.5k|  ada_log("percent_encode done checking, moved to ",
11085|  19.5k|          std::distance(input.begin(), pointer));
11086|       |
11087|       |  // Optimization: Don't iterate if percent encode is not required
11088|  19.5k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11088:7): [True: 12.4k, False: 7.09k]
  ------------------
11089|  12.4k|    ada_log("percent_encode encoding not needed.");
11090|  12.4k|    return false;
11091|  12.4k|  }
11092|       |  if constexpr (!append) {
11093|       |    out.clear();
11094|       |  }
11095|  7.09k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
11096|  7.09k|          " bytes");
11097|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
11098|  7.09k|  out.append(input.data(), std::distance(input.begin(), pointer));
11099|  7.09k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
11100|  7.09k|          " bytes");
11101|  98.9k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11101:10): [True: 91.8k, False: 7.09k]
  ------------------
11102|  91.8k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11102:9): [True: 53.9k, False: 37.9k]
  ------------------
11103|  53.9k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11104|  53.9k|    } else {
11105|  37.9k|      out += *pointer;
11106|  37.9k|    }
11107|  91.8k|  }
11108|  7.09k|  return true;
11109|  19.5k|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
11081|  73.0k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11082|  73.0k|    return character_sets::bit_at(character_set, c);
11083|  73.0k|  });
_ZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
11078|  21.4k|                    std::string& out) {
11079|  21.4k|  ada_log("percent_encode ", input, " to output string while ",
11080|  21.4k|          append ? "appending" : "overwriting");
11081|  21.4k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11082|  21.4k|    return character_sets::bit_at(character_set, c);
11083|  21.4k|  });
11084|  21.4k|  ada_log("percent_encode done checking, moved to ",
11085|  21.4k|          std::distance(input.begin(), pointer));
11086|       |
11087|       |  // Optimization: Don't iterate if percent encode is not required
11088|  21.4k|  if (pointer == input.end()) {
  ------------------
  |  Branch (11088:7): [True: 13.9k, False: 7.50k]
  ------------------
11089|  13.9k|    ada_log("percent_encode encoding not needed.");
11090|  13.9k|    return false;
11091|  13.9k|  }
11092|  7.50k|  if constexpr (!append) {
11093|  7.50k|    out.clear();
11094|  7.50k|  }
11095|  7.50k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
11096|  7.50k|          " bytes");
11097|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
11098|  7.50k|  out.append(input.data(), std::distance(input.begin(), pointer));
11099|  7.50k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
11100|  7.50k|          " bytes");
11101|   103k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (11101:10): [True: 95.6k, False: 7.50k]
  ------------------
11102|  95.6k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (11102:9): [True: 56.3k, False: 39.3k]
  ------------------
11103|  56.3k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
11104|  56.3k|    } else {
11105|  39.3k|      out += *pointer;
11106|  39.3k|    }
11107|  95.6k|  }
11108|  7.50k|  return true;
11109|  21.4k|}
_ZZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
11081|  74.6k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
11082|  74.6k|    return character_sets::bit_at(character_set, c);
11083|  74.6k|  });
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
14711|  5.68k|    const std::string_view input_with_colon) {
14712|  5.68k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
14713|  5.68k|  ADA_ASSERT_TRUE(validate());
14714|  5.68k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
14715|  5.68k|  std::string_view input{input_with_colon};
14716|  5.68k|  input.remove_suffix(1);
14717|  5.68k|  auto parsed_type = ada::scheme::get_scheme_type(input);
14718|  5.68k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
14719|       |  /**
14720|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
14721|       |   *http, https), in which case, we can go really fast.
14722|       |   **/
14723|  5.68k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (14723:7): [True: 5.68k, False: 0]
  ------------------
14724|       |    if constexpr (has_state_override) {
14725|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
14726|       |      // then return.
14727|       |      if (is_special() != is_input_special) {
14728|       |        return false;
14729|       |      }
14730|       |
14731|       |      // If url includes credentials or has a non-null port, and buffer is
14732|       |      // "file", then return.
14733|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14734|       |          parsed_type == ada::scheme::type::FILE) {
14735|       |        return false;
14736|       |      }
14737|       |
14738|       |      // If url's scheme is "file" and its host is an empty host, then return.
14739|       |      // An empty host is the empty string.
14740|       |      if (type == ada::scheme::type::FILE &&
14741|       |          components.host_start == components.host_end) {
14742|       |        return false;
14743|       |      }
14744|       |    }
14745|       |
14746|  5.68k|    type = parsed_type;
14747|  5.68k|    set_scheme_from_view_with_colon(input_with_colon);
14748|       |
14749|       |    if constexpr (has_state_override) {
14750|       |      // This is uncommon.
14751|       |      uint16_t urls_scheme_port = get_special_port();
14752|       |
14753|       |      // If url's port is url's scheme's default port, then set url's port to
14754|       |      // null.
14755|       |      if (components.port == urls_scheme_port) {
14756|       |        clear_port();
14757|       |      }
14758|       |    }
14759|  5.68k|  } else {  // slow path
14760|      0|    std::string _buffer(input);
14761|       |    // Next function is only valid if the input is ASCII and returns false
14762|       |    // otherwise, but it seems that we always have ascii content so we do not
14763|       |    // need to check the return value.
14764|      0|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
14765|       |
14766|       |    if constexpr (has_state_override) {
14767|       |      // The state-override validation errors below ("return" in the WHATWG URL
14768|       |      // parser) leave the URL unchanged. The setter contract is
14769|       |      // "true on success, false if the scheme is invalid" -- the fast path
14770|       |      // above already returns false here, so the slow path must agree.
14771|       |
14772|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
14773|       |      // then return. If url's scheme is not a special scheme and buffer is a
14774|       |      // special scheme, then return.
14775|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
14776|       |        return false;
14777|       |      }
14778|       |
14779|       |      // If url includes credentials or has a non-null port, and buffer is
14780|       |      // "file", then return.
14781|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14782|       |          _buffer == "file") {
14783|       |        return false;
14784|       |      }
14785|       |
14786|       |      // If url's scheme is "file" and its host is an empty host, then return.
14787|       |      // An empty host is the empty string.
14788|       |      if (type == ada::scheme::type::FILE &&
14789|       |          components.host_start == components.host_end) {
14790|       |        return false;
14791|       |      }
14792|       |    }
14793|       |
14794|      0|    set_scheme(_buffer);
14795|       |
14796|       |    if constexpr (has_state_override) {
14797|       |      // This is uncommon.
14798|       |      uint16_t urls_scheme_port = get_special_port();
14799|       |
14800|       |      // If url's port is url's scheme's default port, then set url's port to
14801|       |      // null.
14802|       |      if (components.port == urls_scheme_port) {
14803|       |        clear_port();
14804|       |      }
14805|       |    }
14806|      0|  }
14807|  5.68k|  ADA_ASSERT_TRUE(validate());
14808|  5.68k|  return true;
14809|  5.68k|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14833|  5.68k|    std::string_view new_scheme_with_colon) {
14834|  5.68k|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
14835|  5.68k|          new_scheme_with_colon);
14836|  5.68k|  ADA_ASSERT_TRUE(validate());
14837|  5.68k|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
14838|  5.68k|                  new_scheme_with_colon.back() == ':');
14839|       |  // next line could overflow but unsigned arithmetic has well-defined
14840|       |  // overflows.
14841|  5.68k|  uint32_t new_difference =
14842|  5.68k|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
14843|       |
14844|  5.68k|  if (buffer.empty()) {
  ------------------
  |  Branch (14844:7): [True: 5.68k, False: 0]
  ------------------
14845|  5.68k|    buffer.append(new_scheme_with_colon);
14846|  5.68k|  } else {
14847|      0|    buffer.erase(0, components.protocol_end);
14848|      0|    buffer.insert(0, new_scheme_with_colon);
14849|      0|  }
14850|  5.68k|  components.protocol_end += new_difference;
14851|       |
14852|  5.68k|  apply_shifted_non_scheme_offsets(components, new_difference);
14853|  5.68k|  ADA_ASSERT_TRUE(validate());
14854|  5.68k|}

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5559|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1031|   643k|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1032|   643k|  return !!(a[i >> 3] & (1 << (i & 7)));
 1033|   643k|}
_ZN2tl10unexpectedIN3ada6errorsEEC2EOS2_:
 2011|  9.66k|  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
_ZNR2tl10unexpectedIN3ada6errorsEE5valueEv:
 2025|  4.83k|  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
_ZN3ada8url_baseD2Ev:
 1515|  6.54k|  virtual ~url_base() = default;
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3497|  4.83k|      : impl_base(unexpect, std::move(e.value())),
 3498|  4.83k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2483|  4.83k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3101|  5.68k|  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|    856|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3456|    856|      : impl_base(in_place, std::forward<Args>(args)...),
 3457|    856|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2471|    856|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorC2EOS0_:
 7509|    856|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZN3ada14url_aggregatorD2Ev:
 7512|  6.54k|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6590|  5.68k|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6591|  5.68k|  if (scheme.empty()) {
  ------------------
  |  Branch (6591:7): [True: 0, False: 5.68k]
  ------------------
 6592|      0|    return ada::scheme::NOT_SPECIAL;
 6593|      0|  }
 6594|  5.68k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6595|  5.68k|  const std::string_view target = details::is_special_list[hash_value];
 6596|  5.68k|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6596:7): [True: 5.68k, False: 0]
  ------------------
 6597|  5.68k|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6597:7): [True: 5.68k, False: 0]
  ------------------
 6598|  5.68k|          details::scheme_keys[hash_value]) {
 6599|  5.68k|    return ada::scheme::type(hash_value);
 6600|  5.68k|  } else {
 6601|      0|    return ada::scheme::NOT_SPECIAL;
 6602|      0|  }
 6603|  5.68k|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6531|  5.68k|inline uint64_t branchless_load5(const char* p, size_t n) {
 6532|  5.68k|  uint64_t input = (uint8_t)p[0];
 6533|  5.68k|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6534|  5.68k|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6535|  5.68k|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6536|  5.68k|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6537|  5.68k|  return input;
 6538|  5.68k|}
_ZN3ada14url_aggregatorC2Ev:
 7507|  5.68k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 4757|  5.68k|  url_components() = default;
_ZN3ada8checkers14has_hex_prefixENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1201|  1.08k|constexpr bool has_hex_prefix(std::string_view input) {
 1202|  1.08k|  return input.size() >= 2 && has_hex_prefix_unsafe(input);
  ------------------
  |  Branch (1202:10): [True: 993, False: 87]
  |  Branch (1202:31): [True: 222, False: 771]
  ------------------
 1203|  1.08k|}
_ZN3ada8checkers21has_hex_prefix_unsafeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1186|    993|constexpr bool has_hex_prefix_unsafe(std::string_view input) {
 1187|       |  // This is actually efficient code, see has_hex_prefix for the assembly.
 1188|    993|  constexpr bool is_little_endian = std::endian::native == std::endian::little;
 1189|    993|  constexpr uint16_t word0x = 0x7830;
 1190|    993|  uint16_t two_first_bytes =
 1191|    993|      static_cast<uint16_t>(input[0]) |
 1192|    993|      static_cast<uint16_t>((static_cast<uint16_t>(input[1]) << 8));
 1193|    993|  if constexpr (is_little_endian) {
 1194|    993|    two_first_bytes |= 0x2000;
 1195|       |  } else {
 1196|       |    two_first_bytes |= 0x020;
 1197|       |  }
 1198|    993|  return two_first_bytes == word0x;
 1199|    993|}
_ZN3ada8checkers8is_digitEc:
 1205|  14.7k|constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
_ZNK3ada8url_base10is_specialEv:
 7063|  22.0k|    const noexcept {
 7064|  22.0k|  return type != ada::scheme::NOT_SPECIAL;
 7065|  22.0k|}
_ZN3ada8checkers8is_alphaEc:
 1209|  10.7k|constexpr bool is_alpha(char x) noexcept {
 1210|  10.7k|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1210:10): [True: 7.55k, False: 3.19k]
  |  Branch (1210:34): [True: 7.32k, False: 226]
  ------------------
 1211|  10.7k|}
_ZN3ada8checkers8to_lowerEc:
 1207|  18.2k|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZNK3ada8url_base19scheme_default_portEv:
 7072|     78|url_base::scheme_default_port() const noexcept {
 7073|     78|  return scheme::get_special_port(type);
 7074|     78|}
_ZN3ada6scheme16get_special_portENS0_4typeE:
 6587|     78|constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
 6588|     78|  return details::special_ports[int(type)];
 6589|     78|}
_ZN3ada8checkers23is_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1213|  3.19k|constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
 1214|  3.19k|  return input.size() >= 2 &&
  ------------------
  |  Branch (1214:10): [True: 2.53k, False: 662]
  ------------------
 1215|  2.53k|         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
  ------------------
  |  Branch (1215:11): [True: 819, False: 1.71k]
  |  Branch (1215:34): [True: 5, False: 814]
  |  Branch (1215:55): [True: 8, False: 806]
  ------------------
 1216|     13|         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
  ------------------
  |  Branch (1216:11): [True: 2, False: 11]
  |  Branch (1216:35): [True: 1, False: 10]
  |  Branch (1216:54): [True: 1, False: 9]
  ------------------
 1217|      9|                                  input[2] == '?' || input[2] == '#'));
  ------------------
  |  Branch (1217:35): [True: 2, False: 7]
  |  Branch (1217:54): [True: 0, False: 7]
  ------------------
 1218|  3.19k|}
_ZN3ada8checkers34is_normalized_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1221|  3.19k|    std::string_view input) noexcept {
 1222|  3.19k|  return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':'));
  ------------------
  |  Branch (1222:10): [True: 2.53k, False: 662]
  |  Branch (1222:32): [True: 819, False: 1.71k]
  |  Branch (1222:54): [True: 5, False: 814]
  ------------------
 1223|  3.19k|}
_ZN3ada7helpers14leading_zeroesEj:
 1800|  5.74k|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|  5.74k|  return __builtin_clz(input_num);
 1807|  5.74k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 1808|  5.74k|}
_ZN3ada14url_aggregator7reserveEj:
 8717|  5.68k|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 8718|  5.68k|  buffer.reserve(capacity);
 8719|  5.68k|}
_ZN3ada14url_aggregator20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8207|    423|inline void url_aggregator::update_base_pathname(const std::string_view input) {
 8208|    423|  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
 8209|    423|          " bytes] \n", to_diagram());
 8210|    423|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8211|    423|  ADA_ASSERT_TRUE(validate());
 8212|       |
 8213|    423|  const bool begins_with_dashdash = input.starts_with("//");
 8214|    423|  if (!begins_with_dashdash && has_dash_dot()) {
  ------------------
  |  Branch (8214:7): [True: 274, False: 149]
  |  Branch (8214:32): [True: 0, False: 274]
  ------------------
 8215|       |    // We must delete the ./
 8216|      0|    delete_dash_dot();
 8217|      0|  }
 8218|       |
 8219|    423|  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
  ------------------
  |  Branch (8219:7): [True: 149, False: 274]
  |  Branch (8219:31): [True: 149, False: 0]
  |  Branch (8219:51): [True: 0, False: 149]
  ------------------
 8220|      0|      !has_dash_dot()) {
  ------------------
  |  Branch (8220:7): [True: 0, False: 0]
  ------------------
 8221|       |    // If url's host is null, url does not have an opaque path, url's path's
 8222|       |    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
 8223|       |    // output.
 8224|      0|    buffer.insert(components.pathname_start, "/.");
 8225|      0|    components.pathname_start += 2;
 8226|      0|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8226:9): [True: 0, False: 0]
  ------------------
 8227|      0|      components.search_start += 2;
 8228|      0|    }
 8229|      0|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8229:9): [True: 0, False: 0]
  ------------------
 8230|      0|      components.hash_start += 2;
 8231|      0|    }
 8232|      0|  }
 8233|       |
 8234|    423|  uint32_t difference = replace_and_resize(
 8235|    423|      components.pathname_start,
 8236|    423|      components.pathname_start + get_pathname_length(), input);
 8237|    423|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8237:7): [True: 0, False: 423]
  ------------------
 8238|      0|    components.search_start += difference;
 8239|      0|  }
 8240|    423|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8240:7): [True: 0, False: 423]
  ------------------
 8241|      0|    components.hash_start += difference;
 8242|      0|  }
 8243|    423|  ADA_ASSERT_TRUE(validate());
 8244|    423|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8050|  2.12k|    uint32_t start, uint32_t end, std::string_view input) {
 8051|  2.12k|  uint32_t current_length = end - start;
 8052|  2.12k|  uint32_t input_size = uint32_t(input.size());
 8053|  2.12k|  uint32_t new_difference = input_size - current_length;
 8054|       |
 8055|  2.12k|  if (current_length == 0) {
  ------------------
  |  Branch (8055:7): [True: 1.71k, False: 419]
  ------------------
 8056|  1.71k|    buffer.insert(start, input);
 8057|  1.71k|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8057:14): [True: 14, False: 405]
  ------------------
 8058|     14|    buffer.replace(start, input_size, input);
 8059|    405|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8059:14): [True: 38, False: 367]
  ------------------
 8060|     38|    buffer.erase(start, current_length - input_size);
 8061|     38|    buffer.replace(start, input_size, input);
 8062|    367|  } else {
 8063|    367|    buffer.replace(start, current_length, input.substr(0, current_length));
 8064|    367|    buffer.insert(start + current_length, input.substr(current_length));
 8065|    367|  }
 8066|       |
 8067|  2.12k|  return new_difference;
 8068|  2.12k|}
_ZNK3ada14url_aggregator19get_pathname_lengthEv:
 8099|    423|url_aggregator::get_pathname_length() const noexcept {
 8100|    423|  ada_log("url_aggregator::get_pathname_length");
 8101|    423|  uint32_t ending_index = uint32_t(buffer.size());
 8102|    423|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8102:7): [True: 0, False: 423]
  ------------------
 8103|      0|    ending_index = components.search_start;
 8104|    423|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8104:14): [True: 0, False: 423]
  ------------------
 8105|      0|    ending_index = components.hash_start;
 8106|      0|  }
 8107|    423|  return ending_index - components.pathname_start;
 8108|    423|}
_ZNK3ada14url_aggregator12get_pathnameEv:
 9044|    382|    ada_lifetime_bound {
 9045|    382|  ada_log("url_aggregator::get_pathname pathname_start = ",
 9046|    382|          components.pathname_start, " buffer.size() = ", buffer.size(),
 9047|    382|          " components.search_start = ", components.search_start,
 9048|    382|          " components.hash_start = ", components.hash_start);
 9049|    382|  auto ending_index = uint32_t(buffer.size());
 9050|    382|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9050:7): [True: 0, False: 382]
  ------------------
 9051|      0|    ending_index = components.search_start;
 9052|    382|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9052:14): [True: 0, False: 382]
  ------------------
 9053|      0|    ending_index = components.hash_start;
 9054|      0|  }
 9055|    382|  return helpers::substring(buffer, components.pathname_start, ending_index);
 9056|    382|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8026|     68|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8027|     68|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8028|     68|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8029|     68|          " bytes] components.hash_start = ", components.hash_start);
 8030|     68|  ADA_ASSERT_TRUE(validate());
 8031|     68|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8032|     68|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8032:7): [True: 0, False: 68]
  ------------------
 8033|      0|    buffer.resize(components.hash_start);
 8034|      0|  }
 8035|     68|  components.hash_start = uint32_t(buffer.size());
 8036|     68|  buffer += "#";
 8037|     68|  bool encoding_required = unicode::percent_encode<true>(
 8038|     68|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8039|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8040|       |  // encoding was not deemed required.
 8041|     68|  if (!encoding_required) {
  ------------------
  |  Branch (8041:7): [True: 51, False: 17]
  ------------------
 8042|     51|    buffer.append(input);
 8043|     51|  }
 8044|     68|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8045|     68|          buffer, "' [", buffer.size(), " bytes]");
 8046|     68|  ADA_ASSERT_TRUE(validate());
 8047|     68|}
_ZN3ada14url_aggregator20append_base_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8435|    921|inline void url_aggregator::append_base_password(const std::string_view input) {
 8436|    921|  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
 8437|    921|          "\n", to_diagram());
 8438|    921|  ADA_ASSERT_TRUE(validate());
 8439|    921|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8440|       |#if ADA_DEVELOPMENT_CHECKS
 8441|       |  // computing the expected password.
 8442|       |  std::string password_expected = std::string(get_password());
 8443|       |  password_expected.append(input);
 8444|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8445|    921|  add_authority_slashes_if_needed();
 8446|       |
 8447|       |  // If input is empty, do nothing.
 8448|    921|  if (input.empty()) {
  ------------------
  |  Branch (8448:7): [True: 361, False: 560]
  ------------------
 8449|    361|    return;
 8450|    361|  }
 8451|       |
 8452|    560|  uint32_t difference = uint32_t(input.size());
 8453|    560|  if (has_password()) {
  ------------------
  |  Branch (8453:7): [True: 478, False: 82]
  ------------------
 8454|    478|    buffer.insert(components.host_start, input);
 8455|    478|  } else {
 8456|     82|    difference++;  // Increment for ":"
 8457|     82|    buffer.insert(components.username_end, ":");
 8458|     82|    buffer.insert(components.username_end + 1, input);
 8459|     82|  }
 8460|    560|  components.host_start += difference;
 8461|       |
 8462|       |  // The following line is required to add "@" to hostname. When updating
 8463|       |  // password if hostname does not start with "@", it is "append_base_password"s
 8464|       |  // responsibility to set it.
 8465|    560|  if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8465:7): [True: 25, False: 535]
  ------------------
 8466|     25|    buffer.insert(components.host_start, "@");
 8467|     25|    difference++;
 8468|     25|  }
 8469|       |
 8470|    560|  components.host_end += difference;
 8471|    560|  components.pathname_start += difference;
 8472|    560|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8472:7): [True: 0, False: 560]
  ------------------
 8473|      0|    components.search_start += difference;
 8474|      0|  }
 8475|    560|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8475:7): [True: 0, False: 560]
  ------------------
 8476|      0|    components.hash_start += difference;
 8477|      0|  }
 8478|       |#if ADA_DEVELOPMENT_CHECKS
 8479|       |  std::string password_after(get_password());
 8480|       |  ADA_ASSERT_EQUAL(
 8481|       |      password_expected, password_after,
 8482|       |      "append_base_password problem after inserting " + std::string(input));
 8483|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8484|    560|  ADA_ASSERT_TRUE(validate());
 8485|    560|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 8691|  3.54k|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 8692|  3.54k|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 8693|  3.54k|  ADA_ASSERT_TRUE(validate());
 8694|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 8695|       |  // to insert
 8696|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 8697|  3.54k|  if (has_authority()) {
  ------------------
  |  Branch (8697:7): [True: 2.10k, False: 1.44k]
  ------------------
 8698|  2.10k|    return;
 8699|  2.10k|  }
 8700|       |  // Performance: the common case is components.protocol_end == buffer.size()
 8701|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 8702|       |  // the insert could be fused with another insert.
 8703|  1.44k|  buffer.insert(components.protocol_end, "//");
 8704|  1.44k|  components.username_end += 2;
 8705|  1.44k|  components.host_start += 2;
 8706|  1.44k|  components.host_end += 2;
 8707|  1.44k|  components.pathname_start += 2;
 8708|  1.44k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8708:7): [True: 0, False: 1.44k]
  ------------------
 8709|      0|    components.search_start += 2;
 8710|      0|  }
 8711|  1.44k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8711:7): [True: 0, False: 1.44k]
  ------------------
 8712|      0|    components.hash_start += 2;
 8713|      0|  }
 8714|  1.44k|  ADA_ASSERT_TRUE(validate());
 8715|  1.44k|}
_ZN3ada14url_aggregator20append_base_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8317|    920|inline void url_aggregator::append_base_username(const std::string_view input) {
 8318|    920|  ada_log("url_aggregator::append_base_username ", input);
 8319|    920|  ADA_ASSERT_TRUE(validate());
 8320|    920|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8321|       |#if ADA_DEVELOPMENT_CHECKS
 8322|       |  // computing the expected password.
 8323|       |  std::string username_expected(get_username());
 8324|       |  username_expected.append(input);
 8325|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8326|    920|  add_authority_slashes_if_needed();
 8327|       |
 8328|       |  // If input is empty, do nothing.
 8329|    920|  if (input.empty()) {
  ------------------
  |  Branch (8329:7): [True: 367, False: 553]
  ------------------
 8330|    367|    return;
 8331|    367|  }
 8332|       |
 8333|    553|  uint32_t difference = uint32_t(input.size());
 8334|    553|  buffer.insert(components.username_end, input);
 8335|    553|  components.username_end += difference;
 8336|    553|  components.host_start += difference;
 8337|       |
 8338|    553|  if (buffer[components.host_start] != '@' &&
  ------------------
  |  Branch (8338:7): [True: 133, False: 420]
  ------------------
 8339|    133|      components.host_start != components.host_end) {
  ------------------
  |  Branch (8339:7): [True: 133, False: 0]
  ------------------
 8340|    133|    buffer.insert(components.host_start, "@");
 8341|    133|    difference++;
 8342|    133|  }
 8343|       |
 8344|    553|  components.host_end += difference;
 8345|    553|  components.pathname_start += difference;
 8346|    553|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8346:7): [True: 0, False: 553]
  ------------------
 8347|      0|    components.search_start += difference;
 8348|      0|  }
 8349|    553|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8349:7): [True: 0, False: 553]
  ------------------
 8350|      0|    components.hash_start += difference;
 8351|      0|  }
 8352|       |#if ADA_DEVELOPMENT_CHECKS
 8353|       |  std::string username_after(get_username());
 8354|       |  ADA_ASSERT_EQUAL(
 8355|       |      username_expected, username_after,
 8356|       |      "append_base_username problem after inserting " + std::string(input));
 8357|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8358|    553|  ADA_ASSERT_TRUE(validate());
 8359|    553|}
_ZN3ada14url_aggregator16update_base_portEj:
 8487|     58|inline void url_aggregator::update_base_port(uint32_t input) {
 8488|     58|  ada_log("url_aggregator::update_base_port");
 8489|     58|  ADA_ASSERT_TRUE(validate());
 8490|     58|  if (input == url_components::omitted) {
  ------------------
  |  Branch (8490:7): [True: 0, False: 58]
  ------------------
 8491|      0|    clear_port();
 8492|      0|    return;
 8493|      0|  }
 8494|       |  // calling std::to_string(input.value()) is unfortunate given that the port
 8495|       |  // value is probably already available as a string.
 8496|     58|  std::string value = helpers::concat(":", std::to_string(input));
 8497|     58|  uint32_t difference = uint32_t(value.size());
 8498|       |
 8499|     58|  if (components.port != url_components::omitted) {
  ------------------
  |  Branch (8499:7): [True: 0, False: 58]
  ------------------
 8500|      0|    difference -= components.pathname_start - components.host_end;
 8501|      0|    buffer.erase(components.host_end,
 8502|      0|                 components.pathname_start - components.host_end);
 8503|      0|  }
 8504|       |
 8505|     58|  buffer.insert(components.host_end, value);
 8506|     58|  components.pathname_start += difference;
 8507|     58|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8507:7): [True: 0, False: 58]
  ------------------
 8508|      0|    components.search_start += difference;
 8509|      0|  }
 8510|     58|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8510:7): [True: 0, False: 58]
  ------------------
 8511|      0|    components.hash_start += difference;
 8512|      0|  }
 8513|     58|  components.port = input;
 8514|     58|  ADA_ASSERT_TRUE(validate());
 8515|     58|}
_ZN3ada7helpers6concatIJPKcNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEESA_DpT_:
 1790|     58|std::string concat(Args... args) {
 1791|     58|  std::string answer;
 1792|     58|  inner_concat(answer, args...);
 1793|     58|  return answer;
 1794|     58|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEEvRSA_T_DpT0_:
 1779|     58|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1780|     58|  buffer.append(t);
 1781|     58|  return inner_concat(buffer, args...);
 1782|     58|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJEEEvRS8_T_:
 1771|     58|inline void inner_concat(std::string& buffer, T t) {
 1772|     58|  buffer.append(t);
 1773|     58|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8155|    282|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8156|    282|  ada_log("url_aggregator::update_base_search ", input,
 8157|    282|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8158|    282|  ADA_ASSERT_TRUE(validate());
 8159|    282|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8160|       |
 8161|    282|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8161:7): [True: 282, False: 0]
  ------------------
 8162|    282|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8162:9): [True: 282, False: 0]
  ------------------
 8163|    282|      components.search_start = uint32_t(buffer.size());
 8164|    282|      buffer += "?";
 8165|    282|    } else {
 8166|      0|      buffer.resize(components.search_start + 1);
 8167|      0|    }
 8168|       |
 8169|    282|    bool encoding_required =
 8170|    282|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8171|       |    // When encoding_required is false, then buffer is left unchanged, and
 8172|       |    // percent encoding was not deemed required.
 8173|    282|    if (!encoding_required) {
  ------------------
  |  Branch (8173:9): [True: 243, False: 39]
  ------------------
 8174|    243|      buffer.append(input);
 8175|    243|    }
 8176|    282|  } else {
 8177|      0|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8177:9): [True: 0, False: 0]
  ------------------
 8178|      0|      components.search_start = components.hash_start;
 8179|      0|    } else {
 8180|      0|      buffer.erase(components.search_start,
 8181|      0|                   components.hash_start - components.search_start);
 8182|      0|      components.hash_start = components.search_start;
 8183|      0|    }
 8184|       |
 8185|      0|    buffer.insert(components.search_start, "?");
 8186|      0|    size_t idx =
 8187|      0|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8188|      0|    if (idx == input.size()) {
  ------------------
  |  Branch (8188:9): [True: 0, False: 0]
  ------------------
 8189|      0|      buffer.insert(components.search_start + 1, input);
 8190|      0|      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8191|      0|    } else {
 8192|      0|      buffer.insert(components.search_start + 1, input, 0, idx);
 8193|      0|      input.remove_prefix(idx);
 8194|       |      // We only create a temporary string if we need percent encoding and
 8195|       |      // we attempt to create as small a temporary string as we can.
 8196|      0|      std::string encoded =
 8197|      0|          ada::unicode::percent_encode(input, query_percent_encode_set);
 8198|      0|      buffer.insert(components.search_start + idx + 1, encoded);
 8199|      0|      components.hash_start +=
 8200|      0|          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
 8201|      0|    }
 8202|      0|  }
 8203|       |
 8204|    282|  ADA_ASSERT_TRUE(validate());
 8205|    282|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8070|  1.70k|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8071|  1.70k|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8072|  1.70k|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8073|  1.70k|  ADA_ASSERT_TRUE(validate());
 8074|  1.70k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8075|       |
 8076|       |  // This next line is required for when parsing a URL like `foo://`
 8077|  1.70k|  add_authority_slashes_if_needed();
 8078|       |
 8079|  1.70k|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8080|  1.70k|  uint32_t new_difference =
 8081|  1.70k|      replace_and_resize(components.host_start, components.host_end, input);
 8082|       |
 8083|  1.70k|  if (has_credentials) {
  ------------------
  |  Branch (8083:7): [True: 122, False: 1.58k]
  ------------------
 8084|    122|    buffer.insert(components.host_start, "@");
 8085|    122|    new_difference++;
 8086|    122|  }
 8087|  1.70k|  components.host_end += new_difference;
 8088|  1.70k|  components.pathname_start += new_difference;
 8089|  1.70k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8089:7): [True: 0, False: 1.70k]
  ------------------
 8090|      0|    components.search_start += new_difference;
 8091|      0|  }
 8092|  1.70k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8092:7): [True: 0, False: 1.70k]
  ------------------
 8093|      0|    components.hash_start += new_difference;
 8094|      0|  }
 8095|  1.70k|  ADA_ASSERT_TRUE(validate());
 8096|  1.70k|}
_ZN3ada14url_aggregator10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 8805|    341|url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
 8806|    341|  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
 8807|    341|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (8807:7): [True: 340, False: 1]
  |  Branch (8807:24): [True: 2, False: 338]
  ------------------
 8808|      2|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 8809|      2|    is_valid = false;
 8810|      2|    return 0;
 8811|      2|  }
 8812|    339|  uint16_t parsed_port{};
 8813|    339|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 8814|    339|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (8814:7): [True: 24, False: 315]
  ------------------
 8815|     24|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 8816|     24|    is_valid = false;
 8817|     24|    return 0;
 8818|     24|  }
 8819|    315|  ada_log("parse_port: ", parsed_port);
 8820|    315|  const size_t consumed = size_t(r.ptr - view.data());
 8821|    315|  ada_log("parse_port: consumed ", consumed);
 8822|    315|  if (check_trailing_content) {
  ------------------
  |  Branch (8822:7): [True: 315, False: 0]
  ------------------
 8823|    315|    is_valid &=
 8824|    315|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (8824:10): [True: 21, False: 294]
  |  Branch (8824:37): [True: 39, False: 255]
  ------------------
 8825|    255|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (8825:10): [True: 6, False: 249]
  |  Branch (8825:36): [True: 249, False: 0]
  |  Branch (8825:52): [True: 12, False: 237]
  ------------------
 8826|    315|  }
 8827|    315|  ada_log("parse_port: is_valid = ", is_valid);
 8828|    315|  if (is_valid) {
  ------------------
  |  Branch (8828:7): [True: 78, False: 237]
  ------------------
 8829|     78|    ada_log("parse_port", r.ec == std::errc());
 8830|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 8831|     78|    auto default_port = scheme_default_port();
 8832|     78|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (8832:27): [True: 0, False: 78]
  |  Branch (8832:48): [True: 0, False: 0]
  ------------------
 8833|     78|                         (default_port != parsed_port);
  ------------------
  |  Branch (8833:26): [True: 77, False: 1]
  ------------------
 8834|     78|    if (r.ec == std::errc() && is_port_valid) {
  ------------------
  |  Branch (8834:9): [True: 59, False: 19]
  |  Branch (8834:32): [True: 58, False: 1]
  ------------------
 8835|     58|      update_base_port(parsed_port);
 8836|     58|    } else {
 8837|     20|      clear_port();
 8838|     20|    }
 8839|     78|  }
 8840|    315|  return consumed;
 8841|    339|}
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7928|  3.19k|                                              const uint8_t character_set[]) {
 7929|  3.19k|  const char* data = input.data();
 7930|  3.19k|  const size_t size = input.size();
 7931|       |
 7932|       |  // Process 8 bytes at a time using unrolled loop
 7933|  3.19k|  size_t i = 0;
 7934|  4.09k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (7934:10): [True: 1.32k, False: 2.77k]
  ------------------
 7935|  1.32k|    unsigned char chunk[8];
 7936|  1.32k|    std::memcpy(&chunk, data + i,
 7937|  1.32k|                8);  // entices compiler to unconditionally process 8 characters
 7938|       |
 7939|       |    // Check 8 characters at once
 7940|  9.14k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (7940:24): [True: 8.24k, False: 900]
  ------------------
 7941|  8.24k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (7941:11): [True: 420, False: 7.82k]
  ------------------
 7942|    420|        return i + j;
 7943|    420|      }
 7944|  8.24k|    }
 7945|  1.32k|  }
 7946|       |
 7947|       |  // Handle remaining bytes
 7948|  6.00k|  for (; i < size; i++) {
  ------------------
  |  Branch (7948:10): [True: 3.93k, False: 2.07k]
  ------------------
 7949|  3.93k|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (7949:9): [True: 700, False: 3.23k]
  ------------------
 7950|    700|      return i;
 7951|    700|    }
 7952|  3.93k|  }
 7953|       |
 7954|  2.07k|  return size;
 7955|  2.77k|}
_ZN3ada14url_aggregator10clear_portEv:
 8517|     20|inline void url_aggregator::clear_port() {
 8518|     20|  ada_log("url_aggregator::clear_port");
 8519|     20|  ADA_ASSERT_TRUE(validate());
 8520|     20|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (8520:7): [True: 20, False: 0]
  ------------------
 8521|     20|    return;
 8522|     20|  }
 8523|      0|  uint32_t length = components.pathname_start - components.host_end;
 8524|      0|  buffer.erase(components.host_end, length);
 8525|      0|  components.pathname_start -= length;
 8526|      0|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8526:7): [True: 0, False: 0]
  ------------------
 8527|      0|    components.search_start -= length;
 8528|      0|  }
 8529|      0|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8529:7): [True: 0, False: 0]
  ------------------
 8530|      0|    components.hash_start -= length;
 8531|      0|  }
 8532|      0|  components.port = url_components::omitted;
 8533|      0|  ADA_ASSERT_TRUE(validate());
 8534|      0|}
_ZNK3ada14url_aggregator13has_authorityEv:
 8682|  3.69k|    const noexcept {
 8683|  3.69k|  ada_log("url_aggregator::has_authority");
 8684|       |  // Performance: instead of doing this potentially expensive check, we could
 8685|       |  // have a boolean in the struct.
 8686|  3.69k|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (8686:10): [True: 2.25k, False: 1.44k]
  ------------------
 8687|  2.25k|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (8687:10): [True: 2.25k, False: 0]
  ------------------
 8688|  2.25k|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (8688:10): [True: 2.25k, False: 0]
  ------------------
 8689|  3.69k|}
_ZNK3ada14url_aggregator12has_dash_dotEv:
 8762|    274|[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
 8763|       |  // If url's host is null, url does not have an opaque path, url's path's size
 8764|       |  // is greater than 1, and url's path[0] is the empty string, then append
 8765|       |  // U+002F (/) followed by U+002E (.) to output.
 8766|    274|  ada_log("url_aggregator::has_dash_dot");
 8767|       |#if ADA_DEVELOPMENT_CHECKS
 8768|       |  // If pathname_start and host_end are exactly two characters apart, then we
 8769|       |  // either have a one-digit port such as http://test.com:5?param=1 or else we
 8770|       |  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
 8771|       |  if (components.pathname_start == components.host_end + 2) {
 8772|       |    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
 8773|       |                     buffer[components.host_end + 1] == '.') ||
 8774|       |                    (buffer[components.host_end] == ':' &&
 8775|       |                     checkers::is_digit(buffer[components.host_end + 1])));
 8776|       |  }
 8777|       |  if (components.pathname_start == components.host_end + 2 &&
 8778|       |      buffer[components.host_end] == '/' &&
 8779|       |      buffer[components.host_end + 1] == '.') {
 8780|       |    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
 8781|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
 8782|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
 8783|       |  }
 8784|       |#endif
 8785|       |  // Performance: it should be uncommon for components.pathname_start ==
 8786|       |  // components.host_end + 2 to be true. So we put this check first in the
 8787|       |  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
 8788|       |  // more expensive, but should be uncommon.
 8789|    274|  return components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8789:10): [True: 3, False: 271]
  ------------------
 8790|      3|         !has_opaque_path && buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (8790:10): [True: 3, False: 0]
  |  Branch (8790:30): [True: 0, False: 3]
  ------------------
 8791|      0|         buffer[components.host_end + 1] == '.';
  ------------------
  |  Branch (8791:10): [True: 0, False: 0]
  ------------------
 8792|    274|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 3896|  5.68k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 3865|    983|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 3866|    983|    TL_ASSERT(has_value());
  ------------------
  |  | 1918|    983|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3866:5): [True: 983, False: 0]
  ------------------
 3867|    983|    return valptr();
 3868|    983|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3161|    983|  T* valptr() { return std::addressof(this->m_val); }
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 3895|    983|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2493|  5.68k|  ~expected_storage_base() {
 2494|  5.68k|    if (m_has_val) {
  ------------------
  |  Branch (2494:9): [True: 856, False: 4.83k]
  ------------------
 2495|    856|      m_val.~T();
 2496|    856|    }
 2497|  5.68k|  }
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1712|  3.36k|                                                       size_t pos2) {
 1713|       |#if ADA_DEVELOPMENT_CHECKS
 1714|       |  if (pos2 < pos1) {
 1715|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1716|       |              << std::endl;
 1717|       |    abort();
 1718|       |  }
 1719|       |#endif
 1720|  3.36k|  return input.substr(pos1, pos2 - pos1);
 1721|  3.36k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8111|    433|    const noexcept {
 8112|    433|  return buffer.size() == components.pathname_start;
 8113|    433|}
_ZNK3ada14url_aggregator12has_passwordEv:
 8731|    560|constexpr bool url_aggregator::has_password() const noexcept {
 8732|    560|  ada_log("url_aggregator::has_password");
 8733|       |  // This function does not care about the length of the password
 8734|    560|  return components.host_start > components.username_end &&
  ------------------
  |  Branch (8734:10): [True: 478, False: 82]
  ------------------
 8735|    478|         buffer[components.username_end] == ':';
  ------------------
  |  Branch (8735:10): [True: 478, False: 0]
  ------------------
 8736|    560|}
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1226|  5.63k|    std::string_view input) noexcept {
 1227|  5.63k|  const char* p = input.data();
 1228|  5.63k|  const char* const pend = p + input.size();
 1229|       |
 1230|  5.63k|  uint32_t ipv4 = 0;
 1231|       |
 1232|  6.86k|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1232:19): [True: 6.63k, False: 236]
  ------------------
 1233|  6.63k|    if (p == pend) {
  ------------------
  |  Branch (1233:9): [True: 963, False: 5.66k]
  ------------------
 1234|    963|      return ipv4_fast_fail;
 1235|    963|    }
 1236|       |
 1237|  5.66k|    uint32_t val;
 1238|  5.66k|    char c = *p;
 1239|  5.66k|    if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1239:9): [True: 3.79k, False: 1.87k]
  |  Branch (1239:21): [True: 2.30k, False: 1.48k]
  ------------------
 1240|  2.30k|      val = c - '0';
 1241|  2.30k|      p++;
 1242|  3.36k|    } else {
 1243|  3.36k|      return ipv4_fast_fail;
 1244|  3.36k|    }
 1245|       |
 1246|  2.30k|    if (p < pend) {
  ------------------
  |  Branch (1246:9): [True: 2.15k, False: 143]
  ------------------
 1247|  2.15k|      c = *p;
 1248|  2.15k|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1248:11): [True: 1.54k, False: 612]
  |  Branch (1248:23): [True: 1.20k, False: 338]
  ------------------
 1249|  1.20k|        if (val == 0) return ipv4_fast_fail;
  ------------------
  |  Branch (1249:13): [True: 121, False: 1.08k]
  ------------------
 1250|  1.08k|        val = val * 10 + (c - '0');
 1251|  1.08k|        p++;
 1252|  1.08k|        if (p < pend) {
  ------------------
  |  Branch (1252:13): [True: 1.00k, False: 88]
  ------------------
 1253|  1.00k|          c = *p;
 1254|  1.00k|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1254:15): [True: 749, False: 251]
  |  Branch (1254:27): [True: 696, False: 53]
  ------------------
 1255|    696|            val = val * 10 + (c - '0');
 1256|    696|            p++;
 1257|    696|            if (val > 255) return ipv4_fast_fail;
  ------------------
  |  Branch (1257:17): [True: 216, False: 480]
  ------------------
 1258|    696|          }
 1259|  1.00k|        }
 1260|  1.08k|      }
 1261|  2.15k|    }
 1262|       |
 1263|  1.96k|    ipv4 = (ipv4 << 8) | val;
 1264|       |
 1265|  1.96k|    if (i < 3) {
  ------------------
  |  Branch (1265:9): [True: 1.72k, False: 236]
  ------------------
 1266|  1.72k|      if (p == pend || *p != '.') {
  ------------------
  |  Branch (1266:11): [True: 106, False: 1.62k]
  |  Branch (1266:24): [True: 628, False: 995]
  ------------------
 1267|    734|        return ipv4_fast_fail;
 1268|    734|      }
 1269|    995|      p++;
 1270|    995|    }
 1271|  1.96k|  }
 1272|       |
 1273|    236|  if (p != pend) {
  ------------------
  |  Branch (1273:7): [True: 32, False: 204]
  ------------------
 1274|     32|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1274:9): [True: 12, False: 20]
  |  Branch (1274:26): [True: 6, False: 6]
  ------------------
 1275|      6|      return ipv4;
 1276|      6|    }
 1277|     26|    return ipv4_fast_fail;
 1278|     32|  }
 1279|       |
 1280|    204|  return ipv4;
 1281|    236|}
_ZNK3ada14url_aggregator8validateEv:
 8873|    856|[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
 8874|    856|  if (!is_valid) {
  ------------------
  |  Branch (8874:7): [True: 0, False: 856]
  ------------------
 8875|      0|    return true;
 8876|      0|  }
 8877|    856|  if (!components.check_offset_consistency()) {
  ------------------
  |  Branch (8877:7): [True: 0, False: 856]
  ------------------
 8878|      0|    ada_log("url_aggregator::validate inconsistent components \n",
 8879|      0|            to_diagram());
 8880|      0|    return false;
 8881|      0|  }
 8882|       |  // We have a credible components struct, but let us investivate more
 8883|       |  // carefully:
 8884|       |  /**
 8885|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 8886|       |   *       |     |    |          | ^^^^|       |   |
 8887|       |   *       |     |    |          | |   |       |   `----- hash_start
 8888|       |   *       |     |    |          | |   |       `--------- search_start
 8889|       |   *       |     |    |          | |   `----------------- pathname_start
 8890|       |   *       |     |    |          | `--------------------- port
 8891|       |   *       |     |    |          `----------------------- host_end
 8892|       |   *       |     |    `---------------------------------- host_start
 8893|       |   *       |     `--------------------------------------- username_end
 8894|       |   *       `--------------------------------------------- protocol_end
 8895|       |   */
 8896|    856|  if (components.protocol_end == url_components::omitted) {
  ------------------
  |  Branch (8896:7): [True: 0, False: 856]
  ------------------
 8897|      0|    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
 8898|      0|    return false;
 8899|      0|  }
 8900|    856|  if (components.username_end == url_components::omitted) {
  ------------------
  |  Branch (8900:7): [True: 0, False: 856]
  ------------------
 8901|      0|    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
 8902|      0|    return false;
 8903|      0|  }
 8904|    856|  if (components.host_start == url_components::omitted) {
  ------------------
  |  Branch (8904:7): [True: 0, False: 856]
  ------------------
 8905|      0|    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
 8906|      0|    return false;
 8907|      0|  }
 8908|    856|  if (components.host_end == url_components::omitted) {
  ------------------
  |  Branch (8908:7): [True: 0, False: 856]
  ------------------
 8909|      0|    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
 8910|      0|    return false;
 8911|      0|  }
 8912|    856|  if (components.pathname_start == url_components::omitted) {
  ------------------
  |  Branch (8912:7): [True: 0, False: 856]
  ------------------
 8913|      0|    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
 8914|      0|    return false;
 8915|      0|  }
 8916|       |
 8917|    856|  if (components.protocol_end > buffer.size()) {
  ------------------
  |  Branch (8917:7): [True: 0, False: 856]
  ------------------
 8918|      0|    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
 8919|      0|    return false;
 8920|      0|  }
 8921|    856|  if (components.username_end > buffer.size()) {
  ------------------
  |  Branch (8921:7): [True: 0, False: 856]
  ------------------
 8922|      0|    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
 8923|      0|    return false;
 8924|      0|  }
 8925|    856|  if (components.host_start > buffer.size()) {
  ------------------
  |  Branch (8925:7): [True: 0, False: 856]
  ------------------
 8926|      0|    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
 8927|      0|    return false;
 8928|      0|  }
 8929|    856|  if (components.host_end > buffer.size()) {
  ------------------
  |  Branch (8929:7): [True: 0, False: 856]
  ------------------
 8930|      0|    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
 8931|      0|    return false;
 8932|      0|  }
 8933|    856|  if (components.pathname_start > buffer.size()) {
  ------------------
  |  Branch (8933:7): [True: 0, False: 856]
  ------------------
 8934|      0|    ada_log("url_aggregator::validate pathname_start overflow \n",
 8935|      0|            to_diagram());
 8936|      0|    return false;
 8937|      0|  }
 8938|       |
 8939|    856|  if (components.protocol_end > 0) {
  ------------------
  |  Branch (8939:7): [True: 856, False: 0]
  ------------------
 8940|    856|    if (buffer[components.protocol_end - 1] != ':') {
  ------------------
  |  Branch (8940:9): [True: 0, False: 856]
  ------------------
 8941|      0|      ada_log(
 8942|      0|          "url_aggregator::validate missing : at the end of the protocol \n",
 8943|      0|          to_diagram());
 8944|      0|      return false;
 8945|      0|    }
 8946|    856|  }
 8947|       |
 8948|    856|  if (components.username_end != buffer.size() &&
  ------------------
  |  Branch (8948:7): [True: 856, False: 0]
  ------------------
 8949|    856|      components.username_end > components.protocol_end + 2) {
  ------------------
  |  Branch (8949:7): [True: 41, False: 815]
  ------------------
 8950|     41|    if (buffer[components.username_end] != ':' &&
  ------------------
  |  Branch (8950:9): [True: 17, False: 24]
  ------------------
 8951|     17|        buffer[components.username_end] != '@') {
  ------------------
  |  Branch (8951:9): [True: 0, False: 17]
  ------------------
 8952|      0|      ada_log(
 8953|      0|          "url_aggregator::validate missing : or @ at the end of the username "
 8954|      0|          "\n",
 8955|      0|          to_diagram());
 8956|      0|      return false;
 8957|      0|    }
 8958|     41|  }
 8959|       |
 8960|    856|  if (components.host_start != buffer.size()) {
  ------------------
  |  Branch (8960:7): [True: 856, False: 0]
  ------------------
 8961|    856|    if (components.host_start > components.username_end) {
  ------------------
  |  Branch (8961:9): [True: 35, False: 821]
  ------------------
 8962|     35|      if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8962:11): [True: 0, False: 35]
  ------------------
 8963|      0|        ada_log(
 8964|      0|            "url_aggregator::validate missing @ at the end of the password \n",
 8965|      0|            to_diagram());
 8966|      0|        return false;
 8967|      0|      }
 8968|    821|    } else if (components.host_start == components.username_end &&
  ------------------
  |  Branch (8968:16): [True: 821, False: 0]
  ------------------
 8969|    821|               components.host_end > components.host_start) {
  ------------------
  |  Branch (8969:16): [True: 821, False: 0]
  ------------------
 8970|    821|      if (components.host_start == components.protocol_end + 2) {
  ------------------
  |  Branch (8970:11): [True: 804, False: 17]
  ------------------
 8971|    804|        if (buffer[components.protocol_end] != '/' ||
  ------------------
  |  Branch (8971:13): [True: 0, False: 804]
  ------------------
 8972|    804|            buffer[components.protocol_end + 1] != '/') {
  ------------------
  |  Branch (8972:13): [True: 0, False: 804]
  ------------------
 8973|      0|          ada_log(
 8974|      0|              "url_aggregator::validate missing // between protocol and host "
 8975|      0|              "\n",
 8976|      0|              to_diagram());
 8977|      0|          return false;
 8978|      0|        }
 8979|    804|      } else {
 8980|     17|        if (components.host_start > components.protocol_end &&
  ------------------
  |  Branch (8980:13): [True: 17, False: 0]
  ------------------
 8981|     17|            buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8981:13): [True: 0, False: 17]
  ------------------
 8982|      0|          ada_log(
 8983|      0|              "url_aggregator::validate missing @ at the end of the username "
 8984|      0|              "\n",
 8985|      0|              to_diagram());
 8986|      0|          return false;
 8987|      0|        }
 8988|     17|      }
 8989|    821|    } else {
 8990|      0|      if (components.host_end != components.host_start) {
  ------------------
  |  Branch (8990:11): [True: 0, False: 0]
  ------------------
 8991|      0|        ada_log("url_aggregator::validate expected omitted host \n",
 8992|      0|                to_diagram());
 8993|      0|        return false;
 8994|      0|      }
 8995|      0|    }
 8996|    856|  }
 8997|    856|  if (components.host_end != buffer.size() &&
  ------------------
  |  Branch (8997:7): [True: 856, False: 0]
  ------------------
 8998|    856|      components.pathname_start > components.host_end) {
  ------------------
  |  Branch (8998:7): [True: 58, False: 798]
  ------------------
 8999|     58|    if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8999:9): [True: 8, False: 50]
  ------------------
 9000|      8|        buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9000:9): [True: 0, False: 8]
  ------------------
 9001|      0|        buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (9001:9): [True: 0, False: 0]
  ------------------
 9002|      0|      if (components.pathname_start + 1 >= buffer.size() ||
  ------------------
  |  Branch (9002:11): [True: 0, False: 0]
  ------------------
 9003|      0|          buffer[components.pathname_start] != '/' ||
  ------------------
  |  Branch (9003:11): [True: 0, False: 0]
  ------------------
 9004|      0|          buffer[components.pathname_start + 1] != '/') {
  ------------------
  |  Branch (9004:11): [True: 0, False: 0]
  ------------------
 9005|      0|        ada_log(
 9006|      0|            "url_aggregator::validate expected the path to begin with // \n",
 9007|      0|            to_diagram());
 9008|      0|        return false;
 9009|      0|      }
 9010|     58|    } else if (buffer[components.host_end] != ':') {
  ------------------
  |  Branch (9010:16): [True: 0, False: 58]
  ------------------
 9011|      0|      ada_log("url_aggregator::validate missing : at the port \n",
 9012|      0|              to_diagram());
 9013|      0|      return false;
 9014|      0|    }
 9015|     58|  }
 9016|    856|  if (components.pathname_start != buffer.size() &&
  ------------------
  |  Branch (9016:7): [True: 856, False: 0]
  ------------------
 9017|    856|      components.pathname_start < components.search_start &&
  ------------------
  |  Branch (9017:7): [True: 856, False: 0]
  ------------------
 9018|    856|      components.pathname_start < components.hash_start && !has_opaque_path) {
  ------------------
  |  Branch (9018:7): [True: 856, False: 0]
  |  Branch (9018:60): [True: 856, False: 0]
  ------------------
 9019|    856|    if (buffer[components.pathname_start] != '/') {
  ------------------
  |  Branch (9019:9): [True: 0, False: 856]
  ------------------
 9020|      0|      ada_log("url_aggregator::validate missing / at the path \n",
 9021|      0|              to_diagram());
 9022|      0|      return false;
 9023|      0|    }
 9024|    856|  }
 9025|    856|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9025:7): [True: 282, False: 574]
  ------------------
 9026|    282|    if (buffer[components.search_start] != '?') {
  ------------------
  |  Branch (9026:9): [True: 0, False: 282]
  ------------------
 9027|      0|      ada_log("url_aggregator::validate missing ? at the search \n",
 9028|      0|              to_diagram());
 9029|      0|      return false;
 9030|      0|    }
 9031|    282|  }
 9032|    856|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9032:7): [True: 68, False: 788]
  ------------------
 9033|     68|    if (buffer[components.hash_start] != '#') {
  ------------------
  |  Branch (9033:9): [True: 0, False: 68]
  ------------------
 9034|      0|      ada_log("url_aggregator::validate missing # at the hash \n",
 9035|      0|              to_diagram());
 9036|      0|      return false;
 9037|      0|    }
 9038|     68|  }
 9039|       |
 9040|    856|  return true;
 9041|    856|}
_ZNK3ada14url_components24check_offset_consistencyEv:
 7390|    856|    const noexcept {
 7391|       |  /**
 7392|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 7393|       |   *       |     |    |          | ^^^^|       |   |
 7394|       |   *       |     |    |          | |   |       |   `----- hash_start
 7395|       |   *       |     |    |          | |   |       `--------- search_start
 7396|       |   *       |     |    |          | |   `----------------- pathname_start
 7397|       |   *       |     |    |          | `--------------------- port
 7398|       |   *       |     |    |          `----------------------- host_end
 7399|       |   *       |     |    `---------------------------------- host_start
 7400|       |   *       |     `--------------------------------------- username_end
 7401|       |   *       `--------------------------------------------- protocol_end
 7402|       |   */
 7403|       |  // These conditions can be made more strict.
 7404|    856|  if (protocol_end == url_components::omitted) {
  ------------------
  |  Branch (7404:7): [True: 0, False: 856]
  ------------------
 7405|      0|    return false;
 7406|      0|  }
 7407|    856|  uint32_t index = protocol_end;
 7408|       |
 7409|    856|  if (username_end == url_components::omitted) {
  ------------------
  |  Branch (7409:7): [True: 0, False: 856]
  ------------------
 7410|      0|    return false;
 7411|      0|  }
 7412|    856|  if (username_end < index) {
  ------------------
  |  Branch (7412:7): [True: 0, False: 856]
  ------------------
 7413|      0|    return false;
 7414|      0|  }
 7415|    856|  index = username_end;
 7416|       |
 7417|    856|  if (host_start == url_components::omitted) {
  ------------------
  |  Branch (7417:7): [True: 0, False: 856]
  ------------------
 7418|      0|    return false;
 7419|      0|  }
 7420|    856|  if (host_start < index) {
  ------------------
  |  Branch (7420:7): [True: 0, False: 856]
  ------------------
 7421|      0|    return false;
 7422|      0|  }
 7423|    856|  index = host_start;
 7424|       |
 7425|    856|  if (port != url_components::omitted) {
  ------------------
  |  Branch (7425:7): [True: 58, False: 798]
  ------------------
 7426|     58|    if (port > 0xffff) {
  ------------------
  |  Branch (7426:9): [True: 0, False: 58]
  ------------------
 7427|      0|      return false;
 7428|      0|    }
 7429|     58|    uint32_t port_length = helpers::fast_digit_count(port) + 1;
 7430|     58|    if (index + port_length < index) {
  ------------------
  |  Branch (7430:9): [True: 0, False: 58]
  ------------------
 7431|      0|      return false;
 7432|      0|    }
 7433|     58|    index += port_length;
 7434|     58|  }
 7435|       |
 7436|    856|  if (pathname_start == url_components::omitted) {
  ------------------
  |  Branch (7436:7): [True: 0, False: 856]
  ------------------
 7437|      0|    return false;
 7438|      0|  }
 7439|    856|  if (pathname_start < index) {
  ------------------
  |  Branch (7439:7): [True: 0, False: 856]
  ------------------
 7440|      0|    return false;
 7441|      0|  }
 7442|    856|  index = pathname_start;
 7443|       |
 7444|    856|  if (search_start != url_components::omitted) {
  ------------------
  |  Branch (7444:7): [True: 282, False: 574]
  ------------------
 7445|    282|    if (search_start < index) {
  ------------------
  |  Branch (7445:9): [True: 0, False: 282]
  ------------------
 7446|      0|      return false;
 7447|      0|    }
 7448|    282|    index = search_start;
 7449|    282|  }
 7450|       |
 7451|    856|  if (hash_start != url_components::omitted) {
  ------------------
  |  Branch (7451:7): [True: 68, False: 788]
  ------------------
 7452|     68|    if (hash_start < index) {
  ------------------
  |  Branch (7452:9): [True: 0, False: 68]
  ------------------
 7453|      0|      return false;
 7454|      0|    }
 7455|     68|  }
 7456|       |
 7457|    856|  return true;
 7458|    856|}
_ZN3ada7helpers16fast_digit_countEj:
 1816|     58|inline int fast_digit_count(uint32_t x) noexcept {
 1817|     58|  auto int_log2 = [](uint32_t z) -> int {
 1818|     58|    return 31 - ada::helpers::leading_zeroes(z | 1);
 1819|     58|  };
 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|     58|  const static uint64_t table[] = {
 1825|     58|      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
 1826|     58|      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
 1827|     58|      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
 1828|     58|      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
 1829|     58|      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
 1830|     58|      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
 1831|     58|      42949672960, 42949672960};
 1832|     58|  return int((x + table[int_log2(x)]) >> 32);
 1833|     58|}
_ZZN3ada7helpers16fast_digit_countEjENKUljE_clEj:
 1817|     58|  auto int_log2 = [](uint32_t z) -> int {
 1818|     58|    return 31 - ada::helpers::leading_zeroes(z | 1);
 1819|     58|  };

LLVMFuzzerTestOneInput:
   21|  3.19k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   22|  3.19k|  FuzzedDataProvider fdp(data, size);
   23|       |
   24|       |  // ===== IPv4 Serialization =====
   25|       |  // ipv4() takes a uint64_t (valid addresses are 0..0xFFFFFFFF, but the
   26|       |  // function accepts any value – out-of-range inputs should still be safe).
   27|  3.19k|  uint64_t ipv4_addr = fdp.ConsumeIntegral<uint64_t>();
   28|  3.19k|  std::string ipv4_str = ada::serializers::ipv4(ipv4_addr);
   29|  3.19k|  volatile size_t ipv4_len = ipv4_str.size();
   30|  3.19k|  (void)ipv4_len;
   31|       |
   32|       |  // ===== IPv6 Serialization =====
   33|  3.19k|  std::array<uint16_t, 8> ipv6_addr{};
   34|  25.5k|  for (auto& piece : ipv6_addr) {
  ------------------
  |  Branch (34:20): [True: 25.5k, False: 3.19k]
  ------------------
   35|  25.5k|    piece = fdp.ConsumeIntegral<uint16_t>();
   36|  25.5k|  }
   37|       |
   38|       |  // find_longest_sequence_of_ipv6_pieces: basic invariants must hold.
   39|  3.19k|  size_t compress = 0, compress_length = 0;
   40|  3.19k|  ada::serializers::find_longest_sequence_of_ipv6_pieces(ipv6_addr, compress,
   41|  3.19k|                                                         compress_length);
   42|       |  // The longest run cannot exceed 8 pieces.
   43|  3.19k|  assert(compress_length <= 8);
  ------------------
  |  Branch (43:3): [True: 3.19k, False: 0]
  ------------------
   44|       |  // If a run was found (length > 0) its start index must be in-bounds.
   45|  3.19k|  if (compress_length > 0) {
  ------------------
  |  Branch (45:7): [True: 510, False: 2.68k]
  ------------------
   46|    510|    assert(compress < 8);
  ------------------
  |  Branch (46:5): [True: 510, False: 0]
  ------------------
   47|    510|    assert(compress + compress_length <= 8);
  ------------------
  |  Branch (47:5): [True: 510, False: 0]
  ------------------
   48|    510|  }
   49|       |
   50|  3.19k|  std::string ipv6_str = ada::serializers::ipv6(ipv6_addr);
   51|  3.19k|  volatile size_t ipv6_len = ipv6_str.size();
   52|  3.19k|  (void)ipv6_len;
   53|       |
   54|       |  // Serialized IPv6 must always be a non-empty string.
   55|  3.19k|  assert(!ipv6_str.empty());
  ------------------
  |  Branch (55:3): [True: 3.19k, False: 0]
  ------------------
   56|       |
   57|       |  // ===== Fast IPv4 Parser =====
   58|       |  // try_parse_ipv4_fast() should agree with the full parsing pipeline on its
   59|       |  // output: if it succeeds (result <= 0xFFFFFFFF), re-serializing with
   60|       |  // ipv4() and parsing again must yield the same address value.
   61|  3.19k|  {
   62|  3.19k|    std::string ip_candidate = fdp.ConsumeRandomLengthString(32);
   63|  3.19k|    uint64_t fast_result = ada::checkers::try_parse_ipv4_fast(ip_candidate);
   64|  3.19k|    if (fast_result <= 0xFFFFFFFF) {
  ------------------
  |  Branch (64:9): [True: 35, False: 3.15k]
  ------------------
   65|       |      // Serialize the parsed address and re-parse.
   66|     35|      std::string canonical = ada::serializers::ipv4(fast_result);
   67|     35|      uint64_t recheck = ada::checkers::try_parse_ipv4_fast(canonical);
   68|     35|      if (recheck != fast_result) {
  ------------------
  |  Branch (68:11): [True: 0, False: 35]
  ------------------
   69|      0|        printf(
   70|      0|            "try_parse_ipv4_fast round-trip failure:\n"
   71|      0|            "  input='%s' result=%llu canonical='%s' recheck=%llu\n",
   72|      0|            ip_candidate.c_str(), (unsigned long long)fast_result,
   73|      0|            canonical.c_str(), (unsigned long long)recheck);
   74|      0|        abort();
   75|      0|      }
   76|     35|    }
   77|  3.19k|  }
   78|       |
   79|       |  // ===== Percent Encode / Decode =====
   80|  3.19k|  {
   81|  3.19k|    std::string source = fdp.ConsumeRandomLengthString(128);
   82|       |
   83|       |    // Exercise all six standard character sets used by the URL parser.
   84|  3.19k|    const uint8_t* sets[] = {
   85|  3.19k|        ada::character_sets::C0_CONTROL_PERCENT_ENCODE,
   86|  3.19k|        ada::character_sets::PATH_PERCENT_ENCODE,
   87|  3.19k|        ada::character_sets::QUERY_PERCENT_ENCODE,
   88|  3.19k|        ada::character_sets::FRAGMENT_PERCENT_ENCODE,
   89|  3.19k|        ada::character_sets::USERINFO_PERCENT_ENCODE,
   90|  3.19k|        ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE,
   91|  3.19k|    };
   92|       |
   93|  19.1k|    for (const uint8_t* charset : sets) {
  ------------------
  |  Branch (93:33): [True: 19.1k, False: 3.19k]
  ------------------
   94|       |      // Two-argument percent_encode: returns the encoded string.
   95|  19.1k|      std::string encoded = ada::unicode::percent_encode(source, charset);
   96|  19.1k|      volatile size_t enc_len = encoded.size();
   97|  19.1k|      (void)enc_len;
   98|       |
   99|       |      // Encoded output must be at least as long as the input (each byte
  100|       |      // either stays the same or expands to %XX – three bytes).
  101|  19.1k|      assert(encoded.size() >= source.size());
  ------------------
  |  Branch (101:7): [True: 19.1k, False: 0]
  ------------------
  102|       |
  103|       |      // Three-argument percent_encode: starts encoding from a given index.
  104|  19.1k|      size_t start_idx = fdp.ConsumeIntegralInRange<size_t>(0, source.size());
  105|  19.1k|      std::string encoded_from =
  106|  19.1k|          ada::unicode::percent_encode(source, charset, start_idx);
  107|  19.1k|      volatile size_t enf_len = encoded_from.size();
  108|  19.1k|      (void)enf_len;
  109|       |
  110|       |      // Template form: percent_encode<false> (replace).
  111|  19.1k|      {
  112|  19.1k|        std::string out;
  113|  19.1k|        bool changed =
  114|  19.1k|            ada::unicode::percent_encode<false>(source, charset, out);
  115|  19.1k|        volatile bool c = changed;
  116|  19.1k|        (void)c;
  117|       |        // When encoding was needed 'out' holds the encoded string; when not,
  118|       |        // 'out' is unchanged (empty). If changed, 'out' must not be shorter.
  119|  19.1k|        if (changed) {
  ------------------
  |  Branch (119:13): [True: 7.04k, False: 12.1k]
  ------------------
  120|  7.04k|          assert(out.size() >= source.size());
  ------------------
  |  Branch (120:11): [True: 7.04k, False: 0]
  ------------------
  121|  7.04k|        }
  122|  19.1k|      }
  123|       |
  124|       |      // Template form: percent_encode<true> (append).
  125|  19.1k|      {
  126|  19.1k|        std::string out = "prefix_";
  127|  19.1k|        size_t prefix_len = out.size();
  128|  19.1k|        ada::unicode::percent_encode<true>(source, charset, out);
  129|       |        // Append mode: 'out' must grow by at least source.size() bytes if
  130|       |        // encoding was needed, or stay unchanged if it wasn't.
  131|  19.1k|        assert(out.size() >= prefix_len);
  ------------------
  |  Branch (131:9): [True: 19.1k, False: 0]
  ------------------
  132|  19.1k|      }
  133|  19.1k|    }
  134|       |
  135|       |    // Percent decode: feed raw fuzz input (may contain invalid sequences).
  136|  3.19k|    {
  137|  3.19k|      size_t pct_pos = source.find('%');
  138|  3.19k|      if (pct_pos != std::string::npos) {
  ------------------
  |  Branch (138:11): [True: 128, False: 3.06k]
  ------------------
  139|    128|        std::string decoded = ada::unicode::percent_decode(source, pct_pos);
  140|       |        // Decoded output can't be longer than the input.
  141|    128|        assert(decoded.size() <= source.size());
  ------------------
  |  Branch (141:9): [True: 128, False: 0]
  ------------------
  142|    128|        volatile size_t dec_len = decoded.size();
  143|    128|        (void)dec_len;
  144|    128|      }
  145|  3.19k|    }
  146|       |
  147|       |    // Round-trip: encode with PATH set, then decode the result; the decoded
  148|       |    // form must equal the original (encoding then decoding is an identity).
  149|  3.19k|    {
  150|  3.19k|      std::string encoded = ada::unicode::percent_encode(
  151|  3.19k|          source, ada::character_sets::PATH_PERCENT_ENCODE);
  152|  3.19k|      size_t pct_pos = encoded.find('%');
  153|  3.19k|      if (pct_pos != std::string::npos) {
  ------------------
  |  Branch (153:11): [True: 1.16k, False: 2.03k]
  ------------------
  154|  1.16k|        std::string decoded = ada::unicode::percent_decode(encoded, pct_pos);
  155|  1.16k|        if (decoded != source) {
  ------------------
  |  Branch (155:13): [True: 0, False: 1.16k]
  ------------------
  156|      0|          printf(
  157|      0|              "percent_encode/decode round-trip failure!\n"
  158|      0|              "  source='%s'\n  encoded='%s'\n  decoded='%s'\n",
  159|      0|              source.c_str(), encoded.c_str(), decoded.c_str());
  160|      0|          abort();
  161|      0|        }
  162|  2.03k|      } else {
  163|       |        // No encoding was needed; the output should equal the input.
  164|  2.03k|        assert(encoded == source);
  ------------------
  |  Branch (164:9): [True: 2.03k, False: 0]
  ------------------
  165|  2.03k|      }
  166|  3.19k|    }
  167|       |
  168|       |    // percent_encode_index: the returned index must be within [0, size].
  169|  3.19k|    {
  170|  3.19k|      size_t idx = ada::unicode::percent_encode_index(
  171|  3.19k|          source, ada::character_sets::PATH_PERCENT_ENCODE);
  172|  3.19k|      assert(idx <= source.size());
  ------------------
  |  Branch (172:7): [True: 3.19k, False: 0]
  ------------------
  173|  3.19k|    }
  174|  3.19k|  }
  175|       |
  176|       |  // ===== Checker and Unicode Utility Functions =====
  177|       |  // These are internal helpers used throughout the parser. Fuzzing them
  178|       |  // directly (rather than only through the URL parsing pipeline) ensures
  179|       |  // every edge case is reachable without a valid URL structure.
  180|  3.19k|  {
  181|  3.19k|    std::string util_input = fdp.ConsumeRandomLengthString(128);
  182|       |
  183|       |    // has_tabs_or_newline: any string is valid input.
  184|  3.19k|    volatile bool has_tn = ada::unicode::has_tabs_or_newline(util_input);
  185|  3.19k|    (void)has_tn;
  186|       |
  187|       |    // is_ipv4: must not crash on arbitrary input. Cross-check against URL
  188|       |    // parsing: if is_ipv4 reports true, embedding the string as a hostname
  189|       |    // in an http:// URL must succeed (it must be a parseable IPv4 address).
  190|  3.19k|    volatile bool is_v4 = ada::checkers::is_ipv4(util_input);
  191|  3.19k|    (void)is_v4;
  192|  3.19k|    if (is_v4) {
  ------------------
  |  Branch (192:9): [True: 2.36k, False: 825]
  ------------------
  193|  2.36k|      std::string ipv4_url = "http://" + util_input + "/";
  194|  2.36k|      auto parsed = ada::parse<ada::url_aggregator>(ipv4_url);
  195|       |      // is_ipv4 reports true only for strings that look like IPv4 addresses;
  196|       |      // the full parser may still reject them (e.g. out-of-range octets), but
  197|       |      // if it accepts them the host type must be IPv4.
  198|  2.36k|      if (parsed) {
  ------------------
  |  Branch (198:11): [True: 729, False: 1.63k]
  ------------------
  199|    729|        volatile bool v = parsed->validate();
  200|    729|        (void)v;
  201|    729|      }
  202|  2.36k|    }
  203|       |
  204|       |    // path_signature: returns a bitmask; must not crash.
  205|  3.19k|    volatile uint8_t sig = ada::checkers::path_signature(util_input);
  206|  3.19k|    (void)sig;
  207|       |
  208|       |    // is_windows_drive_letter: must not crash on short or long inputs.
  209|  3.19k|    volatile bool is_wdl = ada::checkers::is_windows_drive_letter(util_input);
  210|  3.19k|    (void)is_wdl;
  211|       |
  212|       |    // is_normalized_windows_drive_letter
  213|  3.19k|    volatile bool is_nwdl =
  214|  3.19k|        ada::checkers::is_normalized_windows_drive_letter(util_input);
  215|  3.19k|    (void)is_nwdl;
  216|       |
  217|       |    // Consistency: a normalised Windows drive letter is a subset of
  218|       |    // Windows drive letters.
  219|  3.19k|    if (is_nwdl && !is_wdl) {
  ------------------
  |  Branch (219:9): [True: 5, False: 3.18k]
  |  Branch (219:20): [True: 0, False: 5]
  ------------------
  220|      0|      printf(
  221|      0|          "is_normalized_windows_drive_letter implies is_windows_drive_letter"
  222|      0|          " but got inconsistent results for '%s'\n",
  223|      0|          util_input.c_str());
  224|      0|      abort();
  225|      0|    }
  226|       |
  227|       |    // to_lower_ascii: works in-place; must not crash.
  228|  3.19k|    {
  229|  3.19k|      std::string lower_copy = util_input;
  230|  3.19k|      volatile bool all_ascii =
  231|  3.19k|          ada::unicode::to_lower_ascii(lower_copy.data(), lower_copy.size());
  232|  3.19k|      (void)all_ascii;
  233|       |      // The result should be at most as long as the input.
  234|  3.19k|      assert(lower_copy.size() == util_input.size());
  ------------------
  |  Branch (234:7): [True: 3.19k, False: 0]
  ------------------
  235|  3.19k|    }
  236|       |
  237|       |    // contains_forbidden_domain_code_point: must not crash.
  238|  3.19k|    volatile bool has_forbidden =
  239|  3.19k|        ada::unicode::contains_forbidden_domain_code_point(util_input.data(),
  240|  3.19k|                                                           util_input.size());
  241|  3.19k|    (void)has_forbidden;
  242|       |
  243|       |    // contains_forbidden_domain_code_point_or_upper: must not crash.
  244|  3.19k|    volatile uint8_t forbidden_or_upper =
  245|  3.19k|        ada::unicode::contains_forbidden_domain_code_point_or_upper(
  246|  3.19k|            util_input.data(), util_input.size());
  247|  3.19k|    (void)forbidden_or_upper;
  248|       |
  249|       |    // verify_dns_length: must not crash; also check consistency with
  250|       |    // to_ascii output.
  251|  3.19k|    volatile bool dns_ok = ada::checkers::verify_dns_length(util_input);
  252|  3.19k|    (void)dns_ok;
  253|  3.19k|  }
  254|       |
  255|       |  // ===== Integration: embed serialized addresses into real URLs =====
  256|       |  // This exercises the full parsing pipeline with our synthetic addresses
  257|       |  // and verifies that serialization output is always round-trip safe.
  258|      0|  {
  259|       |    // Only valid IPv4 address range (0..0xFFFFFFFF) should embed cleanly.
  260|  3.19k|    if (ipv4_addr <= 0xFFFFFFFF) {
  ------------------
  |  Branch (260:9): [True: 127, False: 3.06k]
  ------------------
  261|    127|      std::string url_str = "http://" + ipv4_str + "/path?q=1";
  262|    127|      auto parsed = ada::parse<ada::url_aggregator>(url_str);
  263|    127|      if (parsed) {
  ------------------
  |  Branch (263:11): [True: 127, False: 0]
  ------------------
  264|    127|        volatile bool v = parsed->validate();
  265|    127|        (void)v;
  266|       |        // The URL's hostname must be the canonical dotted-decimal address.
  267|    127|        std::string host = std::string(parsed->get_hostname());
  268|       |        // host == ipv4_str  (not asserted here because the URL parser may
  269|       |        // normalise the address differently for non-standard values, but it
  270|       |        // must always successfully parse our serialized form).
  271|    127|        (void)host;
  272|    127|      }
  273|    127|    }
  274|       |
  275|       |    // IPv6: wrap in brackets.
  276|  3.19k|    std::string ipv6_url_str = "http://[" + ipv6_str + "]/path";
  277|  3.19k|    auto parsed_ipv6 = ada::parse<ada::url_aggregator>(ipv6_url_str);
  278|  3.19k|    if (parsed_ipv6) {
  ------------------
  |  Branch (278:9): [True: 0, False: 3.19k]
  ------------------
  279|      0|      volatile bool v = parsed_ipv6->validate();
  280|      0|      (void)v;
  281|       |      // Re-parse the href – must be idempotent.
  282|      0|      std::string href = std::string(parsed_ipv6->get_href());
  283|      0|      auto reparsed = ada::parse<ada::url_aggregator>(href);
  284|      0|      if (!reparsed) {
  ------------------
  |  Branch (284:11): [True: 0, False: 0]
  ------------------
  285|      0|        printf("IPv6 URL re-parse failure: '%s'\n", href.c_str());
  286|      0|        abort();
  287|      0|      }
  288|      0|      if (std::string(reparsed->get_href()) != href) {
  ------------------
  |  Branch (288:11): [True: 0, False: 0]
  ------------------
  289|      0|        printf("IPv6 URL re-parse href mismatch: '%s' vs '%s'\n", href.c_str(),
  290|      0|               std::string(reparsed->get_href()).c_str());
  291|      0|        abort();
  292|      0|      }
  293|      0|    }
  294|  3.19k|  }
  295|       |
  296|  3.19k|  return 0;
  297|  3.19k|}

