_ZN3ada4idna13utf8_to_utf32EPKcmPDi:
  162|  13.4k|size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
  163|  13.4k|  const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
  164|  13.4k|  size_t pos = 0;
  165|  13.4k|  const char32_t* start{utf32_output};
  166|   183k|  while (pos < len) {
  ------------------
  |  Branch (166:10): [True: 170k, False: 12.4k]
  ------------------
  167|       |    // try to convert the next block of 16 ASCII bytes
  168|   170k|    if (pos + 16 <= len) {  // if it is safe to read 16 more
  ------------------
  |  Branch (168:9): [True: 98.6k, False: 72.1k]
  ------------------
  169|       |                            // bytes, check that they are ascii
  170|  98.6k|      uint64_t v1;
  171|  98.6k|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  172|  98.6k|      uint64_t v2;
  173|  98.6k|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  174|  98.6k|      uint64_t v{v1 | v2};
  175|  98.6k|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (175:11): [True: 8.01k, False: 90.6k]
  ------------------
  176|  8.01k|        size_t final_pos = pos + 16;
  177|   136k|        while (pos < final_pos) {
  ------------------
  |  Branch (177:16): [True: 128k, False: 8.01k]
  ------------------
  178|   128k|          *utf32_output++ = char32_t(buf[pos]);
  179|   128k|          pos++;
  180|   128k|        }
  181|  8.01k|        continue;
  182|  8.01k|      }
  183|  98.6k|    }
  184|   162k|    uint8_t leading_byte = data[pos];  // leading byte
  185|   162k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (185:9): [True: 94.7k, False: 67.9k]
  ------------------
  186|       |      // converting one ASCII byte !!!
  187|  94.7k|      *utf32_output++ = char32_t(leading_byte);
  188|  94.7k|      pos++;
  189|  94.7k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (189:16): [True: 18.9k, False: 49.0k]
  ------------------
  190|       |      // We have a two-byte UTF-8
  191|  18.9k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (191:11): [True: 159, False: 18.7k]
  ------------------
  192|    159|        return 0;
  193|    159|      }  // minimal bound checking
  194|  18.7k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (194:11): [True: 192, False: 18.5k]
  ------------------
  195|    192|        return 0;
  196|    192|      }
  197|       |      // range check
  198|  18.5k|      uint32_t code_point =
  199|  18.5k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  200|  18.5k|      if (code_point < 0x80 || 0x7ff < code_point) {
  ------------------
  |  Branch (200:11): [True: 12, False: 18.5k]
  |  Branch (200:32): [True: 0, False: 18.5k]
  ------------------
  201|     12|        return 0;
  202|     12|      }
  203|  18.5k|      *utf32_output++ = char32_t(code_point);
  204|  18.5k|      pos += 2;
  205|  49.0k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (205:16): [True: 47.2k, False: 1.80k]
  ------------------
  206|       |      // We have a three-byte UTF-8
  207|  47.2k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (207:11): [True: 39, False: 47.1k]
  ------------------
  208|     39|        return 0;
  209|     39|      }  // minimal bound checking
  210|       |
  211|  47.1k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (211:11): [True: 60, False: 47.1k]
  ------------------
  212|     60|        return 0;
  213|     60|      }
  214|  47.1k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (214:11): [True: 9, False: 47.1k]
  ------------------
  215|      9|        return 0;
  216|      9|      }
  217|       |      // range check
  218|  47.1k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  219|  47.1k|                            (data[pos + 1] & 0b00111111) << 6 |
  220|  47.1k|                            (data[pos + 2] & 0b00111111);
  221|  47.1k|      if (code_point < 0x800 || 0xffff < code_point ||
  ------------------
  |  Branch (221:11): [True: 21, False: 47.1k]
  |  Branch (221:33): [True: 0, False: 47.1k]
  ------------------
  222|  47.1k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (222:12): [True: 4.27k, False: 42.8k]
  |  Branch (222:35): [True: 9, False: 4.26k]
  ------------------
  223|     30|        return 0;
  224|     30|      }
  225|  47.0k|      *utf32_output++ = char32_t(code_point);
  226|  47.0k|      pos += 3;
  227|  47.0k|    } else if ((leading_byte & 0b11111000) == 0b11110000) {  // 0b11110000
  ------------------
  |  Branch (227:16): [True: 1.41k, False: 390]
  ------------------
  228|       |      // we have a 4-byte UTF-8 word.
  229|  1.41k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (229:11): [True: 57, False: 1.35k]
  ------------------
  230|     57|        return 0;
  231|     57|      }  // minimal bound checking
  232|  1.35k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (232:11): [True: 27, False: 1.32k]
  ------------------
  233|     27|        return 0;
  234|     27|      }
  235|  1.32k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (235:11): [True: 12, False: 1.31k]
  ------------------
  236|     12|        return 0;
  237|     12|      }
  238|  1.31k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (238:11): [True: 12, False: 1.30k]
  ------------------
  239|     12|        return 0;
  240|     12|      }
  241|       |
  242|       |      // range check
  243|  1.30k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  244|  1.30k|                            (data[pos + 1] & 0b00111111) << 12 |
  245|  1.30k|                            (data[pos + 2] & 0b00111111) << 6 |
  246|  1.30k|                            (data[pos + 3] & 0b00111111);
  247|  1.30k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (247:11): [True: 21, False: 1.28k]
  |  Branch (247:35): [True: 15, False: 1.26k]
  ------------------
  248|     36|        return 0;
  249|     36|      }
  250|  1.26k|      *utf32_output++ = char32_t(code_point);
  251|  1.26k|      pos += 4;
  252|  1.26k|    } else {
  253|    390|      return 0;
  254|    390|    }
  255|   162k|  }
  256|  12.4k|  return utf32_output - start;
  257|  13.4k|}
_ZN3ada4idna22utf32_length_from_utf8EPKcm:
  272|  13.6k|size_t utf32_length_from_utf8(const char* buf, size_t len) {
  273|  13.6k|  const int8_t* p = reinterpret_cast<const int8_t*>(buf);
  274|  13.6k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  275|       |    // -65 is 0b10111111, anything larger in two-complement's
  276|       |    // should start a new code point.
  277|  13.6k|    return c > -65;
  278|  13.6k|  });
  279|  13.6k|}
_ZN3ada4idna9ascii_mapEPcm:
 5338|  6.48k|void ascii_map(char* input, size_t length) {
 5339|  6.48k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5340|  6.48k|    return 0x101010101010101ull * v;
 5341|  6.48k|  };
 5342|  6.48k|  uint64_t broadcast_80 = broadcast(0x80);
 5343|  6.48k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 5344|  6.48k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 5345|  6.48k|  size_t i = 0;
 5346|       |
 5347|  52.1k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (5347:10): [True: 45.6k, False: 6.48k]
  ------------------
 5348|  45.6k|    uint64_t word{};
 5349|  45.6k|    std::memcpy(&word, input + i, sizeof(word));
 5350|  45.6k|    word ^=
 5351|  45.6k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5352|  45.6k|    std::memcpy(input + i, &word, sizeof(word));
 5353|  45.6k|  }
 5354|  6.48k|  if (i < length) {
  ------------------
  |  Branch (5354:7): [True: 5.57k, False: 908]
  ------------------
 5355|  5.57k|    uint64_t word{};
 5356|  5.57k|    std::memcpy(&word, input + i, length - i);
 5357|  5.57k|    word ^=
 5358|  5.57k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5359|  5.57k|    std::memcpy(input + i, &word, length - i);
 5360|  5.57k|  }
 5361|  6.48k|}
_ZN3ada4idna3mapENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIDiS4_NS1_9allocatorIDiEEEE:
 5365|  19.4k|bool map(std::u32string_view input, std::u32string& out) {
 5366|       |  //  [Map](https://www.unicode.org/reports/tr46/#ProcessingStepMap).
 5367|  19.4k|  out.clear();
 5368|  19.4k|  if (!ensure_tables()) {
  ------------------
  |  Branch (5368:7): [True: 0, False: 19.4k]
  ------------------
 5369|      0|    return false;
 5370|      0|  }
 5371|       |
 5372|       |  // Pass 1: validate and compute exact output length.
 5373|  19.4k|  size_t out_size = 0;
 5374|   351k|  for (char32_t x : input) {
  ------------------
  |  Branch (5374:19): [True: 351k, False: 18.8k]
  ------------------
 5375|   351k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5376|   351k|    if (status == IDNA_DISALLOWED) {
  ------------------
  |  Branch (5376:9): [True: 543, False: 350k]
  ------------------
 5377|    543|      return false;
 5378|    543|    }
 5379|   350k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5379:9): [True: 244k, False: 106k]
  ------------------
 5380|   244k|      ++out_size;
 5381|   244k|      continue;
 5382|   244k|    }
 5383|   106k|    if (static_cast<size_t>(status) >= idna_utf8_mappings_size) {
  ------------------
  |  Branch (5383:9): [True: 0, False: 106k]
  ------------------
 5384|      0|      return false;
 5385|      0|    }
 5386|   106k|    out_size += utf8_count_codepoints(idna_utf8_mappings + status);
 5387|   106k|  }
 5388|       |
 5389|       |  // Pass 2: write into a single allocation.
 5390|  18.8k|  out.resize(out_size);
 5391|  18.8k|  size_t w = 0;
 5392|   349k|  for (char32_t x : input) {
  ------------------
  |  Branch (5392:19): [True: 349k, False: 18.8k]
  ------------------
 5393|   349k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5394|   349k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5394:9): [True: 243k, False: 105k]
  ------------------
 5395|   243k|      out[w++] = x;
 5396|   243k|      continue;
 5397|   243k|    }
 5398|       |    // IGNORED or mapped (status already validated in pass 1).
 5399|   105k|    const uint8_t* ptr = idna_utf8_mappings + status;
 5400|   515k|    while (*ptr != 0) {
  ------------------
  |  Branch (5400:12): [True: 409k, False: 105k]
  ------------------
 5401|   409k|      out[w++] = utf8_next(ptr);
 5402|   409k|    }
 5403|   105k|  }
 5404|  18.8k|  return true;
 5405|  19.4k|}
_ZN3ada4idna28compute_decomposition_lengthENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5499|  5.37k|    const std::u32string_view input) noexcept {
 5500|  5.37k|  bool decomposition_needed{false};
 5501|  5.37k|  size_t additional_elements{0};
 5502|  85.3k|  for (char32_t current_character : input) {
  ------------------
  |  Branch (5502:35): [True: 85.3k, False: 5.37k]
  ------------------
 5503|  85.3k|    size_t decomposition_length{0};
 5504|       |
 5505|  85.3k|    if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5505:9): [True: 8.50k, False: 76.8k]
  ------------------
 5506|  8.50k|        current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5506:9): [True: 4.32k, False: 4.17k]
  ------------------
 5507|       |      // Full NFD-style Hangul decomp for the decompose() step.
 5508|  4.32k|      decomposition_length = 2;
 5509|  4.32k|      if ((current_character - hangul_sbase) % hangul_tcount) {
  ------------------
  |  Branch (5509:11): [True: 1.74k, False: 2.58k]
  ------------------
 5510|  1.74k|        decomposition_length = 3;
 5511|  1.74k|      }
 5512|  81.0k|    } else if (current_character < 0x110000) {
  ------------------
  |  Branch (5512:16): [True: 81.0k, False: 0]
  ------------------
 5513|  81.0k|      const uint8_t di = decomposition_index[current_character >> 8];
 5514|  81.0k|      const uint16_t* const decomposition =
 5515|  81.0k|          decomposition_block_row(di) + (current_character % 256);
 5516|  81.0k|      decomposition_length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5517|  81.0k|      if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5517:11): [True: 12.0k, False: 68.9k]
  |  Branch (5517:41): [True: 0, False: 12.0k]
  ------------------
 5518|      0|        decomposition_length = 0;
 5519|      0|      }
 5520|  81.0k|    }
 5521|  85.3k|    if (decomposition_length != 0) {
  ------------------
  |  Branch (5521:9): [True: 16.3k, False: 68.9k]
  ------------------
 5522|  16.3k|      decomposition_needed = true;
 5523|  16.3k|      additional_elements += decomposition_length - 1;
 5524|  16.3k|    }
 5525|  85.3k|  }
 5526|  5.37k|  return {decomposition_needed, additional_elements};
 5527|  5.37k|}
_ZN3ada4idna9decomposeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEEm:
 5529|  4.22k|void decompose(std::u32string& input, size_t additional_elements) {
 5530|  4.22k|  input.resize(input.size() + additional_elements);
 5531|  4.22k|  for (size_t descending_idx = input.size(),
 5532|  4.22k|              input_count = descending_idx - additional_elements;
 5533|  56.3k|       input_count--;) {
  ------------------
  |  Branch (5533:8): [True: 52.1k, False: 4.22k]
  ------------------
 5534|  52.1k|    if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5534:9): [True: 6.60k, False: 45.5k]
  ------------------
 5535|  6.60k|        input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5535:9): [True: 4.32k, False: 2.27k]
  ------------------
 5536|       |      // Hangul decomposition.
 5537|  4.32k|      char32_t s_index = input[input_count] - hangul_sbase;
 5538|  4.32k|      if (s_index % hangul_tcount != 0) {
  ------------------
  |  Branch (5538:11): [True: 1.74k, False: 2.58k]
  ------------------
 5539|  1.74k|        input[--descending_idx] = hangul_tbase + s_index % hangul_tcount;
 5540|  1.74k|      }
 5541|  4.32k|      input[--descending_idx] =
 5542|  4.32k|          hangul_vbase + (s_index % hangul_ncount) / hangul_tcount;
 5543|  4.32k|      input[--descending_idx] = hangul_lbase + s_index / hangul_ncount;
 5544|  47.8k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5544:16): [True: 47.8k, False: 0]
  ------------------
 5545|  47.8k|      const uint16_t* decomposition =
 5546|  47.8k|          decomposition_block_row(
 5547|  47.8k|              decomposition_index[input[input_count] >> 8]) +
 5548|  47.8k|          (input[input_count] % 256);
 5549|  47.8k|      uint16_t decomposition_length =
 5550|  47.8k|          (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5551|  47.8k|      if (decomposition_length > 0 && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5551:11): [True: 12.0k, False: 35.7k]
  |  Branch (5551:39): [True: 0, False: 12.0k]
  ------------------
 5552|      0|        decomposition_length = 0;
 5553|      0|      }
 5554|  47.8k|      if (decomposition_length > 0) {
  ------------------
  |  Branch (5554:11): [True: 12.0k, False: 35.7k]
  ------------------
 5555|  12.0k|        const size_t base = static_cast<size_t>(decomposition[0] >> 2);
 5556|  12.0k|        if (base + decomposition_length > decomposition_data_size) {
  ------------------
  |  Branch (5556:13): [True: 0, False: 12.0k]
  ------------------
 5557|      0|          input[--descending_idx] = input[input_count];
 5558|  12.0k|        } else {
 5559|  39.5k|          while (decomposition_length-- > 0) {
  ------------------
  |  Branch (5559:18): [True: 27.4k, False: 12.0k]
  ------------------
 5560|  27.4k|            input[--descending_idx] =
 5561|  27.4k|                decomposition_data[base + decomposition_length];
 5562|  27.4k|          }
 5563|  12.0k|        }
 5564|  35.7k|      } else {
 5565|  35.7k|        input[--descending_idx] = input[input_count];
 5566|  35.7k|      }
 5567|  47.8k|    } else {
 5568|      0|      input[--descending_idx] = input[input_count];
 5569|      0|    }
 5570|  52.1k|  }
 5571|  4.22k|}
_ZN3ada4idna7get_cccEDi:
 5573|  1.62M|uint8_t get_ccc(char32_t c) noexcept {
 5574|  1.62M|  if (c >= 0x110000) {
  ------------------
  |  Branch (5574:7): [True: 0, False: 1.62M]
  ------------------
 5575|      0|    return 0;
 5576|      0|  }
 5577|  1.62M|  return ccc_block_row(ccc_index[c >> 8])[c % 256];
 5578|  1.62M|}
_ZN3ada4idna10sort_marksERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5580|  5.37k|void sort_marks(std::u32string& input) {
 5581|   106k|  for (size_t idx = 1; idx < input.size(); idx++) {
  ------------------
  |  Branch (5581:24): [True: 101k, False: 5.37k]
  ------------------
 5582|   101k|    uint8_t ccc = get_ccc(input[idx]);
 5583|   101k|    if (ccc == 0) {
  ------------------
  |  Branch (5583:9): [True: 72.8k, False: 28.5k]
  ------------------
 5584|  72.8k|      continue;
 5585|  72.8k|    }
 5586|  28.5k|    auto current_character = input[idx];
 5587|  28.5k|    size_t back_idx = idx;
 5588|  40.6k|    while (back_idx != 0 && get_ccc(input[back_idx - 1]) > ccc) {
  ------------------
  |  Branch (5588:12): [True: 40.2k, False: 366]
  |  Branch (5588:29): [True: 12.0k, False: 28.2k]
  ------------------
 5589|  12.0k|      input[back_idx] = input[back_idx - 1];
 5590|  12.0k|      back_idx--;
 5591|  12.0k|    }
 5592|  28.5k|    input[back_idx] = current_character;
 5593|  28.5k|  }
 5594|  5.37k|}
_ZN3ada4idna13decompose_nfcERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5596|  5.37k|void decompose_nfc(std::u32string& input) {
 5597|       |  /**
 5598|       |   * Decompose the domain_name string to Unicode Normalization Form C.
 5599|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepDecompose
 5600|       |   */
 5601|  5.37k|  auto [decomposition_needed, additional_elements] =
 5602|  5.37k|      compute_decomposition_length(input);
 5603|  5.37k|  if (decomposition_needed) {
  ------------------
  |  Branch (5603:7): [True: 4.22k, False: 1.15k]
  ------------------
 5604|  4.22k|    decompose(input, additional_elements);
 5605|  4.22k|  }
 5606|  5.37k|  sort_marks(input);
 5607|  5.37k|}
_ZN3ada4idna14is_already_nfcENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5742|  21.0k|bool is_already_nfc(std::u32string_view input) noexcept {
 5743|  21.0k|  if (input.empty()) {
  ------------------
  |  Branch (5743:7): [True: 0, False: 21.0k]
  ------------------
 5744|      0|    return true;
 5745|      0|  }
 5746|  21.0k|  if (!tables_are_ready() && !ensure_tables()) {
  ------------------
  |  Branch (5746:7): [True: 0, False: 21.0k]
  |  Branch (5746:30): [True: 0, False: 0]
  ------------------
 5747|      0|    return false;
 5748|      0|  }
 5749|       |  // 1) A character whose canonical decomposition does not compose back to it is
 5750|       |  //    not NFC: singletons (e.g. U+212B), composition exclusions (e.g. U+0958)
 5751|       |  //    and non-starter decompositions (e.g. U+0344), or one whose
 5752|       |  //    decomposition starts with a code point that composes with the previous
 5753|       |  //    character (e.g. U+1611E U+16121).
 5754|  21.0k|  char32_t previous = 0x110000;  // none
 5755|   499k|  for (char32_t c : input) {
  ------------------
  |  Branch (5755:19): [True: 499k, False: 21.0k]
  ------------------
 5756|   499k|    if (decomposition_is_not_nfc(previous, c)) {
  ------------------
  |  Branch (5756:9): [True: 24, False: 499k]
  ------------------
 5757|     24|      return false;
 5758|     24|    }
 5759|   499k|    previous = c;
 5760|   499k|  }
 5761|       |  // 2) Combining marks already in canonical order. prev_ccc carries the
 5762|       |  //    trailing class of the previous character's canonical decomposition so a
 5763|       |  //    composite starter followed by a lower-class mark is caught.
 5764|  21.0k|  uint8_t prev_ccc = 0;
 5765|   481k|  for (char32_t c : input) {
  ------------------
  |  Branch (5765:19): [True: 481k, False: 12.7k]
  ------------------
 5766|   481k|    uint8_t ccc = get_ccc(c);
 5767|   481k|    if (ccc != 0 && prev_ccc > ccc) {
  ------------------
  |  Branch (5767:9): [True: 27.9k, False: 453k]
  |  Branch (5767:21): [True: 8.29k, False: 19.6k]
  ------------------
 5768|  8.29k|      return false;
 5769|  8.29k|    }
 5770|   473k|    prev_ccc = trailing_ccc(c);
 5771|   473k|  }
 5772|       |  // 3) No pairwise composition would apply (including Hangul L+V / LV+T).
 5773|  12.7k|  return !would_compose(input);
 5774|  21.0k|}
_ZN3ada4idna7composeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5776|  37.7k|void compose(std::u32string& input) {
 5777|       |  /**
 5778|       |   * Compose the domain_name string to Unicode Normalization Form C.
 5779|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepCompose
 5780|       |   */
 5781|  37.7k|  size_t input_count{0};
 5782|  37.7k|  size_t composition_count{0};
 5783|   141k|  for (; input_count < input.size(); input_count++, composition_count++) {
  ------------------
  |  Branch (5783:10): [True: 103k, False: 37.7k]
  ------------------
 5784|   103k|    input[composition_count] = input[input_count];
 5785|   103k|    if (input[input_count] >= hangul_lbase &&
  ------------------
  |  Branch (5785:9): [True: 21.6k, False: 82.1k]
  ------------------
 5786|  21.6k|        input[input_count] < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5786:9): [True: 7.58k, False: 14.0k]
  ------------------
 5787|  7.58k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5787:11): [True: 7.56k, False: 27]
  ------------------
 5788|  7.56k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5788:11): [True: 5.04k, False: 2.52k]
  ------------------
 5789|  5.04k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5789:11): [True: 4.48k, False: 558]
  ------------------
 5790|  4.48k|        input[composition_count] =
 5791|  4.48k|            hangul_sbase +
 5792|  4.48k|            ((input[input_count] - hangul_lbase) * hangul_vcount +
 5793|  4.48k|             input[input_count + 1] - hangul_vbase) *
 5794|  4.48k|                hangul_tcount;
 5795|  4.48k|        input_count++;
 5796|  4.48k|        if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5796:13): [True: 4.37k, False: 105]
  ------------------
 5797|  4.37k|            input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5797:13): [True: 2.30k, False: 2.07k]
  ------------------
 5798|  2.30k|            input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5798:13): [True: 1.79k, False: 510]
  ------------------
 5799|  1.79k|          input[composition_count] += input[++input_count] - hangul_tbase;
 5800|  1.79k|        }
 5801|  4.48k|      }
 5802|  96.2k|    } else if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5802:16): [True: 5.31k, False: 90.9k]
  ------------------
 5803|  5.31k|               input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5803:16): [True: 0, False: 5.31k]
  ------------------
 5804|      0|      if ((input[input_count] - hangul_sbase) % hangul_tcount == 0 &&
  ------------------
  |  Branch (5804:11): [True: 0, False: 0]
  ------------------
 5805|      0|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (5805:11): [True: 0, False: 0]
  ------------------
 5806|      0|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5806:11): [True: 0, False: 0]
  ------------------
 5807|      0|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5807:11): [True: 0, False: 0]
  ------------------
 5808|      0|        input[composition_count] += input[++input_count] - hangul_tbase;
 5809|      0|      }
 5810|  96.2k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5810:16): [True: 96.2k, False: 0]
  ------------------
 5811|  96.2k|      const uint16_t* composition =
 5812|  96.2k|          composition_block_row(composition_index[input[input_count] >> 8]) +
 5813|  96.2k|          (input[input_count] % 256);
 5814|  96.2k|      size_t initial_composition_count = composition_count;
 5815|   164k|      for (int32_t previous_ccc = -1; input_count + 1 < input.size();
  ------------------
  |  Branch (5815:39): [True: 127k, False: 37.5k]
  ------------------
 5816|   127k|           input_count++) {
 5817|   127k|        uint8_t ccc = get_ccc(input[input_count + 1]);
 5818|       |
 5819|   127k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (5819:13): [True: 75.4k, False: 51.8k]
  |  Branch (5819:49): [True: 72.4k, False: 3.00k]
  ------------------
 5820|  72.4k|          int left = composition[0];
 5821|  72.4k|          int right = composition[1];
 5822|  72.4k|          if (left < 0 || right < left ||
  ------------------
  |  Branch (5822:15): [True: 0, False: 72.4k]
  |  Branch (5822:27): [True: 0, False: 72.4k]
  ------------------
 5823|  72.4k|              static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5823:15): [True: 0, False: 72.4k]
  ------------------
 5824|      0|            break;
 5825|      0|          }
 5826|   174k|          while (left + 2 < right) {
  ------------------
  |  Branch (5826:18): [True: 101k, False: 72.4k]
  ------------------
 5827|   101k|            int middle = left + (((right - left) >> 1) & ~1);
 5828|   101k|            if (composition_data[static_cast<size_t>(middle)] <=
  ------------------
  |  Branch (5828:17): [True: 71.8k, False: 30.0k]
  ------------------
 5829|   101k|                input[input_count + 1]) {
 5830|  71.8k|              left = middle;
 5831|  71.8k|            }
 5832|   101k|            if (composition_data[static_cast<size_t>(middle)] >=
  ------------------
  |  Branch (5832:17): [True: 69.6k, False: 32.2k]
  ------------------
 5833|   101k|                input[input_count + 1]) {
 5834|  69.6k|              right = middle;
 5835|  69.6k|            }
 5836|   101k|          }
 5837|  72.4k|          if (static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5837:15): [True: 72.4k, False: 0]
  ------------------
 5838|  72.4k|              composition_data[static_cast<size_t>(left)] ==
  ------------------
  |  Branch (5838:15): [True: 56.0k, False: 16.4k]
  ------------------
 5839|  72.4k|                  input[input_count + 1]) {
 5840|  56.0k|            char32_t composed = composition_data[static_cast<size_t>(left + 1)];
 5841|  56.0k|            input[initial_composition_count] = composed;
 5842|  56.0k|            composition =
 5843|  56.0k|                composition_block_row(composition_index[composed >> 8]) +
 5844|  56.0k|                (composed % 256);
 5845|  56.0k|            continue;
 5846|  56.0k|          }
 5847|  72.4k|        }
 5848|       |
 5849|  71.2k|        if (ccc == 0) {
  ------------------
  |  Branch (5849:13): [True: 58.7k, False: 12.5k]
  ------------------
 5850|  58.7k|          break;
 5851|  58.7k|        }
 5852|  12.5k|        previous_ccc = ccc;
 5853|  12.5k|        input[++composition_count] = input[input_count + 1];
 5854|  12.5k|      }
 5855|  96.2k|    }
 5856|   103k|  }
 5857|       |
 5858|  37.7k|  if (composition_count < input_count) {
  ------------------
  |  Branch (5858:7): [True: 37.3k, False: 357]
  ------------------
 5859|  37.3k|    input.resize(composition_count);
 5860|  37.3k|  }
 5861|  37.7k|}
_ZN3ada4idna9normalizeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5863|  5.37k|bool normalize(std::u32string& input) {
 5864|       |  /**
 5865|       |   * Normalize the characters according to IDNA (Unicode Normalization Form C).
 5866|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepNormalize
 5867|       |   */
 5868|  5.37k|  if (!ensure_tables() || decomposition_index == nullptr ||
  ------------------
  |  Branch (5868:7): [True: 0, False: 5.37k]
  |  Branch (5868:27): [True: 0, False: 5.37k]
  ------------------
 5869|  5.37k|      composition_index == nullptr) {
  ------------------
  |  Branch (5869:7): [True: 0, False: 5.37k]
  ------------------
 5870|      0|    return false;
 5871|      0|  }
 5872|  5.37k|  if (is_already_nfc(input)) {
  ------------------
  |  Branch (5872:7): [True: 0, False: 5.37k]
  ------------------
 5873|      0|    return true;
 5874|      0|  }
 5875|  5.37k|  decompose_nfc(input);
 5876|  5.37k|  compose(input);
 5877|  5.37k|  return true;
 5878|  5.37k|}
_ZN3ada4idna17punycode_to_utf32ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIDiNS3_IDiEENS1_9allocatorIDiEEEE:
 5938|  7.58k|bool punycode_to_utf32(std::string_view input, std::u32string &out) {
 5939|  7.58k|  int32_t written_out{0};
 5940|  7.58k|  out.reserve(out.size() + input.size());
 5941|  7.58k|  uint32_t n = initial_n;
 5942|  7.58k|  int32_t i = 0;
 5943|  7.58k|  int32_t bias = initial_bias;
 5944|       |  // grab ascii content
 5945|       |  // RFC 3492 section 6.2: copy the code points before the last delimiter,
 5946|       |  // then consume the delimiter only "if more than zero code points were
 5947|       |  // consumed". With a leading delimiter, b == 0, decoding starts at the
 5948|       |  // delimiter itself, which is not a digit, and fails. This matches the
 5949|       |  // reference implementation in appendix C (`in = b > 0 ? b + 1 : 0`).
 5950|  7.58k|  size_t end_of_ascii = input.find_last_of('-');
 5951|  7.58k|  if (end_of_ascii != std::string_view::npos && end_of_ascii > 0) {
  ------------------
  |  Branch (5951:7): [True: 1.00k, False: 6.58k]
  |  Branch (5951:49): [True: 996, False: 9]
  ------------------
 5952|  11.1k|    for (uint8_t c : input.substr(0, end_of_ascii)) {
  ------------------
  |  Branch (5952:20): [True: 11.1k, False: 996]
  ------------------
 5953|  11.1k|      if (c >= 0x80) {
  ------------------
  |  Branch (5953:11): [True: 0, False: 11.1k]
  ------------------
 5954|      0|        return false;
 5955|      0|      }
 5956|  11.1k|      out.push_back(c);
 5957|  11.1k|      written_out++;
 5958|  11.1k|    }
 5959|    996|    input.remove_prefix(end_of_ascii + 1);
 5960|    996|  }
 5961|  76.5k|  while (!input.empty()) {
  ------------------
  |  Branch (5961:10): [True: 69.3k, False: 7.23k]
  ------------------
 5962|  69.3k|    int32_t oldi = i;
 5963|  69.3k|    int32_t w = 1;
 5964|  94.2k|    for (int32_t k = base;; k += base) {
 5965|  94.2k|      if (input.empty()) {
  ------------------
  |  Branch (5965:11): [True: 189, False: 94.0k]
  ------------------
 5966|    189|        return false;
 5967|    189|      }
 5968|  94.0k|      uint8_t code_point = input.front();
 5969|  94.0k|      input.remove_prefix(1);
 5970|  94.0k|      int32_t digit = char_to_digit_value(code_point);
 5971|  94.0k|      if (digit < 0) {
  ------------------
  |  Branch (5971:11): [True: 72, False: 93.9k]
  ------------------
 5972|     72|        return false;
 5973|     72|      }
 5974|  93.9k|      if (digit > (0x7fffffff - i) / w) {
  ------------------
  |  Branch (5974:11): [True: 24, False: 93.9k]
  ------------------
 5975|     24|        return false;
 5976|     24|      }
 5977|  93.9k|      i = i + digit * w;
 5978|  93.9k|      int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (5978:19): [True: 18.8k, False: 75.0k]
  |  Branch (5978:38): [True: 70.7k, False: 4.27k]
  ------------------
 5979|  93.9k|      if (digit < t) {
  ------------------
  |  Branch (5979:11): [True: 69.0k, False: 24.8k]
  ------------------
 5980|  69.0k|        break;
 5981|  69.0k|      }
 5982|  24.8k|      if (w > 0x7fffffff / (base - t)) {
  ------------------
  |  Branch (5982:11): [True: 0, False: 24.8k]
  ------------------
 5983|      0|        return false;
 5984|      0|      }
 5985|  24.8k|      w = w * (base - t);
 5986|  24.8k|    }
 5987|  69.0k|    bias = adapt(i - oldi, written_out + 1, oldi == 0);
 5988|  69.0k|    if (i / (written_out + 1) > int32_t(0x7fffffff - n)) {
  ------------------
  |  Branch (5988:9): [True: 69, False: 68.9k]
  ------------------
 5989|     69|      return false;
 5990|     69|    }
 5991|  68.9k|    n = n + i / (written_out + 1);
 5992|  68.9k|    i = i % (written_out + 1);
 5993|  68.9k|    if (n < 0x80) {
  ------------------
  |  Branch (5993:9): [True: 0, False: 68.9k]
  ------------------
 5994|      0|      return false;
 5995|      0|    }
 5996|  68.9k|    out.insert(out.begin() + i, n);
 5997|  68.9k|    written_out++;
 5998|  68.9k|    ++i;
 5999|  68.9k|  }
 6000|       |  // See https://github.com/whatwg/url/issues/803
 6001|       |  // Reject labels whose decoded form begins with "xn--" (double-encoded ACE).
 6002|  7.23k|  if (out.size() >= 4 && out[0] == U'x' && out[1] == U'n' && out[2] == U'-' &&
  ------------------
  |  Branch (6002:7): [True: 2.45k, False: 4.78k]
  |  Branch (6002:26): [True: 489, False: 1.96k]
  |  Branch (6002:44): [True: 306, False: 183]
  |  Branch (6002:62): [True: 150, False: 156]
  ------------------
 6003|    150|      out[3] == U'-') {
  ------------------
  |  Branch (6003:7): [True: 12, False: 138]
  ------------------
 6004|     12|    return false;
 6005|     12|  }
 6006|  7.22k|  return true;
 6007|  7.23k|}
_ZN3ada4idna17utf32_to_punycodeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIcNS3_IcEENS1_9allocatorIcEEEE:
 6087|  34.7k|bool utf32_to_punycode(std::u32string_view input, std::string &out) {
 6088|  34.7k|  out.reserve(input.size() + out.size());
 6089|  34.7k|  uint32_t n = initial_n;
 6090|  34.7k|  int32_t d = 0;
 6091|  34.7k|  int32_t bias = initial_bias;
 6092|  34.7k|  size_t h = 0;
 6093|       |  // first push the ascii content
 6094|   157k|  for (uint32_t c : input) {
  ------------------
  |  Branch (6094:19): [True: 157k, False: 34.7k]
  ------------------
 6095|   157k|    if (c < 0x80) {
  ------------------
  |  Branch (6095:9): [True: 18.5k, False: 139k]
  ------------------
 6096|  18.5k|      ++h;
 6097|  18.5k|      out.push_back(char(c));
 6098|  18.5k|    }
 6099|   157k|    if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) {
  ------------------
  |  Branch (6099:9): [True: 0, False: 157k]
  |  Branch (6099:26): [True: 1.55k, False: 156k]
  |  Branch (6099:41): [True: 0, False: 1.55k]
  ------------------
 6100|      0|      return false;
 6101|      0|    }
 6102|   157k|  }
 6103|  34.7k|  size_t b = h;
 6104|  34.7k|  if (b > 0) {
  ------------------
  |  Branch (6104:7): [True: 4.90k, False: 29.8k]
  ------------------
 6105|  4.90k|    out.push_back('-');
 6106|  4.90k|  }
 6107|   154k|  while (h < input.size()) {
  ------------------
  |  Branch (6107:10): [True: 119k, False: 34.7k]
  ------------------
 6108|   119k|    uint32_t m = 0x10FFFF;
 6109|   657k|    for (auto code_point : input) {
  ------------------
  |  Branch (6109:26): [True: 657k, False: 119k]
  ------------------
 6110|   657k|      if (code_point >= n && code_point < m) m = code_point;
  ------------------
  |  Branch (6110:11): [True: 386k, False: 271k]
  |  Branch (6110:30): [True: 206k, False: 179k]
  ------------------
 6111|   657k|    }
 6112|       |
 6113|   119k|    if ((m - n) > (0x7fffffff - d) / (h + 1)) {
  ------------------
  |  Branch (6113:9): [True: 0, False: 119k]
  ------------------
 6114|      0|      return false;
 6115|      0|    }
 6116|   119k|    d = d + int32_t((m - n) * (h + 1));
 6117|   119k|    n = m;
 6118|   657k|    for (auto c : input) {
  ------------------
  |  Branch (6118:17): [True: 657k, False: 119k]
  ------------------
 6119|   657k|      if (c < n) {
  ------------------
  |  Branch (6119:11): [True: 271k, False: 386k]
  ------------------
 6120|   271k|        if (d == 0x7fffffff) {
  ------------------
  |  Branch (6120:13): [True: 0, False: 271k]
  ------------------
 6121|      0|          return false;
 6122|      0|        }
 6123|   271k|        ++d;
 6124|   271k|      }
 6125|   657k|      if (c == n) {
  ------------------
  |  Branch (6125:11): [True: 139k, False: 517k]
  ------------------
 6126|   139k|        int32_t q = d;
 6127|   414k|        for (int32_t k = base;; k += base) {
 6128|   414k|          int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (6128:23): [True: 126k, False: 288k]
  |  Branch (6128:42): [True: 207k, False: 81.0k]
  ------------------
 6129|       |
 6130|   414k|          if (q < t) {
  ------------------
  |  Branch (6130:15): [True: 139k, False: 275k]
  ------------------
 6131|   139k|            break;
 6132|   139k|          }
 6133|   275k|          out.push_back(digit_to_char(t + ((q - t) % (base - t))));
 6134|   275k|          q = (q - t) / (base - t);
 6135|   275k|        }
 6136|   139k|        out.push_back(digit_to_char(q));
 6137|   139k|        bias = adapt(d, int32_t(h + 1), h == b);
 6138|   139k|        d = 0;
 6139|   139k|        ++h;
 6140|   139k|      }
 6141|   657k|    }
 6142|   119k|    ++d;
 6143|   119k|    ++n;
 6144|   119k|  }
 6145|  34.7k|  return true;
 6146|  34.7k|}
_ZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6292|  42.3k|bool is_label_valid(const std::u32string_view label) {
 6293|  42.3k|  if (label.empty()) {
  ------------------
  |  Branch (6293:7): [True: 0, False: 42.3k]
  ------------------
 6294|      0|    return true;
 6295|      0|  }
 6296|  42.3k|  if (!ensure_tables() || combining_ranges == nullptr || dir_start == nullptr ||
  ------------------
  |  Branch (6296:7): [True: 0, False: 42.3k]
  |  Branch (6296:27): [True: 0, False: 42.3k]
  |  Branch (6296:58): [True: 0, False: 42.3k]
  ------------------
 6297|  42.3k|      dir_final == nullptr || dir_value == nullptr ||
  ------------------
  |  Branch (6297:7): [True: 0, False: 42.3k]
  |  Branch (6297:31): [True: 0, False: 42.3k]
  ------------------
 6298|  42.3k|      joining_start == nullptr || joining_final == nullptr ||
  ------------------
  |  Branch (6298:7): [True: 0, False: 42.3k]
  |  Branch (6298:35): [True: 0, False: 42.3k]
  ------------------
 6299|  42.3k|      joining_value == nullptr) {
  ------------------
  |  Branch (6299:7): [True: 0, False: 42.3k]
  ------------------
 6300|      0|    return false;
 6301|      0|  }
 6302|       |
 6303|       |  ///////////////
 6304|       |  // We have a normalization step which ensures that we are in NFC.
 6305|       |  // If we receive punycode, we normalize and check that the normalized
 6306|       |  // version matches the original.
 6307|       |  // --------------------------------------
 6308|       |  // The label must be in Unicode Normalization Form NFC.
 6309|       |
 6310|       |  // Current URL standard indicatest that CheckHyphens is set to false.
 6311|       |  // ---------------------------------------
 6312|       |  // If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character
 6313|       |  // in both the third and fourth positions. If CheckHyphens, the label must
 6314|       |  // neither begin nor end with a U+002D HYPHEN-MINUS character.
 6315|       |
 6316|       |  // This is not necessary because we segment the
 6317|       |  // labels by '.'.
 6318|       |  // ---------------------------------------
 6319|       |  // The label must not contain a U+002E ( . ) FULL STOP.
 6320|       |  // if (label.find('.') != std::string_view::npos) return false;
 6321|       |
 6322|       |  // The label must not begin with a combining mark.
 6323|       |  // Range membership via lower_bound on range end.
 6324|  42.3k|  const std::span<const uint32_t[2]> comb_span{combining_ranges,
 6325|  42.3k|                                               combining_range_count};
 6326|  42.3k|  const auto comb_it = std::ranges::lower_bound(
 6327|  42.3k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
 6328|  42.3k|  if (comb_it != comb_span.end() && label.front() >= (*comb_it)[0]) {
  ------------------
  |  Branch (6328:7): [True: 42.3k, False: 0]
  |  Branch (6328:7): [True: 303, False: 42.0k]
  |  Branch (6328:37): [True: 303, False: 42.0k]
  ------------------
 6329|    303|    return false;
 6330|    303|  }
 6331|       |  // We verify this next step as part of the mapping:
 6332|       |  // ---------------------------------------------
 6333|       |  // Each code point in the label must only have certain status values
 6334|       |  // according to Section 5, IDNA Mapping Table:
 6335|       |  // - For Transitional Processing, each value must be valid.
 6336|       |  // - For Nontransitional Processing, each value must be either valid or
 6337|       |  // deviation.
 6338|       |
 6339|       |  // If CheckJoiners, the label must satisfy the ContextJ rules from Appendix
 6340|       |  // A, in The Unicode Code Points and Internationalized Domain Names for
 6341|       |  // Applications (IDNA) [IDNA2008].
 6342|  42.0k|  if (!is_contextj_valid(label)) {
  ------------------
  |  Branch (6342:7): [True: 468, False: 41.5k]
  ------------------
 6343|    468|    return false;
 6344|    468|  }
 6345|       |
 6346|       |  // If CheckBidi, and if the domain name is a  Bidi domain name, then the label
 6347|       |  // must satisfy all six of the numbered conditions in [IDNA2008] RFC 5893,
 6348|       |  // Section 2.
 6349|       |
 6350|       |  // The following rule, consisting of six conditions, applies to labels
 6351|       |  // in Bidi domain names.  The requirements that this rule satisfies are
 6352|       |  // described in Section 3.  All of the conditions must be satisfied for
 6353|       |  // the rule to be satisfied.
 6354|       |  //
 6355|       |  //  1.  The first character must be a character with Bidi property L, R,
 6356|       |  //     or AL.  If it has the R or AL property, it is an RTL label; if it
 6357|       |  //     has the L property, it is an LTR label.
 6358|       |  //
 6359|       |  //  2.  In an RTL label, only characters with the Bidi properties R, AL,
 6360|       |  //      AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
 6361|       |  //
 6362|       |  //   3.  In an RTL label, the end of the label must be a character with
 6363|       |  //       Bidi property R, AL, EN, or AN, followed by zero or more
 6364|       |  //       characters with Bidi property NSM.
 6365|       |  //
 6366|       |  //   4.  In an RTL label, if an EN is present, no AN may be present, and
 6367|       |  //       vice versa.
 6368|       |  //
 6369|       |  //  5.  In an LTR label, only characters with the Bidi properties L, EN,
 6370|       |  //       ES, CS, ET, ON, BN, or NSM are allowed.
 6371|       |  //
 6372|       |  //   6.  In an LTR label, the end of the label must be a character with
 6373|       |  //       Bidi property L or EN, followed by zero or more characters with
 6374|       |  //       Bidi property NSM.
 6375|       |
 6376|  41.5k|  size_t last_non_nsm_char = find_last_not_of_nsm(label);
 6377|  41.5k|  if (last_non_nsm_char == std::u32string_view::npos) {
  ------------------
  |  Branch (6377:7): [True: 0, False: 41.5k]
  ------------------
 6378|      0|    return false;
 6379|      0|  }
 6380|       |
 6381|       |  // A "Bidi domain name" is a domain name that contains at least one RTL label.
 6382|       |  // The following rule, consisting of six conditions, applies to labels in Bidi
 6383|       |  // domain names.
 6384|  41.5k|  if (is_rtl_label(label)) {
  ------------------
  |  Branch (6384:7): [True: 6.83k, False: 34.7k]
  ------------------
 6385|       |    // The first character must be a character with Bidi property L, R,
 6386|       |    // or AL. If it has the R or AL property, it is an RTL label; if it
 6387|       |    // has the L property, it is an LTR label.
 6388|       |
 6389|  6.83k|    if (find_direction(label[0]) == direction::L) {
  ------------------
  |  Branch (6389:9): [True: 468, False: 6.36k]
  ------------------
 6390|       |      // Eval as LTR
 6391|       |
 6392|       |      // In an LTR label, only characters with the Bidi properties L, EN,
 6393|       |      // ES, CS, ET, ON, BN, or NSM are allowed.
 6394|  10.3k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6394:26): [True: 10.3k, False: 0]
  ------------------
 6395|  10.3k|        const direction d = find_direction(label[i]);
 6396|  10.3k|        if (!(d == direction::L || d == direction::EN || d == direction::ES ||
  ------------------
  |  Branch (6396:15): [True: 2.46k, False: 7.90k]
  |  Branch (6396:36): [True: 1.08k, False: 6.82k]
  |  Branch (6396:58): [True: 1.18k, False: 5.63k]
  ------------------
 6397|  5.63k|              d == direction::CS || d == direction::ET || d == direction::ON ||
  ------------------
  |  Branch (6397:15): [True: 1.05k, False: 4.58k]
  |  Branch (6397:37): [True: 975, False: 3.60k]
  |  Branch (6397:59): [True: 1.13k, False: 2.47k]
  ------------------
 6398|  2.47k|              d == direction::BN || d == direction::NSM)) {
  ------------------
  |  Branch (6398:15): [True: 999, False: 1.47k]
  |  Branch (6398:37): [True: 1.00k, False: 468]
  ------------------
 6399|    468|          return false;
 6400|    468|        }
 6401|  10.3k|      }
 6402|       |
 6403|      0|      const direction last_dir = find_direction(label[last_non_nsm_char]);
 6404|      0|      if (!(last_dir == direction::L || last_dir == direction::EN)) {
  ------------------
  |  Branch (6404:13): [True: 0, False: 0]
  |  Branch (6404:41): [True: 0, False: 0]
  ------------------
 6405|      0|        return false;
 6406|      0|      }
 6407|       |
 6408|      0|      return true;
 6409|       |
 6410|  6.36k|    } else {
 6411|       |      // Eval as RTL
 6412|       |
 6413|       |      // The first character must be R or AL; a leading AN (or any other
 6414|       |      // direction) does not start a valid RTL label.
 6415|  6.36k|      const direction first_dir = find_direction(label[0]);
 6416|  6.36k|      if (first_dir != direction::R && first_dir != direction::AL) {
  ------------------
  |  Branch (6416:11): [True: 5.02k, False: 1.33k]
  |  Branch (6416:40): [True: 81, False: 4.94k]
  ------------------
 6417|     81|        return false;
 6418|     81|      }
 6419|       |
 6420|  6.28k|      bool has_an = false;
 6421|  6.28k|      bool has_en = false;
 6422|  32.8k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6422:26): [True: 27.0k, False: 5.79k]
  ------------------
 6423|  27.0k|        const direction d = find_direction(label[i]);
 6424|       |
 6425|       |        // In an RTL label, if an EN is present, no AN may be present, and vice
 6426|       |        // versa.
 6427|  27.0k|        if ((d == direction::EN && ((has_en = true) && has_an)) ||
  ------------------
  |  Branch (6427:14): [True: 2.03k, False: 25.0k]
  |  Branch (6427:37): [True: 2.03k, False: 0]
  |  Branch (6427:56): [True: 9, False: 2.02k]
  ------------------
 6428|  27.0k|            (d == direction::AN && ((has_an = true) && has_en))) {
  ------------------
  |  Branch (6428:14): [True: 1.46k, False: 25.5k]
  |  Branch (6428:37): [True: 1.46k, False: 0]
  |  Branch (6428:56): [True: 9, False: 1.45k]
  ------------------
 6429|     18|          return false;
 6430|     18|        }
 6431|       |
 6432|  27.0k|        if (!(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6432:15): [True: 2.51k, False: 24.5k]
  |  Branch (6432:36): [True: 12.2k, False: 12.2k]
  |  Branch (6432:58): [True: 1.45k, False: 10.8k]
  ------------------
 6433|  10.8k|              d == direction::EN || d == direction::ES || d == direction::CS ||
  ------------------
  |  Branch (6433:15): [True: 2.02k, False: 8.80k]
  |  Branch (6433:37): [True: 987, False: 7.82k]
  |  Branch (6433:59): [True: 1.02k, False: 6.80k]
  ------------------
 6434|  6.80k|              d == direction::ET || d == direction::ON || d == direction::BN ||
  ------------------
  |  Branch (6434:15): [True: 1.04k, False: 5.75k]
  |  Branch (6434:37): [True: 1.26k, False: 4.49k]
  |  Branch (6434:59): [True: 2.51k, False: 1.98k]
  ------------------
 6435|  1.98k|              d == direction::NSM)) {
  ------------------
  |  Branch (6435:15): [True: 1.73k, False: 249]
  ------------------
 6436|    249|          return false;
 6437|    249|        }
 6438|       |
 6439|  26.7k|        if (i == last_non_nsm_char &&
  ------------------
  |  Branch (6439:13): [True: 6.01k, False: 20.7k]
  ------------------
 6440|  6.01k|            !(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6440:15): [True: 753, False: 5.26k]
  |  Branch (6440:36): [True: 3.99k, False: 1.26k]
  |  Branch (6440:58): [True: 504, False: 762]
  ------------------
 6441|    762|              d == direction::EN)) {
  ------------------
  |  Branch (6441:15): [True: 537, False: 225]
  ------------------
 6442|    225|          return false;
 6443|    225|        }
 6444|  26.7k|      }
 6445|       |
 6446|  5.79k|      return true;
 6447|  6.28k|    }
 6448|  6.83k|  }
 6449|       |
 6450|  34.7k|  return true;
 6451|  41.5k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6610|  20.0k|[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out) {
 6611|  20.0k|  out.clear();
 6612|       |  // The ASCII path is a single same-size copy, so it needs no length bound.
 6613|       |  // Checking first would reject the (longer) ASCII output of a non-ASCII input
 6614|       |  // that was accepted, breaking to_ascii(to_ascii(x)) == to_ascii(x).
 6615|  20.0k|  if (is_ascii(ut8_string)) {
  ------------------
  |  Branch (6615:7): [True: 6.48k, False: 13.6k]
  ------------------
 6616|  6.48k|    from_ascii_to_ascii(ut8_string, out);
 6617|  6.48k|    return true;
 6618|  6.48k|  }
 6619|  13.6k|  if (ut8_string.size() > max_domain_input_bytes) {
  ------------------
  |  Branch (6619:7): [True: 0, False: 13.6k]
  ------------------
 6620|      0|    return false;
 6621|      0|  }
 6622|       |
 6623|       |#ifdef ADA_USE_SIMDUTF
 6624|       |  size_t utf32_length =
 6625|       |      simdutf::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 6626|       |  if (utf32_length == 0 && !ut8_string.empty()) {
 6627|       |    return false;
 6628|       |  }
 6629|       |  std::u32string working(utf32_length, U'\0');
 6630|       |  size_t actual_utf32_length = simdutf::convert_utf8_to_utf32(
 6631|       |      ut8_string.data(), ut8_string.size(), working.data());
 6632|       |#else
 6633|  13.6k|  size_t utf32_length =
 6634|  13.6k|      ada::idna::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 6635|  13.6k|  if (utf32_length == 0 && !ut8_string.empty()) {
  ------------------
  |  Branch (6635:7): [True: 144, False: 13.4k]
  |  Branch (6635:28): [True: 144, False: 0]
  ------------------
 6636|    144|    return false;
 6637|    144|  }
 6638|  13.4k|  std::u32string working(utf32_length, U'\0');
 6639|  13.4k|  size_t actual_utf32_length = ada::idna::utf8_to_utf32(
 6640|  13.4k|      ut8_string.data(), ut8_string.size(), working.data());
 6641|  13.4k|#endif
 6642|  13.4k|  if (actual_utf32_length == 0 || actual_utf32_length != utf32_length) {
  ------------------
  |  Branch (6642:7): [True: 1.03k, False: 12.4k]
  |  Branch (6642:35): [True: 0, False: 12.4k]
  ------------------
 6643|  1.03k|    return false;
 6644|  1.03k|  }
 6645|  12.4k|  working.resize(actual_utf32_length);
 6646|       |
 6647|       |  // Map into a second buffer with exact sizing (no growth reallocs).
 6648|  12.4k|  std::u32string mapped;
 6649|  12.4k|  if (!ada::idna::map(working, mapped)) {
  ------------------
  |  Branch (6649:7): [True: 129, False: 12.3k]
  ------------------
 6650|    129|    return false;
 6651|    129|  }
 6652|       |  // Drop UTF-32 input; reuse `working` as scratch for ACE validation below.
 6653|  12.3k|  working.clear();
 6654|       |
 6655|       |  // Skip NFC when already normalized (ASCII is a fast subset of this check).
 6656|  12.3k|  if (!is_ascii(mapped) && !is_already_nfc(mapped)) {
  ------------------
  |  Branch (6656:7): [True: 9.41k, False: 2.89k]
  |  Branch (6656:28): [True: 1.63k, False: 7.77k]
  ------------------
 6657|  1.63k|    if (!normalize(mapped)) {
  ------------------
  |  Branch (6657:9): [True: 0, False: 1.63k]
  ------------------
 6658|      0|      return false;
 6659|      0|    }
 6660|  1.63k|  }
 6661|       |
 6662|       |  // Estimate ASCII output size (punycode may expand non-ASCII labels).
 6663|  12.3k|  out.reserve(mapped.size() + 8);
 6664|       |
 6665|       |  // Walk labels with a single pointer scan (no repeated string::find).
 6666|  12.3k|  const char32_t* p = mapped.data();
 6667|  12.3k|  const char32_t* const end = p + mapped.size();
 6668|  12.3k|  std::u32string post_map;
 6669|       |
 6670|  61.4k|  while (p < end) {
  ------------------
  |  Branch (6670:10): [True: 52.7k, False: 8.71k]
  ------------------
 6671|  52.7k|    const char32_t* label_begin = p;
 6672|   463k|    while (p < end && *p != U'.') {
  ------------------
  |  Branch (6672:12): [True: 451k, False: 11.6k]
  |  Branch (6672:23): [True: 410k, False: 41.0k]
  ------------------
 6673|   410k|      ++p;
 6674|   410k|    }
 6675|  52.7k|    const size_t label_size = static_cast<size_t>(p - label_begin);
 6676|  52.7k|    std::u32string_view label_view(label_begin, label_size);
 6677|  52.7k|    const bool is_last_label = (p == end);
 6678|  52.7k|    if (p < end) {
  ------------------
  |  Branch (6678:9): [True: 41.0k, False: 11.6k]
  ------------------
 6679|  41.0k|      ++p;  // skip dot
 6680|  41.0k|    }
 6681|       |
 6682|  52.7k|    if (label_size == 0) {
  ------------------
  |  Branch (6682:9): [True: 3.13k, False: 49.6k]
  ------------------
 6683|       |      // empty label
 6684|  49.6k|    } else if (is_ace_prefix(label_view)) {
  ------------------
  |  Branch (6684:16): [True: 7.63k, False: 41.9k]
  ------------------
 6685|   140k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (6685:23): [True: 140k, False: 7.58k]
  ------------------
 6686|   140k|        if (c >= 0x80) {
  ------------------
  |  Branch (6686:13): [True: 48, False: 140k]
  ------------------
 6687|     48|          out.clear();
 6688|     48|          return false;
 6689|     48|        }
 6690|   140k|      }
 6691|  7.58k|      append_ascii_label(out, label_view);
 6692|  7.58k|      std::string_view puny_segment_ascii(
 6693|  7.58k|          out.data() + (out.size() - label_size) + 4, label_size - 4);
 6694|  7.58k|      working.clear();
 6695|  7.58k|      if (!ada::idna::punycode_to_utf32(puny_segment_ascii, working)) {
  ------------------
  |  Branch (6695:11): [True: 366, False: 7.22k]
  ------------------
 6696|    366|        out.clear();
 6697|    366|        return false;
 6698|    366|      }
 6699|  7.22k|      if (is_ascii(working)) {
  ------------------
  |  Branch (6699:11): [True: 246, False: 6.97k]
  ------------------
 6700|    246|        out.clear();
 6701|    246|        return false;
 6702|    246|      }
 6703|  6.97k|      if (!ada::idna::map(working, post_map) || working != post_map) {
  ------------------
  |  Branch (6703:11): [True: 414, False: 6.56k]
  |  Branch (6703:49): [True: 282, False: 6.27k]
  ------------------
 6704|    696|        out.clear();
 6705|    696|        return false;
 6706|    696|      }
 6707|       |      // Mapping must be stable; NFC must not change the mapped form either.
 6708|  6.27k|      if (!is_ascii(post_map) && !is_already_nfc(post_map)) {
  ------------------
  |  Branch (6708:11): [True: 6.27k, False: 0]
  |  Branch (6708:34): [True: 3.74k, False: 2.53k]
  ------------------
 6709|  3.74k|        if (!normalize(post_map) || post_map != working) {
  ------------------
  |  Branch (6709:13): [True: 0, False: 3.74k]
  |  Branch (6709:37): [True: 426, False: 3.31k]
  ------------------
 6710|    426|          out.clear();
 6711|    426|          return false;
 6712|    426|        }
 6713|  3.74k|      }
 6714|  5.85k|      if (post_map.empty() || !is_label_valid(post_map)) {
  ------------------
  |  Branch (6714:11): [True: 0, False: 5.85k]
  |  Branch (6714:31): [True: 111, False: 5.74k]
  ------------------
 6715|    111|        out.clear();
 6716|    111|        return false;
 6717|    111|      }
 6718|  41.9k|    } else if (is_ascii(label_view)) {
  ------------------
  |  Branch (6718:16): [True: 5.49k, False: 36.4k]
  ------------------
 6719|  5.49k|      append_ascii_label(out, label_view);
 6720|  36.4k|    } else {
 6721|  36.4k|      if (!is_label_valid(label_view)) {
  ------------------
  |  Branch (6721:11): [True: 1.70k, False: 34.7k]
  ------------------
 6722|  1.70k|        out.clear();
 6723|  1.70k|        return false;
 6724|  1.70k|      }
 6725|  34.7k|      out.append("xn--");
 6726|  34.7k|      if (!ada::idna::utf32_to_punycode(label_view, out)) {
  ------------------
  |  Branch (6726:11): [True: 0, False: 34.7k]
  ------------------
 6727|      0|        out.clear();
 6728|      0|        return false;
 6729|      0|      }
 6730|  34.7k|    }
 6731|  49.1k|    if (!is_last_label) {
  ------------------
  |  Branch (6731:9): [True: 40.9k, False: 8.18k]
  ------------------
 6732|  40.9k|      out.push_back('.');
 6733|  40.9k|    }
 6734|  49.1k|  }
 6735|  8.71k|  return true;
 6736|  12.3k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6738|  20.0k|std::string to_ascii(std::string_view ut8_string) {
 6739|  20.0k|  std::string out;
 6740|  20.0k|  if (!to_ascii(ut8_string, out)) {
  ------------------
  |  Branch (6740:7): [True: 4.90k, False: 15.1k]
  ------------------
 6741|  4.90k|    return {};
 6742|  4.90k|  }
 6743|  15.1k|  return out;
 6744|  20.0k|}
_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7618|  2.76k|                    std::string& out) {
 7619|  2.76k|  ada_log("percent_encode ", input, " to output string while ",
 7620|  2.76k|          append ? "appending" : "overwriting");
 7621|  2.76k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7622|  2.76k|    return character_sets::bit_at(character_set, c);
 7623|  2.76k|  });
 7624|  2.76k|  ada_log("percent_encode done checking, moved to ",
 7625|  2.76k|          std::distance(input.begin(), pointer));
 7626|       |
 7627|       |  // Optimization: Don't iterate if percent encode is not required
 7628|  2.76k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7628:7): [True: 1.73k, False: 1.03k]
  ------------------
 7629|  1.73k|    ada_log("percent_encode encoding not needed.");
 7630|  1.73k|    return false;
 7631|  1.73k|  }
 7632|       |  if constexpr (!append) {
 7633|       |    out.clear();
 7634|       |  }
 7635|  1.03k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7636|  1.03k|          " bytes");
 7637|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7638|  1.03k|  out.append(input.data(), std::distance(input.begin(), pointer));
 7639|  1.03k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7640|  1.03k|          " bytes");
 7641|  24.1k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7641:10): [True: 23.0k, False: 1.03k]
  ------------------
 7642|  23.0k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7642:9): [True: 19.7k, False: 3.38k]
  ------------------
 7643|  19.7k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7644|  19.7k|    } else {
 7645|  3.38k|      out += *pointer;
 7646|  3.38k|    }
 7647|  23.0k|  }
 7648|  1.03k|  return true;
 7649|  2.76k|}
_ZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7618|  32.6k|                    std::string& out) {
 7619|  32.6k|  ada_log("percent_encode ", input, " to output string while ",
 7620|  32.6k|          append ? "appending" : "overwriting");
 7621|  32.6k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7622|  32.6k|    return character_sets::bit_at(character_set, c);
 7623|  32.6k|  });
 7624|  32.6k|  ada_log("percent_encode done checking, moved to ",
 7625|  32.6k|          std::distance(input.begin(), pointer));
 7626|       |
 7627|       |  // Optimization: Don't iterate if percent encode is not required
 7628|  32.6k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7628:7): [True: 20.8k, False: 11.7k]
  ------------------
 7629|  20.8k|    ada_log("percent_encode encoding not needed.");
 7630|  20.8k|    return false;
 7631|  20.8k|  }
 7632|  11.7k|  if constexpr (!append) {
 7633|  11.7k|    out.clear();
 7634|  11.7k|  }
 7635|  11.7k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7636|  11.7k|          " bytes");
 7637|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7638|  11.7k|  out.append(input.data(), std::distance(input.begin(), pointer));
 7639|  11.7k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7640|  11.7k|          " bytes");
 7641|   110k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7641:10): [True: 98.6k, False: 11.7k]
  ------------------
 7642|  98.6k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7642:9): [True: 91.4k, False: 7.17k]
  ------------------
 7643|  91.4k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7644|  91.4k|    } else {
 7645|  7.17k|      out += *pointer;
 7646|  7.17k|    }
 7647|  98.6k|  }
 7648|  11.7k|  return true;
 7649|  32.6k|}
_ZN3ada7unicode14percent_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 7449|  4.30k|std::string percent_decode(const std::string_view input, size_t first_percent) {
 7450|       |  // next line is for safety only, we expect users to avoid calling
 7451|       |  // percent_decode when first_percent is outside the range.
 7452|  4.30k|  if (first_percent == std::string_view::npos) {
  ------------------
  |  Branch (7452:7): [True: 0, False: 4.30k]
  ------------------
 7453|      0|    return std::string(input);
 7454|      0|  }
 7455|       |
 7456|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7457|  4.30k|  const char* const src = input.data();
 7458|  4.30k|  const char* const end = src + input.size();
 7459|       |
 7460|       |  // Decoding never grows the string, so a single pre-sized buffer written via
 7461|       |  // bulk memcpy of the plain runs (then shrunk to the final length) avoids the
 7462|       |  // byte-at-a-time appends of the naive version.
 7463|  4.30k|  std::string out(input.size(), '\0');
 7464|  4.30k|  char* d = out.data();
 7465|  4.30k|  char* const d0 = d;
 7466|       |
 7467|  4.30k|  std::memcpy(d, src, first_percent);
 7468|  4.30k|  d += first_percent;
 7469|       |
 7470|  4.30k|  const char* p = src + first_percent;
 7471|  23.5k|  while (p < end) {
  ------------------
  |  Branch (7471:10): [True: 19.2k, False: 4.30k]
  ------------------
 7472|  19.2k|    if (*p == '%') {
  ------------------
  |  Branch (7472:9): [True: 13.4k, False: 5.84k]
  ------------------
 7473|       |      // Decode runs of valid %XX tightly (common for nested/encoded URLs).
 7474|  19.0k|      while (p + 2 < end && *p == '%') {
  ------------------
  |  Branch (7474:14): [True: 16.2k, False: 2.75k]
  |  Branch (7474:29): [True: 14.6k, False: 1.58k]
  ------------------
 7475|  14.6k|        if (!is_ascii_hex_digit(p[1]) || !is_ascii_hex_digit(p[2])) {
  ------------------
  |  Branch (7475:13): [True: 6.45k, False: 8.22k]
  |  Branch (7475:42): [True: 2.64k, False: 5.58k]
  ------------------
 7476|  9.09k|          break;
 7477|  9.09k|        }
 7478|  5.58k|        *d++ = static_cast<char>(convert_hex_to_binary(p[1]) * 16 +
 7479|  5.58k|                                 convert_hex_to_binary(p[2]));
 7480|  5.58k|        p += 3;
 7481|  5.58k|      }
 7482|  13.4k|      if (p < end && *p == '%') {
  ------------------
  |  Branch (7482:11): [True: 12.2k, False: 1.14k]
  |  Branch (7482:22): [True: 10.0k, False: 2.21k]
  ------------------
 7483|       |        // Not a valid escape (too few chars left or bad hex): copy '%'
 7484|       |        // literally and keep scanning after it.
 7485|  10.0k|        *d++ = *p++;
 7486|  10.0k|      }
 7487|  13.4k|    } else {
 7488|  5.84k|      const char* q = static_cast<const char*>(
 7489|  5.84k|          std::memchr(p, '%', static_cast<size_t>(end - p)));
 7490|  5.84k|      const char* run_end = q ? q : end;
  ------------------
  |  Branch (7490:29): [True: 3.30k, False: 2.54k]
  ------------------
 7491|  5.84k|      const size_t n = static_cast<size_t>(run_end - p);
 7492|  5.84k|      std::memcpy(d, p, n);
 7493|  5.84k|      d += n;
 7494|  5.84k|      p = run_end;
 7495|  5.84k|    }
 7496|  19.2k|  }
 7497|       |
 7498|  4.30k|  out.resize(static_cast<size_t>(d - d0));
 7499|  4.30k|  return out;
 7500|  4.30k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7588|  24.8k|                           const uint8_t character_set[]) {
 7589|  24.8k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7590|  24.8k|    return character_sets::bit_at(character_set, c);
 7591|  24.8k|  });
 7592|       |  // Optimization: Don't iterate if percent encode is not required
 7593|  24.8k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7593:7): [True: 20.5k, False: 4.26k]
  ------------------
 7594|  20.5k|    return std::string(input);
 7595|  20.5k|  }
 7596|       |
 7597|  4.26k|  std::string result;
 7598|  4.26k|  result.reserve(input.length());  // in the worst case, percent encoding might
 7599|       |                                   // produce 3 characters.
 7600|  4.26k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
 7601|  4.26k|  if (static_cast<size_t>(input.end() - pointer) >= 48) {
  ------------------
  |  Branch (7601:7): [True: 664, False: 3.59k]
  ------------------
 7602|    664|    percent_encode_suffix(&*pointer, input.data() + input.size(), character_set,
 7603|    664|                          result);
 7604|  3.59k|  } else {
 7605|  30.1k|    for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7605:12): [True: 26.5k, False: 3.59k]
  ------------------
 7606|  26.5k|      if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7606:11): [True: 22.8k, False: 3.73k]
  ------------------
 7607|  22.8k|        result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7608|  22.8k|      } else {
 7609|  3.73k|        result += *pointer;
 7610|  3.73k|      }
 7611|  26.5k|    }
 7612|  3.59k|  }
 7613|  4.26k|  return result;
 7614|  24.8k|}
_ZN3ada7unicode8to_asciiERNSt3__18optionalINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEEENS1_17basic_string_viewIcS5_EEm:
 7659|  20.0k|              size_t first_percent) {
 7660|  20.0k|  std::string percent_decoded_buffer;
 7661|  20.0k|  std::string_view input = plain;
 7662|  20.0k|  if (first_percent != std::string_view::npos) {
  ------------------
  |  Branch (7662:7): [True: 1.21k, False: 18.8k]
  ------------------
 7663|  1.21k|    percent_decoded_buffer = unicode::percent_decode(plain, first_percent);
 7664|  1.21k|    input = percent_decoded_buffer;
 7665|  1.21k|  }
 7666|       |  // input is a non-empty UTF-8 string, must be percent decoded
 7667|  20.0k|  std::string idna_ascii = ada::idna::to_ascii(input);
 7668|  20.0k|  if (idna_ascii.empty() || contains_forbidden_domain_code_point(
  ------------------
  |  Branch (7668:7): [True: 4.95k, False: 15.1k]
  |  Branch (7668:29): [True: 1.14k, False: 14.0k]
  ------------------
 7669|  15.1k|                                idna_ascii.data(), idna_ascii.size())) {
 7670|  6.09k|    return false;
 7671|  6.09k|  }
 7672|  14.0k|  out = std::move(idna_ascii);
 7673|  14.0k|  return true;
 7674|  20.0k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhm:
 7677|    514|                           const uint8_t character_set[], size_t index) {
 7678|    514|  std::string out;
 7679|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7680|    514|  out.append(input.data(), index);
 7681|    514|  auto pointer = input.begin() + index;
 7682|    514|  if (static_cast<size_t>(input.end() - pointer) >= 48) {
  ------------------
  |  Branch (7682:7): [True: 186, False: 328]
  ------------------
 7683|    186|    percent_encode_suffix(&*pointer, input.data() + input.size(), character_set,
 7684|    186|                          out);
 7685|    328|  } else {
 7686|  2.98k|    for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7686:12): [True: 2.65k, False: 328]
  ------------------
 7687|  2.65k|      if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7687:11): [True: 2.27k, False: 386]
  ------------------
 7688|  2.27k|        out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7689|  2.27k|      } else {
 7690|    386|        out += *pointer;
 7691|    386|      }
 7692|  2.65k|    }
 7693|    328|  }
 7694|    514|  return out;
 7695|    514|}
_ZN3ada7unicode21percent_encode_suffixEPKcS2_PKhRNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEE:
 7993|    850|                           const uint8_t character_set[], std::string& out) {
 7994|    850|#if ADA_UNICODE_SSSE3 || ADA_NEON || ADA_RVV
 7995|    850|#ifdef ADA_UNICODE_NEED_SSSE3_TARGET
 7996|    850|  static const bool ssse3_supported = __builtin_cpu_supports("ssse3") != 0;
 7997|    850|  const bool use_wide = ssse3_supported;
 7998|       |#else
 7999|       |  const bool use_wide = true;
 8000|       |#endif
 8001|    850|  if (use_wide && static_cast<size_t>(end - p) >= kPercentEncodeSimdMin) {
  ------------------
  |  Branch (8001:7): [True: 850, False: 0]
  |  Branch (8001:19): [True: 850, False: 0]
  ------------------
 8002|    850|    percent_encode_to_wide(p, end, character_set, out);
 8003|    850|    return;
 8004|    850|  }
 8005|      0|#endif
 8006|      0|  percent_encode_to_scalar(p, end, character_set, out);
 8007|      0|}
_ZN3ada11serializers36find_longest_sequence_of_ipv6_piecesERKNSt3__15arrayItLm8EEERmS6_:
 8072|    972|    size_t& compress_length) noexcept {
 8073|    972|  compress = 0;
 8074|    972|  compress_length = 0;
 8075|    972|  size_t i = 0;
 8076|  3.89k|  while (i < 8) {
  ------------------
  |  Branch (8076:10): [True: 2.91k, False: 972]
  ------------------
 8077|  2.91k|    if (address[i] != 0) {
  ------------------
  |  Branch (8077:9): [True: 1.74k, False: 1.17k]
  ------------------
 8078|  1.74k|      ++i;
 8079|  1.74k|      continue;
 8080|  1.74k|    }
 8081|  1.17k|    size_t next = i + 1;
 8082|  6.03k|    while (next < 8 && address[next] == 0) {
  ------------------
  |  Branch (8082:12): [True: 5.44k, False: 591]
  |  Branch (8082:24): [True: 4.85k, False: 585]
  ------------------
 8083|  4.85k|      ++next;
 8084|  4.85k|    }
 8085|  1.17k|    const size_t count = next - i;
 8086|  1.17k|    if (count > compress_length) {
  ------------------
  |  Branch (8086:9): [True: 1.00k, False: 171]
  ------------------
 8087|  1.00k|      compress_length = count;
 8088|  1.00k|      compress = i;
 8089|  1.00k|    }
 8090|  1.17k|    i = next;
 8091|  1.17k|  }
 8092|    972|}
_ZN3ada11serializers4ipv6ERKNSt3__15arrayItLm8EEE:
 8094|    972|std::string ipv6(const std::array<uint16_t, 8>& address) {
 8095|    972|  size_t compress = 0;
 8096|    972|  size_t compress_length = 0;
 8097|    972|  find_longest_sequence_of_ipv6_pieces(address, compress, compress_length);
 8098|       |
 8099|       |  // Skip compression when the longest zero run is a single piece.
 8100|    972|  if (compress_length <= 1) {
  ------------------
  |  Branch (8100:7): [True: 57, False: 915]
  ------------------
 8101|     57|    compress = compress_length = 8;
 8102|     57|  }
 8103|       |
 8104|       |  // Max: '[' + 8*4 hex + 7 ':' + ']' = 41
 8105|    972|  std::string output(4 * 8 + 7 + 2, '\0');
 8106|    972|  char* point = output.data();
 8107|    972|  *point++ = '[';
 8108|    972|  size_t piece_index = 0;
 8109|  2.53k|  while (true) {
  ------------------
  |  Branch (8109:10): [True: 2.53k, Folded]
  ------------------
 8110|  2.53k|    if (piece_index == compress) {
  ------------------
  |  Branch (8110:9): [True: 915, False: 1.62k]
  ------------------
 8111|    915|      *point++ = ':';
 8112|       |      // If we skip a value initially, we need to write '::'.
 8113|    915|      if (piece_index == 0) {
  ------------------
  |  Branch (8113:11): [True: 546, False: 369]
  ------------------
 8114|    546|        *point++ = ':';
 8115|    546|      }
 8116|    915|      piece_index += compress_length;
 8117|    915|      if (piece_index == 8) {
  ------------------
  |  Branch (8117:11): [True: 513, False: 402]
  ------------------
 8118|    513|        break;
 8119|    513|      }
 8120|    915|    }
 8121|  2.02k|    point = write_hex_u16(point, address[piece_index]);
 8122|  2.02k|    ++piece_index;
 8123|  2.02k|    if (piece_index == 8) {
  ------------------
  |  Branch (8123:9): [True: 459, False: 1.56k]
  ------------------
 8124|    459|      break;
 8125|    459|    }
 8126|  1.56k|    *point++ = ':';
 8127|  1.56k|  }
 8128|    972|  *point++ = ']';
 8129|    972|  output.resize(static_cast<size_t>(point - output.data()));
 8130|    972|  return output;
 8131|    972|}
_ZN3ada11serializers4ipv4Em:
 8133|  4.63k|std::string ipv4(const uint64_t address) {
 8134|  4.63k|  std::string output(15, '\0');
 8135|  4.63k|  char* point = output.data();
 8136|  4.63k|  point = write_u8(point, static_cast<uint8_t>(address >> 24));
 8137|  4.63k|  *point++ = '.';
 8138|  4.63k|  point = write_u8(point, static_cast<uint8_t>(address >> 16));
 8139|  4.63k|  *point++ = '.';
 8140|  4.63k|  point = write_u8(point, static_cast<uint8_t>(address >> 8));
 8141|  4.63k|  *point++ = '.';
 8142|  4.63k|  point = write_u8(point, static_cast<uint8_t>(address));
 8143|  4.63k|  output.resize(static_cast<size_t>(point - output.data()));
 8144|  4.63k|  return output;
 8145|  4.63k|}
_ZN3ada5parseINS_3urlEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 8549|  51.7k|    std::string_view input, const result_type* base_url) {
 8550|  51.7k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 8551|  51.7k|  if (!u.is_valid) {
  ------------------
  |  Branch (8551:7): [True: 26.4k, False: 25.3k]
  ------------------
 8552|  26.4k|    return tl::unexpected(errors::type_error);
 8553|  26.4k|  }
 8554|  25.3k|  return u;
 8555|  51.7k|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 8549|  51.7k|    std::string_view input, const result_type* base_url) {
 8550|  51.7k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 8551|  51.7k|  if (!u.is_valid) {
  ------------------
  |  Branch (8551:7): [True: 26.4k, False: 25.3k]
  ------------------
 8552|  26.4k|    return tl::unexpected(errors::type_error);
 8553|  26.4k|  }
 8554|  25.3k|  return u;
 8555|  51.7k|}
_ZN3ada20get_max_input_lengthEv:
 8170|   167k|uint32_t get_max_input_length() {
 8171|   167k|  return max_input_length_.load(std::memory_order_relaxed);
 8172|   167k|}
_ZN3ada9can_parseENSt3__117basic_string_viewIcNS0_11char_traitsIcEEEEPKS4_:
 8597|  47.1k|bool can_parse(std::string_view input, const std::string_view* base_input) {
 8598|       |  // Must match parse().has_value(), including post-normalization max length.
 8599|       |  // Percent-encoding expands a byte by at most 3x, but IDNA expands more: a
 8600|       |  // 3-byte UTF-8 label such as U+337F becomes the 17-byte "xn--6oqv20b1zgzxr",
 8601|       |  // so a dotted host sustains 4.5x. When the input (plus base, if any) fits in
 8602|       |  // max_length/5, the normalized href cannot exceed max_length, so
 8603|       |  // validation-only parsing (store_values=false) is safe.
 8604|       |
 8605|       |  // Hot path first: absolute special URLs, no base. Avoid loading max_length
 8606|       |  // until we need it (common absolute-fast true/false cases).
 8607|  47.1k|  if (base_input == nullptr) {
  ------------------
  |  Branch (8607:7): [True: 34.2k, False: 12.8k]
  ------------------
 8608|  34.2k|    auto r = try_can_parse_clean_http(input);
 8609|  34.2k|    if (!r.has_value()) {
  ------------------
  |  Branch (8609:9): [True: 34.0k, False: 198]
  ------------------
 8610|  34.0k|      r = try_can_parse_absolute_fast(input);
 8611|  34.0k|    }
 8612|  34.2k|    if (r.has_value()) {
  ------------------
  |  Branch (8612:9): [True: 19.9k, False: 14.3k]
  ------------------
 8613|  19.9k|      if (!*r) {
  ------------------
  |  Branch (8613:11): [True: 14.5k, False: 5.40k]
  ------------------
 8614|  14.5k|        return false;
 8615|  14.5k|      }
 8616|       |      // size <= max/5 => normalized href cannot exceed max (4.5x expansion).
 8617|       |      // Check this first: default max is ~4GB so almost all URLs return true.
 8618|  5.40k|      const uint32_t max_length = ada::get_max_input_length();
 8619|  5.40k|      if (input.size() <= static_cast<size_t>(max_length) / 5) {
  ------------------
  |  Branch (8619:11): [True: 5.40k, False: 0]
  ------------------
 8620|  5.40k|        return true;
 8621|  5.40k|      }
 8622|      0|      if (input.size() > max_length) {
  ------------------
  |  Branch (8622:11): [True: 0, False: 0]
  ------------------
 8623|      0|        return false;
 8624|      0|      }
 8625|      0|      return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8626|      0|                                                                    nullptr)
 8627|      0|          .is_valid;
 8628|      0|    }
 8629|  34.2k|  }
 8630|       |
 8631|  27.2k|  const uint32_t max_length = ada::get_max_input_length();
 8632|  27.2k|  if (input.size() > max_length) {
  ------------------
  |  Branch (8632:7): [True: 0, False: 27.2k]
  ------------------
 8633|      0|    return false;
 8634|      0|  }
 8635|  27.2k|  if (base_input != nullptr && base_input->size() > max_length) {
  ------------------
  |  Branch (8635:7): [True: 12.8k, False: 14.3k]
  |  Branch (8635:32): [True: 0, False: 12.8k]
  ------------------
 8636|      0|    return false;
 8637|      0|  }
 8638|       |
 8639|       |  // Relative resolution combines base + input; bound the sum so 4.5x expansion
 8640|       |  // of either side cannot push the final href past max_length.
 8641|  27.2k|  const size_t combined =
 8642|  27.2k|      input.size() + (base_input == nullptr ? 0 : base_input->size());
  ------------------
  |  Branch (8642:23): [True: 14.3k, False: 12.8k]
  ------------------
 8643|  27.2k|  const bool size_safe = combined <= static_cast<size_t>(max_length) / 5;
 8644|       |
 8645|  27.2k|  if (size_safe) {
  ------------------
  |  Branch (8645:7): [True: 27.2k, False: 0]
  ------------------
 8646|       |    // Validation-only: no buffer build, host still fully checked.
 8647|  27.2k|    ada::url_aggregator base_agg;
 8648|  27.2k|    ada::url_aggregator* base_ptr = nullptr;
 8649|  27.2k|    if (base_input != nullptr) {
  ------------------
  |  Branch (8649:9): [True: 12.8k, False: 14.3k]
  ------------------
 8650|  12.8k|      base_agg = ada::parser::parse_url_impl<ada::url_aggregator, false>(
 8651|  12.8k|          *base_input, nullptr);
 8652|  12.8k|      if (!base_agg.is_valid) {
  ------------------
  |  Branch (8652:11): [True: 8.29k, False: 4.57k]
  ------------------
 8653|  8.29k|        return false;
 8654|  8.29k|      }
 8655|  4.57k|      base_ptr = &base_agg;
 8656|  4.57k|    }
 8657|  18.9k|    return ada::parser::parse_url_impl<ada::url_aggregator, false>(input,
 8658|  18.9k|                                                                   base_ptr)
 8659|  18.9k|        .is_valid;
 8660|  27.2k|  }
 8661|       |
 8662|       |  // Near the limit: full parse so post-normalization length matches parse().
 8663|      0|  if (base_input == nullptr) {
  ------------------
  |  Branch (8663:7): [True: 0, False: 0]
  ------------------
 8664|      0|    return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8665|      0|                                                                  nullptr)
 8666|      0|        .is_valid;
 8667|      0|  }
 8668|      0|  ada::url_aggregator base_agg =
 8669|      0|      ada::parser::parse_url_impl<ada::url_aggregator, true>(*base_input,
 8670|      0|                                                             nullptr);
 8671|      0|  if (!base_agg.is_valid) {
  ------------------
  |  Branch (8671:7): [True: 0, False: 0]
  ------------------
 8672|      0|    return false;
 8673|      0|  }
 8674|      0|  return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8675|      0|                                                                &base_agg)
 8676|      0|      .is_valid;
 8677|      0|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9539|   123k|void trim_c0_whitespace(std::string_view& input) noexcept {
 9540|   126k|  while (!input.empty() &&
  ------------------
  |  Branch (9540:10): [True: 74.9k, False: 51.4k]
  ------------------
 9541|  74.9k|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (9541:10): [True: 2.33k, False: 72.5k]
  ------------------
 9542|  2.33k|    input.remove_prefix(1);
 9543|  2.33k|  }
 9544|   128k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (9544:10): [True: 77.1k, False: 51.4k]
  |  Branch (9544:28): [True: 4.55k, False: 72.5k]
  ------------------
 9545|  4.55k|    input.remove_suffix(1);
 9546|  4.55k|  }
 9547|   123k|}
_ZN3ada7helpers8overlapsENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 9714|     46|bool overlaps(std::string_view input1, const std::string& input2) noexcept {
 9715|     46|  ada_log("helpers::overlaps check if string_view '", input1, "' [",
 9716|     46|          input1.size(), " bytes] is part of string '", input2, "' [",
 9717|     46|          input2.size(), " bytes]");
 9718|     46|  return !input1.empty() && !input2.empty() && input1.data() >= input2.data() &&
  ------------------
  |  Branch (9718:10): [True: 46, False: 0]
  |  Branch (9718:29): [True: 46, False: 0]
  |  Branch (9718:48): [True: 17, False: 29]
  ------------------
 9719|     17|         input1.data() < input2.data() + input2.size();
  ------------------
  |  Branch (9719:10): [True: 0, False: 17]
  ------------------
 9720|     46|}
_ZN3ada3url17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10099|    615|bool url::parse_opaque_host(std::string_view input) {
10100|    615|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
10101|    615|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (10101:7): [True: 12, False: 603]
  ------------------
10102|     12|    return is_valid = false;
10103|     12|  }
10104|       |
10105|       |  // Return the result of running UTF-8 percent-encode on input using the C0
10106|       |  // control percent-encode set.
10107|    603|  host = ada::unicode::percent_encode(
10108|    603|      input, ada::character_sets::C0_CONTROL_PERCENT_ENCODE);
10109|    603|  return true;
10110|    615|}
_ZN3ada3url10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10112|  1.96k|bool url::parse_ipv4(std::string_view input) {
10113|  1.96k|  ada_log("parse_ipv4 ", input, " [", input.size(), " bytes]");
10114|  1.96k|  if (input.empty()) {
  ------------------
  |  Branch (10114:7): [True: 0, False: 1.96k]
  ------------------
10115|      0|    return is_valid = false;
10116|      0|  }
10117|  1.96k|  std::string_view original_input = input;
10118|  1.96k|  if (original_input.back() == '.') {
  ------------------
  |  Branch (10118:7): [True: 69, False: 1.89k]
  ------------------
10119|     69|    original_input.remove_suffix(1);
10120|     69|  }
10121|  1.96k|  if (input.back() == '.') {
  ------------------
  |  Branch (10121:7): [True: 69, False: 1.89k]
  ------------------
10122|     69|    input.remove_suffix(1);
10123|     69|    if (input.empty()) {
  ------------------
  |  Branch (10123:9): [True: 0, False: 69]
  ------------------
10124|      0|      return is_valid = false;
10125|      0|    }
10126|     69|  }
10127|       |
10128|  1.96k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
10129|  1.96k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (10129:7): [True: 23, False: 1.94k]
  ------------------
10130|     23|    host = original_input;
10131|     23|    host_type = IPV4;
10132|     23|    return true;
10133|     23|  }
10134|       |
10135|  1.94k|  const char* p = input.data();
10136|  1.94k|  const char* end = p + input.size();
10137|  1.94k|  uint64_t ipv4 = 0;
10138|  1.94k|  int digit_count = 0;
10139|  1.94k|  int pure_decimal_count = 0;
10140|       |
10141|  2.41k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (10141:10): [True: 2.40k, False: 11]
  |  Branch (10141:29): [True: 2.40k, False: 0]
  ------------------
10142|  2.40k|    uint64_t segment = 0;
10143|  2.40k|    bool pure = false;
10144|  2.40k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (10144:9): [True: 333, False: 2.07k]
  ------------------
10145|    333|      return is_valid = false;
10146|    333|    }
10147|  2.07k|    if (pure) {
  ------------------
  |  Branch (10147:9): [True: 1.57k, False: 500]
  ------------------
10148|  1.57k|      ++pure_decimal_count;
10149|  1.57k|    }
10150|  2.07k|    if (p >= end) {
  ------------------
  |  Branch (10150:9): [True: 1.56k, False: 504]
  ------------------
10151|  1.56k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
10152|  1.56k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (10152:11): [True: 22, False: 1.54k]
  ------------------
10153|     22|        return is_valid = false;
10154|     22|      }
10155|  1.54k|      ipv4 = (ipv4 << shift) | segment;
10156|  1.54k|      host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (10156:14): [True: 0, False: 1.54k]
  ------------------
10157|  1.54k|                                       : ada::serializers::ipv4(ipv4);
10158|  1.54k|      host_type = IPV4;
10159|  1.54k|      return true;
10160|  1.56k|    }
10161|    504|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (10161:9): [True: 29, False: 475]
  |  Branch (10161:26): [True: 0, False: 475]
  ------------------
10162|     29|      return is_valid = false;
10163|     29|    }
10164|    475|    ipv4 = (ipv4 << 8) | segment;
10165|    475|    ++p;
10166|    475|  }
10167|     11|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (10167:7): [True: 0, False: 11]
  |  Branch (10167:27): [True: 11, False: 0]
  ------------------
10168|     11|    return is_valid = false;
10169|     11|  }
10170|      0|  host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (10170:10): [True: 0, False: 0]
  ------------------
10171|      0|                                   : ada::serializers::ipv4(ipv4);
10172|      0|  host_type = IPV4;
10173|      0|  return true;
10174|     11|}
_ZN3ada3url10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10176|    538|bool url::parse_ipv6(std::string_view input) {
10177|    538|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
10178|    538|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (10178:7): [True: 16, False: 522]
  |  Branch (10178:24): [True: 16, False: 506]
  ------------------
10179|     32|    return is_valid = false;
10180|     32|  }
10181|    506|  std::array<uint16_t, 8> address{};
10182|       |#if defined(ADA_AVX512_IPV6)
10183|       |  if (bool simd_valid; detail::try_parse_ipv6_avx512(input.data(), input.size(),
10184|       |                                                     address, simd_valid)) {
10185|       |    if (!simd_valid) [[unlikely]] {
10186|       |      return is_valid = false;
10187|       |    }
10188|       |    host = ada::serializers::ipv6(address);
10189|       |    host_type = IPV6;
10190|       |    return true;
10191|       |  }
10192|       |  address = {};
10193|       |#endif
10194|    506|  const char* pointer = input.data();
10195|    506|  const char* const end = pointer + input.size();
10196|    506|  int piece_index = 0;
10197|    506|  int compress = -1;
10198|       |
10199|    506|  if (*pointer == ':') {
  ------------------
  |  Branch (10199:7): [True: 192, False: 314]
  ------------------
10200|    192|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (10200:9): [True: 3, False: 189]
  |  Branch (10200:30): [True: 6, False: 183]
  ------------------
10201|      9|      return is_valid = false;
10202|      9|    }
10203|    183|    pointer += 2;
10204|    183|    compress = ++piece_index;
10205|    183|  }
10206|       |
10207|  1.36k|  while (pointer != end) {
  ------------------
  |  Branch (10207:10): [True: 1.03k, False: 333]
  ------------------
10208|  1.03k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (10208:9): [True: 3, False: 1.02k]
  ------------------
10209|      3|      return is_valid = false;
10210|      3|    }
10211|  1.02k|    if (*pointer == ':') {
  ------------------
  |  Branch (10211:9): [True: 142, False: 885]
  ------------------
10212|    142|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (10212:11): [True: 3, False: 139]
  ------------------
10213|      3|        return is_valid = false;
10214|      3|      }
10215|    139|      ++pointer;
10216|    139|      compress = ++piece_index;
10217|    139|      continue;
10218|    142|    }
10219|       |
10220|    885|    uint16_t value = 0;
10221|    885|    const int length = detail::parse_hex_piece(pointer, end, value);
10222|       |
10223|    885|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (10223:9): [True: 720, False: 165]
  |  Branch (10223:27): [True: 99, False: 621]
  ------------------
10224|     99|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (10224:11): [True: 3, False: 96]
  ------------------
10225|      3|        return is_valid = false;
10226|      3|      }
10227|     96|      pointer -= length;
10228|     96|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (10228:11): [True: 3, False: 93]
  ------------------
10229|      3|        return is_valid = false;
10230|      3|      }
10231|       |
10232|     93|      int numbers_seen = 0;
10233|    276|      while (pointer != end) {
  ------------------
  |  Branch (10233:14): [True: 246, False: 30]
  ------------------
10234|    246|        int ipv4_piece = -1;
10235|    246|        if (numbers_seen > 0) {
  ------------------
  |  Branch (10235:13): [True: 153, False: 93]
  ------------------
10236|    153|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (10236:15): [True: 127, False: 26]
  |  Branch (10236:34): [True: 122, False: 5]
  ------------------
10237|    122|            ++pointer;
10238|    122|          } else {
10239|     31|            return is_valid = false;
10240|     31|          }
10241|    153|        }
10242|    215|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (10242:13): [True: 12, False: 203]
  |  Branch (10242:31): [True: 6, False: 197]
  |  Branch (10242:49): [True: 6, False: 191]
  ------------------
10243|     24|          return is_valid = false;
10244|     24|        }
10245|    191|        ipv4_piece = *pointer - '0';
10246|    191|        ++pointer;
10247|    191|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (10247:13): [True: 173, False: 18]
  |  Branch (10247:31): [True: 61, False: 112]
  |  Branch (10247:50): [True: 55, False: 6]
  ------------------
10248|     55|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (10248:15): [True: 3, False: 52]
  ------------------
10249|      3|            return is_valid = false;
10250|      3|          }
10251|     52|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
10252|     52|          ++pointer;
10253|     52|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (10253:15): [True: 46, False: 6]
  |  Branch (10253:33): [True: 27, False: 19]
  |  Branch (10253:52): [True: 23, False: 4]
  ------------------
10254|     23|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
10255|     23|            ++pointer;
10256|     23|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (10256:17): [True: 5, False: 18]
  ------------------
10257|      5|              return is_valid = false;
10258|      5|            }
10259|     23|          }
10260|     52|        }
10261|    183|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
10262|    183|            address[static_cast<size_t>(piece_index)] * 0x100 +
10263|    183|            static_cast<uint16_t>(ipv4_piece));
10264|    183|        ++numbers_seen;
10265|    183|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (10265:13): [True: 57, False: 126]
  |  Branch (10265:34): [True: 19, False: 107]
  ------------------
10266|     76|          ++piece_index;
10267|     76|        }
10268|    183|      }
10269|     30|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (10269:11): [True: 18, False: 12]
  ------------------
10270|     18|        return is_valid = false;
10271|     18|      }
10272|     12|      break;
10273|     30|    }
10274|       |
10275|    786|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (10275:9): [True: 47, False: 739]
  ------------------
10276|     47|      return is_valid = false;
10277|     47|    }
10278|       |
10279|    739|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (10279:9): [True: 574, False: 165]
  |  Branch (10279:27): [True: 565, False: 9]
  ------------------
10280|    565|      ++pointer;
10281|    565|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (10281:11): [True: 3, False: 562]
  ------------------
10282|      3|        return is_valid = false;
10283|      3|      }
10284|    565|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (10284:16): [True: 9, False: 165]
  ------------------
10285|      9|      return is_valid = false;
10286|      9|    }
10287|       |
10288|    727|    address[static_cast<size_t>(piece_index)] = value;
10289|    727|    ++piece_index;
10290|    727|  }
10291|       |
10292|    345|  if (compress != -1) {
  ------------------
  |  Branch (10292:7): [True: 312, False: 33]
  ------------------
10293|    312|    const int right = piece_index - compress;
10294|    312|    if (right > 0) {
  ------------------
  |  Branch (10294:9): [True: 144, False: 168]
  ------------------
10295|    144|      const size_t dest = static_cast<size_t>(8 - right);
10296|    144|      const size_t src = static_cast<size_t>(compress);
10297|    144|      if (dest != src) {
  ------------------
  |  Branch (10297:11): [True: 137, False: 7]
  ------------------
10298|    398|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (10298:53): [True: 261, False: 137]
  ------------------
10299|    261|          address[dest + i] = address[src + i];
10300|    261|          address[src + i] = 0;
10301|    261|        }
10302|    137|      }
10303|    144|    }
10304|    312|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (10304:14): [True: 21, False: 12]
  ------------------
10305|     21|    return is_valid = false;
10306|     21|  }
10307|       |
10308|    324|  host = ada::serializers::ipv6(address);
10309|    324|  ada_log("parse_ipv6 ", *host);
10310|    324|  host_type = IPV6;
10311|    324|  return true;
10312|    345|}
_ZN3ada6parser25try_parse_simple_absoluteINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
12269|  46.5k|                                                result_type& out) {
12270|  46.5k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12271|  46.5k|  constexpr bool is_aggregator =
12272|  46.5k|      std::is_same_v<result_type, ada::url_aggregator>;
12273|  46.5k|  static_assert(is_ada_url || is_aggregator);
12274|       |
12275|  46.5k|  const size_t len = input.size();
12276|       |  // Shortest accepted form is "ws://x" (6).
12277|  46.5k|  if (len < 6) [[unlikely]] {
  ------------------
  |  Branch (12277:7): [True: 26.0k, False: 20.4k]
  ------------------
12278|  26.0k|    return false;
12279|  26.0k|  }
12280|  20.4k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12281|       |
12282|  20.4k|  size_t pos = 0;
12283|  20.4k|  ada::scheme::type scheme_type = ada::scheme::type::NOT_SPECIAL;
12284|  20.4k|  uint32_t protocol_end = 0;
12285|  20.4k|#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
12286|  20.4k|    defined(_M_X64) || defined(_M_IX86) || defined(_M_AMD64)
12287|       |  // One 8-byte load + integer compare is a single cmp/jcc on x86-64.
12288|       |  // Masked compares cover the shorter special schemes without extra loads.
12289|  20.4k|  if (len >= 8) {
  ------------------
  |  Branch (12289:7): [True: 17.5k, False: 2.87k]
  ------------------
12290|  17.5k|    uint64_t first8 = 0;
12291|  17.5k|    std::memcpy(&first8, b, 8);
12292|  17.5k|    if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (12292:9): [True: 12, False: 17.5k]
  ------------------
12293|     12|      pos = 8;
12294|     12|      scheme_type = ada::scheme::type::HTTPS;
12295|     12|      protocol_end = 6;
12296|  17.5k|    } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (12296:16): [True: 439, False: 17.1k]
  ------------------
12297|  17.5k|               0x002f2f3a70747468ull) {  // "http://"
12298|    439|      pos = 7;
12299|    439|      scheme_type = ada::scheme::type::HTTP;
12300|    439|      protocol_end = 5;
12301|  17.1k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12301:16): [True: 74, False: 17.0k]
  ------------------
12302|  17.1k|               0x00002f2f3a737377ull) {  // "wss://"
12303|     74|      pos = 6;
12304|     74|      scheme_type = ada::scheme::type::WSS;
12305|     74|      protocol_end = 4;
12306|  17.0k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12306:16): [True: 21, False: 17.0k]
  ------------------
12307|  17.0k|               0x00002f2f3a707466ull) {  // "ftp://"
12308|     21|      pos = 6;
12309|     21|      scheme_type = ada::scheme::type::FTP;
12310|     21|      protocol_end = 4;
12311|  17.0k|    } else if ((first8 & 0x000000ffffffffffull) ==
  ------------------
  |  Branch (12311:16): [True: 7.54k, False: 9.51k]
  ------------------
12312|  17.0k|               0x0000002f2f3a7377ull) {  // "ws://"
12313|  7.54k|      pos = 5;
12314|  7.54k|      scheme_type = ada::scheme::type::WS;
12315|  7.54k|      protocol_end = 3;
12316|  9.51k|    } else {
12317|  9.51k|      return false;
12318|  9.51k|    }
12319|  17.5k|  } else if (b[0] == 'w' && b[1] == 's') {
  ------------------
  |  Branch (12319:14): [True: 1.74k, False: 1.13k]
  |  Branch (12319:29): [True: 1.46k, False: 275]
  ------------------
12320|  1.46k|    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
  ------------------
  |  Branch (12320:9): [True: 1.35k, False: 112]
  |  Branch (12320:24): [True: 568, False: 787]
  |  Branch (12320:39): [True: 560, False: 8]
  ------------------
12321|    560|      pos = 5;
12322|    560|      scheme_type = ada::scheme::type::WS;
12323|    560|      protocol_end = 3;
12324|    907|    } else if (b[2] == 's' && b[3] == ':' && b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12324:16): [True: 82, False: 825]
  |  Branch (12324:31): [True: 70, False: 12]
  |  Branch (12324:46): [True: 9, False: 61]
  |  Branch (12324:61): [True: 4, False: 5]
  ------------------
12325|      4|      pos = 6;
12326|      4|      scheme_type = ada::scheme::type::WSS;
12327|      4|      protocol_end = 4;
12328|    903|    } else {
12329|    903|      return false;
12330|    903|    }
12331|  1.46k|  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
  ------------------
  |  Branch (12331:14): [True: 126, False: 1.28k]
  |  Branch (12331:29): [True: 42, False: 84]
  |  Branch (12331:44): [True: 30, False: 12]
  |  Branch (12331:59): [True: 24, False: 6]
  ------------------
12332|     24|             b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12332:14): [True: 10, False: 14]
  |  Branch (12332:29): [True: 3, False: 7]
  ------------------
12333|      3|    pos = 6;
12334|      3|    scheme_type = ada::scheme::type::FTP;
12335|      3|    protocol_end = 4;
12336|  1.40k|  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
  ------------------
  |  Branch (12336:14): [True: 701, False: 708]
  |  Branch (12336:26): [True: 107, False: 594]
  |  Branch (12336:41): [True: 84, False: 23]
  |  Branch (12336:56): [True: 69, False: 15]
  ------------------
12337|     69|             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (12337:14): [True: 52, False: 17]
  |  Branch (12337:29): [True: 35, False: 17]
  |  Branch (12337:44): [True: 14, False: 21]
  |  Branch (12337:59): [True: 3, False: 11]
  ------------------
12338|      3|    pos = 7;
12339|      3|    scheme_type = ada::scheme::type::HTTP;
12340|      3|    protocol_end = 5;
12341|  1.40k|  } else {
12342|  1.40k|    return false;
12343|  1.40k|  }
12344|       |#else
12345|       |  // Big-endian / uncommon arches: byte-wise special-scheme match.
12346|       |  if (len >= 8 && b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p' &&
12347|       |      b[4] == 's' && b[5] == ':' && b[6] == '/' && b[7] == '/') {
12348|       |    pos = 8;
12349|       |    scheme_type = ada::scheme::type::HTTPS;
12350|       |    protocol_end = 6;
12351|       |  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
12352|       |             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
12353|       |    pos = 7;
12354|       |    scheme_type = ada::scheme::type::HTTP;
12355|       |    protocol_end = 5;
12356|       |  } else if (b[0] == 'w' && b[1] == 's') {
12357|       |    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
12358|       |      pos = 5;
12359|       |      scheme_type = ada::scheme::type::WS;
12360|       |      protocol_end = 3;
12361|       |    } else if (len >= 6 && b[2] == 's' && b[3] == ':' && b[4] == '/' &&
12362|       |               b[5] == '/') {
12363|       |      pos = 6;
12364|       |      scheme_type = ada::scheme::type::WSS;
12365|       |      protocol_end = 4;
12366|       |    } else {
12367|       |      return false;
12368|       |    }
12369|       |  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
12370|       |             b[4] == '/' && b[5] == '/') {
12371|       |    pos = 6;
12372|       |    scheme_type = ada::scheme::type::FTP;
12373|       |    protocol_end = 4;
12374|       |  } else {
12375|       |    return false;
12376|       |  }
12377|       |#endif
12378|  8.65k|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (12378:7): [True: 8.64k, False: 10]
  |  Branch (12378:21): [True: 13, False: 8.63k]
  |  Branch (12378:38): [True: 8, False: 8.62k]
  ------------------
12379|     21|    return false;
12380|     21|  }
12381|       |
12382|       |  // Digit-led hosts are IPv4/numeric; '[' is IPv6. Skip before scanning.
12383|  8.63k|  if (pos < len && ((b[pos] >= '0' && b[pos] <= '9') || b[pos] == '[')) {
  ------------------
  |  Branch (12383:7): [True: 8.62k, False: 10]
  |  Branch (12383:22): [True: 6.82k, False: 1.80k]
  |  Branch (12383:39): [True: 1.09k, False: 5.73k]
  |  Branch (12383:57): [True: 98, False: 7.43k]
  ------------------
12384|  1.18k|    return false;
12385|  1.18k|  }
12386|       |
12387|  7.44k|  const size_t host_start = pos;
12388|  7.44k|  bool has_upper = false;
12389|  7.44k|  bool has_x = false;
12390|  7.44k|  size_t host_end = pos;
12391|  7.44k|  if (!scan_plain_host(b, pos, len, host_end, has_upper, has_x)) {
  ------------------
  |  Branch (12391:7): [True: 266, False: 7.18k]
  ------------------
12392|    266|    return false;
12393|    266|  }
12394|  7.18k|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (12394:7): [True: 53, False: 7.12k]
  ------------------
12395|     53|    return false;
12396|     53|  }
12397|  7.12k|  const size_t host_len = host_end - host_start;
12398|  7.12k|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (12398:7): [True: 89, False: 7.04k]
  ------------------
12399|     89|    return false;
12400|     89|  }
12401|  7.04k|  {
12402|  7.04k|    std::string_view hv(input.data() + host_start, host_len);
12403|  7.04k|    char host_buf[256];
12404|  7.04k|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (12404:9): [True: 548, False: 6.49k]
  ------------------
12405|    548|      std::memcpy(host_buf, input.data() + host_start, host_len);
12406|    548|      unicode::to_lower_ascii(host_buf, host_len);
12407|    548|      hv = std::string_view(host_buf, host_len);
12408|    548|    }
12409|  7.04k|    if (ada::checkers::last_label_may_be_a_number(hv)) [[unlikely]] {
  ------------------
  |  Branch (12409:9): [True: 93, False: 6.94k]
  ------------------
12410|     93|      return false;
12411|     93|    }
12412|       |    // Punycode is "xn--...". The host scan already notes a lowercase 'x';
12413|       |    // uppercase 'X' is covered by has_upper after the in-place lower.
12414|  6.94k|    static constexpr std::string_view xn{"xn-", 3};
12415|  6.94k|    if ((has_x || has_upper) && hv.find(xn) != std::string_view::npos)
  ------------------
  |  Branch (12415:10): [True: 1.92k, False: 5.02k]
  |  Branch (12415:19): [True: 494, False: 4.52k]
  |  Branch (12415:33): [True: 1.36k, False: 1.05k]
  ------------------
12416|  1.36k|        [[unlikely]] {
12417|  1.36k|      return false;
12418|  1.36k|    }
12419|  6.94k|  }
12420|       |
12421|  5.58k|  if (host_end < len && b[host_end] == ':') [[unlikely]] {
  ------------------
  |  Branch (12421:7): [True: 5.40k, False: 181]
  |  Branch (12421:25): [True: 1.94k, False: 3.45k]
  ------------------
12422|  1.94k|    return finish_simple_absolute_with_port(input, out, scheme_type,
12423|  1.94k|                                            protocol_end, host_start, host_end,
12424|  1.94k|                                            host_len, has_upper);
12425|  1.94k|  }
12426|       |
12427|  3.63k|  size_t i = host_end;
12428|  3.63k|  size_t path_start = host_end;
12429|  3.63k|  size_t path_end = host_end;
12430|  3.63k|  size_t query_start = std::string_view::npos;
12431|  3.63k|  size_t hash_start = std::string_view::npos;
12432|  3.63k|  bool has_path = false;
12433|  3.63k|  bool maybe_dot_segment = false;
12434|  3.63k|  bool rest_simple = true;
12435|       |
12436|  3.63k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12436:7): [True: 3.45k, False: 181]
  |  Branch (12436:18): [True: 3.29k, False: 160]
  ------------------
12437|  3.29k|    has_path = true;
12438|  3.29k|    path_start = i;
12439|  3.29k|    ++i;
12440|  3.29k|    scan_path_run(b, i, len, maybe_dot_segment);
12441|  3.29k|    if (i < len) {
  ------------------
  |  Branch (12441:9): [True: 1.36k, False: 1.93k]
  ------------------
12442|  1.36k|      const uint8_t cls = k_path[b[i]];
12443|  1.36k|      if (cls == 1) {
  ------------------
  |  Branch (12443:11): [True: 408, False: 958]
  ------------------
12444|    408|        path_end = i;
12445|    408|        if (b[i] == '?') {
  ------------------
  |  Branch (12445:13): [True: 246, False: 162]
  ------------------
12446|    246|          query_start = i;
12447|    246|          ++i;
12448|    246|          goto scan_query;
12449|    246|        }
12450|    162|        hash_start = i;
12451|    162|        ++i;
12452|    162|        goto scan_hash;
12453|    408|      }
12454|    958|      rest_simple = false;
12455|  32.6k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12455:14): [True: 31.7k, False: 874]
  ------------------
12456|  31.7k|        if (b[i] == '?') {
  ------------------
  |  Branch (12456:13): [True: 59, False: 31.6k]
  ------------------
12457|     59|          path_end = i;
12458|     59|          query_start = i;
12459|     59|          ++i;
12460|     59|          goto scan_query_boundary;
12461|     59|        }
12462|  31.6k|        if (b[i] == '#') {
  ------------------
  |  Branch (12462:13): [True: 25, False: 31.6k]
  ------------------
12463|     25|          path_end = i;
12464|     25|          hash_start = i;
12465|     25|          ++i;
12466|     25|          goto after_rest;
12467|     25|        }
12468|  31.6k|      }
12469|    874|      path_end = i;
12470|    874|      goto after_rest;
12471|    958|    }
12472|  1.93k|    path_end = i;
12473|  1.93k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12473:14): [True: 160, False: 181]
  |  Branch (12473:25): [True: 86, False: 74]
  ------------------
12474|     86|    query_start = i;
12475|     86|    ++i;
12476|     86|    goto scan_query;
12477|    255|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12477:14): [True: 74, False: 181]
  |  Branch (12477:25): [True: 74, False: 0]
  ------------------
12478|     74|    hash_start = i;
12479|     74|    ++i;
12480|     74|    goto scan_hash;
12481|     74|  }
12482|  2.11k|  goto after_rest;
12483|       |
12484|  2.11k|scan_query:
12485|    332|  scan_query_run(b, i, len);
12486|    332|  if (i < len) {
  ------------------
  |  Branch (12486:7): [True: 149, False: 183]
  ------------------
12487|    149|    if (b[i] == '#') {
  ------------------
  |  Branch (12487:9): [True: 99, False: 50]
  ------------------
12488|     99|      hash_start = i;
12489|     99|      ++i;
12490|     99|      goto scan_hash;
12491|     99|    }
12492|     50|    rest_simple = false;
12493|     50|    goto scan_query_boundary;
12494|    149|  }
12495|    183|  goto after_rest;
12496|       |
12497|    183|scan_query_boundary:
12498|    943|  for (; i < len; ++i) {
  ------------------
  |  Branch (12498:10): [True: 858, False: 85]
  ------------------
12499|    858|    if (b[i] == '#') {
  ------------------
  |  Branch (12499:9): [True: 24, False: 834]
  ------------------
12500|     24|      hash_start = i;
12501|     24|      ++i;
12502|     24|      goto after_rest;
12503|     24|    }
12504|    858|  }
12505|     85|  goto after_rest;
12506|       |
12507|    335|scan_hash:
12508|    335|  scan_hash_run(b, i, len);
12509|    335|  if (i < len) {
  ------------------
  |  Branch (12509:7): [True: 64, False: 271]
  ------------------
12510|     64|    rest_simple = false;
12511|     64|  }
12512|       |
12513|  3.63k|after_rest:
12514|  3.63k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12514:7): [True: 2.56k, False: 1.07k]
  |  Branch (12514:22): [True: 436, False: 2.13k]
  ------------------
12515|    436|    const std::string_view path_body(input.data() + path_start,
12516|    436|                                     path_end - path_start);
12517|    436|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12517:9): [True: 208, False: 228]
  ------------------
12518|    208|      rest_simple = false;
12519|    208|    }
12520|    436|  }
12521|       |
12522|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12523|       |  // trailing C0 control or space; this fast path does neither. A query or
12524|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12525|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12526|       |  // inputs back to the slow path.
12527|  3.63k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12527:7): [True: 1.28k, False: 2.35k]
  |  Branch (12527:24): [True: 35, False: 1.24k]
  ------------------
12528|  1.24k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12528:24): [True: 28, False: 1.21k]
  ------------------
12529|     63|    return false;
12530|     63|  }
12531|       |
12532|  3.57k|  out.type = scheme_type;
12533|  3.57k|  out.is_valid = true;
12534|  3.57k|  out.has_opaque_path = false;
12535|  3.57k|  out.host_type = DEFAULT;
12536|       |
12537|  3.57k|  if (!rest_simple) {
  ------------------
  |  Branch (12537:7): [True: 1.21k, False: 2.35k]
  ------------------
12538|       |    // Host is a plain domain. Finish path/query/hash with
12539|       |    // the regular helpers
12540|       |    // so percent-encoding and dot segments do not re-parse the authority.
12541|  1.21k|    const std::string_view path_view =
12542|  1.21k|        has_path
  ------------------
  |  Branch (12542:9): [True: 1.15k, False: 62]
  ------------------
12543|  1.21k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12544|  1.21k|            : std::string_view{};
12545|  1.21k|    auto apply_query_and_hash = [&]() {
12546|  1.21k|      if (query_start != std::string_view::npos) {
12547|  1.21k|        const size_t q_end =
12548|  1.21k|            (hash_start != std::string_view::npos) ? hash_start : len;
12549|  1.21k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12550|  1.21k|                                                q_end - query_start - 1),
12551|  1.21k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12552|  1.21k|      }
12553|  1.21k|      if (hash_start != std::string_view::npos) {
12554|  1.21k|        out.update_unencoded_base_hash(std::string_view(
12555|  1.21k|            input.data() + hash_start + 1, len - hash_start - 1));
12556|  1.21k|      }
12557|  1.21k|    };
12558|       |    if constexpr (is_aggregator) {
12559|       |      out.buffer.assign(input.substr(0, host_end));
12560|       |      if (has_upper) {
12561|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12562|       |      }
12563|       |      out.components.protocol_end = protocol_end;
12564|       |      out.components.username_end = protocol_end + 2;
12565|       |      out.components.host_start = protocol_end + 2;
12566|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12567|       |      out.components.port = url_components::omitted;
12568|       |      out.components.pathname_start = static_cast<uint32_t>(host_end);
12569|       |      out.components.search_start = url_components::omitted;
12570|       |      out.components.hash_start = url_components::omitted;
12571|       |      out.parse_path(path_view);
12572|       |      apply_query_and_hash();
12573|  1.21k|    } else {
12574|  1.21k|      std::string host_str(input.substr(host_start, host_len));
12575|  1.21k|      if (has_upper) {
  ------------------
  |  Branch (12575:11): [True: 138, False: 1.07k]
  ------------------
12576|    138|        unicode::to_lower_ascii(host_str.data(), host_str.size());
12577|    138|      }
12578|  1.21k|      out.host = std::move(host_str);
12579|  1.21k|      out.parse_path(path_view);
12580|  1.21k|      apply_query_and_hash();
12581|  1.21k|    }
12582|  1.21k|    return true;
12583|  1.21k|  }
12584|       |
12585|  2.35k|  const bool need_slash = !has_path;
12586|       |  if constexpr (is_aggregator) {
12587|       |    if (!need_slash) {
12588|       |      // assign copies once. resize()+memcpy would value-init then overwrite.
12589|       |      out.buffer.assign(input);
12590|       |      if (has_upper) {
12591|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12592|       |      }
12593|       |      out.components.protocol_end = protocol_end;
12594|       |      out.components.username_end = protocol_end + 2;
12595|       |      out.components.host_start = protocol_end + 2;
12596|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12597|       |      out.components.port = url_components::omitted;
12598|       |      out.components.pathname_start = static_cast<uint32_t>(path_start);
12599|       |      out.components.search_start = (query_start != std::string_view::npos)
12600|       |                                        ? static_cast<uint32_t>(query_start)
12601|       |                                        : url_components::omitted;
12602|       |      out.components.hash_start = (hash_start != std::string_view::npos)
12603|       |                                      ? static_cast<uint32_t>(hash_start)
12604|       |                                      : url_components::omitted;
12605|       |    } else {
12606|       |      out.buffer.clear();
12607|       |      out.buffer.reserve(len + 1);
12608|       |      out.buffer.append(input.substr(0, host_end));
12609|       |      out.buffer.push_back('/');
12610|       |      if (host_end < len) {
12611|       |        out.buffer.append(input.substr(host_end));
12612|       |      }
12613|       |      if (has_upper) {
12614|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12615|       |      }
12616|       |      out.components.protocol_end = protocol_end;
12617|       |      out.components.username_end = protocol_end + 2;
12618|       |      out.components.host_start = protocol_end + 2;
12619|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12620|       |      out.components.port = url_components::omitted;
12621|       |      out.components.pathname_start = static_cast<uint32_t>(host_end);
12622|       |      out.components.search_start = (query_start != std::string_view::npos)
12623|       |                                        ? static_cast<uint32_t>(query_start + 1)
12624|       |                                        : url_components::omitted;
12625|       |      out.components.hash_start = (hash_start != std::string_view::npos)
12626|       |                                      ? static_cast<uint32_t>(hash_start + 1)
12627|       |                                      : url_components::omitted;
12628|       |    }
12629|  2.35k|  } else {
12630|  2.35k|    std::string host_str(input.substr(host_start, host_len));
12631|  2.35k|    if (has_upper) {
  ------------------
  |  Branch (12631:9): [True: 143, False: 2.21k]
  ------------------
12632|    143|      unicode::to_lower_ascii(host_str.data(), host_str.size());
12633|    143|    }
12634|  2.35k|    out.host = std::move(host_str);
12635|  2.35k|    if (need_slash) {
  ------------------
  |  Branch (12635:9): [True: 249, False: 2.10k]
  ------------------
12636|    249|      out.path = "/";
12637|  2.10k|    } else {
12638|  2.10k|      out.path.assign(input.data() + path_start, path_end - path_start);
12639|  2.10k|    }
12640|  2.35k|    if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12640:9): [True: 273, False: 2.08k]
  ------------------
12641|    273|      const size_t q_end =
12642|    273|          (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12642:11): [True: 92, False: 181]
  ------------------
12643|    273|      out.query.emplace(input.data() + query_start + 1,
12644|    273|                        q_end - query_start - 1);
12645|    273|    }
12646|  2.35k|    if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12646:9): [True: 270, False: 2.08k]
  ------------------
12647|    270|      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12648|    270|    }
12649|  2.35k|  }
12650|  2.35k|  return true;
12651|  3.57k|}
_ZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
12269|  46.5k|                                                result_type& out) {
12270|  46.5k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12271|  46.5k|  constexpr bool is_aggregator =
12272|  46.5k|      std::is_same_v<result_type, ada::url_aggregator>;
12273|  46.5k|  static_assert(is_ada_url || is_aggregator);
12274|       |
12275|  46.5k|  const size_t len = input.size();
12276|       |  // Shortest accepted form is "ws://x" (6).
12277|  46.5k|  if (len < 6) [[unlikely]] {
  ------------------
  |  Branch (12277:7): [True: 26.0k, False: 20.4k]
  ------------------
12278|  26.0k|    return false;
12279|  26.0k|  }
12280|  20.4k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12281|       |
12282|  20.4k|  size_t pos = 0;
12283|  20.4k|  ada::scheme::type scheme_type = ada::scheme::type::NOT_SPECIAL;
12284|  20.4k|  uint32_t protocol_end = 0;
12285|  20.4k|#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
12286|  20.4k|    defined(_M_X64) || defined(_M_IX86) || defined(_M_AMD64)
12287|       |  // One 8-byte load + integer compare is a single cmp/jcc on x86-64.
12288|       |  // Masked compares cover the shorter special schemes without extra loads.
12289|  20.4k|  if (len >= 8) {
  ------------------
  |  Branch (12289:7): [True: 17.5k, False: 2.87k]
  ------------------
12290|  17.5k|    uint64_t first8 = 0;
12291|  17.5k|    std::memcpy(&first8, b, 8);
12292|  17.5k|    if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (12292:9): [True: 12, False: 17.5k]
  ------------------
12293|     12|      pos = 8;
12294|     12|      scheme_type = ada::scheme::type::HTTPS;
12295|     12|      protocol_end = 6;
12296|  17.5k|    } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (12296:16): [True: 439, False: 17.1k]
  ------------------
12297|  17.5k|               0x002f2f3a70747468ull) {  // "http://"
12298|    439|      pos = 7;
12299|    439|      scheme_type = ada::scheme::type::HTTP;
12300|    439|      protocol_end = 5;
12301|  17.1k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12301:16): [True: 74, False: 17.0k]
  ------------------
12302|  17.1k|               0x00002f2f3a737377ull) {  // "wss://"
12303|     74|      pos = 6;
12304|     74|      scheme_type = ada::scheme::type::WSS;
12305|     74|      protocol_end = 4;
12306|  17.0k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12306:16): [True: 21, False: 17.0k]
  ------------------
12307|  17.0k|               0x00002f2f3a707466ull) {  // "ftp://"
12308|     21|      pos = 6;
12309|     21|      scheme_type = ada::scheme::type::FTP;
12310|     21|      protocol_end = 4;
12311|  17.0k|    } else if ((first8 & 0x000000ffffffffffull) ==
  ------------------
  |  Branch (12311:16): [True: 7.54k, False: 9.51k]
  ------------------
12312|  17.0k|               0x0000002f2f3a7377ull) {  // "ws://"
12313|  7.54k|      pos = 5;
12314|  7.54k|      scheme_type = ada::scheme::type::WS;
12315|  7.54k|      protocol_end = 3;
12316|  9.51k|    } else {
12317|  9.51k|      return false;
12318|  9.51k|    }
12319|  17.5k|  } else if (b[0] == 'w' && b[1] == 's') {
  ------------------
  |  Branch (12319:14): [True: 1.74k, False: 1.13k]
  |  Branch (12319:29): [True: 1.46k, False: 275]
  ------------------
12320|  1.46k|    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
  ------------------
  |  Branch (12320:9): [True: 1.35k, False: 112]
  |  Branch (12320:24): [True: 568, False: 787]
  |  Branch (12320:39): [True: 560, False: 8]
  ------------------
12321|    560|      pos = 5;
12322|    560|      scheme_type = ada::scheme::type::WS;
12323|    560|      protocol_end = 3;
12324|    907|    } else if (b[2] == 's' && b[3] == ':' && b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12324:16): [True: 82, False: 825]
  |  Branch (12324:31): [True: 70, False: 12]
  |  Branch (12324:46): [True: 9, False: 61]
  |  Branch (12324:61): [True: 4, False: 5]
  ------------------
12325|      4|      pos = 6;
12326|      4|      scheme_type = ada::scheme::type::WSS;
12327|      4|      protocol_end = 4;
12328|    903|    } else {
12329|    903|      return false;
12330|    903|    }
12331|  1.46k|  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
  ------------------
  |  Branch (12331:14): [True: 126, False: 1.28k]
  |  Branch (12331:29): [True: 42, False: 84]
  |  Branch (12331:44): [True: 30, False: 12]
  |  Branch (12331:59): [True: 24, False: 6]
  ------------------
12332|     24|             b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12332:14): [True: 10, False: 14]
  |  Branch (12332:29): [True: 3, False: 7]
  ------------------
12333|      3|    pos = 6;
12334|      3|    scheme_type = ada::scheme::type::FTP;
12335|      3|    protocol_end = 4;
12336|  1.40k|  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
  ------------------
  |  Branch (12336:14): [True: 701, False: 708]
  |  Branch (12336:26): [True: 107, False: 594]
  |  Branch (12336:41): [True: 84, False: 23]
  |  Branch (12336:56): [True: 69, False: 15]
  ------------------
12337|     69|             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (12337:14): [True: 52, False: 17]
  |  Branch (12337:29): [True: 35, False: 17]
  |  Branch (12337:44): [True: 14, False: 21]
  |  Branch (12337:59): [True: 3, False: 11]
  ------------------
12338|      3|    pos = 7;
12339|      3|    scheme_type = ada::scheme::type::HTTP;
12340|      3|    protocol_end = 5;
12341|  1.40k|  } else {
12342|  1.40k|    return false;
12343|  1.40k|  }
12344|       |#else
12345|       |  // Big-endian / uncommon arches: byte-wise special-scheme match.
12346|       |  if (len >= 8 && b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p' &&
12347|       |      b[4] == 's' && b[5] == ':' && b[6] == '/' && b[7] == '/') {
12348|       |    pos = 8;
12349|       |    scheme_type = ada::scheme::type::HTTPS;
12350|       |    protocol_end = 6;
12351|       |  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
12352|       |             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
12353|       |    pos = 7;
12354|       |    scheme_type = ada::scheme::type::HTTP;
12355|       |    protocol_end = 5;
12356|       |  } else if (b[0] == 'w' && b[1] == 's') {
12357|       |    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
12358|       |      pos = 5;
12359|       |      scheme_type = ada::scheme::type::WS;
12360|       |      protocol_end = 3;
12361|       |    } else if (len >= 6 && b[2] == 's' && b[3] == ':' && b[4] == '/' &&
12362|       |               b[5] == '/') {
12363|       |      pos = 6;
12364|       |      scheme_type = ada::scheme::type::WSS;
12365|       |      protocol_end = 4;
12366|       |    } else {
12367|       |      return false;
12368|       |    }
12369|       |  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
12370|       |             b[4] == '/' && b[5] == '/') {
12371|       |    pos = 6;
12372|       |    scheme_type = ada::scheme::type::FTP;
12373|       |    protocol_end = 4;
12374|       |  } else {
12375|       |    return false;
12376|       |  }
12377|       |#endif
12378|  8.65k|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (12378:7): [True: 8.64k, False: 10]
  |  Branch (12378:21): [True: 13, False: 8.63k]
  |  Branch (12378:38): [True: 8, False: 8.62k]
  ------------------
12379|     21|    return false;
12380|     21|  }
12381|       |
12382|       |  // Digit-led hosts are IPv4/numeric; '[' is IPv6. Skip before scanning.
12383|  8.63k|  if (pos < len && ((b[pos] >= '0' && b[pos] <= '9') || b[pos] == '[')) {
  ------------------
  |  Branch (12383:7): [True: 8.62k, False: 10]
  |  Branch (12383:22): [True: 6.82k, False: 1.80k]
  |  Branch (12383:39): [True: 1.09k, False: 5.73k]
  |  Branch (12383:57): [True: 98, False: 7.43k]
  ------------------
12384|  1.18k|    return false;
12385|  1.18k|  }
12386|       |
12387|  7.44k|  const size_t host_start = pos;
12388|  7.44k|  bool has_upper = false;
12389|  7.44k|  bool has_x = false;
12390|  7.44k|  size_t host_end = pos;
12391|  7.44k|  if (!scan_plain_host(b, pos, len, host_end, has_upper, has_x)) {
  ------------------
  |  Branch (12391:7): [True: 266, False: 7.18k]
  ------------------
12392|    266|    return false;
12393|    266|  }
12394|  7.18k|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (12394:7): [True: 53, False: 7.12k]
  ------------------
12395|     53|    return false;
12396|     53|  }
12397|  7.12k|  const size_t host_len = host_end - host_start;
12398|  7.12k|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (12398:7): [True: 89, False: 7.04k]
  ------------------
12399|     89|    return false;
12400|     89|  }
12401|  7.04k|  {
12402|  7.04k|    std::string_view hv(input.data() + host_start, host_len);
12403|  7.04k|    char host_buf[256];
12404|  7.04k|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (12404:9): [True: 548, False: 6.49k]
  ------------------
12405|    548|      std::memcpy(host_buf, input.data() + host_start, host_len);
12406|    548|      unicode::to_lower_ascii(host_buf, host_len);
12407|    548|      hv = std::string_view(host_buf, host_len);
12408|    548|    }
12409|  7.04k|    if (ada::checkers::last_label_may_be_a_number(hv)) [[unlikely]] {
  ------------------
  |  Branch (12409:9): [True: 93, False: 6.94k]
  ------------------
12410|     93|      return false;
12411|     93|    }
12412|       |    // Punycode is "xn--...". The host scan already notes a lowercase 'x';
12413|       |    // uppercase 'X' is covered by has_upper after the in-place lower.
12414|  6.94k|    static constexpr std::string_view xn{"xn-", 3};
12415|  6.94k|    if ((has_x || has_upper) && hv.find(xn) != std::string_view::npos)
  ------------------
  |  Branch (12415:10): [True: 1.92k, False: 5.02k]
  |  Branch (12415:19): [True: 494, False: 4.52k]
  |  Branch (12415:33): [True: 1.36k, False: 1.05k]
  ------------------
12416|  1.36k|        [[unlikely]] {
12417|  1.36k|      return false;
12418|  1.36k|    }
12419|  6.94k|  }
12420|       |
12421|  5.58k|  if (host_end < len && b[host_end] == ':') [[unlikely]] {
  ------------------
  |  Branch (12421:7): [True: 5.40k, False: 181]
  |  Branch (12421:25): [True: 1.94k, False: 3.45k]
  ------------------
12422|  1.94k|    return finish_simple_absolute_with_port(input, out, scheme_type,
12423|  1.94k|                                            protocol_end, host_start, host_end,
12424|  1.94k|                                            host_len, has_upper);
12425|  1.94k|  }
12426|       |
12427|  3.63k|  size_t i = host_end;
12428|  3.63k|  size_t path_start = host_end;
12429|  3.63k|  size_t path_end = host_end;
12430|  3.63k|  size_t query_start = std::string_view::npos;
12431|  3.63k|  size_t hash_start = std::string_view::npos;
12432|  3.63k|  bool has_path = false;
12433|  3.63k|  bool maybe_dot_segment = false;
12434|  3.63k|  bool rest_simple = true;
12435|       |
12436|  3.63k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12436:7): [True: 3.45k, False: 181]
  |  Branch (12436:18): [True: 3.29k, False: 160]
  ------------------
12437|  3.29k|    has_path = true;
12438|  3.29k|    path_start = i;
12439|  3.29k|    ++i;
12440|  3.29k|    scan_path_run(b, i, len, maybe_dot_segment);
12441|  3.29k|    if (i < len) {
  ------------------
  |  Branch (12441:9): [True: 1.36k, False: 1.93k]
  ------------------
12442|  1.36k|      const uint8_t cls = k_path[b[i]];
12443|  1.36k|      if (cls == 1) {
  ------------------
  |  Branch (12443:11): [True: 408, False: 958]
  ------------------
12444|    408|        path_end = i;
12445|    408|        if (b[i] == '?') {
  ------------------
  |  Branch (12445:13): [True: 246, False: 162]
  ------------------
12446|    246|          query_start = i;
12447|    246|          ++i;
12448|    246|          goto scan_query;
12449|    246|        }
12450|    162|        hash_start = i;
12451|    162|        ++i;
12452|    162|        goto scan_hash;
12453|    408|      }
12454|    958|      rest_simple = false;
12455|  32.6k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12455:14): [True: 31.7k, False: 874]
  ------------------
12456|  31.7k|        if (b[i] == '?') {
  ------------------
  |  Branch (12456:13): [True: 59, False: 31.6k]
  ------------------
12457|     59|          path_end = i;
12458|     59|          query_start = i;
12459|     59|          ++i;
12460|     59|          goto scan_query_boundary;
12461|     59|        }
12462|  31.6k|        if (b[i] == '#') {
  ------------------
  |  Branch (12462:13): [True: 25, False: 31.6k]
  ------------------
12463|     25|          path_end = i;
12464|     25|          hash_start = i;
12465|     25|          ++i;
12466|     25|          goto after_rest;
12467|     25|        }
12468|  31.6k|      }
12469|    874|      path_end = i;
12470|    874|      goto after_rest;
12471|    958|    }
12472|  1.93k|    path_end = i;
12473|  1.93k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12473:14): [True: 160, False: 181]
  |  Branch (12473:25): [True: 86, False: 74]
  ------------------
12474|     86|    query_start = i;
12475|     86|    ++i;
12476|     86|    goto scan_query;
12477|    255|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12477:14): [True: 74, False: 181]
  |  Branch (12477:25): [True: 74, False: 0]
  ------------------
12478|     74|    hash_start = i;
12479|     74|    ++i;
12480|     74|    goto scan_hash;
12481|     74|  }
12482|  2.11k|  goto after_rest;
12483|       |
12484|  2.11k|scan_query:
12485|    332|  scan_query_run(b, i, len);
12486|    332|  if (i < len) {
  ------------------
  |  Branch (12486:7): [True: 149, False: 183]
  ------------------
12487|    149|    if (b[i] == '#') {
  ------------------
  |  Branch (12487:9): [True: 99, False: 50]
  ------------------
12488|     99|      hash_start = i;
12489|     99|      ++i;
12490|     99|      goto scan_hash;
12491|     99|    }
12492|     50|    rest_simple = false;
12493|     50|    goto scan_query_boundary;
12494|    149|  }
12495|    183|  goto after_rest;
12496|       |
12497|    183|scan_query_boundary:
12498|    943|  for (; i < len; ++i) {
  ------------------
  |  Branch (12498:10): [True: 858, False: 85]
  ------------------
12499|    858|    if (b[i] == '#') {
  ------------------
  |  Branch (12499:9): [True: 24, False: 834]
  ------------------
12500|     24|      hash_start = i;
12501|     24|      ++i;
12502|     24|      goto after_rest;
12503|     24|    }
12504|    858|  }
12505|     85|  goto after_rest;
12506|       |
12507|    335|scan_hash:
12508|    335|  scan_hash_run(b, i, len);
12509|    335|  if (i < len) {
  ------------------
  |  Branch (12509:7): [True: 64, False: 271]
  ------------------
12510|     64|    rest_simple = false;
12511|     64|  }
12512|       |
12513|  3.63k|after_rest:
12514|  3.63k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12514:7): [True: 2.56k, False: 1.07k]
  |  Branch (12514:22): [True: 436, False: 2.13k]
  ------------------
12515|    436|    const std::string_view path_body(input.data() + path_start,
12516|    436|                                     path_end - path_start);
12517|    436|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12517:9): [True: 208, False: 228]
  ------------------
12518|    208|      rest_simple = false;
12519|    208|    }
12520|    436|  }
12521|       |
12522|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12523|       |  // trailing C0 control or space; this fast path does neither. A query or
12524|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12525|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12526|       |  // inputs back to the slow path.
12527|  3.63k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12527:7): [True: 1.28k, False: 2.35k]
  |  Branch (12527:24): [True: 35, False: 1.24k]
  ------------------
12528|  1.24k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12528:24): [True: 28, False: 1.21k]
  ------------------
12529|     63|    return false;
12530|     63|  }
12531|       |
12532|  3.57k|  out.type = scheme_type;
12533|  3.57k|  out.is_valid = true;
12534|  3.57k|  out.has_opaque_path = false;
12535|  3.57k|  out.host_type = DEFAULT;
12536|       |
12537|  3.57k|  if (!rest_simple) {
  ------------------
  |  Branch (12537:7): [True: 1.21k, False: 2.35k]
  ------------------
12538|       |    // Host is a plain domain. Finish path/query/hash with
12539|       |    // the regular helpers
12540|       |    // so percent-encoding and dot segments do not re-parse the authority.
12541|  1.21k|    const std::string_view path_view =
12542|  1.21k|        has_path
  ------------------
  |  Branch (12542:9): [True: 1.15k, False: 62]
  ------------------
12543|  1.21k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12544|  1.21k|            : std::string_view{};
12545|  1.21k|    auto apply_query_and_hash = [&]() {
12546|  1.21k|      if (query_start != std::string_view::npos) {
12547|  1.21k|        const size_t q_end =
12548|  1.21k|            (hash_start != std::string_view::npos) ? hash_start : len;
12549|  1.21k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12550|  1.21k|                                                q_end - query_start - 1),
12551|  1.21k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12552|  1.21k|      }
12553|  1.21k|      if (hash_start != std::string_view::npos) {
12554|  1.21k|        out.update_unencoded_base_hash(std::string_view(
12555|  1.21k|            input.data() + hash_start + 1, len - hash_start - 1));
12556|  1.21k|      }
12557|  1.21k|    };
12558|  1.21k|    if constexpr (is_aggregator) {
12559|  1.21k|      out.buffer.assign(input.substr(0, host_end));
12560|  1.21k|      if (has_upper) {
  ------------------
  |  Branch (12560:11): [True: 138, False: 1.07k]
  ------------------
12561|    138|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12562|    138|      }
12563|  1.21k|      out.components.protocol_end = protocol_end;
12564|  1.21k|      out.components.username_end = protocol_end + 2;
12565|  1.21k|      out.components.host_start = protocol_end + 2;
12566|  1.21k|      out.components.host_end = static_cast<uint32_t>(host_end);
12567|  1.21k|      out.components.port = url_components::omitted;
12568|  1.21k|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12569|  1.21k|      out.components.search_start = url_components::omitted;
12570|  1.21k|      out.components.hash_start = url_components::omitted;
12571|  1.21k|      out.parse_path(path_view);
12572|  1.21k|      apply_query_and_hash();
12573|       |    } else {
12574|       |      std::string host_str(input.substr(host_start, host_len));
12575|       |      if (has_upper) {
12576|       |        unicode::to_lower_ascii(host_str.data(), host_str.size());
12577|       |      }
12578|       |      out.host = std::move(host_str);
12579|       |      out.parse_path(path_view);
12580|       |      apply_query_and_hash();
12581|       |    }
12582|  1.21k|    return true;
12583|  1.21k|  }
12584|       |
12585|  2.35k|  const bool need_slash = !has_path;
12586|  2.35k|  if constexpr (is_aggregator) {
12587|  2.35k|    if (!need_slash) {
  ------------------
  |  Branch (12587:9): [True: 2.10k, False: 249]
  ------------------
12588|       |      // assign copies once. resize()+memcpy would value-init then overwrite.
12589|  2.10k|      out.buffer.assign(input);
12590|  2.10k|      if (has_upper) {
  ------------------
  |  Branch (12590:11): [True: 31, False: 2.07k]
  ------------------
12591|     31|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12592|     31|      }
12593|  2.10k|      out.components.protocol_end = protocol_end;
12594|  2.10k|      out.components.username_end = protocol_end + 2;
12595|  2.10k|      out.components.host_start = protocol_end + 2;
12596|  2.10k|      out.components.host_end = static_cast<uint32_t>(host_end);
12597|  2.10k|      out.components.port = url_components::omitted;
12598|  2.10k|      out.components.pathname_start = static_cast<uint32_t>(path_start);
12599|  2.10k|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12599:37): [True: 233, False: 1.87k]
  ------------------
12600|  2.10k|                                        ? static_cast<uint32_t>(query_start)
12601|  2.10k|                                        : url_components::omitted;
12602|  2.10k|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12602:35): [True: 229, False: 1.88k]
  ------------------
12603|  2.10k|                                      ? static_cast<uint32_t>(hash_start)
12604|  2.10k|                                      : url_components::omitted;
12605|  2.10k|    } else {
12606|    249|      out.buffer.clear();
12607|    249|      out.buffer.reserve(len + 1);
12608|    249|      out.buffer.append(input.substr(0, host_end));
12609|    249|      out.buffer.push_back('/');
12610|    249|      if (host_end < len) {
  ------------------
  |  Branch (12610:11): [True: 68, False: 181]
  ------------------
12611|     68|        out.buffer.append(input.substr(host_end));
12612|     68|      }
12613|    249|      if (has_upper) {
  ------------------
  |  Branch (12613:11): [True: 112, False: 137]
  ------------------
12614|    112|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12615|    112|      }
12616|    249|      out.components.protocol_end = protocol_end;
12617|    249|      out.components.username_end = protocol_end + 2;
12618|    249|      out.components.host_start = protocol_end + 2;
12619|    249|      out.components.host_end = static_cast<uint32_t>(host_end);
12620|    249|      out.components.port = url_components::omitted;
12621|    249|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12622|    249|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12622:37): [True: 40, False: 209]
  ------------------
12623|    249|                                        ? static_cast<uint32_t>(query_start + 1)
12624|    249|                                        : url_components::omitted;
12625|    249|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12625:35): [True: 41, False: 208]
  ------------------
12626|    249|                                      ? static_cast<uint32_t>(hash_start + 1)
12627|    249|                                      : url_components::omitted;
12628|    249|    }
12629|       |  } else {
12630|       |    std::string host_str(input.substr(host_start, host_len));
12631|       |    if (has_upper) {
12632|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
12633|       |    }
12634|       |    out.host = std::move(host_str);
12635|       |    if (need_slash) {
12636|       |      out.path = "/";
12637|       |    } else {
12638|       |      out.path.assign(input.data() + path_start, path_end - path_start);
12639|       |    }
12640|       |    if (query_start != std::string_view::npos) {
12641|       |      const size_t q_end =
12642|       |          (hash_start != std::string_view::npos) ? hash_start : len;
12643|       |      out.query.emplace(input.data() + query_start + 1,
12644|       |                        q_end - query_start - 1);
12645|       |    }
12646|       |    if (hash_start != std::string_view::npos) {
12647|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12648|       |    }
12649|       |  }
12650|  2.35k|  return true;
12651|  3.57k|}
_ZN3ada6parser32finish_simple_absolute_with_portINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmb:
11997|  1.94k|    bool has_upper) {
11998|  1.94k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11999|  1.94k|  constexpr bool is_aggregator =
12000|  1.94k|      std::is_same_v<result_type, ada::url_aggregator>;
12001|  1.94k|  static_assert(is_ada_url || is_aggregator);
12002|       |
12003|  1.94k|  const size_t len = input.size();
12004|  1.94k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12005|  1.94k|  size_t p = host_end + 1;
12006|  1.94k|  uint32_t port_value = 0;
12007|  1.94k|  bool any_digit = false;
12008|  4.10k|  while (p < len && b[p] >= '0' && b[p] <= '9') {
  ------------------
  |  Branch (12008:10): [True: 4.03k, False: 71]
  |  Branch (12008:21): [True: 2.30k, False: 1.72k]
  |  Branch (12008:36): [True: 2.16k, False: 138]
  ------------------
12009|  2.16k|    any_digit = true;
12010|  2.16k|    port_value = port_value * 10 + static_cast<uint32_t>(b[p] - '0');
12011|  2.16k|    if (port_value > 65535) [[unlikely]] {
  ------------------
  |  Branch (12011:9): [True: 11, False: 2.15k]
  ------------------
12012|     11|      return false;
12013|     11|    }
12014|  2.15k|    ++p;
12015|  2.15k|  }
12016|  1.93k|  if (p < len && b[p] != '/' && b[p] != '?' && b[p] != '#') [[unlikely]] {
  ------------------
  |  Branch (12016:7): [True: 1.86k, False: 71]
  |  Branch (12016:18): [True: 227, False: 1.63k]
  |  Branch (12016:33): [True: 127, False: 100]
  |  Branch (12016:48): [True: 55, False: 72]
  ------------------
12017|     55|    return false;
12018|     55|  }
12019|  1.88k|  uint32_t parsed_port = url_components::omitted;
12020|  1.88k|  const uint16_t default_port = ada::scheme::get_special_port(scheme_type);
12021|  1.88k|  if (any_digit && port_value != default_port) {
  ------------------
  |  Branch (12021:7): [True: 1.17k, False: 701]
  |  Branch (12021:20): [True: 1.17k, False: 4]
  ------------------
12022|  1.17k|    parsed_port = port_value;
12023|  1.17k|  }
12024|  1.88k|  const size_t authority_end = p;
12025|       |
12026|  1.88k|  size_t i = authority_end;
12027|  1.88k|  size_t path_start = host_end;
12028|  1.88k|  size_t path_end = host_end;
12029|  1.88k|  size_t query_start = std::string_view::npos;
12030|  1.88k|  size_t hash_start = std::string_view::npos;
12031|  1.88k|  bool has_path = false;
12032|  1.88k|  bool maybe_dot_segment = false;
12033|  1.88k|  bool rest_simple = true;
12034|       |
12035|  1.88k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12035:7): [True: 1.80k, False: 71]
  |  Branch (12035:18): [True: 1.63k, False: 172]
  ------------------
12036|  1.63k|    has_path = true;
12037|  1.63k|    path_start = i;
12038|  1.63k|    ++i;
12039|  1.63k|    scan_path_run(b, i, len, maybe_dot_segment);
12040|  1.63k|    if (i < len) {
  ------------------
  |  Branch (12040:9): [True: 1.01k, False: 623]
  ------------------
12041|  1.01k|      const uint8_t cls = k_path[b[i]];
12042|  1.01k|      if (cls == 1) {
  ------------------
  |  Branch (12042:11): [True: 205, False: 809]
  ------------------
12043|    205|        path_end = i;
12044|    205|        if (b[i] == '?') {
  ------------------
  |  Branch (12044:13): [True: 133, False: 72]
  ------------------
12045|    133|          query_start = i;
12046|    133|          ++i;
12047|    133|          goto scan_query;
12048|    133|        }
12049|     72|        hash_start = i;
12050|     72|        ++i;
12051|     72|        goto scan_hash;
12052|    205|      }
12053|    809|      rest_simple = false;
12054|  25.0k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12054:14): [True: 24.2k, False: 728]
  ------------------
12055|  24.2k|        if (b[i] == '?') {
  ------------------
  |  Branch (12055:13): [True: 54, False: 24.2k]
  ------------------
12056|     54|          path_end = i;
12057|     54|          query_start = i;
12058|     54|          ++i;
12059|     54|          goto scan_query_boundary;
12060|     54|        }
12061|  24.2k|        if (b[i] == '#') {
  ------------------
  |  Branch (12061:13): [True: 27, False: 24.2k]
  ------------------
12062|     27|          path_end = i;
12063|     27|          hash_start = i;
12064|     27|          ++i;
12065|     27|          goto after_rest;
12066|     27|        }
12067|  24.2k|      }
12068|    728|      path_end = i;
12069|    728|      goto after_rest;
12070|    809|    }
12071|    623|    path_end = i;
12072|    623|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12072:14): [True: 172, False: 71]
  |  Branch (12072:25): [True: 100, False: 72]
  ------------------
12073|    100|    query_start = i;
12074|    100|    ++i;
12075|    100|    goto scan_query;
12076|    143|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12076:14): [True: 72, False: 71]
  |  Branch (12076:25): [True: 72, False: 0]
  ------------------
12077|     72|    hash_start = i;
12078|     72|    ++i;
12079|     72|    goto scan_hash;
12080|     72|  }
12081|    694|  goto after_rest;
12082|       |
12083|    694|scan_query:
12084|    233|  scan_query_run(b, i, len);
12085|    233|  if (i < len) {
  ------------------
  |  Branch (12085:7): [True: 120, False: 113]
  ------------------
12086|    120|    if (b[i] == '#') {
  ------------------
  |  Branch (12086:9): [True: 54, False: 66]
  ------------------
12087|     54|      hash_start = i;
12088|     54|      ++i;
12089|     54|      goto scan_hash;
12090|     54|    }
12091|     66|    rest_simple = false;
12092|     66|    goto scan_query_boundary;
12093|    120|  }
12094|    113|  goto after_rest;
12095|       |
12096|    120|scan_query_boundary:
12097|  2.19k|  for (; i < len; ++i) {
  ------------------
  |  Branch (12097:10): [True: 2.09k, False: 100]
  ------------------
12098|  2.09k|    if (b[i] == '#') {
  ------------------
  |  Branch (12098:9): [True: 20, False: 2.07k]
  ------------------
12099|     20|      hash_start = i;
12100|     20|      ++i;
12101|     20|      goto after_rest;
12102|     20|    }
12103|  2.09k|  }
12104|    100|  goto after_rest;
12105|       |
12106|    198|scan_hash:
12107|    198|  scan_hash_run(b, i, len);
12108|    198|  if (i < len) {
  ------------------
  |  Branch (12108:7): [True: 64, False: 134]
  ------------------
12109|     64|    rest_simple = false;
12110|     64|  }
12111|       |
12112|  1.88k|after_rest:
12113|  1.88k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12113:7): [True: 941, False: 939]
  |  Branch (12113:22): [True: 372, False: 569]
  ------------------
12114|    372|    const std::string_view path_body(input.data() + path_start,
12115|    372|                                     path_end - path_start);
12116|    372|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12116:9): [True: 266, False: 106]
  ------------------
12117|    266|      rest_simple = false;
12118|    266|    }
12119|    372|  }
12120|       |
12121|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12122|       |  // trailing C0 control or space; this fast path does neither. A query or
12123|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12124|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12125|       |  // inputs back to the slow path.
12126|  1.88k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12126:7): [True: 1.20k, False: 675]
  |  Branch (12126:24): [True: 39, False: 1.16k]
  ------------------
12127|  1.16k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12127:24): [True: 27, False: 1.13k]
  ------------------
12128|     66|    return false;
12129|     66|  }
12130|       |
12131|  1.81k|  out.type = scheme_type;
12132|  1.81k|  out.is_valid = true;
12133|  1.81k|  out.has_opaque_path = false;
12134|  1.81k|  out.host_type = DEFAULT;
12135|       |
12136|  1.81k|  const uint32_t port_bytes = (parsed_port != url_components::omitted)
  ------------------
  |  Branch (12136:31): [True: 1.16k, False: 650]
  ------------------
12137|  1.81k|                                  ? (1 + port_decimal_digit_count(parsed_port))
12138|  1.81k|                                  : 0;
12139|       |
12140|  1.81k|  if (!rest_simple) {
  ------------------
  |  Branch (12140:7): [True: 1.13k, False: 675]
  ------------------
12141|  1.13k|    const std::string_view path_view =
12142|  1.13k|        has_path
  ------------------
  |  Branch (12142:9): [True: 1.06k, False: 78]
  ------------------
12143|  1.13k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12144|  1.13k|            : std::string_view{};
12145|  1.13k|    auto apply_query_and_hash = [&]() {
12146|  1.13k|      if (query_start != std::string_view::npos) {
12147|  1.13k|        const size_t q_end =
12148|  1.13k|            (hash_start != std::string_view::npos) ? hash_start : len;
12149|  1.13k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12150|  1.13k|                                                q_end - query_start - 1),
12151|  1.13k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12152|  1.13k|      }
12153|  1.13k|      if (hash_start != std::string_view::npos) {
12154|  1.13k|        out.update_unencoded_base_hash(std::string_view(
12155|  1.13k|            input.data() + hash_start + 1, len - hash_start - 1));
12156|  1.13k|      }
12157|  1.13k|    };
12158|       |    if constexpr (is_aggregator) {
12159|       |      out.buffer.assign(input.substr(0, host_end));
12160|       |      if (parsed_port != url_components::omitted) {
12161|       |        append_canonical_port(out.buffer, parsed_port);
12162|       |      }
12163|       |      if (has_upper) {
12164|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12165|       |      }
12166|       |      out.components.protocol_end = protocol_end;
12167|       |      out.components.username_end = protocol_end + 2;
12168|       |      out.components.host_start = protocol_end + 2;
12169|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12170|       |      out.components.port = parsed_port;
12171|       |      out.components.pathname_start = static_cast<uint32_t>(out.buffer.size());
12172|       |      out.components.search_start = url_components::omitted;
12173|       |      out.components.hash_start = url_components::omitted;
12174|       |      out.parse_path(path_view);
12175|       |      apply_query_and_hash();
12176|  1.13k|    } else {
12177|  1.13k|      std::string host_str(input.substr(host_start, host_len));
12178|  1.13k|      if (has_upper) {
  ------------------
  |  Branch (12178:11): [True: 140, False: 999]
  ------------------
12179|    140|        unicode::to_lower_ascii(host_str.data(), host_str.size());
12180|    140|      }
12181|  1.13k|      out.host = std::move(host_str);
12182|  1.13k|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12182:11): [True: 582, False: 557]
  ------------------
12183|    582|        out.port = static_cast<uint16_t>(parsed_port);
12184|    582|      }
12185|  1.13k|      out.parse_path(path_view);
12186|  1.13k|      apply_query_and_hash();
12187|  1.13k|    }
12188|  1.13k|    return true;
12189|  1.13k|  }
12190|       |
12191|    675|  const bool insert_slash = !has_path;
12192|       |  if constexpr (is_aggregator) {
12193|       |    const size_t out_len =
12194|       |        host_end + port_bytes + (insert_slash ? 1 : 0) + (len - authority_end);
12195|       |    const bool omit_port_bytes = parsed_port == url_components::omitted;
12196|       |    if (out_len == len && !insert_slash && !omit_port_bytes) {
12197|       |      out.buffer.assign(input);
12198|       |    } else {
12199|       |      out.buffer.clear();
12200|       |      out.buffer.reserve(out_len);
12201|       |      out.buffer.append(input.substr(0, host_end));
12202|       |      if (parsed_port != url_components::omitted) {
12203|       |        append_canonical_port(out.buffer, parsed_port);
12204|       |      }
12205|       |      if (insert_slash) {
12206|       |        out.buffer.push_back('/');
12207|       |      }
12208|       |      if (authority_end < len) {
12209|       |        out.buffer.append(input.substr(authority_end));
12210|       |      }
12211|       |    }
12212|       |    if (has_upper) {
12213|       |      unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12214|       |    }
12215|       |    const uint32_t pathname_start =
12216|       |        static_cast<uint32_t>(host_end) + port_bytes;
12217|       |    const int32_t tail_delta = static_cast<int32_t>(pathname_start) +
12218|       |                               (insert_slash ? 1 : 0) -
12219|       |                               static_cast<int32_t>(authority_end);
12220|       |    out.components.protocol_end = protocol_end;
12221|       |    out.components.username_end = protocol_end + 2;
12222|       |    out.components.host_start = protocol_end + 2;
12223|       |    out.components.host_end = static_cast<uint32_t>(host_end);
12224|       |    out.components.port = parsed_port;
12225|       |    out.components.pathname_start = pathname_start;
12226|       |    out.components.search_start =
12227|       |        (query_start != std::string_view::npos)
12228|       |            ? static_cast<uint32_t>(static_cast<int32_t>(query_start) +
12229|       |                                    tail_delta)
12230|       |            : url_components::omitted;
12231|       |    out.components.hash_start =
12232|       |        (hash_start != std::string_view::npos)
12233|       |            ? static_cast<uint32_t>(static_cast<int32_t>(hash_start) +
12234|       |                                    tail_delta)
12235|       |            : url_components::omitted;
12236|    675|  } else {
12237|    675|    std::string host_str(input.substr(host_start, host_len));
12238|    675|    if (has_upper) {
  ------------------
  |  Branch (12238:9): [True: 70, False: 605]
  ------------------
12239|     70|      unicode::to_lower_ascii(host_str.data(), host_str.size());
12240|     70|    }
12241|    675|    out.host = std::move(host_str);
12242|    675|    if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12242:9): [True: 582, False: 93]
  ------------------
12243|    582|      out.port = static_cast<uint16_t>(parsed_port);
12244|    582|    }
12245|    675|    if (insert_slash) {
  ------------------
  |  Branch (12245:9): [True: 140, False: 535]
  ------------------
12246|    140|      out.path = "/";
12247|    535|    } else {
12248|    535|      out.path.assign(input.data() + path_start, path_end - path_start);
12249|    535|    }
12250|    675|    if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12250:9): [True: 161, False: 514]
  ------------------
12251|    161|      const size_t q_end =
12252|    161|          (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12252:11): [True: 48, False: 113]
  ------------------
12253|    161|      out.query.emplace(input.data() + query_start + 1,
12254|    161|                        q_end - query_start - 1);
12255|    161|    }
12256|    675|    if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12256:9): [True: 134, False: 541]
  ------------------
12257|    134|      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12258|    134|    }
12259|    675|  }
12260|    675|  return true;
12261|  1.81k|}
_ZN3ada6parser32finish_simple_absolute_with_portINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmb:
11997|  1.94k|    bool has_upper) {
11998|  1.94k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11999|  1.94k|  constexpr bool is_aggregator =
12000|  1.94k|      std::is_same_v<result_type, ada::url_aggregator>;
12001|  1.94k|  static_assert(is_ada_url || is_aggregator);
12002|       |
12003|  1.94k|  const size_t len = input.size();
12004|  1.94k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12005|  1.94k|  size_t p = host_end + 1;
12006|  1.94k|  uint32_t port_value = 0;
12007|  1.94k|  bool any_digit = false;
12008|  4.10k|  while (p < len && b[p] >= '0' && b[p] <= '9') {
  ------------------
  |  Branch (12008:10): [True: 4.03k, False: 71]
  |  Branch (12008:21): [True: 2.30k, False: 1.72k]
  |  Branch (12008:36): [True: 2.16k, False: 138]
  ------------------
12009|  2.16k|    any_digit = true;
12010|  2.16k|    port_value = port_value * 10 + static_cast<uint32_t>(b[p] - '0');
12011|  2.16k|    if (port_value > 65535) [[unlikely]] {
  ------------------
  |  Branch (12011:9): [True: 11, False: 2.15k]
  ------------------
12012|     11|      return false;
12013|     11|    }
12014|  2.15k|    ++p;
12015|  2.15k|  }
12016|  1.93k|  if (p < len && b[p] != '/' && b[p] != '?' && b[p] != '#') [[unlikely]] {
  ------------------
  |  Branch (12016:7): [True: 1.86k, False: 71]
  |  Branch (12016:18): [True: 227, False: 1.63k]
  |  Branch (12016:33): [True: 127, False: 100]
  |  Branch (12016:48): [True: 55, False: 72]
  ------------------
12017|     55|    return false;
12018|     55|  }
12019|  1.88k|  uint32_t parsed_port = url_components::omitted;
12020|  1.88k|  const uint16_t default_port = ada::scheme::get_special_port(scheme_type);
12021|  1.88k|  if (any_digit && port_value != default_port) {
  ------------------
  |  Branch (12021:7): [True: 1.17k, False: 701]
  |  Branch (12021:20): [True: 1.17k, False: 4]
  ------------------
12022|  1.17k|    parsed_port = port_value;
12023|  1.17k|  }
12024|  1.88k|  const size_t authority_end = p;
12025|       |
12026|  1.88k|  size_t i = authority_end;
12027|  1.88k|  size_t path_start = host_end;
12028|  1.88k|  size_t path_end = host_end;
12029|  1.88k|  size_t query_start = std::string_view::npos;
12030|  1.88k|  size_t hash_start = std::string_view::npos;
12031|  1.88k|  bool has_path = false;
12032|  1.88k|  bool maybe_dot_segment = false;
12033|  1.88k|  bool rest_simple = true;
12034|       |
12035|  1.88k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12035:7): [True: 1.80k, False: 71]
  |  Branch (12035:18): [True: 1.63k, False: 172]
  ------------------
12036|  1.63k|    has_path = true;
12037|  1.63k|    path_start = i;
12038|  1.63k|    ++i;
12039|  1.63k|    scan_path_run(b, i, len, maybe_dot_segment);
12040|  1.63k|    if (i < len) {
  ------------------
  |  Branch (12040:9): [True: 1.01k, False: 623]
  ------------------
12041|  1.01k|      const uint8_t cls = k_path[b[i]];
12042|  1.01k|      if (cls == 1) {
  ------------------
  |  Branch (12042:11): [True: 205, False: 809]
  ------------------
12043|    205|        path_end = i;
12044|    205|        if (b[i] == '?') {
  ------------------
  |  Branch (12044:13): [True: 133, False: 72]
  ------------------
12045|    133|          query_start = i;
12046|    133|          ++i;
12047|    133|          goto scan_query;
12048|    133|        }
12049|     72|        hash_start = i;
12050|     72|        ++i;
12051|     72|        goto scan_hash;
12052|    205|      }
12053|    809|      rest_simple = false;
12054|  25.0k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12054:14): [True: 24.2k, False: 728]
  ------------------
12055|  24.2k|        if (b[i] == '?') {
  ------------------
  |  Branch (12055:13): [True: 54, False: 24.2k]
  ------------------
12056|     54|          path_end = i;
12057|     54|          query_start = i;
12058|     54|          ++i;
12059|     54|          goto scan_query_boundary;
12060|     54|        }
12061|  24.2k|        if (b[i] == '#') {
  ------------------
  |  Branch (12061:13): [True: 27, False: 24.2k]
  ------------------
12062|     27|          path_end = i;
12063|     27|          hash_start = i;
12064|     27|          ++i;
12065|     27|          goto after_rest;
12066|     27|        }
12067|  24.2k|      }
12068|    728|      path_end = i;
12069|    728|      goto after_rest;
12070|    809|    }
12071|    623|    path_end = i;
12072|    623|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12072:14): [True: 172, False: 71]
  |  Branch (12072:25): [True: 100, False: 72]
  ------------------
12073|    100|    query_start = i;
12074|    100|    ++i;
12075|    100|    goto scan_query;
12076|    143|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12076:14): [True: 72, False: 71]
  |  Branch (12076:25): [True: 72, False: 0]
  ------------------
12077|     72|    hash_start = i;
12078|     72|    ++i;
12079|     72|    goto scan_hash;
12080|     72|  }
12081|    694|  goto after_rest;
12082|       |
12083|    694|scan_query:
12084|    233|  scan_query_run(b, i, len);
12085|    233|  if (i < len) {
  ------------------
  |  Branch (12085:7): [True: 120, False: 113]
  ------------------
12086|    120|    if (b[i] == '#') {
  ------------------
  |  Branch (12086:9): [True: 54, False: 66]
  ------------------
12087|     54|      hash_start = i;
12088|     54|      ++i;
12089|     54|      goto scan_hash;
12090|     54|    }
12091|     66|    rest_simple = false;
12092|     66|    goto scan_query_boundary;
12093|    120|  }
12094|    113|  goto after_rest;
12095|       |
12096|    120|scan_query_boundary:
12097|  2.19k|  for (; i < len; ++i) {
  ------------------
  |  Branch (12097:10): [True: 2.09k, False: 100]
  ------------------
12098|  2.09k|    if (b[i] == '#') {
  ------------------
  |  Branch (12098:9): [True: 20, False: 2.07k]
  ------------------
12099|     20|      hash_start = i;
12100|     20|      ++i;
12101|     20|      goto after_rest;
12102|     20|    }
12103|  2.09k|  }
12104|    100|  goto after_rest;
12105|       |
12106|    198|scan_hash:
12107|    198|  scan_hash_run(b, i, len);
12108|    198|  if (i < len) {
  ------------------
  |  Branch (12108:7): [True: 64, False: 134]
  ------------------
12109|     64|    rest_simple = false;
12110|     64|  }
12111|       |
12112|  1.88k|after_rest:
12113|  1.88k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12113:7): [True: 941, False: 939]
  |  Branch (12113:22): [True: 372, False: 569]
  ------------------
12114|    372|    const std::string_view path_body(input.data() + path_start,
12115|    372|                                     path_end - path_start);
12116|    372|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12116:9): [True: 266, False: 106]
  ------------------
12117|    266|      rest_simple = false;
12118|    266|    }
12119|    372|  }
12120|       |
12121|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12122|       |  // trailing C0 control or space; this fast path does neither. A query or
12123|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12124|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12125|       |  // inputs back to the slow path.
12126|  1.88k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12126:7): [True: 1.20k, False: 675]
  |  Branch (12126:24): [True: 39, False: 1.16k]
  ------------------
12127|  1.16k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12127:24): [True: 27, False: 1.13k]
  ------------------
12128|     66|    return false;
12129|     66|  }
12130|       |
12131|  1.81k|  out.type = scheme_type;
12132|  1.81k|  out.is_valid = true;
12133|  1.81k|  out.has_opaque_path = false;
12134|  1.81k|  out.host_type = DEFAULT;
12135|       |
12136|  1.81k|  const uint32_t port_bytes = (parsed_port != url_components::omitted)
  ------------------
  |  Branch (12136:31): [True: 1.16k, False: 650]
  ------------------
12137|  1.81k|                                  ? (1 + port_decimal_digit_count(parsed_port))
12138|  1.81k|                                  : 0;
12139|       |
12140|  1.81k|  if (!rest_simple) {
  ------------------
  |  Branch (12140:7): [True: 1.13k, False: 675]
  ------------------
12141|  1.13k|    const std::string_view path_view =
12142|  1.13k|        has_path
  ------------------
  |  Branch (12142:9): [True: 1.06k, False: 78]
  ------------------
12143|  1.13k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12144|  1.13k|            : std::string_view{};
12145|  1.13k|    auto apply_query_and_hash = [&]() {
12146|  1.13k|      if (query_start != std::string_view::npos) {
12147|  1.13k|        const size_t q_end =
12148|  1.13k|            (hash_start != std::string_view::npos) ? hash_start : len;
12149|  1.13k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12150|  1.13k|                                                q_end - query_start - 1),
12151|  1.13k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12152|  1.13k|      }
12153|  1.13k|      if (hash_start != std::string_view::npos) {
12154|  1.13k|        out.update_unencoded_base_hash(std::string_view(
12155|  1.13k|            input.data() + hash_start + 1, len - hash_start - 1));
12156|  1.13k|      }
12157|  1.13k|    };
12158|  1.13k|    if constexpr (is_aggregator) {
12159|  1.13k|      out.buffer.assign(input.substr(0, host_end));
12160|  1.13k|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12160:11): [True: 582, False: 557]
  ------------------
12161|    582|        append_canonical_port(out.buffer, parsed_port);
12162|    582|      }
12163|  1.13k|      if (has_upper) {
  ------------------
  |  Branch (12163:11): [True: 140, False: 999]
  ------------------
12164|    140|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12165|    140|      }
12166|  1.13k|      out.components.protocol_end = protocol_end;
12167|  1.13k|      out.components.username_end = protocol_end + 2;
12168|  1.13k|      out.components.host_start = protocol_end + 2;
12169|  1.13k|      out.components.host_end = static_cast<uint32_t>(host_end);
12170|  1.13k|      out.components.port = parsed_port;
12171|  1.13k|      out.components.pathname_start = static_cast<uint32_t>(out.buffer.size());
12172|  1.13k|      out.components.search_start = url_components::omitted;
12173|  1.13k|      out.components.hash_start = url_components::omitted;
12174|  1.13k|      out.parse_path(path_view);
12175|  1.13k|      apply_query_and_hash();
12176|       |    } else {
12177|       |      std::string host_str(input.substr(host_start, host_len));
12178|       |      if (has_upper) {
12179|       |        unicode::to_lower_ascii(host_str.data(), host_str.size());
12180|       |      }
12181|       |      out.host = std::move(host_str);
12182|       |      if (parsed_port != url_components::omitted) {
12183|       |        out.port = static_cast<uint16_t>(parsed_port);
12184|       |      }
12185|       |      out.parse_path(path_view);
12186|       |      apply_query_and_hash();
12187|       |    }
12188|  1.13k|    return true;
12189|  1.13k|  }
12190|       |
12191|    675|  const bool insert_slash = !has_path;
12192|    675|  if constexpr (is_aggregator) {
12193|    675|    const size_t out_len =
12194|    675|        host_end + port_bytes + (insert_slash ? 1 : 0) + (len - authority_end);
  ------------------
  |  Branch (12194:34): [True: 140, False: 535]
  ------------------
12195|    675|    const bool omit_port_bytes = parsed_port == url_components::omitted;
12196|    675|    if (out_len == len && !insert_slash && !omit_port_bytes) {
  ------------------
  |  Branch (12196:9): [True: 545, False: 130]
  |  Branch (12196:27): [True: 494, False: 51]
  |  Branch (12196:44): [True: 494, False: 0]
  ------------------
12197|    494|      out.buffer.assign(input);
12198|    494|    } else {
12199|    181|      out.buffer.clear();
12200|    181|      out.buffer.reserve(out_len);
12201|    181|      out.buffer.append(input.substr(0, host_end));
12202|    181|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12202:11): [True: 88, False: 93]
  ------------------
12203|     88|        append_canonical_port(out.buffer, parsed_port);
12204|     88|      }
12205|    181|      if (insert_slash) {
  ------------------
  |  Branch (12205:11): [True: 140, False: 41]
  ------------------
12206|    140|        out.buffer.push_back('/');
12207|    140|      }
12208|    181|      if (authority_end < len) {
  ------------------
  |  Branch (12208:11): [True: 110, False: 71]
  ------------------
12209|    110|        out.buffer.append(input.substr(authority_end));
12210|    110|      }
12211|    181|    }
12212|    675|    if (has_upper) {
  ------------------
  |  Branch (12212:9): [True: 70, False: 605]
  ------------------
12213|     70|      unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12214|     70|    }
12215|    675|    const uint32_t pathname_start =
12216|    675|        static_cast<uint32_t>(host_end) + port_bytes;
12217|    675|    const int32_t tail_delta = static_cast<int32_t>(pathname_start) +
12218|    675|                               (insert_slash ? 1 : 0) -
  ------------------
  |  Branch (12218:33): [True: 140, False: 535]
  ------------------
12219|    675|                               static_cast<int32_t>(authority_end);
12220|    675|    out.components.protocol_end = protocol_end;
12221|    675|    out.components.username_end = protocol_end + 2;
12222|    675|    out.components.host_start = protocol_end + 2;
12223|    675|    out.components.host_end = static_cast<uint32_t>(host_end);
12224|    675|    out.components.port = parsed_port;
12225|    675|    out.components.pathname_start = pathname_start;
12226|    675|    out.components.search_start =
12227|    675|        (query_start != std::string_view::npos)
  ------------------
  |  Branch (12227:9): [True: 161, False: 514]
  ------------------
12228|    675|            ? static_cast<uint32_t>(static_cast<int32_t>(query_start) +
12229|    161|                                    tail_delta)
12230|    675|            : url_components::omitted;
12231|    675|    out.components.hash_start =
12232|    675|        (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12232:9): [True: 134, False: 541]
  ------------------
12233|    675|            ? static_cast<uint32_t>(static_cast<int32_t>(hash_start) +
12234|    134|                                    tail_delta)
12235|    675|            : url_components::omitted;
12236|       |  } else {
12237|       |    std::string host_str(input.substr(host_start, host_len));
12238|       |    if (has_upper) {
12239|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
12240|       |    }
12241|       |    out.host = std::move(host_str);
12242|       |    if (parsed_port != url_components::omitted) {
12243|       |      out.port = static_cast<uint16_t>(parsed_port);
12244|       |    }
12245|       |    if (insert_slash) {
12246|       |      out.path = "/";
12247|       |    } else {
12248|       |      out.path.assign(input.data() + path_start, path_end - path_start);
12249|       |    }
12250|       |    if (query_start != std::string_view::npos) {
12251|       |      const size_t q_end =
12252|       |          (hash_start != std::string_view::npos) ? hash_start : len;
12253|       |      out.query.emplace(input.data() + query_start + 1,
12254|       |                        q_end - query_start - 1);
12255|       |    }
12256|       |    if (hash_start != std::string_view::npos) {
12257|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12258|       |    }
12259|       |  }
12260|    675|  return true;
12261|  1.81k|}
_ZN3ada6parser25try_parse_simple_relativeINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERKT_RS8_:
12659|  4.57k|                                                result_type& out) {
12660|  4.57k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12661|  4.57k|  constexpr bool is_aggregator =
12662|  4.57k|      std::is_same_v<result_type, ada::url_aggregator>;
12663|  4.57k|  static_assert(is_ada_url || is_aggregator);
12664|       |
12665|  4.57k|  if (!base.is_special() || base.type == ada::scheme::type::FILE ||
  ------------------
  |  Branch (12665:7): [True: 2.39k, False: 2.18k]
  |  Branch (12665:29): [True: 387, False: 1.79k]
  ------------------
12666|  2.87k|      base.has_opaque_path || input.empty()) {
  ------------------
  |  Branch (12666:7): [True: 0, False: 1.79k]
  |  Branch (12666:31): [True: 86, False: 1.70k]
  ------------------
12667|  2.87k|    return false;
12668|  2.87k|  }
12669|  1.70k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12670|  1.70k|  const size_t len = input.size();
12671|  1.70k|  const uint8_t first = b[0];
12672|  1.70k|  const bool path_relative = first != '/' && first != '?' && first != '#';
  ------------------
  |  Branch (12672:30): [True: 1.64k, False: 62]
  |  Branch (12672:46): [True: 1.52k, False: 116]
  |  Branch (12672:62): [True: 1.46k, False: 63]
  ------------------
12673|  1.70k|  if (first == '/' && len > 1 && (b[1] == '/' || b[1] == '\\')) {
  ------------------
  |  Branch (12673:7): [True: 62, False: 1.64k]
  |  Branch (12673:23): [True: 50, False: 12]
  |  Branch (12673:35): [True: 4, False: 46]
  |  Branch (12673:50): [True: 1, False: 45]
  ------------------
12674|      5|    return false;
12675|      5|  }
12676|       |  // A ':' before the first / ? # is a scheme, not a path segment.
12677|  1.70k|  if (path_relative) {
  ------------------
  |  Branch (12677:7): [True: 1.46k, False: 236]
  ------------------
12678|  1.46k|    const size_t delim = input.find_first_of("/?#:");
12679|  1.46k|    if (delim != std::string_view::npos && input[delim] == ':') {
  ------------------
  |  Branch (12679:9): [True: 1.25k, False: 211]
  |  Branch (12679:44): [True: 958, False: 297]
  ------------------
12680|    958|      return false;
12681|    958|    }
12682|  1.46k|  }
12683|       |
12684|    744|  size_t i = 0;
12685|    744|  size_t path_start = 0;
12686|    744|  size_t path_end = 0;
12687|    744|  size_t query_start = std::string_view::npos;
12688|    744|  size_t hash_start = std::string_view::npos;
12689|    744|  bool has_path = false;
12690|    744|  bool maybe_dot_segment = false;
12691|       |
12692|    744|  if (first == '/' || path_relative) {
  ------------------
  |  Branch (12692:7): [True: 57, False: 687]
  |  Branch (12692:23): [True: 508, False: 179]
  ------------------
12693|    565|    has_path = true;
12694|    565|    path_start = 0;
12695|    565|    if (first == '/') {
  ------------------
  |  Branch (12695:9): [True: 57, False: 508]
  ------------------
12696|     57|      i = 1;
12697|     57|    }
12698|    565|    scan_path_run(b, i, len, maybe_dot_segment);
12699|    565|    if (i < len) {
  ------------------
  |  Branch (12699:9): [True: 330, False: 235]
  ------------------
12700|    330|      const uint8_t cls = k_path[b[i]];
12701|    330|      if (cls == 2) {
  ------------------
  |  Branch (12701:11): [True: 168, False: 162]
  ------------------
12702|    168|        return false;
12703|    168|      }
12704|    330|    }
12705|    397|    path_end = i;
12706|    397|    if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12706:9): [True: 162, False: 235]
  |  Branch (12706:20): [True: 121, False: 41]
  ------------------
12707|    121|      query_start = i;
12708|    121|      ++i;
12709|    121|      scan_query_run(b, i, len);
12710|    121|      if (i < len) {
  ------------------
  |  Branch (12710:11): [True: 84, False: 37]
  ------------------
12711|     84|        if (b[i] != '#') {
  ------------------
  |  Branch (12711:13): [True: 30, False: 54]
  ------------------
12712|     30|          return false;
12713|     30|        }
12714|     54|        hash_start = i;
12715|     54|        ++i;
12716|     54|        scan_hash_run(b, i, len);
12717|     54|        if (i < len) {
  ------------------
  |  Branch (12717:13): [True: 38, False: 16]
  ------------------
12718|     38|          return false;
12719|     38|        }
12720|     54|      }
12721|    276|    } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12721:16): [True: 41, False: 235]
  |  Branch (12721:27): [True: 41, False: 0]
  ------------------
12722|     41|      hash_start = i;
12723|     41|      ++i;
12724|     41|      scan_hash_run(b, i, len);
12725|     41|      if (i < len) {
  ------------------
  |  Branch (12725:11): [True: 25, False: 16]
  ------------------
12726|     25|        return false;
12727|     25|      }
12728|    235|    } else if (i < len) {
  ------------------
  |  Branch (12728:16): [True: 0, False: 235]
  ------------------
12729|      0|      return false;
12730|      0|    }
12731|    397|  } else if (first == '?') {
  ------------------
  |  Branch (12731:14): [True: 116, False: 63]
  ------------------
12732|    116|    query_start = 0;
12733|    116|    i = 1;
12734|    116|    scan_query_run(b, i, len);
12735|    116|    if (i < len) {
  ------------------
  |  Branch (12735:9): [True: 95, False: 21]
  ------------------
12736|     95|      if (b[i] != '#') {
  ------------------
  |  Branch (12736:11): [True: 32, False: 63]
  ------------------
12737|     32|        return false;
12738|     32|      }
12739|     63|      hash_start = i;
12740|     63|      ++i;
12741|     63|      scan_hash_run(b, i, len);
12742|     63|      if (i < len) {
  ------------------
  |  Branch (12742:11): [True: 48, False: 15]
  ------------------
12743|     48|        return false;
12744|     48|      }
12745|     63|    }
12746|    116|  } else {
12747|     63|    hash_start = 0;
12748|     63|    i = 1;
12749|     63|    scan_hash_run(b, i, len);
12750|     63|    if (i < len) {
  ------------------
  |  Branch (12750:9): [True: 37, False: 26]
  ------------------
12751|     37|      return false;
12752|     37|    }
12753|     63|  }
12754|       |
12755|    366|  if (has_path && maybe_dot_segment) {
  ------------------
  |  Branch (12755:7): [True: 304, False: 62]
  |  Branch (12755:19): [True: 116, False: 188]
  ------------------
12756|    116|    const std::string_view path_body(input.data() + path_start,
12757|    116|                                     path_end - path_start);
12758|    116|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12758:9): [True: 87, False: 29]
  ------------------
12759|     87|      return false;
12760|     87|    }
12761|    116|  }
12762|       |
12763|    279|  out = base;
12764|    279|  out.is_valid = true;
12765|    279|  out.has_opaque_path = false;
12766|       |
12767|    279|  const size_t q_end =
12768|    279|      (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12768:7): [True: 69, False: 210]
  ------------------
12769|    279|  const bool has_query = query_start != std::string_view::npos;
12770|    279|  const bool has_hash = hash_start != std::string_view::npos;
12771|    279|  const std::string_view query =
12772|    279|      has_query ? std::string_view(input.data() + query_start + 1,
  ------------------
  |  Branch (12772:7): [True: 86, False: 193]
  ------------------
12773|     86|                                   q_end - query_start - 1)
12774|    279|                : std::string_view{};
12775|    279|  const std::string_view hash =
12776|    279|      has_hash ? std::string_view(input.data() + hash_start + 1,
  ------------------
  |  Branch (12776:7): [True: 69, False: 210]
  ------------------
12777|     69|                                  len - hash_start - 1)
12778|    279|               : std::string_view{};
12779|       |
12780|       |  if constexpr (is_aggregator) {
12781|       |    if (first == '/' || first == '?' || path_relative) {
12782|       |      out.clear_hash();
12783|       |      out.clear_search();
12784|       |    }
12785|       |    if (first == '/') {
12786|       |      out.update_base_pathname(input.substr(path_start, path_end - path_start));
12787|       |    } else if (path_relative) {
12788|       |      const std::string_view base_path = out.get_pathname();
12789|       |      const size_t slash = base_path.rfind('/');
12790|       |      const std::string_view prefix = (slash == std::string_view::npos)
12791|       |                                          ? std::string_view("/")
12792|       |                                          : base_path.substr(0, slash + 1);
12793|       |      const std::string_view rel(input.data() + path_start,
12794|       |                                 path_end - path_start);
12795|       |      std::string new_path;
12796|       |      new_path.reserve(prefix.size() + rel.size());
12797|       |      new_path.assign(prefix);
12798|       |      new_path.append(rel);
12799|       |      out.update_base_pathname(new_path);
12800|       |    }
12801|       |    if (has_query) {
12802|       |      // Do not use update_base_search: it strips a leading '?' and would
12803|       |      // collapse '??a=b' into '?a=b'.
12804|       |      out.components.search_start = static_cast<uint32_t>(out.buffer.size());
12805|       |      out.buffer += '?';
12806|       |      out.buffer.append(query);
12807|       |    }
12808|       |    if (has_hash) {
12809|       |      out.update_unencoded_base_hash(hash);
12810|       |    }
12811|    279|  } else {
12812|    279|    if (first == '/' || first == '?' || path_relative) {
  ------------------
  |  Branch (12812:9): [True: 27, False: 252]
  |  Branch (12812:25): [True: 36, False: 216]
  |  Branch (12812:41): [True: 190, False: 26]
  ------------------
12813|    253|      out.hash.reset();
12814|    253|    }
12815|    279|    if (first == '/') {
  ------------------
  |  Branch (12815:9): [True: 27, False: 252]
  ------------------
12816|     27|      out.query.reset();
12817|     27|      out.path.assign(input.substr(path_start, path_end - path_start));
12818|    252|    } else if (path_relative) {
  ------------------
  |  Branch (12818:16): [True: 190, False: 62]
  ------------------
12819|    190|      out.query.reset();
12820|    190|      const size_t slash = out.path.rfind('/');
12821|    190|      if (slash == std::string::npos) {
  ------------------
  |  Branch (12821:11): [True: 0, False: 190]
  ------------------
12822|      0|        out.path = '/';
12823|    190|      } else {
12824|    190|        out.path.resize(slash + 1);
12825|    190|      }
12826|    190|      out.path.append(input.substr(path_start, path_end - path_start));
12827|    190|    }
12828|    279|    if (has_query) {
  ------------------
  |  Branch (12828:9): [True: 86, False: 193]
  ------------------
12829|     86|      out.query.emplace(query);
12830|     86|    }
12831|    279|    if (has_hash) {
  ------------------
  |  Branch (12831:9): [True: 69, False: 210]
  ------------------
12832|     69|      out.hash.emplace(hash);
12833|     69|    }
12834|    279|  }
12835|    279|  return true;
12836|    366|}
_ZN3ada6parser25try_parse_simple_relativeINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERKT_RS8_:
12659|  4.57k|                                                result_type& out) {
12660|  4.57k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12661|  4.57k|  constexpr bool is_aggregator =
12662|  4.57k|      std::is_same_v<result_type, ada::url_aggregator>;
12663|  4.57k|  static_assert(is_ada_url || is_aggregator);
12664|       |
12665|  4.57k|  if (!base.is_special() || base.type == ada::scheme::type::FILE ||
  ------------------
  |  Branch (12665:7): [True: 2.39k, False: 2.18k]
  |  Branch (12665:29): [True: 387, False: 1.79k]
  ------------------
12666|  2.87k|      base.has_opaque_path || input.empty()) {
  ------------------
  |  Branch (12666:7): [True: 0, False: 1.79k]
  |  Branch (12666:31): [True: 86, False: 1.70k]
  ------------------
12667|  2.87k|    return false;
12668|  2.87k|  }
12669|  1.70k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12670|  1.70k|  const size_t len = input.size();
12671|  1.70k|  const uint8_t first = b[0];
12672|  1.70k|  const bool path_relative = first != '/' && first != '?' && first != '#';
  ------------------
  |  Branch (12672:30): [True: 1.64k, False: 62]
  |  Branch (12672:46): [True: 1.52k, False: 116]
  |  Branch (12672:62): [True: 1.46k, False: 63]
  ------------------
12673|  1.70k|  if (first == '/' && len > 1 && (b[1] == '/' || b[1] == '\\')) {
  ------------------
  |  Branch (12673:7): [True: 62, False: 1.64k]
  |  Branch (12673:23): [True: 50, False: 12]
  |  Branch (12673:35): [True: 4, False: 46]
  |  Branch (12673:50): [True: 1, False: 45]
  ------------------
12674|      5|    return false;
12675|      5|  }
12676|       |  // A ':' before the first / ? # is a scheme, not a path segment.
12677|  1.70k|  if (path_relative) {
  ------------------
  |  Branch (12677:7): [True: 1.46k, False: 236]
  ------------------
12678|  1.46k|    const size_t delim = input.find_first_of("/?#:");
12679|  1.46k|    if (delim != std::string_view::npos && input[delim] == ':') {
  ------------------
  |  Branch (12679:9): [True: 1.25k, False: 211]
  |  Branch (12679:44): [True: 958, False: 297]
  ------------------
12680|    958|      return false;
12681|    958|    }
12682|  1.46k|  }
12683|       |
12684|    744|  size_t i = 0;
12685|    744|  size_t path_start = 0;
12686|    744|  size_t path_end = 0;
12687|    744|  size_t query_start = std::string_view::npos;
12688|    744|  size_t hash_start = std::string_view::npos;
12689|    744|  bool has_path = false;
12690|    744|  bool maybe_dot_segment = false;
12691|       |
12692|    744|  if (first == '/' || path_relative) {
  ------------------
  |  Branch (12692:7): [True: 57, False: 687]
  |  Branch (12692:23): [True: 508, False: 179]
  ------------------
12693|    565|    has_path = true;
12694|    565|    path_start = 0;
12695|    565|    if (first == '/') {
  ------------------
  |  Branch (12695:9): [True: 57, False: 508]
  ------------------
12696|     57|      i = 1;
12697|     57|    }
12698|    565|    scan_path_run(b, i, len, maybe_dot_segment);
12699|    565|    if (i < len) {
  ------------------
  |  Branch (12699:9): [True: 330, False: 235]
  ------------------
12700|    330|      const uint8_t cls = k_path[b[i]];
12701|    330|      if (cls == 2) {
  ------------------
  |  Branch (12701:11): [True: 168, False: 162]
  ------------------
12702|    168|        return false;
12703|    168|      }
12704|    330|    }
12705|    397|    path_end = i;
12706|    397|    if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12706:9): [True: 162, False: 235]
  |  Branch (12706:20): [True: 121, False: 41]
  ------------------
12707|    121|      query_start = i;
12708|    121|      ++i;
12709|    121|      scan_query_run(b, i, len);
12710|    121|      if (i < len) {
  ------------------
  |  Branch (12710:11): [True: 84, False: 37]
  ------------------
12711|     84|        if (b[i] != '#') {
  ------------------
  |  Branch (12711:13): [True: 30, False: 54]
  ------------------
12712|     30|          return false;
12713|     30|        }
12714|     54|        hash_start = i;
12715|     54|        ++i;
12716|     54|        scan_hash_run(b, i, len);
12717|     54|        if (i < len) {
  ------------------
  |  Branch (12717:13): [True: 38, False: 16]
  ------------------
12718|     38|          return false;
12719|     38|        }
12720|     54|      }
12721|    276|    } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12721:16): [True: 41, False: 235]
  |  Branch (12721:27): [True: 41, False: 0]
  ------------------
12722|     41|      hash_start = i;
12723|     41|      ++i;
12724|     41|      scan_hash_run(b, i, len);
12725|     41|      if (i < len) {
  ------------------
  |  Branch (12725:11): [True: 25, False: 16]
  ------------------
12726|     25|        return false;
12727|     25|      }
12728|    235|    } else if (i < len) {
  ------------------
  |  Branch (12728:16): [True: 0, False: 235]
  ------------------
12729|      0|      return false;
12730|      0|    }
12731|    397|  } else if (first == '?') {
  ------------------
  |  Branch (12731:14): [True: 116, False: 63]
  ------------------
12732|    116|    query_start = 0;
12733|    116|    i = 1;
12734|    116|    scan_query_run(b, i, len);
12735|    116|    if (i < len) {
  ------------------
  |  Branch (12735:9): [True: 95, False: 21]
  ------------------
12736|     95|      if (b[i] != '#') {
  ------------------
  |  Branch (12736:11): [True: 32, False: 63]
  ------------------
12737|     32|        return false;
12738|     32|      }
12739|     63|      hash_start = i;
12740|     63|      ++i;
12741|     63|      scan_hash_run(b, i, len);
12742|     63|      if (i < len) {
  ------------------
  |  Branch (12742:11): [True: 48, False: 15]
  ------------------
12743|     48|        return false;
12744|     48|      }
12745|     63|    }
12746|    116|  } else {
12747|     63|    hash_start = 0;
12748|     63|    i = 1;
12749|     63|    scan_hash_run(b, i, len);
12750|     63|    if (i < len) {
  ------------------
  |  Branch (12750:9): [True: 37, False: 26]
  ------------------
12751|     37|      return false;
12752|     37|    }
12753|     63|  }
12754|       |
12755|    366|  if (has_path && maybe_dot_segment) {
  ------------------
  |  Branch (12755:7): [True: 304, False: 62]
  |  Branch (12755:19): [True: 116, False: 188]
  ------------------
12756|    116|    const std::string_view path_body(input.data() + path_start,
12757|    116|                                     path_end - path_start);
12758|    116|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12758:9): [True: 87, False: 29]
  ------------------
12759|     87|      return false;
12760|     87|    }
12761|    116|  }
12762|       |
12763|    279|  out = base;
12764|    279|  out.is_valid = true;
12765|    279|  out.has_opaque_path = false;
12766|       |
12767|    279|  const size_t q_end =
12768|    279|      (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12768:7): [True: 69, False: 210]
  ------------------
12769|    279|  const bool has_query = query_start != std::string_view::npos;
12770|    279|  const bool has_hash = hash_start != std::string_view::npos;
12771|    279|  const std::string_view query =
12772|    279|      has_query ? std::string_view(input.data() + query_start + 1,
  ------------------
  |  Branch (12772:7): [True: 86, False: 193]
  ------------------
12773|     86|                                   q_end - query_start - 1)
12774|    279|                : std::string_view{};
12775|    279|  const std::string_view hash =
12776|    279|      has_hash ? std::string_view(input.data() + hash_start + 1,
  ------------------
  |  Branch (12776:7): [True: 69, False: 210]
  ------------------
12777|     69|                                  len - hash_start - 1)
12778|    279|               : std::string_view{};
12779|       |
12780|    279|  if constexpr (is_aggregator) {
12781|    279|    if (first == '/' || first == '?' || path_relative) {
  ------------------
  |  Branch (12781:9): [True: 27, False: 252]
  |  Branch (12781:25): [True: 36, False: 216]
  |  Branch (12781:41): [True: 190, False: 26]
  ------------------
12782|    253|      out.clear_hash();
12783|    253|      out.clear_search();
12784|    253|    }
12785|    279|    if (first == '/') {
  ------------------
  |  Branch (12785:9): [True: 27, False: 252]
  ------------------
12786|     27|      out.update_base_pathname(input.substr(path_start, path_end - path_start));
12787|    252|    } else if (path_relative) {
  ------------------
  |  Branch (12787:16): [True: 190, False: 62]
  ------------------
12788|    190|      const std::string_view base_path = out.get_pathname();
12789|    190|      const size_t slash = base_path.rfind('/');
12790|    190|      const std::string_view prefix = (slash == std::string_view::npos)
  ------------------
  |  Branch (12790:39): [True: 0, False: 190]
  ------------------
12791|    190|                                          ? std::string_view("/")
12792|    190|                                          : base_path.substr(0, slash + 1);
12793|    190|      const std::string_view rel(input.data() + path_start,
12794|    190|                                 path_end - path_start);
12795|    190|      std::string new_path;
12796|    190|      new_path.reserve(prefix.size() + rel.size());
12797|    190|      new_path.assign(prefix);
12798|    190|      new_path.append(rel);
12799|    190|      out.update_base_pathname(new_path);
12800|    190|    }
12801|    279|    if (has_query) {
  ------------------
  |  Branch (12801:9): [True: 86, False: 193]
  ------------------
12802|       |      // Do not use update_base_search: it strips a leading '?' and would
12803|       |      // collapse '??a=b' into '?a=b'.
12804|     86|      out.components.search_start = static_cast<uint32_t>(out.buffer.size());
12805|     86|      out.buffer += '?';
12806|     86|      out.buffer.append(query);
12807|     86|    }
12808|    279|    if (has_hash) {
  ------------------
  |  Branch (12808:9): [True: 69, False: 210]
  ------------------
12809|     69|      out.update_unencoded_base_hash(hash);
12810|     69|    }
12811|       |  } else {
12812|       |    if (first == '/' || first == '?' || path_relative) {
12813|       |      out.hash.reset();
12814|       |    }
12815|       |    if (first == '/') {
12816|       |      out.query.reset();
12817|       |      out.path.assign(input.substr(path_start, path_end - path_start));
12818|       |    } else if (path_relative) {
12819|       |      out.query.reset();
12820|       |      const size_t slash = out.path.rfind('/');
12821|       |      if (slash == std::string::npos) {
12822|       |        out.path = '/';
12823|       |      } else {
12824|       |        out.path.resize(slash + 1);
12825|       |      }
12826|       |      out.path.append(input.substr(path_start, path_end - path_start));
12827|       |    }
12828|       |    if (has_query) {
12829|       |      out.query.emplace(query);
12830|       |    }
12831|       |    if (has_hash) {
12832|       |      out.hash.emplace(hash);
12833|       |    }
12834|       |  }
12835|    279|  return true;
12836|    366|}
_ZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12840|  51.7k|                           const result_type* base_url) {
12841|       |  // We can specialize the implementation per type.
12842|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12843|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12844|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12845|       |  // and ada::url **do not have to support the exact same API**.
12846|  51.7k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12847|  51.7k|  constexpr bool result_type_is_ada_url_aggregator =
12848|  51.7k|      std::is_same_v<url_aggregator, result_type>;
12849|  51.7k|  static_assert(result_type_is_ada_url ||
12850|  51.7k|                result_type_is_ada_url_aggregator);  // We don't support
12851|       |                                                     // anything else for now.
12852|       |
12853|  51.7k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12854|  51.7k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12855|  51.7k|          ")");
12856|       |
12857|  51.7k|  result_type url{};
12858|       |
12859|  51.7k|  const uint32_t max_input_length = ada::get_max_input_length();
12860|       |
12861|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12862|       |  // max_input_length even when the input fit under it. The check at the end of
12863|       |  // the state machine covers URLs that run to completion, but several states
12864|       |  // return early after appending a query or fragment, so run the same check on
12865|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12866|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12867|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12868|  51.7k|  const auto enforce_max_input_length = [&]() {
12869|  51.7k|    if constexpr (store_values) {
12870|  51.7k|      if (url.is_valid) {
12871|  51.7k|        if constexpr (result_type_is_ada_url_aggregator) {
12872|  51.7k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12873|  51.7k|            url.is_valid = false;
12874|  51.7k|          }
12875|  51.7k|        } else {
12876|  51.7k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12877|  51.7k|            url.is_valid = false;
12878|  51.7k|          }
12879|  51.7k|        }
12880|  51.7k|      }
12881|  51.7k|    }
12882|  51.7k|  };
12883|       |
12884|       |  // We refuse to parse URL strings that exceed the maximum input length.
12885|       |  // By default, this is 4GB but can be configured via
12886|       |  // ada::set_max_input_length().
12887|  51.7k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12887:7): [True: 0, False: 51.7k]
  ------------------
12888|      0|    url.is_valid = false;
12889|      0|  }
12890|       |  // Going forward, user_input.size() is in [0,
12891|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12892|       |  // base, or the optional_url was invalid, we must return.
12893|  51.7k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12893:7): [True: 4.57k, False: 47.1k]
  ------------------
12894|  4.57k|    url.is_valid &= base_url->is_valid;
12895|  4.57k|  }
12896|  51.7k|  if (!url.is_valid) {
  ------------------
  |  Branch (12896:7): [True: 0, False: 51.7k]
  ------------------
12897|      0|    return url;
12898|      0|  }
12899|       |
12900|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12901|  51.7k|  if constexpr (store_values) {
12902|  51.7k|    bool hit_fast_path = false;
12903|  51.7k|    if (base_url == nullptr) {
  ------------------
  |  Branch (12903:9): [True: 47.1k, False: 4.57k]
  ------------------
12904|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12905|       |      // call so those URLs (including SetHref) do not pay for a miss.
12906|  47.1k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12907|  47.1k|      const size_t n = user_input.size();
12908|  47.1k|      size_t host_start = 0;
12909|  47.1k|      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
  ------------------
  |  Branch (12909:11): [True: 18.2k, False: 28.9k]
  |  Branch (12909:21): [True: 3.30k, False: 14.9k]
  |  Branch (12909:36): [True: 2.44k, False: 860]
  |  Branch (12909:51): [True: 2.41k, False: 29]
  ------------------
12910|  2.41k|        host_start = 7;
12911|  44.7k|      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
  ------------------
  |  Branch (12911:18): [True: 14.4k, False: 30.3k]
  |  Branch (12911:28): [True: 279, False: 14.1k]
  |  Branch (12911:43): [True: 57, False: 222]
  |  Branch (12911:58): [True: 33, False: 24]
  ------------------
12912|     33|        host_start = 8;
12913|     33|      }
12914|  47.1k|      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
  ------------------
  |  Branch (12914:34): [True: 2.44k, False: 44.7k]
  ------------------
12915|  47.1k|      const bool skip_ip =
12916|  47.1k|          host_first == '[' || (host_first >= '0' && host_first <= '9');
  ------------------
  |  Branch (12916:11): [True: 31, False: 47.1k]
  |  Branch (12916:33): [True: 1.76k, False: 45.3k]
  |  Branch (12916:54): [True: 578, False: 1.18k]
  ------------------
12917|  47.1k|      const bool skip_userinfo =
12918|  47.1k|          host_start != 0 && authority_has_at(p, host_start, n);
  ------------------
  |  Branch (12918:11): [True: 2.44k, False: 44.7k]
  |  Branch (12918:30): [True: 20, False: 2.42k]
  ------------------
12919|  47.1k|      hit_fast_path = !skip_ip && !skip_userinfo &&
  ------------------
  |  Branch (12919:23): [True: 46.5k, False: 609]
  |  Branch (12919:35): [True: 46.5k, False: 18]
  ------------------
12920|  46.5k|                      try_parse_simple_absolute(user_input, url);
  ------------------
  |  Branch (12920:23): [True: 5.38k, False: 41.1k]
  ------------------
12921|  47.1k|    } else {
12922|  4.57k|      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12923|  4.57k|    }
12924|  51.7k|    if (hit_fast_path) {
  ------------------
  |  Branch (12924:9): [True: 5.66k, False: 46.0k]
  ------------------
12925|       |      if constexpr (result_type_is_ada_url_aggregator) {
12926|       |        if (url.buffer.size() > max_input_length) [[unlikely]] {
12927|       |          url.is_valid = false;
12928|       |        }
12929|  5.66k|      } else {
12930|       |        // For a small absolute input, even worst-case normalization cannot
12931|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12932|       |        // most 4.5x. Avoid traversing every stored component on the common
12933|       |        // default-limit path.
12934|  5.66k|        if ((base_url != nullptr ||
  ------------------
  |  Branch (12934:14): [True: 279, False: 5.38k]
  ------------------
12935|  5.38k|             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
  ------------------
  |  Branch (12935:14): [True: 0, False: 5.38k]
  ------------------
12936|    279|            url.get_href_size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12936:13): [True: 0, False: 279]
  ------------------
12937|      0|          url.is_valid = false;
12938|      0|        }
12939|  5.66k|      }
12940|  5.66k|      return url;
12941|  5.66k|    }
12942|  51.7k|  }
12943|       |
12944|  46.0k|  state state = state::SCHEME_START;
12945|       |
12946|  51.7k|  std::string tmp_buffer;
12947|  51.7k|  std::string_view url_data;
12948|  51.7k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12948:7): [True: 423, False: 51.3k]
  ------------------
12949|    423|    tmp_buffer = user_input;
12950|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12951|       |    // just directly build the string from user_input.
12952|    423|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12953|    423|    url_data = tmp_buffer;
12954|  51.3k|  } else [[likely]] {
12955|  51.3k|    url_data = user_input;
12956|  51.3k|  }
12957|       |
12958|       |  // Leading and trailing control characters are uncommon and easy to deal with
12959|       |  // (no performance concern).
12960|  51.7k|  helpers::trim_c0_whitespace(url_data);
12961|       |
12962|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12963|       |    // Most of the time, we just need user_input.size().
12964|       |    // In some instances, we may need a bit more.
12965|       |    ///////////////////////////
12966|       |    // This is *very* important. This line should *not* be removed
12967|       |    // hastily. There are principled reasons why reserve is important
12968|       |    // for performance. If you have a benchmark with small inputs,
12969|       |    // it may not matter, but in other instances, it could.
12970|       |    ////
12971|       |    // This rounds up to the next power of two.
12972|       |    // We know that user_input.size() is in [0,
12973|       |    // std::numeric_limits<uint32_t>::max).
12974|       |    uint32_t reserve_capacity =
12975|       |        (0xFFFFFFFF >>
12976|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12977|       |        1;
12978|       |    url.reserve(reserve_capacity);
12979|       |  }
12980|       |
12981|       |  // Optimization opportunity. Most websites do not have fragment.
12982|  51.7k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12983|       |  // We add it last so that an implementation like ada::url_aggregator
12984|       |  // can append it last to its internal buffer, thus improving performance.
12985|       |
12986|       |  // Here url_data no longer has its fragment.
12987|       |  // We are going to access the data from url_data (it is immutable).
12988|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12989|       |  // The input_position variable should range from 0 to input_size.
12990|       |  // It is illegal to access url_data at input_size.
12991|  51.7k|  size_t input_position = 0;
12992|  51.7k|  const size_t input_size = url_data.size();
12993|       |  // Keep running the following state machine by switching on state.
12994|       |  // If after a run pointer points to the EOF code point, go to the next step.
12995|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12996|       |  // We never decrement input_position.
12997|   189k|  while (input_position <= input_size) {
  ------------------
  |  Branch (12997:10): [True: 172k, False: 17.3k]
  ------------------
12998|   172k|    ada_log("In parsing at ", input_position, " out of ", input_size,
12999|   172k|            " in state ", ada::to_string(state));
13000|   172k|    switch (state) {
13001|  46.0k|      case state::SCHEME_START: {
  ------------------
  |  Branch (13001:7): [True: 46.0k, False: 126k]
  ------------------
13002|  46.0k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13003|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13004|       |        // state to scheme state.
13005|  46.0k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13005:13): [True: 24.2k, False: 21.7k]
  ------------------
13006|  24.2k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13006:13): [True: 22.8k, False: 1.44k]
  ------------------
13007|  22.8k|          state = state::SCHEME;
13008|  22.8k|          input_position++;
13009|  23.2k|        } else {
13010|       |          // Otherwise, if state override is not given, set state to no scheme
13011|       |          // state and decrease pointer by 1.
13012|  23.2k|          state = state::NO_SCHEME;
13013|  23.2k|        }
13014|  46.0k|        break;
13015|      0|      }
13016|  22.8k|      case state::SCHEME: {
  ------------------
  |  Branch (13016:7): [True: 22.8k, False: 149k]
  ------------------
13017|  22.8k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13018|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13019|       |        // append c, lowercased, to buffer.
13020|  54.1k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13020:16): [True: 53.7k, False: 360]
  ------------------
13021|  53.7k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13021:16): [True: 31.2k, False: 22.4k]
  ------------------
13022|  31.2k|          input_position++;
13023|  31.2k|        }
13024|       |        // Otherwise, if c is U+003A (:), then:
13025|  22.8k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13025:13): [True: 22.4k, False: 360]
  ------------------
13026|  22.4k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13026:13): [True: 22.0k, False: 472]
  ------------------
13027|  22.0k|          ada_log("SCHEME the scheme should be ",
13028|  22.0k|                  url_data.substr(0, input_position));
13029|  22.0k|          if constexpr (result_type_is_ada_url) {
13030|  22.0k|            if (!url.parse_scheme(url_data.substr(0, input_position))) {
  ------------------
  |  Branch (13030:17): [True: 0, False: 22.0k]
  ------------------
13031|      0|              return url;
13032|      0|            }
13033|       |          } else {
13034|       |            // we pass the colon along instead of painfully adding it back.
13035|       |            if (!url.parse_scheme_with_colon(
13036|       |                    url_data.substr(0, input_position + 1))) {
13037|       |              return url;
13038|       |            }
13039|       |          }
13040|  22.0k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13041|       |
13042|       |          // If url's scheme is "file", then:
13043|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13044|  22.0k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13044:15): [True: 2.74k, False: 19.2k]
  ------------------
13045|       |            // Set state to file state.
13046|  2.74k|            state = state::FILE;
13047|  2.74k|          }
13048|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13049|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13050|       |          // != nullptr is false.
13051|  19.2k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13051:20): [True: 13.5k, False: 5.68k]
  |  Branch (13051:40): [True: 2.32k, False: 11.2k]
  ------------------
13052|  2.32k|                   base_url->type == url.type) {
  ------------------
  |  Branch (13052:20): [True: 436, False: 1.88k]
  ------------------
13053|       |            // Set state to special relative or authority state.
13054|    436|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13055|    436|          }
13056|       |          // Otherwise, if url is special, set state to special authority
13057|       |          // slashes state.
13058|  18.8k|          else if (url.is_special()) {
  ------------------
  |  Branch (13058:20): [True: 13.1k, False: 5.68k]
  ------------------
13059|  13.1k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13060|  13.1k|          }
13061|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13062|       |          // path or authority state and increase pointer by 1.
13063|  5.68k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13063:20): [True: 3.27k, False: 2.40k]
  ------------------
13064|  3.27k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13064:20): [True: 2.25k, False: 1.02k]
  ------------------
13065|  2.25k|            state = state::PATH_OR_AUTHORITY;
13066|  2.25k|            input_position++;
13067|  2.25k|          }
13068|       |          // Otherwise, set url's path to the empty string and set state to
13069|       |          // opaque path state.
13070|  3.43k|          else {
13071|  3.43k|            state = state::OPAQUE_PATH;
13072|  3.43k|          }
13073|  22.0k|        }
13074|       |        // Otherwise, if state override is not given, set buffer to the empty
13075|       |        // string, state to no scheme state, and start over (from the first code
13076|       |        // point in input).
13077|    832|        else {
13078|    832|          state = state::NO_SCHEME;
13079|    832|          input_position = 0;
13080|    832|          break;
13081|    832|        }
13082|  22.0k|        input_position++;
13083|  22.0k|        break;
13084|  22.8k|      }
13085|  24.0k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13085:7): [True: 24.0k, False: 148k]
  ------------------
13086|  24.0k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13087|       |        // The fragment was pruned from url_data before the state machine ran,
13088|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
13089|       |        // nothing else remains in front of it.
13090|  24.0k|        const bool c_is_hash =
13091|  24.0k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (13091:13): [True: 581, False: 23.4k]
  |  Branch (13091:37): [True: 266, False: 315]
  ------------------
13092|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13093|       |        // validation error, return failure.
13094|  24.0k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (13094:13): [True: 22.9k, False: 1.12k]
  |  Branch (13094:37): [True: 133, False: 994]
  |  Branch (13094:66): [True: 54, False: 79]
  ------------------
13095|  22.9k|          ada_log("NO_SCHEME validation error");
13096|  22.9k|          url.is_valid = false;
13097|  22.9k|          return url;
13098|  22.9k|        }
13099|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13100|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13101|       |        // query to base's query, and set state to fragment state.
13102|  1.07k|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (13102:18): [True: 79, False: 994]
  |  Branch (13102:47): [True: 79, False: 0]
  ------------------
13103|     79|          ada_log("NO_SCHEME opaque base with fragment");
13104|     79|          url.copy_scheme(*base_url);
13105|     79|          url.has_opaque_path = base_url->has_opaque_path;
13106|       |
13107|     79|          if constexpr (result_type_is_ada_url) {
13108|     79|            url.path = base_url->path;
13109|     79|            url.query = base_url->query;
13110|       |          } else {
13111|       |            url.update_base_pathname(base_url->get_pathname());
13112|       |            if (base_url->has_search()) {
13113|       |              // get_search() returns "" for an empty query string (URL ends
13114|       |              // with '?'). update_base_search("") would incorrectly clear the
13115|       |              // query, so pass "?" to preserve the empty query distinction.
13116|       |              auto s = base_url->get_search();
13117|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13118|       |            }
13119|       |          }
13120|     79|          url.update_unencoded_base_hash(*fragment);
13121|     79|          enforce_max_input_length();
13122|     79|          return url;
13123|     79|        }
13124|       |        // Otherwise, if base's scheme is not "file", set state to relative
13125|       |        // state and decrease pointer by 1.
13126|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13127|    994|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13127:18): [True: 752, False: 242]
  ------------------
13128|    752|          ada_log("NO_SCHEME non-file relative path");
13129|    752|          state = state::RELATIVE_SCHEME;
13130|    752|        }
13131|       |        // Otherwise, set state to file state and decrease pointer by 1.
13132|    242|        else {
13133|    242|          ada_log("NO_SCHEME file base type");
13134|    242|          state = state::FILE;
13135|    242|        }
13136|    994|        break;
13137|  24.0k|      }
13138|  14.4k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13138:7): [True: 14.4k, False: 158k]
  ------------------
13139|  14.4k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13140|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13141|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13142|       |        // worry about AUTHORITY.
13143|       |        // TODO: Instead of just collecting a bool, collect the location of the
13144|       |        // '@' and do something useful with it.
13145|       |        // TODO: We could do various processing early on, using a single pass
13146|       |        // over the string to collect information about it, e.g., telling us
13147|       |        // whether there is a @ and if so, where (or how many).
13148|       |
13149|       |        // Check if url data contains an @.
13150|  14.4k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13150:13): [True: 13.1k, False: 1.26k]
  ------------------
13151|  13.1k|          state = state::HOST;
13152|  13.1k|          break;
13153|  13.1k|        }
13154|  1.26k|        bool at_sign_seen{false};
13155|  1.26k|        bool password_token_seen{false};
13156|       |        /**
13157|       |         * We expect something of the sort...
13158|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13159|       |         * --------^
13160|       |         */
13161|  8.23k|        do {
13162|  8.23k|          std::string_view view = url_data.substr(input_position);
13163|       |          // The delimiters are @, /, ? \\.
13164|  8.23k|          size_t location =
13165|  8.23k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13165:15): [True: 7.36k, False: 866]
  ------------------
13166|  8.23k|                               : helpers::find_authority_delimiter(view);
13167|  8.23k|          std::string_view authority_view = view.substr(0, location);
13168|  8.23k|          size_t end_of_authority = input_position + authority_view.size();
13169|       |          // If c is U+0040 (@), then:
13170|  8.23k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13170:15): [True: 7.41k, False: 822]
  ------------------
13171|  7.41k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13171:15): [True: 6.96k, False: 445]
  ------------------
13172|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13173|  6.96k|            if (at_sign_seen) {
  ------------------
  |  Branch (13173:17): [True: 5.83k, False: 1.13k]
  ------------------
13174|  5.83k|              if (password_token_seen) {
  ------------------
  |  Branch (13174:19): [True: 2.35k, False: 3.47k]
  ------------------
13175|  2.35k|                if constexpr (result_type_is_ada_url) {
13176|  2.35k|                  url.password += "%40";
13177|       |                } else {
13178|       |                  url.append_base_password("%40");
13179|       |                }
13180|  3.47k|              } else {
13181|  3.47k|                if constexpr (result_type_is_ada_url) {
13182|  3.47k|                  url.username += "%40";
13183|       |                } else {
13184|       |                  url.append_base_username("%40");
13185|       |                }
13186|  3.47k|              }
13187|  5.83k|            }
13188|       |
13189|  6.96k|            at_sign_seen = true;
13190|       |
13191|  6.96k|            if (!password_token_seen) {
  ------------------
  |  Branch (13191:17): [True: 4.61k, False: 2.35k]
  ------------------
13192|  4.61k|              size_t password_token_location = authority_view.find(':');
13193|  4.61k|              password_token_seen =
13194|  4.61k|                  password_token_location != std::string_view::npos;
13195|       |
13196|  4.61k|              if constexpr (store_values) {
13197|  4.61k|                if (!password_token_seen) {
  ------------------
  |  Branch (13197:21): [True: 4.21k, False: 403]
  ------------------
13198|  4.21k|                  if constexpr (result_type_is_ada_url) {
13199|  4.21k|                    url.username += unicode::percent_encode(
13200|  4.21k|                        authority_view,
13201|  4.21k|                        character_sets::USERINFO_PERCENT_ENCODE);
13202|       |                  } else {
13203|       |                    url.append_base_username(unicode::percent_encode(
13204|       |                        authority_view,
13205|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13206|       |                  }
13207|  4.21k|                } else {
13208|    403|                  if constexpr (result_type_is_ada_url) {
13209|    403|                    url.username += unicode::percent_encode(
13210|    403|                        authority_view.substr(0, password_token_location),
13211|    403|                        character_sets::USERINFO_PERCENT_ENCODE);
13212|    403|                    url.password += unicode::percent_encode(
13213|    403|                        authority_view.substr(password_token_location + 1),
13214|    403|                        character_sets::USERINFO_PERCENT_ENCODE);
13215|       |                  } else {
13216|       |                    url.append_base_username(unicode::percent_encode(
13217|       |                        authority_view.substr(0, password_token_location),
13218|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13219|       |                    url.append_base_password(unicode::percent_encode(
13220|       |                        authority_view.substr(password_token_location + 1),
13221|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13222|       |                  }
13223|    403|                }
13224|  4.61k|              }
13225|  4.61k|            } else if constexpr (store_values) {
13226|  2.35k|              if constexpr (result_type_is_ada_url) {
13227|  2.35k|                url.password += unicode::percent_encode(
13228|  2.35k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13229|       |              } else {
13230|       |                url.append_base_password(unicode::percent_encode(
13231|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13232|       |              }
13233|  2.35k|            }
13234|  6.96k|          }
13235|       |          // Otherwise, if one of the following is true:
13236|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13237|       |          // - url is special and c is U+005C (\)
13238|  1.26k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13238:20): [True: 822, False: 445]
  ------------------
13239|    445|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13239:20): [True: 396, False: 49]
  ------------------
13240|     49|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13240:20): [True: 43, False: 6]
  ------------------
13241|  1.26k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13241:21): [True: 6, False: 0]
  |  Branch (13241:41): [True: 6, False: 0]
  ------------------
13242|       |            // If atSignSeen is true and authority_view is the empty string,
13243|       |            // validation error, return failure.
13244|  1.26k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13244:17): [True: 1.13k, False: 131]
  |  Branch (13244:33): [True: 345, False: 791]
  ------------------
13245|    345|              url.is_valid = false;
13246|    345|              return url;
13247|    345|            }
13248|    922|            state = state::HOST;
13249|    922|            break;
13250|  1.26k|          }
13251|  6.96k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13251:15): [True: 0, False: 6.96k]
  ------------------
13252|      0|            if constexpr (store_values) {
13253|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13253:19): [True: 0, False: 0]
  ------------------
13254|      0|                url.update_unencoded_base_hash(*fragment);
13255|      0|              }
13256|      0|            }
13257|      0|            enforce_max_input_length();
13258|      0|            return url;
13259|      0|          }
13260|  6.96k|          input_position = end_of_authority + 1;
13261|  6.96k|        } while (true);
  ------------------
  |  Branch (13261:18): [True: 6.96k, Folded]
  ------------------
13262|       |
13263|    922|        break;
13264|  1.26k|      }
13265|    922|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13265:7): [True: 436, False: 172k]
  ------------------
13266|    436|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13267|    436|                helpers::substring(url_data, input_position));
13268|       |
13269|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13270|       |        // then set state to special authority ignore slashes state and increase
13271|       |        // pointer by 1.
13272|    436|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13272:13): [True: 353, False: 83]
  ------------------
13273|    353|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13274|    353|          input_position += 2;
13275|    353|        } else {
13276|       |          // Otherwise, validation error, set state to relative state and
13277|       |          // decrease pointer by 1.
13278|     83|          state = state::RELATIVE_SCHEME;
13279|     83|        }
13280|       |
13281|    436|        break;
13282|  1.26k|      }
13283|  2.25k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (13283:7): [True: 2.25k, False: 170k]
  ------------------
13284|  2.25k|        ada_log("PATH_OR_AUTHORITY ",
13285|  2.25k|                helpers::substring(url_data, input_position));
13286|       |
13287|       |        // If c is U+002F (/), then set state to authority state.
13288|  2.25k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13288:13): [True: 2.13k, False: 122]
  ------------------
13289|  2.13k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13289:13): [True: 907, False: 1.22k]
  ------------------
13290|    907|          state = state::AUTHORITY;
13291|    907|          input_position++;
13292|  1.34k|        } else {
13293|       |          // Otherwise, set state to path state, and decrease pointer by 1.
13294|  1.34k|          state = state::PATH;
13295|  1.34k|        }
13296|       |
13297|  2.25k|        break;
13298|  1.26k|      }
13299|    835|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (13299:7): [True: 835, False: 171k]
  ------------------
13300|    835|        ada_log("RELATIVE_SCHEME ",
13301|    835|                helpers::substring(url_data, input_position));
13302|       |
13303|       |        // Set url's scheme to base's scheme.
13304|    835|        url.copy_scheme(*base_url);
13305|       |
13306|       |        // If c is U+002F (/), then set state to relative slash state.
13307|    835|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13307:13): [True: 679, False: 156]
  ------------------
13308|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
13309|    679|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13309:13): [True: 72, False: 607]
  ------------------
13310|     72|          ada_log(
13311|     72|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13312|     72|              "slash state");
13313|     72|          state = state::RELATIVE_SLASH;
13314|    763|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13314:20): [True: 631, False: 132]
  |  Branch (13314:40): [True: 502, False: 129]
  ------------------
13315|    502|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13315:20): [True: 4, False: 498]
  ------------------
13316|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13317|       |          // set state to relative slash state.
13318|      4|          ada_log(
13319|      4|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13320|      4|              "error, set state to relative slash state");
13321|      4|          state = state::RELATIVE_SLASH;
13322|    759|        } else {
13323|    759|          ada_log("RELATIVE_SCHEME otherwise");
13324|       |          // Set url's username to base's username, url's password to base's
13325|       |          // password, url's host to base's host, url's port to base's port,
13326|       |          // url's path to a clone of base's path, and url's query to base's
13327|       |          // query.
13328|    759|          if constexpr (result_type_is_ada_url) {
13329|    759|            url.username = base_url->username;
13330|    759|            url.password = base_url->password;
13331|    759|            url.host = base_url->host;
13332|    759|            url.port = base_url->port;
13333|       |            // cloning the base path includes cloning the has_opaque_path flag
13334|    759|            url.has_opaque_path = base_url->has_opaque_path;
13335|    759|            url.path = base_url->path;
13336|    759|            url.query = base_url->query;
13337|       |          } else {
13338|       |            url.update_base_authority(base_url->get_href(),
13339|       |                                      base_url->get_components());
13340|       |            url.update_host_to_base_host(base_url->get_hostname());
13341|       |            url.update_base_port(base_url->retrieve_base_port());
13342|       |            // cloning the base path includes cloning the has_opaque_path flag
13343|       |            url.has_opaque_path = base_url->has_opaque_path;
13344|       |            url.update_base_pathname(base_url->get_pathname());
13345|       |            if (base_url->has_search()) {
13346|       |              // get_search() returns "" for an empty query string (URL ends
13347|       |              // with '?'). update_base_search("") would incorrectly clear the
13348|       |              // query, so pass "?" to preserve the empty query distinction.
13349|       |              auto s = base_url->get_search();
13350|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13351|       |            }
13352|       |          }
13353|       |
13354|    759|          url.has_opaque_path = base_url->has_opaque_path;
13355|       |
13356|       |          // If c is U+003F (?), then set url's query to the empty string, and
13357|       |          // state to query state.
13358|    759|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13358:15): [True: 603, False: 156]
  ------------------
13359|    603|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13359:15): [True: 88, False: 515]
  ------------------
13360|     88|            state = state::QUERY;
13361|     88|          }
13362|       |          // Otherwise, if c is not the EOF code point:
13363|    671|          else if (input_position != input_size) {
  ------------------
  |  Branch (13363:20): [True: 515, False: 156]
  ------------------
13364|       |            // Set url's query to null.
13365|    515|            url.clear_search();
13366|    515|            if constexpr (result_type_is_ada_url) {
13367|       |              // Shorten url's path.
13368|    515|              helpers::shorten_path(url.path, url.type);
13369|       |            } else {
13370|       |              std::string_view path = url.get_pathname();
13371|       |              if (helpers::shorten_path(path, url.type)) {
13372|       |                url.update_base_pathname(std::move(std::string(path)));
13373|       |              }
13374|       |            }
13375|       |            // Set state to path state and decrease pointer by 1.
13376|    515|            state = state::PATH;
13377|    515|            break;
13378|    515|          }
13379|    759|        }
13380|    320|        input_position++;
13381|    320|        break;
13382|    835|      }
13383|     76|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13383:7): [True: 76, False: 172k]
  ------------------
13384|     76|        ada_log("RELATIVE_SLASH ",
13385|     76|                helpers::substring(url_data, input_position));
13386|       |
13387|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13388|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13389|     76|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13389:13): [True: 55, False: 21]
  |  Branch (13389:33): [True: 42, False: 13]
  ------------------
13390|     42|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13390:14): [True: 4, False: 38]
  ------------------
13391|     38|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13391:14): [True: 2, False: 36]
  ------------------
13392|       |          // Set state to special authority ignore slashes state.
13393|      6|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13394|      6|        }
13395|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13396|     70|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13396:18): [True: 47, False: 23]
  ------------------
13397|     47|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13397:18): [True: 6, False: 41]
  ------------------
13398|      6|          state = state::AUTHORITY;
13399|      6|        }
13400|       |        // Otherwise, set
13401|       |        // - url's username to base's username,
13402|       |        // - url's password to base's password,
13403|       |        // - url's host to base's host,
13404|       |        // - url's port to base's port,
13405|       |        // - state to path state, and then, decrease pointer by 1.
13406|     64|        else {
13407|     64|          if constexpr (result_type_is_ada_url) {
13408|     64|            url.username = base_url->username;
13409|     64|            url.password = base_url->password;
13410|     64|            url.host = base_url->host;
13411|     64|            url.port = base_url->port;
13412|       |          } else {
13413|       |            url.update_base_authority(base_url->get_href(),
13414|       |                                      base_url->get_components());
13415|       |            url.update_host_to_base_host(base_url->get_hostname());
13416|       |            url.update_base_port(base_url->retrieve_base_port());
13417|       |          }
13418|     64|          state = state::PATH;
13419|     64|          break;
13420|     64|        }
13421|       |
13422|     12|        input_position++;
13423|     12|        break;
13424|     76|      }
13425|  13.1k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13425:7): [True: 13.1k, False: 159k]
  ------------------
13426|  13.1k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13427|  13.1k|                helpers::substring(url_data, input_position));
13428|       |
13429|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13430|       |        // then set state to special authority ignore slashes state and increase
13431|       |        // pointer by 1.
13432|  13.1k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13432:13): [True: 3.39k, False: 9.74k]
  ------------------
13433|  3.39k|          input_position += 2;
13434|  3.39k|        }
13435|       |
13436|  13.1k|        [[fallthrough]];
13437|  13.1k|      }
13438|  13.4k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13438:7): [True: 359, False: 172k]
  ------------------
13439|  13.4k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13440|  13.4k|                helpers::substring(url_data, input_position));
13441|       |
13442|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13443|       |        // authority state and decrease pointer by 1.
13444|  15.3k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13444:16): [True: 15.2k, False: 95]
  ------------------
13445|  15.2k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13445:17): [True: 1.19k, False: 14.0k]
  ------------------
13446|  14.0k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13446:17): [True: 614, False: 13.4k]
  ------------------
13447|  1.80k|          input_position++;
13448|  1.80k|        }
13449|  13.4k|        state = state::AUTHORITY;
13450|       |
13451|  13.4k|        break;
13452|  13.1k|      }
13453|  1.22k|      case state::QUERY: {
  ------------------
  |  Branch (13453:7): [True: 1.22k, False: 171k]
  ------------------
13454|  1.22k|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13455|  1.22k|        if constexpr (store_values) {
13456|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13457|       |          // if url is special; otherwise the query percent-encode set.
13458|  1.22k|          const uint8_t* query_percent_encode_set =
13459|  1.22k|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13459:15): [True: 786, False: 439]
  ------------------
13460|  1.22k|                               : character_sets::QUERY_PERCENT_ENCODE;
13461|       |
13462|       |          // Percent-encode after encoding, with encoding, buffer, and
13463|       |          // queryPercentEncodeSet, and append the result to url's query.
13464|  1.22k|          url.update_base_search(url_data.substr(input_position),
13465|  1.22k|                                 query_percent_encode_set);
13466|  1.22k|          ada_log("QUERY update_base_search completed ");
13467|  1.22k|          if (fragment.has_value()) {
  ------------------
  |  Branch (13467:15): [True: 317, False: 908]
  ------------------
13468|    317|            url.update_unencoded_base_hash(*fragment);
13469|    317|          }
13470|  1.22k|        }
13471|  1.22k|        enforce_max_input_length();
13472|  1.22k|        return url;
13473|  13.1k|      }
13474|  14.0k|      case state::HOST: {
  ------------------
  |  Branch (13474:7): [True: 14.0k, False: 158k]
  ------------------
13475|  14.0k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13476|       |
13477|  14.0k|        std::string_view host_view = url_data.substr(input_position);
13478|  14.0k|        auto [location, found_colon] =
13479|  14.0k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13480|  14.0k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13480:26): [True: 14.0k, False: 0]
  ------------------
13481|  14.0k|                             ? input_position + location
13482|  14.0k|                             : input_size;
13483|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13484|       |        // Note: the 'found_colon' value is true if and only if a colon was
13485|       |        // encountered while not inside brackets.
13486|  14.0k|        if (found_colon) {
  ------------------
  |  Branch (13486:13): [True: 2.51k, False: 11.5k]
  ------------------
13487|       |          // If buffer is the empty string, validation error, return failure.
13488|       |          // Let host be the result of host parsing buffer with url is not
13489|       |          // special.
13490|  2.51k|          ada_log("HOST parsing ", host_view);
13491|  2.51k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13491:15): [True: 223, False: 2.29k]
  ------------------
13492|    223|            return url;
13493|    223|          }
13494|  2.29k|          ada_log("HOST parsing results in ", url.get_hostname());
13495|       |          // Set url's host to host, buffer to the empty string, and state to
13496|       |          // port state.
13497|  2.29k|          state = state::PORT;
13498|  2.29k|          input_position++;
13499|  2.29k|        }
13500|       |        // Otherwise, if one of the following is true:
13501|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13502|       |        // - url is special and c is U+005C (\)
13503|       |        // The get_host_delimiter_location function either brings us to
13504|       |        // the colon outside of the bracket, or to one of those characters.
13505|  11.5k|        else {
13506|       |          // If url is special and host_view is the empty string, validation
13507|       |          // error, return failure.
13508|  11.5k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13508:15): [True: 245, False: 11.3k]
  |  Branch (13508:36): [True: 101, False: 144]
  ------------------
13509|    101|            url.is_valid = false;
13510|    101|            return url;
13511|    101|          }
13512|  11.4k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13513|       |          // Let host be the result of host parsing host_view with url is not
13514|       |          // special.
13515|  11.4k|          if (host_view.empty()) {
  ------------------
  |  Branch (13515:15): [True: 144, False: 11.3k]
  ------------------
13516|    144|            url.update_base_hostname("");
13517|  11.3k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13517:22): [True: 2.41k, False: 8.88k]
  ------------------
13518|  2.41k|            return url;
13519|  2.41k|          }
13520|  9.03k|          ada_log("HOST parsing results in ", url.get_hostname(),
13521|  9.03k|                  " href=", url.get_href());
13522|       |
13523|       |          // Set url's host to host, and state to path start state.
13524|  9.03k|          state = state::PATH_START;
13525|  9.03k|        }
13526|       |
13527|  11.3k|        break;
13528|  14.0k|      }
13529|  11.3k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13529:7): [True: 3.43k, False: 169k]
  ------------------
13530|  3.43k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13531|       |        // Opaque path, query, and fragment are structurally always valid:
13532|       |        // the parser would just percent-encode whatever is there. When we
13533|       |        // are not storing values (can_parse), we can return immediately.
13534|       |        // We must set has_opaque_path = true before returning so that when
13535|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13536|       |        // correctly rejects relative inputs against an opaque-path base
13537|       |        // (e.g. can_parse("", &"W:") must return false).
13538|       |        if constexpr (!store_values) {
13539|       |          url.has_opaque_path = true;
13540|       |          return url;
13541|       |        }
13542|  3.43k|        std::string_view view = url_data.substr(input_position);
13543|       |        // If c is U+003F (?), then set url's query to the empty string and
13544|       |        // state to query state.
13545|  3.43k|        size_t location = view.find('?');
13546|  3.43k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13546:13): [True: 314, False: 3.12k]
  ------------------
13547|    314|          view.remove_suffix(view.size() - location);
13548|    314|          state = state::QUERY;
13549|    314|          input_position += location + 1;
13550|  3.12k|        } else {
13551|  3.12k|          input_position = input_size + 1;
13552|  3.12k|        }
13553|  3.43k|        url.has_opaque_path = true;
13554|       |
13555|       |        // This is a really unlikely scenario in real world. We should not seek
13556|       |        // to optimize it.
13557|  3.43k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13557:13): [True: 119, False: 3.31k]
  ------------------
13558|    119|          std::string modified_view =
13559|    119|              std::string(view.substr(0, view.size() - 1)) + "%20";
13560|    119|          url.update_base_pathname(unicode::percent_encode(
13561|    119|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13562|  3.31k|        } else {
13563|  3.31k|          url.update_base_pathname(unicode::percent_encode(
13564|  3.31k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13565|  3.31k|        }
13566|  3.43k|        break;
13567|  14.0k|      }
13568|  2.29k|      case state::PORT: {
  ------------------
  |  Branch (13568:7): [True: 2.29k, False: 170k]
  ------------------
13569|  2.29k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13570|  2.29k|        std::string_view port_view = url_data.substr(input_position);
13571|  2.29k|        input_position += url.parse_port(port_view, true);
13572|  2.29k|        if (!url.is_valid) {
  ------------------
  |  Branch (13572:13): [True: 125, False: 2.16k]
  ------------------
13573|    125|          return url;
13574|    125|        }
13575|  2.16k|        state = state::PATH_START;
13576|  2.16k|        [[fallthrough]];
13577|  2.16k|      }
13578|  13.1k|      case state::PATH_START: {
  ------------------
  |  Branch (13578:7): [True: 11.0k, False: 161k]
  ------------------
13579|  13.1k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13580|       |        // Path, query, and fragment are structurally always valid: the
13581|       |        // parser would just percent-encode whatever is there. When we are
13582|       |        // not storing values (can_parse), we can return immediately since
13583|       |        // no subsequent state can invalidate the URL.
13584|       |        if constexpr (!store_values) {
13585|       |          return url;
13586|       |        }
13587|       |
13588|       |        // If url is special, then:
13589|  13.1k|        if (url.is_special()) {
  ------------------
  |  Branch (13589:13): [True: 12.4k, False: 777]
  ------------------
13590|       |          // Set state to path state.
13591|  12.4k|          state = state::PATH;
13592|       |
13593|       |          // Optimization: Avoiding going into PATH state improves the
13594|       |          // performance of urls ending with /.
13595|  12.4k|          if (input_position == input_size) {
  ------------------
  |  Branch (13595:15): [True: 6.67k, False: 5.72k]
  ------------------
13596|  6.67k|            if constexpr (store_values) {
13597|  6.67k|              url.update_base_pathname("/");
13598|  6.67k|              if (fragment.has_value()) {
  ------------------
  |  Branch (13598:19): [True: 170, False: 6.50k]
  ------------------
13599|    170|                url.update_unencoded_base_hash(*fragment);
13600|    170|              }
13601|  6.67k|            }
13602|  6.67k|            enforce_max_input_length();
13603|  6.67k|            return url;
13604|  6.67k|          }
13605|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13606|       |          // by 1. We know that (input_position == input_size) is impossible
13607|       |          // here, because of the previous if-check.
13608|  5.72k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13608:15): [True: 378, False: 5.34k]
  ------------------
13609|    378|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13609:15): [True: 361, False: 17]
  ------------------
13610|    361|            break;
13611|    361|          }
13612|  5.72k|        }
13613|       |        // Otherwise, if state override is not given and c is U+003F (?),
13614|       |        // set url's query to the empty string and state to query state.
13615|    777|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13615:18): [True: 171, False: 606]
  ------------------
13616|    171|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13616:18): [True: 60, False: 111]
  ------------------
13617|     60|          state = state::QUERY;
13618|     60|        }
13619|       |        // Otherwise, if c is not the EOF code point:
13620|    717|        else if (input_position != input_size) {
  ------------------
  |  Branch (13620:18): [True: 111, False: 606]
  ------------------
13621|       |          // Set state to path state.
13622|    111|          state = state::PATH;
13623|       |
13624|       |          // If c is not U+002F (/), then decrease pointer by 1.
13625|    111|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13625:15): [True: 0, False: 111]
  ------------------
13626|      0|            break;
13627|      0|          }
13628|    111|        }
13629|       |
13630|  6.14k|        input_position++;
13631|  6.14k|        break;
13632|  13.1k|      }
13633|  8.50k|      case state::PATH: {
  ------------------
  |  Branch (13633:7): [True: 8.50k, False: 164k]
  ------------------
13634|  8.50k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13635|       |        // Path, query, and fragment are structurally always valid: the
13636|       |        // parser would just percent-encode whatever is there. When we are
13637|       |        // not storing values (can_parse), we can return immediately since
13638|       |        // no subsequent state can invalidate the URL.
13639|       |        if constexpr (!store_values) {
13640|       |          return url;
13641|       |        }
13642|  8.50k|        std::string_view view = url_data.substr(input_position);
13643|       |
13644|       |        // Most time, we do not need percent encoding.
13645|       |        // Furthermore, we can immediately locate the '?'.
13646|  8.50k|        size_t locofquestionmark = view.find('?');
13647|  8.50k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13647:13): [True: 755, False: 7.75k]
  ------------------
13648|    755|          state = state::QUERY;
13649|    755|          view.remove_suffix(view.size() - locofquestionmark);
13650|    755|          input_position += locofquestionmark + 1;
13651|  7.75k|        } else {
13652|  7.75k|          input_position = input_size + 1;
13653|  7.75k|        }
13654|  8.50k|        if constexpr (store_values) {
13655|  8.50k|          if constexpr (result_type_is_ada_url) {
13656|  8.50k|            helpers::parse_prepared_path(view, url.type, url.path);
13657|       |          } else {
13658|       |            url.consume_prepared_path(view);
13659|       |            ADA_ASSERT_TRUE(url.validate());
13660|       |          }
13661|  8.50k|        }
13662|  8.50k|        break;
13663|  13.1k|      }
13664|  2.30k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13664:7): [True: 2.30k, False: 170k]
  ------------------
13665|  2.30k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13666|       |
13667|       |        // If c is U+002F (/) or U+005C (\), then:
13668|  2.30k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13668:13): [True: 2.23k, False: 70]
  ------------------
13669|  2.23k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13669:14): [True: 2.18k, False: 49]
  ------------------
13670|  2.19k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13670:14): [True: 6, False: 43]
  ------------------
13671|  2.19k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13672|       |          // Set state to file host state.
13673|  2.19k|          state = state::FILE_HOST;
13674|  2.19k|          input_position++;
13675|  2.19k|        } else {
13676|    113|          ada_log("FILE_SLASH otherwise");
13677|       |          // If base is non-null and base's scheme is "file", then:
13678|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13679|       |          // base_url_has_value() is true.
13680|    113|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13680:15): [True: 89, False: 24]
  |  Branch (13680:38): [True: 87, False: 2]
  ------------------
13681|       |            // Set url's host to base's host.
13682|     87|            if constexpr (result_type_is_ada_url) {
13683|     87|              url.host = base_url->host;
13684|       |            } else {
13685|       |              url.update_host_to_base_host(base_url->get_host());
13686|       |            }
13687|       |            // If the code point substring from pointer to the end of input does
13688|       |            // not start with a Windows drive letter and base's path[0] is a
13689|       |            // normalized Windows drive letter, then append base's path[0] to
13690|       |            // url's path.
13691|     87|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13691:17): [True: 87, False: 0]
  ------------------
13692|     87|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13692:19): [True: 82, False: 5]
  ------------------
13693|     87|                      url_data.substr(input_position))) {
13694|     82|                std::string_view first_base_url_path =
13695|     82|                    base_url->get_pathname().substr(1);
13696|     82|                size_t loc = first_base_url_path.find('/');
13697|     82|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13697:21): [True: 11, False: 71]
  ------------------
13698|     11|                  helpers::resize(first_base_url_path, loc);
13699|     11|                }
13700|     82|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13700:21): [True: 4, False: 78]
  ------------------
13701|     82|                        first_base_url_path)) {
13702|      4|                  if constexpr (result_type_is_ada_url) {
13703|      4|                    url.path += '/';
13704|      4|                    url.path += first_base_url_path;
13705|       |                  } else {
13706|       |                    url.append_base_pathname(
13707|       |                        helpers::concat("/", first_base_url_path));
13708|       |                  }
13709|      4|                }
13710|     82|              }
13711|     87|            }
13712|     87|          }
13713|       |
13714|       |          // Set state to path state, and decrease pointer by 1.
13715|    113|          state = state::PATH;
13716|    113|        }
13717|       |
13718|  2.30k|        break;
13719|  13.1k|      }
13720|  2.19k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13720:7): [True: 2.19k, False: 170k]
  ------------------
13721|  2.19k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13722|  2.19k|        std::string_view view = url_data.substr(input_position);
13723|       |
13724|  2.19k|        size_t location = view.find_first_of("/\\?");
13725|  2.19k|        std::string_view file_host_buffer = view.substr(
13726|  2.19k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13726:16): [True: 943, False: 1.25k]
  ------------------
13727|       |
13728|  2.19k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13728:13): [True: 12, False: 2.18k]
  ------------------
13729|     12|          state = state::PATH;
13730|  2.18k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13730:20): [True: 286, False: 1.89k]
  ------------------
13731|       |          // Set url's host to the empty string.
13732|    286|          if constexpr (result_type_is_ada_url) {
13733|    286|            url.host = "";
13734|       |          } else {
13735|       |            url.update_base_hostname("");
13736|       |          }
13737|       |          // Set state to path start state.
13738|    286|          state = state::PATH_START;
13739|  1.89k|        } else {
13740|  1.89k|          size_t consumed_bytes = file_host_buffer.size();
13741|  1.89k|          input_position += consumed_bytes;
13742|       |          // Let host be the result of host parsing buffer with url is not
13743|       |          // special.
13744|  1.89k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13744:15): [True: 199, False: 1.69k]
  ------------------
13745|    199|            return url;
13746|    199|          }
13747|       |
13748|  1.69k|          if constexpr (result_type_is_ada_url) {
13749|       |            // If host is "localhost", then set host to the empty string.
13750|  1.69k|            if (url.host.has_value() && url.host.value() == "localhost") {
  ------------------
  |  Branch (13750:17): [True: 1.69k, False: 0]
  |  Branch (13750:41): [True: 11, False: 1.68k]
  ------------------
13751|     11|              url.host = "";
13752|     11|            }
13753|       |          } else {
13754|       |            if (url.get_hostname() == "localhost") {
13755|       |              url.update_base_hostname("");
13756|       |            }
13757|       |          }
13758|       |
13759|       |          // Set buffer to the empty string and state to path start state.
13760|  1.69k|          state = state::PATH_START;
13761|  1.69k|        }
13762|       |
13763|  1.99k|        break;
13764|  2.19k|      }
13765|  2.98k|      case state::FILE: {
  ------------------
  |  Branch (13765:7): [True: 2.98k, False: 169k]
  ------------------
13766|  2.98k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13767|  2.98k|        std::string_view file_view = url_data.substr(input_position);
13768|       |
13769|  2.98k|        url.set_protocol_as_file();
13770|  2.98k|        if constexpr (result_type_is_ada_url) {
13771|       |          // Set url's host to the empty string.
13772|  2.98k|          url.host = "";
13773|       |        } else {
13774|       |          url.update_base_hostname("");
13775|       |        }
13776|       |        // If c is U+002F (/) or U+005C (\), then:
13777|  2.98k|        if (input_position != input_size &&
  ------------------
  |  Branch (13777:13): [True: 2.86k, False: 124]
  ------------------
13778|  2.86k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13778:14): [True: 2.30k, False: 562]
  ------------------
13779|  2.30k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13779:14): [True: 6, False: 556]
  ------------------
13780|  2.30k|          ada_log("FILE c is U+002F or U+005C");
13781|       |          // Set state to file slash state.
13782|  2.30k|          state = state::FILE_SLASH;
13783|  2.30k|        }
13784|       |        // Otherwise, if base is non-null and base's scheme is "file":
13785|    680|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13785:18): [True: 252, False: 428]
  |  Branch (13785:41): [True: 178, False: 74]
  ------------------
13786|       |          // Set url's host to base's host, url's path to a clone of base's
13787|       |          // path, and url's query to base's query.
13788|    178|          ada_log("FILE base non-null");
13789|    178|          if constexpr (result_type_is_ada_url) {
13790|    178|            url.host = base_url->host;
13791|    178|            url.path = base_url->path;
13792|    178|            url.query = base_url->query;
13793|       |          } else {
13794|       |            url.update_host_to_base_host(base_url->get_hostname());
13795|       |            url.update_base_pathname(base_url->get_pathname());
13796|       |            if (base_url->has_search()) {
13797|       |              // get_search() returns "" for an empty query string (URL ends
13798|       |              // with '?'). update_base_search("") would incorrectly clear the
13799|       |              // query, so pass "?" to preserve the empty query distinction.
13800|       |              auto s = base_url->get_search();
13801|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13802|       |            }
13803|       |          }
13804|    178|          url.has_opaque_path = base_url->has_opaque_path;
13805|       |
13806|       |          // If c is U+003F (?), then set url's query to the empty string and
13807|       |          // state to query state.
13808|    178|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13808:15): [True: 124, False: 54]
  |  Branch (13808:47): [True: 8, False: 116]
  ------------------
13809|      8|            state = state::QUERY;
13810|      8|          }
13811|       |          // Otherwise, if c is not the EOF code point:
13812|    170|          else if (input_position != input_size) {
  ------------------
  |  Branch (13812:20): [True: 116, False: 54]
  ------------------
13813|       |            // Set url's query to null.
13814|    116|            url.clear_search();
13815|       |            // If the code point substring from pointer to the end of input does
13816|       |            // not start with a Windows drive letter, then shorten url's path.
13817|    116|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13817:17): [True: 97, False: 19]
  ------------------
13818|     97|              if constexpr (result_type_is_ada_url) {
13819|     97|                helpers::shorten_path(url.path, url.type);
13820|       |              } else {
13821|       |                std::string_view path = url.get_pathname();
13822|       |                if (helpers::shorten_path(path, url.type)) {
13823|       |                  url.update_base_pathname(std::move(std::string(path)));
13824|       |                }
13825|       |              }
13826|     97|            }
13827|       |            // Otherwise:
13828|     19|            else {
13829|       |              // Set url's path to an empty list.
13830|     19|              url.clear_pathname();
13831|     19|              url.has_opaque_path = true;
13832|     19|            }
13833|       |
13834|       |            // Set state to path state and decrease pointer by 1.
13835|    116|            state = state::PATH;
13836|    116|            break;
13837|    116|          }
13838|    178|        }
13839|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13840|    502|        else {
13841|    502|          ada_log("FILE go to path");
13842|    502|          state = state::PATH;
13843|    502|          break;
13844|    502|        }
13845|       |
13846|  2.36k|        input_position++;
13847|  2.36k|        break;
13848|  2.98k|      }
13849|      0|      default:
  ------------------
  |  Branch (13849:7): [True: 0, False: 172k]
  ------------------
13850|      0|        unreachable();
13851|   172k|    }
13852|   172k|  }
13853|  17.3k|  if constexpr (store_values) {
13854|  17.3k|    if (fragment.has_value()) {
  ------------------
  |  Branch (13854:9): [True: 424, False: 16.9k]
  ------------------
13855|    424|      url.update_unencoded_base_hash(*fragment);
13856|    424|    }
13857|  17.3k|  }
13858|  17.3k|  enforce_max_input_length();
13859|  17.3k|  return url;
13860|  51.7k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12840|  51.7k|                           const result_type* base_url) {
12841|       |  // We can specialize the implementation per type.
12842|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12843|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12844|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12845|       |  // and ada::url **do not have to support the exact same API**.
12846|  51.7k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12847|  51.7k|  constexpr bool result_type_is_ada_url_aggregator =
12848|  51.7k|      std::is_same_v<url_aggregator, result_type>;
12849|  51.7k|  static_assert(result_type_is_ada_url ||
12850|  51.7k|                result_type_is_ada_url_aggregator);  // We don't support
12851|       |                                                     // anything else for now.
12852|       |
12853|  51.7k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12854|  51.7k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12855|  51.7k|          ")");
12856|       |
12857|  51.7k|  result_type url{};
12858|       |
12859|  51.7k|  const uint32_t max_input_length = ada::get_max_input_length();
12860|       |
12861|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12862|       |  // max_input_length even when the input fit under it. The check at the end of
12863|       |  // the state machine covers URLs that run to completion, but several states
12864|       |  // return early after appending a query or fragment, so run the same check on
12865|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12866|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12867|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12868|  51.7k|  const auto enforce_max_input_length = [&]() {
12869|  51.7k|    if constexpr (store_values) {
12870|  51.7k|      if (url.is_valid) {
12871|  51.7k|        if constexpr (result_type_is_ada_url_aggregator) {
12872|  51.7k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12873|  51.7k|            url.is_valid = false;
12874|  51.7k|          }
12875|  51.7k|        } else {
12876|  51.7k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12877|  51.7k|            url.is_valid = false;
12878|  51.7k|          }
12879|  51.7k|        }
12880|  51.7k|      }
12881|  51.7k|    }
12882|  51.7k|  };
12883|       |
12884|       |  // We refuse to parse URL strings that exceed the maximum input length.
12885|       |  // By default, this is 4GB but can be configured via
12886|       |  // ada::set_max_input_length().
12887|  51.7k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12887:7): [True: 0, False: 51.7k]
  ------------------
12888|      0|    url.is_valid = false;
12889|      0|  }
12890|       |  // Going forward, user_input.size() is in [0,
12891|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12892|       |  // base, or the optional_url was invalid, we must return.
12893|  51.7k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12893:7): [True: 4.57k, False: 47.1k]
  ------------------
12894|  4.57k|    url.is_valid &= base_url->is_valid;
12895|  4.57k|  }
12896|  51.7k|  if (!url.is_valid) {
  ------------------
  |  Branch (12896:7): [True: 0, False: 51.7k]
  ------------------
12897|      0|    return url;
12898|      0|  }
12899|       |
12900|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12901|  51.7k|  if constexpr (store_values) {
12902|  51.7k|    bool hit_fast_path = false;
12903|  51.7k|    if (base_url == nullptr) {
  ------------------
  |  Branch (12903:9): [True: 47.1k, False: 4.57k]
  ------------------
12904|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12905|       |      // call so those URLs (including SetHref) do not pay for a miss.
12906|  47.1k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12907|  47.1k|      const size_t n = user_input.size();
12908|  47.1k|      size_t host_start = 0;
12909|  47.1k|      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
  ------------------
  |  Branch (12909:11): [True: 18.2k, False: 28.9k]
  |  Branch (12909:21): [True: 3.30k, False: 14.9k]
  |  Branch (12909:36): [True: 2.44k, False: 860]
  |  Branch (12909:51): [True: 2.41k, False: 29]
  ------------------
12910|  2.41k|        host_start = 7;
12911|  44.7k|      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
  ------------------
  |  Branch (12911:18): [True: 14.4k, False: 30.3k]
  |  Branch (12911:28): [True: 279, False: 14.1k]
  |  Branch (12911:43): [True: 57, False: 222]
  |  Branch (12911:58): [True: 33, False: 24]
  ------------------
12912|     33|        host_start = 8;
12913|     33|      }
12914|  47.1k|      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
  ------------------
  |  Branch (12914:34): [True: 2.44k, False: 44.7k]
  ------------------
12915|  47.1k|      const bool skip_ip =
12916|  47.1k|          host_first == '[' || (host_first >= '0' && host_first <= '9');
  ------------------
  |  Branch (12916:11): [True: 31, False: 47.1k]
  |  Branch (12916:33): [True: 1.76k, False: 45.3k]
  |  Branch (12916:54): [True: 578, False: 1.18k]
  ------------------
12917|  47.1k|      const bool skip_userinfo =
12918|  47.1k|          host_start != 0 && authority_has_at(p, host_start, n);
  ------------------
  |  Branch (12918:11): [True: 2.44k, False: 44.7k]
  |  Branch (12918:30): [True: 20, False: 2.42k]
  ------------------
12919|  47.1k|      hit_fast_path = !skip_ip && !skip_userinfo &&
  ------------------
  |  Branch (12919:23): [True: 46.5k, False: 609]
  |  Branch (12919:35): [True: 46.5k, False: 18]
  ------------------
12920|  46.5k|                      try_parse_simple_absolute(user_input, url);
  ------------------
  |  Branch (12920:23): [True: 5.38k, False: 41.1k]
  ------------------
12921|  47.1k|    } else {
12922|  4.57k|      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12923|  4.57k|    }
12924|  51.7k|    if (hit_fast_path) {
  ------------------
  |  Branch (12924:9): [True: 5.66k, False: 46.0k]
  ------------------
12925|  5.66k|      if constexpr (result_type_is_ada_url_aggregator) {
12926|  5.66k|        if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12926:13): [True: 0, False: 5.66k]
  ------------------
12927|      0|          url.is_valid = false;
12928|      0|        }
12929|       |      } else {
12930|       |        // For a small absolute input, even worst-case normalization cannot
12931|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12932|       |        // most 4.5x. Avoid traversing every stored component on the common
12933|       |        // default-limit path.
12934|       |        if ((base_url != nullptr ||
12935|       |             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
12936|       |            url.get_href_size() > max_input_length) [[unlikely]] {
12937|       |          url.is_valid = false;
12938|       |        }
12939|       |      }
12940|  5.66k|      return url;
12941|  5.66k|    }
12942|  51.7k|  }
12943|       |
12944|  46.0k|  state state = state::SCHEME_START;
12945|       |
12946|  51.7k|  std::string tmp_buffer;
12947|  51.7k|  std::string_view url_data;
12948|  51.7k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12948:7): [True: 423, False: 51.3k]
  ------------------
12949|    423|    tmp_buffer = user_input;
12950|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12951|       |    // just directly build the string from user_input.
12952|    423|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12953|    423|    url_data = tmp_buffer;
12954|  51.3k|  } else [[likely]] {
12955|  51.3k|    url_data = user_input;
12956|  51.3k|  }
12957|       |
12958|       |  // Leading and trailing control characters are uncommon and easy to deal with
12959|       |  // (no performance concern).
12960|  51.7k|  helpers::trim_c0_whitespace(url_data);
12961|       |
12962|  51.7k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12963|       |    // Most of the time, we just need user_input.size().
12964|       |    // In some instances, we may need a bit more.
12965|       |    ///////////////////////////
12966|       |    // This is *very* important. This line should *not* be removed
12967|       |    // hastily. There are principled reasons why reserve is important
12968|       |    // for performance. If you have a benchmark with small inputs,
12969|       |    // it may not matter, but in other instances, it could.
12970|       |    ////
12971|       |    // This rounds up to the next power of two.
12972|       |    // We know that user_input.size() is in [0,
12973|       |    // std::numeric_limits<uint32_t>::max).
12974|  51.7k|    uint32_t reserve_capacity =
12975|  51.7k|        (0xFFFFFFFF >>
12976|  51.7k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12977|  51.7k|        1;
12978|  51.7k|    url.reserve(reserve_capacity);
12979|  51.7k|  }
12980|       |
12981|       |  // Optimization opportunity. Most websites do not have fragment.
12982|  51.7k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12983|       |  // We add it last so that an implementation like ada::url_aggregator
12984|       |  // can append it last to its internal buffer, thus improving performance.
12985|       |
12986|       |  // Here url_data no longer has its fragment.
12987|       |  // We are going to access the data from url_data (it is immutable).
12988|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12989|       |  // The input_position variable should range from 0 to input_size.
12990|       |  // It is illegal to access url_data at input_size.
12991|  51.7k|  size_t input_position = 0;
12992|  51.7k|  const size_t input_size = url_data.size();
12993|       |  // Keep running the following state machine by switching on state.
12994|       |  // If after a run pointer points to the EOF code point, go to the next step.
12995|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12996|       |  // We never decrement input_position.
12997|   189k|  while (input_position <= input_size) {
  ------------------
  |  Branch (12997:10): [True: 172k, False: 17.3k]
  ------------------
12998|   172k|    ada_log("In parsing at ", input_position, " out of ", input_size,
12999|   172k|            " in state ", ada::to_string(state));
13000|   172k|    switch (state) {
13001|  46.0k|      case state::SCHEME_START: {
  ------------------
  |  Branch (13001:7): [True: 46.0k, False: 126k]
  ------------------
13002|  46.0k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13003|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13004|       |        // state to scheme state.
13005|  46.0k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13005:13): [True: 24.2k, False: 21.7k]
  ------------------
13006|  24.2k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13006:13): [True: 22.8k, False: 1.44k]
  ------------------
13007|  22.8k|          state = state::SCHEME;
13008|  22.8k|          input_position++;
13009|  23.2k|        } else {
13010|       |          // Otherwise, if state override is not given, set state to no scheme
13011|       |          // state and decrease pointer by 1.
13012|  23.2k|          state = state::NO_SCHEME;
13013|  23.2k|        }
13014|  46.0k|        break;
13015|      0|      }
13016|  22.8k|      case state::SCHEME: {
  ------------------
  |  Branch (13016:7): [True: 22.8k, False: 149k]
  ------------------
13017|  22.8k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13018|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13019|       |        // append c, lowercased, to buffer.
13020|  54.1k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13020:16): [True: 53.7k, False: 360]
  ------------------
13021|  53.7k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13021:16): [True: 31.2k, False: 22.4k]
  ------------------
13022|  31.2k|          input_position++;
13023|  31.2k|        }
13024|       |        // Otherwise, if c is U+003A (:), then:
13025|  22.8k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13025:13): [True: 22.4k, False: 360]
  ------------------
13026|  22.4k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13026:13): [True: 22.0k, False: 472]
  ------------------
13027|  22.0k|          ada_log("SCHEME the scheme should be ",
13028|  22.0k|                  url_data.substr(0, input_position));
13029|       |          if constexpr (result_type_is_ada_url) {
13030|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
13031|       |              return url;
13032|       |            }
13033|  22.0k|          } else {
13034|       |            // we pass the colon along instead of painfully adding it back.
13035|  22.0k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (13035:17): [True: 0, False: 22.0k]
  ------------------
13036|  22.0k|                    url_data.substr(0, input_position + 1))) {
13037|      0|              return url;
13038|      0|            }
13039|  22.0k|          }
13040|  22.0k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13041|       |
13042|       |          // If url's scheme is "file", then:
13043|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13044|  22.0k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13044:15): [True: 2.74k, False: 19.2k]
  ------------------
13045|       |            // Set state to file state.
13046|  2.74k|            state = state::FILE;
13047|  2.74k|          }
13048|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13049|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13050|       |          // != nullptr is false.
13051|  19.2k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13051:20): [True: 13.5k, False: 5.68k]
  |  Branch (13051:40): [True: 2.32k, False: 11.2k]
  ------------------
13052|  2.32k|                   base_url->type == url.type) {
  ------------------
  |  Branch (13052:20): [True: 436, False: 1.88k]
  ------------------
13053|       |            // Set state to special relative or authority state.
13054|    436|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13055|    436|          }
13056|       |          // Otherwise, if url is special, set state to special authority
13057|       |          // slashes state.
13058|  18.8k|          else if (url.is_special()) {
  ------------------
  |  Branch (13058:20): [True: 13.1k, False: 5.68k]
  ------------------
13059|  13.1k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13060|  13.1k|          }
13061|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13062|       |          // path or authority state and increase pointer by 1.
13063|  5.68k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13063:20): [True: 3.27k, False: 2.40k]
  ------------------
13064|  3.27k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13064:20): [True: 2.25k, False: 1.02k]
  ------------------
13065|  2.25k|            state = state::PATH_OR_AUTHORITY;
13066|  2.25k|            input_position++;
13067|  2.25k|          }
13068|       |          // Otherwise, set url's path to the empty string and set state to
13069|       |          // opaque path state.
13070|  3.43k|          else {
13071|  3.43k|            state = state::OPAQUE_PATH;
13072|  3.43k|          }
13073|  22.0k|        }
13074|       |        // Otherwise, if state override is not given, set buffer to the empty
13075|       |        // string, state to no scheme state, and start over (from the first code
13076|       |        // point in input).
13077|    832|        else {
13078|    832|          state = state::NO_SCHEME;
13079|    832|          input_position = 0;
13080|    832|          break;
13081|    832|        }
13082|  22.0k|        input_position++;
13083|  22.0k|        break;
13084|  22.8k|      }
13085|  24.0k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13085:7): [True: 24.0k, False: 148k]
  ------------------
13086|  24.0k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13087|       |        // The fragment was pruned from url_data before the state machine ran,
13088|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
13089|       |        // nothing else remains in front of it.
13090|  24.0k|        const bool c_is_hash =
13091|  24.0k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (13091:13): [True: 581, False: 23.4k]
  |  Branch (13091:37): [True: 266, False: 315]
  ------------------
13092|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13093|       |        // validation error, return failure.
13094|  24.0k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (13094:13): [True: 22.9k, False: 1.12k]
  |  Branch (13094:37): [True: 133, False: 994]
  |  Branch (13094:66): [True: 54, False: 79]
  ------------------
13095|  22.9k|          ada_log("NO_SCHEME validation error");
13096|  22.9k|          url.is_valid = false;
13097|  22.9k|          return url;
13098|  22.9k|        }
13099|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13100|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13101|       |        // query to base's query, and set state to fragment state.
13102|  1.07k|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (13102:18): [True: 79, False: 994]
  |  Branch (13102:47): [True: 79, False: 0]
  ------------------
13103|     79|          ada_log("NO_SCHEME opaque base with fragment");
13104|     79|          url.copy_scheme(*base_url);
13105|     79|          url.has_opaque_path = base_url->has_opaque_path;
13106|       |
13107|       |          if constexpr (result_type_is_ada_url) {
13108|       |            url.path = base_url->path;
13109|       |            url.query = base_url->query;
13110|     79|          } else {
13111|     79|            url.update_base_pathname(base_url->get_pathname());
13112|     79|            if (base_url->has_search()) {
  ------------------
  |  Branch (13112:17): [True: 35, False: 44]
  ------------------
13113|       |              // get_search() returns "" for an empty query string (URL ends
13114|       |              // with '?'). update_base_search("") would incorrectly clear the
13115|       |              // query, so pass "?" to preserve the empty query distinction.
13116|     35|              auto s = base_url->get_search();
13117|     35|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13117:38): [True: 12, False: 23]
  ------------------
13118|     35|            }
13119|     79|          }
13120|     79|          url.update_unencoded_base_hash(*fragment);
13121|     79|          enforce_max_input_length();
13122|     79|          return url;
13123|     79|        }
13124|       |        // Otherwise, if base's scheme is not "file", set state to relative
13125|       |        // state and decrease pointer by 1.
13126|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13127|    994|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13127:18): [True: 752, False: 242]
  ------------------
13128|    752|          ada_log("NO_SCHEME non-file relative path");
13129|    752|          state = state::RELATIVE_SCHEME;
13130|    752|        }
13131|       |        // Otherwise, set state to file state and decrease pointer by 1.
13132|    242|        else {
13133|    242|          ada_log("NO_SCHEME file base type");
13134|    242|          state = state::FILE;
13135|    242|        }
13136|    994|        break;
13137|  24.0k|      }
13138|  14.4k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13138:7): [True: 14.4k, False: 158k]
  ------------------
13139|  14.4k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13140|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13141|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13142|       |        // worry about AUTHORITY.
13143|       |        // TODO: Instead of just collecting a bool, collect the location of the
13144|       |        // '@' and do something useful with it.
13145|       |        // TODO: We could do various processing early on, using a single pass
13146|       |        // over the string to collect information about it, e.g., telling us
13147|       |        // whether there is a @ and if so, where (or how many).
13148|       |
13149|       |        // Check if url data contains an @.
13150|  14.4k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13150:13): [True: 13.1k, False: 1.26k]
  ------------------
13151|  13.1k|          state = state::HOST;
13152|  13.1k|          break;
13153|  13.1k|        }
13154|  1.26k|        bool at_sign_seen{false};
13155|  1.26k|        bool password_token_seen{false};
13156|       |        /**
13157|       |         * We expect something of the sort...
13158|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13159|       |         * --------^
13160|       |         */
13161|  8.23k|        do {
13162|  8.23k|          std::string_view view = url_data.substr(input_position);
13163|       |          // The delimiters are @, /, ? \\.
13164|  8.23k|          size_t location =
13165|  8.23k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13165:15): [True: 7.36k, False: 866]
  ------------------
13166|  8.23k|                               : helpers::find_authority_delimiter(view);
13167|  8.23k|          std::string_view authority_view = view.substr(0, location);
13168|  8.23k|          size_t end_of_authority = input_position + authority_view.size();
13169|       |          // If c is U+0040 (@), then:
13170|  8.23k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13170:15): [True: 7.41k, False: 822]
  ------------------
13171|  7.41k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13171:15): [True: 6.96k, False: 445]
  ------------------
13172|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13173|  6.96k|            if (at_sign_seen) {
  ------------------
  |  Branch (13173:17): [True: 5.83k, False: 1.13k]
  ------------------
13174|  5.83k|              if (password_token_seen) {
  ------------------
  |  Branch (13174:19): [True: 2.35k, False: 3.47k]
  ------------------
13175|       |                if constexpr (result_type_is_ada_url) {
13176|       |                  url.password += "%40";
13177|  2.35k|                } else {
13178|  2.35k|                  url.append_base_password("%40");
13179|  2.35k|                }
13180|  3.47k|              } else {
13181|       |                if constexpr (result_type_is_ada_url) {
13182|       |                  url.username += "%40";
13183|  3.47k|                } else {
13184|  3.47k|                  url.append_base_username("%40");
13185|  3.47k|                }
13186|  3.47k|              }
13187|  5.83k|            }
13188|       |
13189|  6.96k|            at_sign_seen = true;
13190|       |
13191|  6.96k|            if (!password_token_seen) {
  ------------------
  |  Branch (13191:17): [True: 4.61k, False: 2.35k]
  ------------------
13192|  4.61k|              size_t password_token_location = authority_view.find(':');
13193|  4.61k|              password_token_seen =
13194|  4.61k|                  password_token_location != std::string_view::npos;
13195|       |
13196|  4.61k|              if constexpr (store_values) {
13197|  4.61k|                if (!password_token_seen) {
  ------------------
  |  Branch (13197:21): [True: 4.21k, False: 403]
  ------------------
13198|       |                  if constexpr (result_type_is_ada_url) {
13199|       |                    url.username += unicode::percent_encode(
13200|       |                        authority_view,
13201|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13202|  4.21k|                  } else {
13203|  4.21k|                    url.append_base_username(unicode::percent_encode(
13204|  4.21k|                        authority_view,
13205|  4.21k|                        character_sets::USERINFO_PERCENT_ENCODE));
13206|  4.21k|                  }
13207|  4.21k|                } else {
13208|       |                  if constexpr (result_type_is_ada_url) {
13209|       |                    url.username += unicode::percent_encode(
13210|       |                        authority_view.substr(0, password_token_location),
13211|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13212|       |                    url.password += unicode::percent_encode(
13213|       |                        authority_view.substr(password_token_location + 1),
13214|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13215|    403|                  } else {
13216|    403|                    url.append_base_username(unicode::percent_encode(
13217|    403|                        authority_view.substr(0, password_token_location),
13218|    403|                        character_sets::USERINFO_PERCENT_ENCODE));
13219|    403|                    url.append_base_password(unicode::percent_encode(
13220|    403|                        authority_view.substr(password_token_location + 1),
13221|    403|                        character_sets::USERINFO_PERCENT_ENCODE));
13222|    403|                  }
13223|    403|                }
13224|  4.61k|              }
13225|  4.61k|            } else if constexpr (store_values) {
13226|       |              if constexpr (result_type_is_ada_url) {
13227|       |                url.password += unicode::percent_encode(
13228|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13229|  2.35k|              } else {
13230|  2.35k|                url.append_base_password(unicode::percent_encode(
13231|  2.35k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13232|  2.35k|              }
13233|  2.35k|            }
13234|  6.96k|          }
13235|       |          // Otherwise, if one of the following is true:
13236|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13237|       |          // - url is special and c is U+005C (\)
13238|  1.26k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13238:20): [True: 822, False: 445]
  ------------------
13239|    445|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13239:20): [True: 396, False: 49]
  ------------------
13240|     49|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13240:20): [True: 43, False: 6]
  ------------------
13241|  1.26k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13241:21): [True: 6, False: 0]
  |  Branch (13241:41): [True: 6, False: 0]
  ------------------
13242|       |            // If atSignSeen is true and authority_view is the empty string,
13243|       |            // validation error, return failure.
13244|  1.26k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13244:17): [True: 1.13k, False: 131]
  |  Branch (13244:33): [True: 345, False: 791]
  ------------------
13245|    345|              url.is_valid = false;
13246|    345|              return url;
13247|    345|            }
13248|    922|            state = state::HOST;
13249|    922|            break;
13250|  1.26k|          }
13251|  6.96k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13251:15): [True: 0, False: 6.96k]
  ------------------
13252|      0|            if constexpr (store_values) {
13253|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13253:19): [True: 0, False: 0]
  ------------------
13254|      0|                url.update_unencoded_base_hash(*fragment);
13255|      0|              }
13256|      0|            }
13257|      0|            enforce_max_input_length();
13258|      0|            return url;
13259|      0|          }
13260|  6.96k|          input_position = end_of_authority + 1;
13261|  6.96k|        } while (true);
  ------------------
  |  Branch (13261:18): [True: 6.96k, Folded]
  ------------------
13262|       |
13263|    922|        break;
13264|  1.26k|      }
13265|    922|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13265:7): [True: 436, False: 172k]
  ------------------
13266|    436|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13267|    436|                helpers::substring(url_data, input_position));
13268|       |
13269|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13270|       |        // then set state to special authority ignore slashes state and increase
13271|       |        // pointer by 1.
13272|    436|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13272:13): [True: 353, False: 83]
  ------------------
13273|    353|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13274|    353|          input_position += 2;
13275|    353|        } else {
13276|       |          // Otherwise, validation error, set state to relative state and
13277|       |          // decrease pointer by 1.
13278|     83|          state = state::RELATIVE_SCHEME;
13279|     83|        }
13280|       |
13281|    436|        break;
13282|  1.26k|      }
13283|  2.25k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (13283:7): [True: 2.25k, False: 170k]
  ------------------
13284|  2.25k|        ada_log("PATH_OR_AUTHORITY ",
13285|  2.25k|                helpers::substring(url_data, input_position));
13286|       |
13287|       |        // If c is U+002F (/), then set state to authority state.
13288|  2.25k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13288:13): [True: 2.13k, False: 122]
  ------------------
13289|  2.13k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13289:13): [True: 907, False: 1.22k]
  ------------------
13290|    907|          state = state::AUTHORITY;
13291|    907|          input_position++;
13292|  1.34k|        } else {
13293|       |          // Otherwise, set state to path state, and decrease pointer by 1.
13294|  1.34k|          state = state::PATH;
13295|  1.34k|        }
13296|       |
13297|  2.25k|        break;
13298|  1.26k|      }
13299|    835|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (13299:7): [True: 835, False: 171k]
  ------------------
13300|    835|        ada_log("RELATIVE_SCHEME ",
13301|    835|                helpers::substring(url_data, input_position));
13302|       |
13303|       |        // Set url's scheme to base's scheme.
13304|    835|        url.copy_scheme(*base_url);
13305|       |
13306|       |        // If c is U+002F (/), then set state to relative slash state.
13307|    835|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13307:13): [True: 679, False: 156]
  ------------------
13308|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
13309|    679|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13309:13): [True: 72, False: 607]
  ------------------
13310|     72|          ada_log(
13311|     72|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13312|     72|              "slash state");
13313|     72|          state = state::RELATIVE_SLASH;
13314|    763|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13314:20): [True: 631, False: 132]
  |  Branch (13314:40): [True: 502, False: 129]
  ------------------
13315|    502|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13315:20): [True: 4, False: 498]
  ------------------
13316|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13317|       |          // set state to relative slash state.
13318|      4|          ada_log(
13319|      4|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13320|      4|              "error, set state to relative slash state");
13321|      4|          state = state::RELATIVE_SLASH;
13322|    759|        } else {
13323|    759|          ada_log("RELATIVE_SCHEME otherwise");
13324|       |          // Set url's username to base's username, url's password to base's
13325|       |          // password, url's host to base's host, url's port to base's port,
13326|       |          // url's path to a clone of base's path, and url's query to base's
13327|       |          // query.
13328|       |          if constexpr (result_type_is_ada_url) {
13329|       |            url.username = base_url->username;
13330|       |            url.password = base_url->password;
13331|       |            url.host = base_url->host;
13332|       |            url.port = base_url->port;
13333|       |            // cloning the base path includes cloning the has_opaque_path flag
13334|       |            url.has_opaque_path = base_url->has_opaque_path;
13335|       |            url.path = base_url->path;
13336|       |            url.query = base_url->query;
13337|    759|          } else {
13338|    759|            url.update_base_authority(base_url->get_href(),
13339|    759|                                      base_url->get_components());
13340|    759|            url.update_host_to_base_host(base_url->get_hostname());
13341|    759|            url.update_base_port(base_url->retrieve_base_port());
13342|       |            // cloning the base path includes cloning the has_opaque_path flag
13343|    759|            url.has_opaque_path = base_url->has_opaque_path;
13344|    759|            url.update_base_pathname(base_url->get_pathname());
13345|    759|            if (base_url->has_search()) {
  ------------------
  |  Branch (13345:17): [True: 73, False: 686]
  ------------------
13346|       |              // get_search() returns "" for an empty query string (URL ends
13347|       |              // with '?'). update_base_search("") would incorrectly clear the
13348|       |              // query, so pass "?" to preserve the empty query distinction.
13349|     73|              auto s = base_url->get_search();
13350|     73|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13350:38): [True: 35, False: 38]
  ------------------
13351|     73|            }
13352|    759|          }
13353|       |
13354|    759|          url.has_opaque_path = base_url->has_opaque_path;
13355|       |
13356|       |          // If c is U+003F (?), then set url's query to the empty string, and
13357|       |          // state to query state.
13358|    759|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13358:15): [True: 603, False: 156]
  ------------------
13359|    603|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13359:15): [True: 88, False: 515]
  ------------------
13360|     88|            state = state::QUERY;
13361|     88|          }
13362|       |          // Otherwise, if c is not the EOF code point:
13363|    671|          else if (input_position != input_size) {
  ------------------
  |  Branch (13363:20): [True: 515, False: 156]
  ------------------
13364|       |            // Set url's query to null.
13365|    515|            url.clear_search();
13366|       |            if constexpr (result_type_is_ada_url) {
13367|       |              // Shorten url's path.
13368|       |              helpers::shorten_path(url.path, url.type);
13369|    515|            } else {
13370|    515|              std::string_view path = url.get_pathname();
13371|    515|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13371:19): [True: 512, False: 3]
  ------------------
13372|    512|                url.update_base_pathname(std::move(std::string(path)));
13373|    512|              }
13374|    515|            }
13375|       |            // Set state to path state and decrease pointer by 1.
13376|    515|            state = state::PATH;
13377|    515|            break;
13378|    515|          }
13379|    759|        }
13380|    320|        input_position++;
13381|    320|        break;
13382|    835|      }
13383|     76|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13383:7): [True: 76, False: 172k]
  ------------------
13384|     76|        ada_log("RELATIVE_SLASH ",
13385|     76|                helpers::substring(url_data, input_position));
13386|       |
13387|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13388|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13389|     76|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13389:13): [True: 55, False: 21]
  |  Branch (13389:33): [True: 42, False: 13]
  ------------------
13390|     42|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13390:14): [True: 4, False: 38]
  ------------------
13391|     38|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13391:14): [True: 2, False: 36]
  ------------------
13392|       |          // Set state to special authority ignore slashes state.
13393|      6|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13394|      6|        }
13395|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13396|     70|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13396:18): [True: 47, False: 23]
  ------------------
13397|     47|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13397:18): [True: 6, False: 41]
  ------------------
13398|      6|          state = state::AUTHORITY;
13399|      6|        }
13400|       |        // Otherwise, set
13401|       |        // - url's username to base's username,
13402|       |        // - url's password to base's password,
13403|       |        // - url's host to base's host,
13404|       |        // - url's port to base's port,
13405|       |        // - state to path state, and then, decrease pointer by 1.
13406|     64|        else {
13407|       |          if constexpr (result_type_is_ada_url) {
13408|       |            url.username = base_url->username;
13409|       |            url.password = base_url->password;
13410|       |            url.host = base_url->host;
13411|       |            url.port = base_url->port;
13412|     64|          } else {
13413|     64|            url.update_base_authority(base_url->get_href(),
13414|     64|                                      base_url->get_components());
13415|     64|            url.update_host_to_base_host(base_url->get_hostname());
13416|     64|            url.update_base_port(base_url->retrieve_base_port());
13417|     64|          }
13418|     64|          state = state::PATH;
13419|     64|          break;
13420|     64|        }
13421|       |
13422|     12|        input_position++;
13423|     12|        break;
13424|     76|      }
13425|  13.1k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13425:7): [True: 13.1k, False: 159k]
  ------------------
13426|  13.1k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13427|  13.1k|                helpers::substring(url_data, input_position));
13428|       |
13429|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13430|       |        // then set state to special authority ignore slashes state and increase
13431|       |        // pointer by 1.
13432|  13.1k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13432:13): [True: 3.39k, False: 9.74k]
  ------------------
13433|  3.39k|          input_position += 2;
13434|  3.39k|        }
13435|       |
13436|  13.1k|        [[fallthrough]];
13437|  13.1k|      }
13438|  13.4k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13438:7): [True: 359, False: 172k]
  ------------------
13439|  13.4k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13440|  13.4k|                helpers::substring(url_data, input_position));
13441|       |
13442|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13443|       |        // authority state and decrease pointer by 1.
13444|  15.3k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13444:16): [True: 15.2k, False: 95]
  ------------------
13445|  15.2k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13445:17): [True: 1.19k, False: 14.0k]
  ------------------
13446|  14.0k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13446:17): [True: 614, False: 13.4k]
  ------------------
13447|  1.80k|          input_position++;
13448|  1.80k|        }
13449|  13.4k|        state = state::AUTHORITY;
13450|       |
13451|  13.4k|        break;
13452|  13.1k|      }
13453|  1.22k|      case state::QUERY: {
  ------------------
  |  Branch (13453:7): [True: 1.22k, False: 171k]
  ------------------
13454|  1.22k|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13455|  1.22k|        if constexpr (store_values) {
13456|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13457|       |          // if url is special; otherwise the query percent-encode set.
13458|  1.22k|          const uint8_t* query_percent_encode_set =
13459|  1.22k|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13459:15): [True: 786, False: 439]
  ------------------
13460|  1.22k|                               : character_sets::QUERY_PERCENT_ENCODE;
13461|       |
13462|       |          // Percent-encode after encoding, with encoding, buffer, and
13463|       |          // queryPercentEncodeSet, and append the result to url's query.
13464|  1.22k|          url.update_base_search(url_data.substr(input_position),
13465|  1.22k|                                 query_percent_encode_set);
13466|  1.22k|          ada_log("QUERY update_base_search completed ");
13467|  1.22k|          if (fragment.has_value()) {
  ------------------
  |  Branch (13467:15): [True: 317, False: 908]
  ------------------
13468|    317|            url.update_unencoded_base_hash(*fragment);
13469|    317|          }
13470|  1.22k|        }
13471|  1.22k|        enforce_max_input_length();
13472|  1.22k|        return url;
13473|  13.1k|      }
13474|  14.0k|      case state::HOST: {
  ------------------
  |  Branch (13474:7): [True: 14.0k, False: 158k]
  ------------------
13475|  14.0k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13476|       |
13477|  14.0k|        std::string_view host_view = url_data.substr(input_position);
13478|  14.0k|        auto [location, found_colon] =
13479|  14.0k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13480|  14.0k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13480:26): [True: 14.0k, False: 0]
  ------------------
13481|  14.0k|                             ? input_position + location
13482|  14.0k|                             : input_size;
13483|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13484|       |        // Note: the 'found_colon' value is true if and only if a colon was
13485|       |        // encountered while not inside brackets.
13486|  14.0k|        if (found_colon) {
  ------------------
  |  Branch (13486:13): [True: 2.51k, False: 11.5k]
  ------------------
13487|       |          // If buffer is the empty string, validation error, return failure.
13488|       |          // Let host be the result of host parsing buffer with url is not
13489|       |          // special.
13490|  2.51k|          ada_log("HOST parsing ", host_view);
13491|  2.51k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13491:15): [True: 223, False: 2.29k]
  ------------------
13492|    223|            return url;
13493|    223|          }
13494|  2.29k|          ada_log("HOST parsing results in ", url.get_hostname());
13495|       |          // Set url's host to host, buffer to the empty string, and state to
13496|       |          // port state.
13497|  2.29k|          state = state::PORT;
13498|  2.29k|          input_position++;
13499|  2.29k|        }
13500|       |        // Otherwise, if one of the following is true:
13501|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13502|       |        // - url is special and c is U+005C (\)
13503|       |        // The get_host_delimiter_location function either brings us to
13504|       |        // the colon outside of the bracket, or to one of those characters.
13505|  11.5k|        else {
13506|       |          // If url is special and host_view is the empty string, validation
13507|       |          // error, return failure.
13508|  11.5k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13508:15): [True: 245, False: 11.3k]
  |  Branch (13508:36): [True: 101, False: 144]
  ------------------
13509|    101|            url.is_valid = false;
13510|    101|            return url;
13511|    101|          }
13512|  11.4k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13513|       |          // Let host be the result of host parsing host_view with url is not
13514|       |          // special.
13515|  11.4k|          if (host_view.empty()) {
  ------------------
  |  Branch (13515:15): [True: 144, False: 11.3k]
  ------------------
13516|    144|            url.update_base_hostname("");
13517|  11.3k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13517:22): [True: 2.41k, False: 8.88k]
  ------------------
13518|  2.41k|            return url;
13519|  2.41k|          }
13520|  9.03k|          ada_log("HOST parsing results in ", url.get_hostname(),
13521|  9.03k|                  " href=", url.get_href());
13522|       |
13523|       |          // Set url's host to host, and state to path start state.
13524|  9.03k|          state = state::PATH_START;
13525|  9.03k|        }
13526|       |
13527|  11.3k|        break;
13528|  14.0k|      }
13529|  11.3k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13529:7): [True: 3.43k, False: 169k]
  ------------------
13530|  3.43k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13531|       |        // Opaque path, query, and fragment are structurally always valid:
13532|       |        // the parser would just percent-encode whatever is there. When we
13533|       |        // are not storing values (can_parse), we can return immediately.
13534|       |        // We must set has_opaque_path = true before returning so that when
13535|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13536|       |        // correctly rejects relative inputs against an opaque-path base
13537|       |        // (e.g. can_parse("", &"W:") must return false).
13538|       |        if constexpr (!store_values) {
13539|       |          url.has_opaque_path = true;
13540|       |          return url;
13541|       |        }
13542|  3.43k|        std::string_view view = url_data.substr(input_position);
13543|       |        // If c is U+003F (?), then set url's query to the empty string and
13544|       |        // state to query state.
13545|  3.43k|        size_t location = view.find('?');
13546|  3.43k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13546:13): [True: 314, False: 3.12k]
  ------------------
13547|    314|          view.remove_suffix(view.size() - location);
13548|    314|          state = state::QUERY;
13549|    314|          input_position += location + 1;
13550|  3.12k|        } else {
13551|  3.12k|          input_position = input_size + 1;
13552|  3.12k|        }
13553|  3.43k|        url.has_opaque_path = true;
13554|       |
13555|       |        // This is a really unlikely scenario in real world. We should not seek
13556|       |        // to optimize it.
13557|  3.43k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13557:13): [True: 119, False: 3.31k]
  ------------------
13558|    119|          std::string modified_view =
13559|    119|              std::string(view.substr(0, view.size() - 1)) + "%20";
13560|    119|          url.update_base_pathname(unicode::percent_encode(
13561|    119|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13562|  3.31k|        } else {
13563|  3.31k|          url.update_base_pathname(unicode::percent_encode(
13564|  3.31k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13565|  3.31k|        }
13566|  3.43k|        break;
13567|  14.0k|      }
13568|  2.29k|      case state::PORT: {
  ------------------
  |  Branch (13568:7): [True: 2.29k, False: 170k]
  ------------------
13569|  2.29k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13570|  2.29k|        std::string_view port_view = url_data.substr(input_position);
13571|  2.29k|        input_position += url.parse_port(port_view, true);
13572|  2.29k|        if (!url.is_valid) {
  ------------------
  |  Branch (13572:13): [True: 125, False: 2.16k]
  ------------------
13573|    125|          return url;
13574|    125|        }
13575|  2.16k|        state = state::PATH_START;
13576|  2.16k|        [[fallthrough]];
13577|  2.16k|      }
13578|  13.1k|      case state::PATH_START: {
  ------------------
  |  Branch (13578:7): [True: 11.0k, False: 161k]
  ------------------
13579|  13.1k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13580|       |        // Path, query, and fragment are structurally always valid: the
13581|       |        // parser would just percent-encode whatever is there. When we are
13582|       |        // not storing values (can_parse), we can return immediately since
13583|       |        // no subsequent state can invalidate the URL.
13584|       |        if constexpr (!store_values) {
13585|       |          return url;
13586|       |        }
13587|       |
13588|       |        // If url is special, then:
13589|  13.1k|        if (url.is_special()) {
  ------------------
  |  Branch (13589:13): [True: 12.4k, False: 777]
  ------------------
13590|       |          // Set state to path state.
13591|  12.4k|          state = state::PATH;
13592|       |
13593|       |          // Optimization: Avoiding going into PATH state improves the
13594|       |          // performance of urls ending with /.
13595|  12.4k|          if (input_position == input_size) {
  ------------------
  |  Branch (13595:15): [True: 6.67k, False: 5.72k]
  ------------------
13596|  6.67k|            if constexpr (store_values) {
13597|  6.67k|              url.update_base_pathname("/");
13598|  6.67k|              if (fragment.has_value()) {
  ------------------
  |  Branch (13598:19): [True: 170, False: 6.50k]
  ------------------
13599|    170|                url.update_unencoded_base_hash(*fragment);
13600|    170|              }
13601|  6.67k|            }
13602|  6.67k|            enforce_max_input_length();
13603|  6.67k|            return url;
13604|  6.67k|          }
13605|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13606|       |          // by 1. We know that (input_position == input_size) is impossible
13607|       |          // here, because of the previous if-check.
13608|  5.72k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13608:15): [True: 378, False: 5.34k]
  ------------------
13609|    378|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13609:15): [True: 361, False: 17]
  ------------------
13610|    361|            break;
13611|    361|          }
13612|  5.72k|        }
13613|       |        // Otherwise, if state override is not given and c is U+003F (?),
13614|       |        // set url's query to the empty string and state to query state.
13615|    777|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13615:18): [True: 171, False: 606]
  ------------------
13616|    171|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13616:18): [True: 60, False: 111]
  ------------------
13617|     60|          state = state::QUERY;
13618|     60|        }
13619|       |        // Otherwise, if c is not the EOF code point:
13620|    717|        else if (input_position != input_size) {
  ------------------
  |  Branch (13620:18): [True: 111, False: 606]
  ------------------
13621|       |          // Set state to path state.
13622|    111|          state = state::PATH;
13623|       |
13624|       |          // If c is not U+002F (/), then decrease pointer by 1.
13625|    111|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13625:15): [True: 0, False: 111]
  ------------------
13626|      0|            break;
13627|      0|          }
13628|    111|        }
13629|       |
13630|  6.14k|        input_position++;
13631|  6.14k|        break;
13632|  13.1k|      }
13633|  8.50k|      case state::PATH: {
  ------------------
  |  Branch (13633:7): [True: 8.50k, False: 164k]
  ------------------
13634|  8.50k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13635|       |        // Path, query, and fragment are structurally always valid: the
13636|       |        // parser would just percent-encode whatever is there. When we are
13637|       |        // not storing values (can_parse), we can return immediately since
13638|       |        // no subsequent state can invalidate the URL.
13639|       |        if constexpr (!store_values) {
13640|       |          return url;
13641|       |        }
13642|  8.50k|        std::string_view view = url_data.substr(input_position);
13643|       |
13644|       |        // Most time, we do not need percent encoding.
13645|       |        // Furthermore, we can immediately locate the '?'.
13646|  8.50k|        size_t locofquestionmark = view.find('?');
13647|  8.50k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13647:13): [True: 755, False: 7.75k]
  ------------------
13648|    755|          state = state::QUERY;
13649|    755|          view.remove_suffix(view.size() - locofquestionmark);
13650|    755|          input_position += locofquestionmark + 1;
13651|  7.75k|        } else {
13652|  7.75k|          input_position = input_size + 1;
13653|  7.75k|        }
13654|  8.50k|        if constexpr (store_values) {
13655|       |          if constexpr (result_type_is_ada_url) {
13656|       |            helpers::parse_prepared_path(view, url.type, url.path);
13657|  8.50k|          } else {
13658|  8.50k|            url.consume_prepared_path(view);
13659|  8.50k|            ADA_ASSERT_TRUE(url.validate());
13660|  8.50k|          }
13661|  8.50k|        }
13662|  8.50k|        break;
13663|  13.1k|      }
13664|  2.30k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13664:7): [True: 2.30k, False: 170k]
  ------------------
13665|  2.30k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13666|       |
13667|       |        // If c is U+002F (/) or U+005C (\), then:
13668|  2.30k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13668:13): [True: 2.23k, False: 70]
  ------------------
13669|  2.23k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13669:14): [True: 2.18k, False: 49]
  ------------------
13670|  2.19k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13670:14): [True: 6, False: 43]
  ------------------
13671|  2.19k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13672|       |          // Set state to file host state.
13673|  2.19k|          state = state::FILE_HOST;
13674|  2.19k|          input_position++;
13675|  2.19k|        } else {
13676|    113|          ada_log("FILE_SLASH otherwise");
13677|       |          // If base is non-null and base's scheme is "file", then:
13678|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13679|       |          // base_url_has_value() is true.
13680|    113|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13680:15): [True: 89, False: 24]
  |  Branch (13680:38): [True: 87, False: 2]
  ------------------
13681|       |            // Set url's host to base's host.
13682|       |            if constexpr (result_type_is_ada_url) {
13683|       |              url.host = base_url->host;
13684|     87|            } else {
13685|     87|              url.update_host_to_base_host(base_url->get_host());
13686|     87|            }
13687|       |            // If the code point substring from pointer to the end of input does
13688|       |            // not start with a Windows drive letter and base's path[0] is a
13689|       |            // normalized Windows drive letter, then append base's path[0] to
13690|       |            // url's path.
13691|     87|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13691:17): [True: 87, False: 0]
  ------------------
13692|     87|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13692:19): [True: 82, False: 5]
  ------------------
13693|     87|                      url_data.substr(input_position))) {
13694|     82|                std::string_view first_base_url_path =
13695|     82|                    base_url->get_pathname().substr(1);
13696|     82|                size_t loc = first_base_url_path.find('/');
13697|     82|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13697:21): [True: 11, False: 71]
  ------------------
13698|     11|                  helpers::resize(first_base_url_path, loc);
13699|     11|                }
13700|     82|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13700:21): [True: 4, False: 78]
  ------------------
13701|     82|                        first_base_url_path)) {
13702|       |                  if constexpr (result_type_is_ada_url) {
13703|       |                    url.path += '/';
13704|       |                    url.path += first_base_url_path;
13705|      4|                  } else {
13706|      4|                    url.append_base_pathname(
13707|      4|                        helpers::concat("/", first_base_url_path));
13708|      4|                  }
13709|      4|                }
13710|     82|              }
13711|     87|            }
13712|     87|          }
13713|       |
13714|       |          // Set state to path state, and decrease pointer by 1.
13715|    113|          state = state::PATH;
13716|    113|        }
13717|       |
13718|  2.30k|        break;
13719|  13.1k|      }
13720|  2.19k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13720:7): [True: 2.19k, False: 170k]
  ------------------
13721|  2.19k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13722|  2.19k|        std::string_view view = url_data.substr(input_position);
13723|       |
13724|  2.19k|        size_t location = view.find_first_of("/\\?");
13725|  2.19k|        std::string_view file_host_buffer = view.substr(
13726|  2.19k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13726:16): [True: 943, False: 1.25k]
  ------------------
13727|       |
13728|  2.19k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13728:13): [True: 12, False: 2.18k]
  ------------------
13729|     12|          state = state::PATH;
13730|  2.18k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13730:20): [True: 286, False: 1.89k]
  ------------------
13731|       |          // Set url's host to the empty string.
13732|       |          if constexpr (result_type_is_ada_url) {
13733|       |            url.host = "";
13734|    286|          } else {
13735|    286|            url.update_base_hostname("");
13736|    286|          }
13737|       |          // Set state to path start state.
13738|    286|          state = state::PATH_START;
13739|  1.89k|        } else {
13740|  1.89k|          size_t consumed_bytes = file_host_buffer.size();
13741|  1.89k|          input_position += consumed_bytes;
13742|       |          // Let host be the result of host parsing buffer with url is not
13743|       |          // special.
13744|  1.89k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13744:15): [True: 199, False: 1.69k]
  ------------------
13745|    199|            return url;
13746|    199|          }
13747|       |
13748|       |          if constexpr (result_type_is_ada_url) {
13749|       |            // If host is "localhost", then set host to the empty string.
13750|       |            if (url.host.has_value() && url.host.value() == "localhost") {
13751|       |              url.host = "";
13752|       |            }
13753|  1.69k|          } else {
13754|  1.69k|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (13754:17): [True: 11, False: 1.68k]
  ------------------
13755|     11|              url.update_base_hostname("");
13756|     11|            }
13757|  1.69k|          }
13758|       |
13759|       |          // Set buffer to the empty string and state to path start state.
13760|  1.69k|          state = state::PATH_START;
13761|  1.69k|        }
13762|       |
13763|  1.99k|        break;
13764|  2.19k|      }
13765|  2.98k|      case state::FILE: {
  ------------------
  |  Branch (13765:7): [True: 2.98k, False: 169k]
  ------------------
13766|  2.98k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13767|  2.98k|        std::string_view file_view = url_data.substr(input_position);
13768|       |
13769|  2.98k|        url.set_protocol_as_file();
13770|       |        if constexpr (result_type_is_ada_url) {
13771|       |          // Set url's host to the empty string.
13772|       |          url.host = "";
13773|  2.98k|        } else {
13774|  2.98k|          url.update_base_hostname("");
13775|  2.98k|        }
13776|       |        // If c is U+002F (/) or U+005C (\), then:
13777|  2.98k|        if (input_position != input_size &&
  ------------------
  |  Branch (13777:13): [True: 2.86k, False: 124]
  ------------------
13778|  2.86k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13778:14): [True: 2.30k, False: 562]
  ------------------
13779|  2.30k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13779:14): [True: 6, False: 556]
  ------------------
13780|  2.30k|          ada_log("FILE c is U+002F or U+005C");
13781|       |          // Set state to file slash state.
13782|  2.30k|          state = state::FILE_SLASH;
13783|  2.30k|        }
13784|       |        // Otherwise, if base is non-null and base's scheme is "file":
13785|    680|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13785:18): [True: 252, False: 428]
  |  Branch (13785:41): [True: 178, False: 74]
  ------------------
13786|       |          // Set url's host to base's host, url's path to a clone of base's
13787|       |          // path, and url's query to base's query.
13788|    178|          ada_log("FILE base non-null");
13789|       |          if constexpr (result_type_is_ada_url) {
13790|       |            url.host = base_url->host;
13791|       |            url.path = base_url->path;
13792|       |            url.query = base_url->query;
13793|    178|          } else {
13794|    178|            url.update_host_to_base_host(base_url->get_hostname());
13795|    178|            url.update_base_pathname(base_url->get_pathname());
13796|    178|            if (base_url->has_search()) {
  ------------------
  |  Branch (13796:17): [True: 62, False: 116]
  ------------------
13797|       |              // get_search() returns "" for an empty query string (URL ends
13798|       |              // with '?'). update_base_search("") would incorrectly clear the
13799|       |              // query, so pass "?" to preserve the empty query distinction.
13800|     62|              auto s = base_url->get_search();
13801|     62|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13801:38): [True: 18, False: 44]
  ------------------
13802|     62|            }
13803|    178|          }
13804|    178|          url.has_opaque_path = base_url->has_opaque_path;
13805|       |
13806|       |          // If c is U+003F (?), then set url's query to the empty string and
13807|       |          // state to query state.
13808|    178|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13808:15): [True: 124, False: 54]
  |  Branch (13808:47): [True: 8, False: 116]
  ------------------
13809|      8|            state = state::QUERY;
13810|      8|          }
13811|       |          // Otherwise, if c is not the EOF code point:
13812|    170|          else if (input_position != input_size) {
  ------------------
  |  Branch (13812:20): [True: 116, False: 54]
  ------------------
13813|       |            // Set url's query to null.
13814|    116|            url.clear_search();
13815|       |            // If the code point substring from pointer to the end of input does
13816|       |            // not start with a Windows drive letter, then shorten url's path.
13817|    116|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13817:17): [True: 97, False: 19]
  ------------------
13818|       |              if constexpr (result_type_is_ada_url) {
13819|       |                helpers::shorten_path(url.path, url.type);
13820|     97|              } else {
13821|     97|                std::string_view path = url.get_pathname();
13822|     97|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13822:21): [True: 93, False: 4]
  ------------------
13823|     93|                  url.update_base_pathname(std::move(std::string(path)));
13824|     93|                }
13825|     97|              }
13826|     97|            }
13827|       |            // Otherwise:
13828|     19|            else {
13829|       |              // Set url's path to an empty list.
13830|     19|              url.clear_pathname();
13831|     19|              url.has_opaque_path = true;
13832|     19|            }
13833|       |
13834|       |            // Set state to path state and decrease pointer by 1.
13835|    116|            state = state::PATH;
13836|    116|            break;
13837|    116|          }
13838|    178|        }
13839|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13840|    502|        else {
13841|    502|          ada_log("FILE go to path");
13842|    502|          state = state::PATH;
13843|    502|          break;
13844|    502|        }
13845|       |
13846|  2.36k|        input_position++;
13847|  2.36k|        break;
13848|  2.98k|      }
13849|      0|      default:
  ------------------
  |  Branch (13849:7): [True: 0, False: 172k]
  ------------------
13850|      0|        unreachable();
13851|   172k|    }
13852|   172k|  }
13853|  17.3k|  if constexpr (store_values) {
13854|  17.3k|    if (fragment.has_value()) {
  ------------------
  |  Branch (13854:9): [True: 424, False: 16.9k]
  ------------------
13855|    424|      url.update_unencoded_base_hash(*fragment);
13856|    424|    }
13857|  17.3k|  }
13858|  17.3k|  enforce_max_input_length();
13859|  17.3k|  return url;
13860|  51.7k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb0EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12840|  31.8k|                           const result_type* base_url) {
12841|       |  // We can specialize the implementation per type.
12842|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12843|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12844|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12845|       |  // and ada::url **do not have to support the exact same API**.
12846|  31.8k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12847|  31.8k|  constexpr bool result_type_is_ada_url_aggregator =
12848|  31.8k|      std::is_same_v<url_aggregator, result_type>;
12849|  31.8k|  static_assert(result_type_is_ada_url ||
12850|  31.8k|                result_type_is_ada_url_aggregator);  // We don't support
12851|       |                                                     // anything else for now.
12852|       |
12853|  31.8k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12854|  31.8k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12855|  31.8k|          ")");
12856|       |
12857|  31.8k|  result_type url{};
12858|       |
12859|  31.8k|  const uint32_t max_input_length = ada::get_max_input_length();
12860|       |
12861|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12862|       |  // max_input_length even when the input fit under it. The check at the end of
12863|       |  // the state machine covers URLs that run to completion, but several states
12864|       |  // return early after appending a query or fragment, so run the same check on
12865|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12866|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12867|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12868|  31.8k|  const auto enforce_max_input_length = [&]() {
12869|  31.8k|    if constexpr (store_values) {
12870|  31.8k|      if (url.is_valid) {
12871|  31.8k|        if constexpr (result_type_is_ada_url_aggregator) {
12872|  31.8k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12873|  31.8k|            url.is_valid = false;
12874|  31.8k|          }
12875|  31.8k|        } else {
12876|  31.8k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12877|  31.8k|            url.is_valid = false;
12878|  31.8k|          }
12879|  31.8k|        }
12880|  31.8k|      }
12881|  31.8k|    }
12882|  31.8k|  };
12883|       |
12884|       |  // We refuse to parse URL strings that exceed the maximum input length.
12885|       |  // By default, this is 4GB but can be configured via
12886|       |  // ada::set_max_input_length().
12887|  31.8k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12887:7): [True: 0, False: 31.8k]
  ------------------
12888|      0|    url.is_valid = false;
12889|      0|  }
12890|       |  // Going forward, user_input.size() is in [0,
12891|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12892|       |  // base, or the optional_url was invalid, we must return.
12893|  31.8k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12893:7): [True: 4.57k, False: 27.2k]
  ------------------
12894|  4.57k|    url.is_valid &= base_url->is_valid;
12895|  4.57k|  }
12896|  31.8k|  if (!url.is_valid) {
  ------------------
  |  Branch (12896:7): [True: 0, False: 31.8k]
  ------------------
12897|      0|    return url;
12898|      0|  }
12899|       |
12900|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12901|       |  if constexpr (store_values) {
12902|       |    bool hit_fast_path = false;
12903|       |    if (base_url == nullptr) {
12904|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12905|       |      // call so those URLs (including SetHref) do not pay for a miss.
12906|       |      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12907|       |      const size_t n = user_input.size();
12908|       |      size_t host_start = 0;
12909|       |      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
12910|       |        host_start = 7;
12911|       |      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
12912|       |        host_start = 8;
12913|       |      }
12914|       |      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
12915|       |      const bool skip_ip =
12916|       |          host_first == '[' || (host_first >= '0' && host_first <= '9');
12917|       |      const bool skip_userinfo =
12918|       |          host_start != 0 && authority_has_at(p, host_start, n);
12919|       |      hit_fast_path = !skip_ip && !skip_userinfo &&
12920|       |                      try_parse_simple_absolute(user_input, url);
12921|       |    } else {
12922|       |      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12923|       |    }
12924|       |    if (hit_fast_path) {
12925|       |      if constexpr (result_type_is_ada_url_aggregator) {
12926|       |        if (url.buffer.size() > max_input_length) [[unlikely]] {
12927|       |          url.is_valid = false;
12928|       |        }
12929|       |      } else {
12930|       |        // For a small absolute input, even worst-case normalization cannot
12931|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12932|       |        // most 4.5x. Avoid traversing every stored component on the common
12933|       |        // default-limit path.
12934|       |        if ((base_url != nullptr ||
12935|       |             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
12936|       |            url.get_href_size() > max_input_length) [[unlikely]] {
12937|       |          url.is_valid = false;
12938|       |        }
12939|       |      }
12940|       |      return url;
12941|       |    }
12942|       |  }
12943|       |
12944|  31.8k|  state state = state::SCHEME_START;
12945|       |
12946|  31.8k|  std::string tmp_buffer;
12947|  31.8k|  std::string_view url_data;
12948|  31.8k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12948:7): [True: 291, False: 31.5k]
  ------------------
12949|    291|    tmp_buffer = user_input;
12950|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12951|       |    // just directly build the string from user_input.
12952|    291|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12953|    291|    url_data = tmp_buffer;
12954|  31.5k|  } else [[likely]] {
12955|  31.5k|    url_data = user_input;
12956|  31.5k|  }
12957|       |
12958|       |  // Leading and trailing control characters are uncommon and easy to deal with
12959|       |  // (no performance concern).
12960|  31.8k|  helpers::trim_c0_whitespace(url_data);
12961|       |
12962|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12963|       |    // Most of the time, we just need user_input.size().
12964|       |    // In some instances, we may need a bit more.
12965|       |    ///////////////////////////
12966|       |    // This is *very* important. This line should *not* be removed
12967|       |    // hastily. There are principled reasons why reserve is important
12968|       |    // for performance. If you have a benchmark with small inputs,
12969|       |    // it may not matter, but in other instances, it could.
12970|       |    ////
12971|       |    // This rounds up to the next power of two.
12972|       |    // We know that user_input.size() is in [0,
12973|       |    // std::numeric_limits<uint32_t>::max).
12974|       |    uint32_t reserve_capacity =
12975|       |        (0xFFFFFFFF >>
12976|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12977|       |        1;
12978|       |    url.reserve(reserve_capacity);
12979|       |  }
12980|       |
12981|       |  // Optimization opportunity. Most websites do not have fragment.
12982|  31.8k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12983|       |  // We add it last so that an implementation like ada::url_aggregator
12984|       |  // can append it last to its internal buffer, thus improving performance.
12985|       |
12986|       |  // Here url_data no longer has its fragment.
12987|       |  // We are going to access the data from url_data (it is immutable).
12988|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12989|       |  // The input_position variable should range from 0 to input_size.
12990|       |  // It is illegal to access url_data at input_size.
12991|  31.8k|  size_t input_position = 0;
12992|  31.8k|  const size_t input_size = url_data.size();
12993|       |  // Keep running the following state machine by switching on state.
12994|       |  // If after a run pointer points to the EOF code point, go to the next step.
12995|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12996|       |  // We never decrement input_position.
12997|   137k|  while (input_position <= input_size) {
  ------------------
  |  Branch (12997:10): [True: 137k, False: 236]
  ------------------
12998|   137k|    ada_log("In parsing at ", input_position, " out of ", input_size,
12999|   137k|            " in state ", ada::to_string(state));
13000|   137k|    switch (state) {
13001|  31.8k|      case state::SCHEME_START: {
  ------------------
  |  Branch (13001:7): [True: 31.8k, False: 105k]
  ------------------
13002|  31.8k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
13003|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
13004|       |        // state to scheme state.
13005|  31.8k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13005:13): [True: 23.3k, False: 8.50k]
  ------------------
13006|  23.3k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (13006:13): [True: 22.5k, False: 748]
  ------------------
13007|  22.5k|          state = state::SCHEME;
13008|  22.5k|          input_position++;
13009|  22.5k|        } else {
13010|       |          // Otherwise, if state override is not given, set state to no scheme
13011|       |          // state and decrease pointer by 1.
13012|  9.25k|          state = state::NO_SCHEME;
13013|  9.25k|        }
13014|  31.8k|        break;
13015|      0|      }
13016|  22.5k|      case state::SCHEME: {
  ------------------
  |  Branch (13016:7): [True: 22.5k, False: 114k]
  ------------------
13017|  22.5k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
13018|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
13019|       |        // append c, lowercased, to buffer.
13020|  54.3k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13020:16): [True: 53.9k, False: 396]
  ------------------
13021|  53.9k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13021:16): [True: 31.7k, False: 22.1k]
  ------------------
13022|  31.7k|          input_position++;
13023|  31.7k|        }
13024|       |        // Otherwise, if c is U+003A (:), then:
13025|  22.5k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13025:13): [True: 22.1k, False: 396]
  ------------------
13026|  22.1k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13026:13): [True: 21.9k, False: 265]
  ------------------
13027|  21.9k|          ada_log("SCHEME the scheme should be ",
13028|  21.9k|                  url_data.substr(0, input_position));
13029|       |          if constexpr (result_type_is_ada_url) {
13030|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
13031|       |              return url;
13032|       |            }
13033|  21.9k|          } else {
13034|       |            // we pass the colon along instead of painfully adding it back.
13035|  21.9k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (13035:17): [True: 0, False: 21.9k]
  ------------------
13036|  21.9k|                    url_data.substr(0, input_position + 1))) {
13037|      0|              return url;
13038|      0|            }
13039|  21.9k|          }
13040|  21.9k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13041|       |
13042|       |          // If url's scheme is "file", then:
13043|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13044|  21.9k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13044:15): [True: 2.74k, False: 19.1k]
  ------------------
13045|       |            // Set state to file state.
13046|  2.74k|            state = state::FILE;
13047|  2.74k|          }
13048|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13049|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13050|       |          // != nullptr is false.
13051|  19.1k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13051:20): [True: 13.4k, False: 5.68k]
  |  Branch (13051:40): [True: 2.32k, False: 11.1k]
  ------------------
13052|  2.32k|                   base_url->type == url.type) {
  ------------------
  |  Branch (13052:20): [True: 436, False: 1.88k]
  ------------------
13053|       |            // Set state to special relative or authority state.
13054|    436|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13055|    436|          }
13056|       |          // Otherwise, if url is special, set state to special authority
13057|       |          // slashes state.
13058|  18.7k|          else if (url.is_special()) {
  ------------------
  |  Branch (13058:20): [True: 13.0k, False: 5.68k]
  ------------------
13059|  13.0k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13060|  13.0k|          }
13061|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13062|       |          // path or authority state and increase pointer by 1.
13063|  5.68k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13063:20): [True: 3.27k, False: 2.40k]
  ------------------
13064|  3.27k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13064:20): [True: 2.25k, False: 1.02k]
  ------------------
13065|  2.25k|            state = state::PATH_OR_AUTHORITY;
13066|  2.25k|            input_position++;
13067|  2.25k|          }
13068|       |          // Otherwise, set url's path to the empty string and set state to
13069|       |          // opaque path state.
13070|  3.43k|          else {
13071|  3.43k|            state = state::OPAQUE_PATH;
13072|  3.43k|          }
13073|  21.9k|        }
13074|       |        // Otherwise, if state override is not given, set buffer to the empty
13075|       |        // string, state to no scheme state, and start over (from the first code
13076|       |        // point in input).
13077|    661|        else {
13078|    661|          state = state::NO_SCHEME;
13079|    661|          input_position = 0;
13080|    661|          break;
13081|    661|        }
13082|  21.9k|        input_position++;
13083|  21.9k|        break;
13084|  22.5k|      }
13085|  9.91k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13085:7): [True: 9.91k, False: 127k]
  ------------------
13086|  9.91k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13087|       |        // The fragment was pruned from url_data before the state machine ran,
13088|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
13089|       |        // nothing else remains in front of it.
13090|  9.91k|        const bool c_is_hash =
13091|  9.91k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (13091:13): [True: 326, False: 9.58k]
  |  Branch (13091:37): [True: 147, False: 179]
  ------------------
13092|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13093|       |        // validation error, return failure.
13094|  9.91k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (13094:13): [True: 8.50k, False: 1.40k]
  |  Branch (13094:37): [True: 133, False: 1.27k]
  |  Branch (13094:66): [True: 54, False: 79]
  ------------------
13095|  8.56k|          ada_log("NO_SCHEME validation error");
13096|  8.56k|          url.is_valid = false;
13097|  8.56k|          return url;
13098|  8.56k|        }
13099|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13100|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13101|       |        // query to base's query, and set state to fragment state.
13102|  1.35k|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (13102:18): [True: 79, False: 1.27k]
  |  Branch (13102:47): [True: 79, False: 0]
  ------------------
13103|     79|          ada_log("NO_SCHEME opaque base with fragment");
13104|     79|          url.copy_scheme(*base_url);
13105|     79|          url.has_opaque_path = base_url->has_opaque_path;
13106|       |
13107|       |          if constexpr (result_type_is_ada_url) {
13108|       |            url.path = base_url->path;
13109|       |            url.query = base_url->query;
13110|     79|          } else {
13111|     79|            url.update_base_pathname(base_url->get_pathname());
13112|     79|            if (base_url->has_search()) {
  ------------------
  |  Branch (13112:17): [True: 0, False: 79]
  ------------------
13113|       |              // get_search() returns "" for an empty query string (URL ends
13114|       |              // with '?'). update_base_search("") would incorrectly clear the
13115|       |              // query, so pass "?" to preserve the empty query distinction.
13116|      0|              auto s = base_url->get_search();
13117|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13117:38): [True: 0, False: 0]
  ------------------
13118|      0|            }
13119|     79|          }
13120|     79|          url.update_unencoded_base_hash(*fragment);
13121|     79|          enforce_max_input_length();
13122|     79|          return url;
13123|     79|        }
13124|       |        // Otherwise, if base's scheme is not "file", set state to relative
13125|       |        // state and decrease pointer by 1.
13126|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13127|  1.27k|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13127:18): [True: 1.03k, False: 242]
  ------------------
13128|  1.03k|          ada_log("NO_SCHEME non-file relative path");
13129|  1.03k|          state = state::RELATIVE_SCHEME;
13130|  1.03k|        }
13131|       |        // Otherwise, set state to file state and decrease pointer by 1.
13132|    242|        else {
13133|    242|          ada_log("NO_SCHEME file base type");
13134|    242|          state = state::FILE;
13135|    242|        }
13136|  1.27k|        break;
13137|  9.91k|      }
13138|  14.3k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13138:7): [True: 14.3k, False: 122k]
  ------------------
13139|  14.3k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13140|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13141|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13142|       |        // worry about AUTHORITY.
13143|       |        // TODO: Instead of just collecting a bool, collect the location of the
13144|       |        // '@' and do something useful with it.
13145|       |        // TODO: We could do various processing early on, using a single pass
13146|       |        // over the string to collect information about it, e.g., telling us
13147|       |        // whether there is a @ and if so, where (or how many).
13148|       |
13149|       |        // Check if url data contains an @.
13150|  14.3k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13150:13): [True: 13.0k, False: 1.28k]
  ------------------
13151|  13.0k|          state = state::HOST;
13152|  13.0k|          break;
13153|  13.0k|        }
13154|  1.28k|        bool at_sign_seen{false};
13155|  1.28k|        bool password_token_seen{false};
13156|       |        /**
13157|       |         * We expect something of the sort...
13158|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13159|       |         * --------^
13160|       |         */
13161|  8.25k|        do {
13162|  8.25k|          std::string_view view = url_data.substr(input_position);
13163|       |          // The delimiters are @, /, ? \\.
13164|  8.25k|          size_t location =
13165|  8.25k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13165:15): [True: 7.38k, False: 866]
  ------------------
13166|  8.25k|                               : helpers::find_authority_delimiter(view);
13167|  8.25k|          std::string_view authority_view = view.substr(0, location);
13168|  8.25k|          size_t end_of_authority = input_position + authority_view.size();
13169|       |          // If c is U+0040 (@), then:
13170|  8.25k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13170:15): [True: 7.42k, False: 822]
  ------------------
13171|  7.42k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13171:15): [True: 6.96k, False: 463]
  ------------------
13172|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13173|  6.96k|            if (at_sign_seen) {
  ------------------
  |  Branch (13173:17): [True: 5.83k, False: 1.13k]
  ------------------
13174|  5.83k|              if (password_token_seen) {
  ------------------
  |  Branch (13174:19): [True: 2.35k, False: 3.47k]
  ------------------
13175|       |                if constexpr (result_type_is_ada_url) {
13176|       |                  url.password += "%40";
13177|  2.35k|                } else {
13178|  2.35k|                  url.append_base_password("%40");
13179|  2.35k|                }
13180|  3.47k|              } else {
13181|       |                if constexpr (result_type_is_ada_url) {
13182|       |                  url.username += "%40";
13183|  3.47k|                } else {
13184|  3.47k|                  url.append_base_username("%40");
13185|  3.47k|                }
13186|  3.47k|              }
13187|  5.83k|            }
13188|       |
13189|  6.96k|            at_sign_seen = true;
13190|       |
13191|  6.96k|            if (!password_token_seen) {
  ------------------
  |  Branch (13191:17): [True: 4.61k, False: 2.35k]
  ------------------
13192|  4.61k|              size_t password_token_location = authority_view.find(':');
13193|  4.61k|              password_token_seen =
13194|  4.61k|                  password_token_location != std::string_view::npos;
13195|       |
13196|       |              if constexpr (store_values) {
13197|       |                if (!password_token_seen) {
13198|       |                  if constexpr (result_type_is_ada_url) {
13199|       |                    url.username += unicode::percent_encode(
13200|       |                        authority_view,
13201|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13202|       |                  } else {
13203|       |                    url.append_base_username(unicode::percent_encode(
13204|       |                        authority_view,
13205|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13206|       |                  }
13207|       |                } else {
13208|       |                  if constexpr (result_type_is_ada_url) {
13209|       |                    url.username += unicode::percent_encode(
13210|       |                        authority_view.substr(0, password_token_location),
13211|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13212|       |                    url.password += unicode::percent_encode(
13213|       |                        authority_view.substr(password_token_location + 1),
13214|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13215|       |                  } else {
13216|       |                    url.append_base_username(unicode::percent_encode(
13217|       |                        authority_view.substr(0, password_token_location),
13218|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13219|       |                    url.append_base_password(unicode::percent_encode(
13220|       |                        authority_view.substr(password_token_location + 1),
13221|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13222|       |                  }
13223|       |                }
13224|       |              }
13225|  4.61k|            } else if constexpr (store_values) {
13226|  2.35k|              if constexpr (result_type_is_ada_url) {
13227|  2.35k|                url.password += unicode::percent_encode(
13228|  2.35k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13229|  2.35k|              } else {
13230|  2.35k|                url.append_base_password(unicode::percent_encode(
13231|  2.35k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13232|  2.35k|              }
13233|  2.35k|            }
13234|  6.96k|          }
13235|       |          // Otherwise, if one of the following is true:
13236|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13237|       |          // - url is special and c is U+005C (\)
13238|  1.28k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13238:20): [True: 822, False: 463]
  ------------------
13239|    463|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13239:20): [True: 413, False: 50]
  ------------------
13240|     50|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13240:20): [True: 44, False: 6]
  ------------------
13241|  1.28k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13241:21): [True: 6, False: 0]
  |  Branch (13241:41): [True: 6, False: 0]
  ------------------
13242|       |            // If atSignSeen is true and authority_view is the empty string,
13243|       |            // validation error, return failure.
13244|  1.28k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13244:17): [True: 1.13k, False: 149]
  |  Branch (13244:33): [True: 345, False: 791]
  ------------------
13245|    345|              url.is_valid = false;
13246|    345|              return url;
13247|    345|            }
13248|    940|            state = state::HOST;
13249|    940|            break;
13250|  1.28k|          }
13251|  6.96k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13251:15): [True: 0, False: 6.96k]
  ------------------
13252|       |            if constexpr (store_values) {
13253|       |              if (fragment.has_value()) {
13254|       |                url.update_unencoded_base_hash(*fragment);
13255|       |              }
13256|       |            }
13257|      0|            enforce_max_input_length();
13258|      0|            return url;
13259|      0|          }
13260|  6.96k|          input_position = end_of_authority + 1;
13261|  6.96k|        } while (true);
  ------------------
  |  Branch (13261:18): [True: 6.96k, Folded]
  ------------------
13262|       |
13263|    940|        break;
13264|  1.28k|      }
13265|    940|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13265:7): [True: 436, False: 136k]
  ------------------
13266|    436|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13267|    436|                helpers::substring(url_data, input_position));
13268|       |
13269|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13270|       |        // then set state to special authority ignore slashes state and increase
13271|       |        // pointer by 1.
13272|    436|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13272:13): [True: 353, False: 83]
  ------------------
13273|    353|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13274|    353|          input_position += 2;
13275|    353|        } else {
13276|       |          // Otherwise, validation error, set state to relative state and
13277|       |          // decrease pointer by 1.
13278|     83|          state = state::RELATIVE_SCHEME;
13279|     83|        }
13280|       |
13281|    436|        break;
13282|  1.28k|      }
13283|  2.25k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (13283:7): [True: 2.25k, False: 134k]
  ------------------
13284|  2.25k|        ada_log("PATH_OR_AUTHORITY ",
13285|  2.25k|                helpers::substring(url_data, input_position));
13286|       |
13287|       |        // If c is U+002F (/), then set state to authority state.
13288|  2.25k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13288:13): [True: 2.13k, False: 122]
  ------------------
13289|  2.13k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13289:13): [True: 907, False: 1.22k]
  ------------------
13290|    907|          state = state::AUTHORITY;
13291|    907|          input_position++;
13292|  1.34k|        } else {
13293|       |          // Otherwise, set state to path state, and decrease pointer by 1.
13294|  1.34k|          state = state::PATH;
13295|  1.34k|        }
13296|       |
13297|  2.25k|        break;
13298|  1.28k|      }
13299|  1.11k|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (13299:7): [True: 1.11k, False: 136k]
  ------------------
13300|  1.11k|        ada_log("RELATIVE_SCHEME ",
13301|  1.11k|                helpers::substring(url_data, input_position));
13302|       |
13303|       |        // Set url's scheme to base's scheme.
13304|  1.11k|        url.copy_scheme(*base_url);
13305|       |
13306|       |        // If c is U+002F (/), then set state to relative slash state.
13307|  1.11k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13307:13): [True: 932, False: 182]
  ------------------
13308|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
13309|    932|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13309:13): [True: 99, False: 833]
  ------------------
13310|     99|          ada_log(
13311|     99|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13312|     99|              "slash state");
13313|     99|          state = state::RELATIVE_SLASH;
13314|  1.01k|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13314:20): [True: 883, False: 132]
  |  Branch (13314:40): [True: 728, False: 155]
  ------------------
13315|    728|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13315:20): [True: 4, False: 724]
  ------------------
13316|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13317|       |          // set state to relative slash state.
13318|      4|          ada_log(
13319|      4|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13320|      4|              "error, set state to relative slash state");
13321|      4|          state = state::RELATIVE_SLASH;
13322|  1.01k|        } else {
13323|  1.01k|          ada_log("RELATIVE_SCHEME otherwise");
13324|       |          // Set url's username to base's username, url's password to base's
13325|       |          // password, url's host to base's host, url's port to base's port,
13326|       |          // url's path to a clone of base's path, and url's query to base's
13327|       |          // query.
13328|       |          if constexpr (result_type_is_ada_url) {
13329|       |            url.username = base_url->username;
13330|       |            url.password = base_url->password;
13331|       |            url.host = base_url->host;
13332|       |            url.port = base_url->port;
13333|       |            // cloning the base path includes cloning the has_opaque_path flag
13334|       |            url.has_opaque_path = base_url->has_opaque_path;
13335|       |            url.path = base_url->path;
13336|       |            url.query = base_url->query;
13337|  1.01k|          } else {
13338|  1.01k|            url.update_base_authority(base_url->get_href(),
13339|  1.01k|                                      base_url->get_components());
13340|  1.01k|            url.update_host_to_base_host(base_url->get_hostname());
13341|  1.01k|            url.update_base_port(base_url->retrieve_base_port());
13342|       |            // cloning the base path includes cloning the has_opaque_path flag
13343|  1.01k|            url.has_opaque_path = base_url->has_opaque_path;
13344|  1.01k|            url.update_base_pathname(base_url->get_pathname());
13345|  1.01k|            if (base_url->has_search()) {
  ------------------
  |  Branch (13345:17): [True: 0, False: 1.01k]
  ------------------
13346|       |              // get_search() returns "" for an empty query string (URL ends
13347|       |              // with '?'). update_base_search("") would incorrectly clear the
13348|       |              // query, so pass "?" to preserve the empty query distinction.
13349|      0|              auto s = base_url->get_search();
13350|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13350:38): [True: 0, False: 0]
  ------------------
13351|      0|            }
13352|  1.01k|          }
13353|       |
13354|  1.01k|          url.has_opaque_path = base_url->has_opaque_path;
13355|       |
13356|       |          // If c is U+003F (?), then set url's query to the empty string, and
13357|       |          // state to query state.
13358|  1.01k|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13358:15): [True: 829, False: 182]
  ------------------
13359|    829|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13359:15): [True: 124, False: 705]
  ------------------
13360|    124|            state = state::QUERY;
13361|    124|          }
13362|       |          // Otherwise, if c is not the EOF code point:
13363|    887|          else if (input_position != input_size) {
  ------------------
  |  Branch (13363:20): [True: 705, False: 182]
  ------------------
13364|       |            // Set url's query to null.
13365|    705|            url.clear_search();
13366|       |            if constexpr (result_type_is_ada_url) {
13367|       |              // Shorten url's path.
13368|       |              helpers::shorten_path(url.path, url.type);
13369|    705|            } else {
13370|    705|              std::string_view path = url.get_pathname();
13371|    705|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13371:19): [True: 0, False: 705]
  ------------------
13372|      0|                url.update_base_pathname(std::move(std::string(path)));
13373|      0|              }
13374|    705|            }
13375|       |            // Set state to path state and decrease pointer by 1.
13376|    705|            state = state::PATH;
13377|    705|            break;
13378|    705|          }
13379|  1.01k|        }
13380|    409|        input_position++;
13381|    409|        break;
13382|  1.11k|      }
13383|    103|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13383:7): [True: 103, False: 137k]
  ------------------
13384|    103|        ada_log("RELATIVE_SLASH ",
13385|    103|                helpers::substring(url_data, input_position));
13386|       |
13387|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13388|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13389|    103|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13389:13): [True: 82, False: 21]
  |  Branch (13389:33): [True: 56, False: 26]
  ------------------
13390|     56|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13390:14): [True: 4, False: 52]
  ------------------
13391|     52|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13391:14): [True: 2, False: 50]
  ------------------
13392|       |          // Set state to special authority ignore slashes state.
13393|      6|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13394|      6|        }
13395|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13396|     97|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13396:18): [True: 61, False: 36]
  ------------------
13397|     61|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13397:18): [True: 6, False: 55]
  ------------------
13398|      6|          state = state::AUTHORITY;
13399|      6|        }
13400|       |        // Otherwise, set
13401|       |        // - url's username to base's username,
13402|       |        // - url's password to base's password,
13403|       |        // - url's host to base's host,
13404|       |        // - url's port to base's port,
13405|       |        // - state to path state, and then, decrease pointer by 1.
13406|     91|        else {
13407|       |          if constexpr (result_type_is_ada_url) {
13408|       |            url.username = base_url->username;
13409|       |            url.password = base_url->password;
13410|       |            url.host = base_url->host;
13411|       |            url.port = base_url->port;
13412|     91|          } else {
13413|     91|            url.update_base_authority(base_url->get_href(),
13414|     91|                                      base_url->get_components());
13415|     91|            url.update_host_to_base_host(base_url->get_hostname());
13416|     91|            url.update_base_port(base_url->retrieve_base_port());
13417|     91|          }
13418|     91|          state = state::PATH;
13419|     91|          break;
13420|     91|        }
13421|       |
13422|     12|        input_position++;
13423|     12|        break;
13424|    103|      }
13425|  13.0k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13425:7): [True: 13.0k, False: 124k]
  ------------------
13426|  13.0k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13427|  13.0k|                helpers::substring(url_data, input_position));
13428|       |
13429|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13430|       |        // then set state to special authority ignore slashes state and increase
13431|       |        // pointer by 1.
13432|  13.0k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13432:13): [True: 3.29k, False: 9.74k]
  ------------------
13433|  3.29k|          input_position += 2;
13434|  3.29k|        }
13435|       |
13436|  13.0k|        [[fallthrough]];
13437|  13.0k|      }
13438|  13.4k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13438:7): [True: 359, False: 136k]
  ------------------
13439|  13.4k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13440|  13.4k|                helpers::substring(url_data, input_position));
13441|       |
13442|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13443|       |        // authority state and decrease pointer by 1.
13444|  14.8k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13444:16): [True: 14.8k, False: 64]
  ------------------
13445|  14.8k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13445:17): [True: 910, False: 13.9k]
  ------------------
13446|  13.9k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13446:17): [True: 559, False: 13.3k]
  ------------------
13447|  1.46k|          input_position++;
13448|  1.46k|        }
13449|  13.4k|        state = state::AUTHORITY;
13450|       |
13451|  13.4k|        break;
13452|  13.0k|      }
13453|    132|      case state::QUERY: {
  ------------------
  |  Branch (13453:7): [True: 132, False: 136k]
  ------------------
13454|    132|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13455|       |        if constexpr (store_values) {
13456|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13457|       |          // if url is special; otherwise the query percent-encode set.
13458|       |          const uint8_t* query_percent_encode_set =
13459|       |              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
13460|       |                               : character_sets::QUERY_PERCENT_ENCODE;
13461|       |
13462|       |          // Percent-encode after encoding, with encoding, buffer, and
13463|       |          // queryPercentEncodeSet, and append the result to url's query.
13464|       |          url.update_base_search(url_data.substr(input_position),
13465|       |                                 query_percent_encode_set);
13466|       |          ada_log("QUERY update_base_search completed ");
13467|       |          if (fragment.has_value()) {
13468|       |            url.update_unencoded_base_hash(*fragment);
13469|       |          }
13470|       |        }
13471|    132|        enforce_max_input_length();
13472|    132|        return url;
13473|  13.0k|      }
13474|  13.9k|      case state::HOST: {
  ------------------
  |  Branch (13474:7): [True: 13.9k, False: 123k]
  ------------------
13475|  13.9k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13476|       |
13477|  13.9k|        std::string_view host_view = url_data.substr(input_position);
13478|  13.9k|        auto [location, found_colon] =
13479|  13.9k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13480|  13.9k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13480:26): [True: 13.9k, False: 0]
  ------------------
13481|  13.9k|                             ? input_position + location
13482|  13.9k|                             : input_size;
13483|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13484|       |        // Note: the 'found_colon' value is true if and only if a colon was
13485|       |        // encountered while not inside brackets.
13486|  13.9k|        if (found_colon) {
  ------------------
  |  Branch (13486:13): [True: 2.64k, False: 11.3k]
  ------------------
13487|       |          // If buffer is the empty string, validation error, return failure.
13488|       |          // Let host be the result of host parsing buffer with url is not
13489|       |          // special.
13490|  2.64k|          ada_log("HOST parsing ", host_view);
13491|  2.64k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13491:15): [True: 213, False: 2.43k]
  ------------------
13492|    213|            return url;
13493|    213|          }
13494|  2.43k|          ada_log("HOST parsing results in ", url.get_hostname());
13495|       |          // Set url's host to host, buffer to the empty string, and state to
13496|       |          // port state.
13497|  2.43k|          state = state::PORT;
13498|  2.43k|          input_position++;
13499|  2.43k|        }
13500|       |        // Otherwise, if one of the following is true:
13501|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13502|       |        // - url is special and c is U+005C (\)
13503|       |        // The get_host_delimiter_location function either brings us to
13504|       |        // the colon outside of the bracket, or to one of those characters.
13505|  11.3k|        else {
13506|       |          // If url is special and host_view is the empty string, validation
13507|       |          // error, return failure.
13508|  11.3k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13508:15): [True: 212, False: 11.1k]
  |  Branch (13508:36): [True: 68, False: 144]
  ------------------
13509|     68|            url.is_valid = false;
13510|     68|            return url;
13511|     68|          }
13512|  11.2k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13513|       |          // Let host be the result of host parsing host_view with url is not
13514|       |          // special.
13515|  11.2k|          if (host_view.empty()) {
  ------------------
  |  Branch (13515:15): [True: 144, False: 11.1k]
  ------------------
13516|    144|            url.update_base_hostname("");
13517|  11.1k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13517:22): [True: 2.41k, False: 8.70k]
  ------------------
13518|  2.41k|            return url;
13519|  2.41k|          }
13520|  8.84k|          ada_log("HOST parsing results in ", url.get_hostname(),
13521|  8.84k|                  " href=", url.get_href());
13522|       |
13523|       |          // Set url's host to host, and state to path start state.
13524|  8.84k|          state = state::PATH_START;
13525|  8.84k|        }
13526|       |
13527|  11.2k|        break;
13528|  13.9k|      }
13529|  11.2k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13529:7): [True: 3.43k, False: 133k]
  ------------------
13530|  3.43k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13531|       |        // Opaque path, query, and fragment are structurally always valid:
13532|       |        // the parser would just percent-encode whatever is there. When we
13533|       |        // are not storing values (can_parse), we can return immediately.
13534|       |        // We must set has_opaque_path = true before returning so that when
13535|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13536|       |        // correctly rejects relative inputs against an opaque-path base
13537|       |        // (e.g. can_parse("", &"W:") must return false).
13538|  3.43k|        if constexpr (!store_values) {
13539|  3.43k|          url.has_opaque_path = true;
13540|  3.43k|          return url;
13541|  3.43k|        }
13542|      0|        std::string_view view = url_data.substr(input_position);
13543|       |        // If c is U+003F (?), then set url's query to the empty string and
13544|       |        // state to query state.
13545|  3.43k|        size_t location = view.find('?');
13546|  3.43k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13546:13): [True: 0, False: 3.43k]
  ------------------
13547|      0|          view.remove_suffix(view.size() - location);
13548|      0|          state = state::QUERY;
13549|      0|          input_position += location + 1;
13550|  3.43k|        } else {
13551|  3.43k|          input_position = input_size + 1;
13552|  3.43k|        }
13553|  3.43k|        url.has_opaque_path = true;
13554|       |
13555|       |        // This is a really unlikely scenario in real world. We should not seek
13556|       |        // to optimize it.
13557|  3.43k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13557:13): [True: 0, False: 3.43k]
  ------------------
13558|      0|          std::string modified_view =
13559|      0|              std::string(view.substr(0, view.size() - 1)) + "%20";
13560|      0|          url.update_base_pathname(unicode::percent_encode(
13561|      0|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13562|  3.43k|        } else {
13563|  3.43k|          url.update_base_pathname(unicode::percent_encode(
13564|  3.43k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13565|  3.43k|        }
13566|  3.43k|        break;
13567|  13.9k|      }
13568|  2.43k|      case state::PORT: {
  ------------------
  |  Branch (13568:7): [True: 2.43k, False: 134k]
  ------------------
13569|  2.43k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13570|  2.43k|        std::string_view port_view = url_data.substr(input_position);
13571|  2.43k|        input_position += url.parse_port(port_view, true);
13572|  2.43k|        if (!url.is_valid) {
  ------------------
  |  Branch (13572:13): [True: 96, False: 2.33k]
  ------------------
13573|     96|          return url;
13574|     96|        }
13575|  2.33k|        state = state::PATH_START;
13576|  2.33k|        [[fallthrough]];
13577|  2.33k|      }
13578|  13.1k|      case state::PATH_START: {
  ------------------
  |  Branch (13578:7): [True: 10.8k, False: 126k]
  ------------------
13579|  13.1k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13580|       |        // Path, query, and fragment are structurally always valid: the
13581|       |        // parser would just percent-encode whatever is there. When we are
13582|       |        // not storing values (can_parse), we can return immediately since
13583|       |        // no subsequent state can invalidate the URL.
13584|  13.1k|        if constexpr (!store_values) {
13585|  13.1k|          return url;
13586|  13.1k|        }
13587|       |
13588|       |        // If url is special, then:
13589|  13.1k|        if (url.is_special()) {
  ------------------
  |  Branch (13589:13): [True: 0, False: 13.1k]
  ------------------
13590|       |          // Set state to path state.
13591|      0|          state = state::PATH;
13592|       |
13593|       |          // Optimization: Avoiding going into PATH state improves the
13594|       |          // performance of urls ending with /.
13595|      0|          if (input_position == input_size) {
  ------------------
  |  Branch (13595:15): [True: 0, False: 0]
  ------------------
13596|       |            if constexpr (store_values) {
13597|       |              url.update_base_pathname("/");
13598|       |              if (fragment.has_value()) {
13599|       |                url.update_unencoded_base_hash(*fragment);
13600|       |              }
13601|       |            }
13602|      0|            enforce_max_input_length();
13603|      0|            return url;
13604|      0|          }
13605|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13606|       |          // by 1. We know that (input_position == input_size) is impossible
13607|       |          // here, because of the previous if-check.
13608|      0|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13608:15): [True: 0, False: 0]
  ------------------
13609|      0|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13609:15): [True: 0, False: 0]
  ------------------
13610|      0|            break;
13611|      0|          }
13612|      0|        }
13613|       |        // Otherwise, if state override is not given and c is U+003F (?),
13614|       |        // set url's query to the empty string and state to query state.
13615|  13.1k|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13615:18): [True: 0, False: 13.1k]
  ------------------
13616|      0|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13616:18): [True: 0, False: 0]
  ------------------
13617|      0|          state = state::QUERY;
13618|      0|        }
13619|       |        // Otherwise, if c is not the EOF code point:
13620|  13.1k|        else if (input_position != input_size) {
  ------------------
  |  Branch (13620:18): [True: 0, False: 13.1k]
  ------------------
13621|       |          // Set state to path state.
13622|      0|          state = state::PATH;
13623|       |
13624|       |          // If c is not U+002F (/), then decrease pointer by 1.
13625|      0|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13625:15): [True: 0, False: 0]
  ------------------
13626|      0|            break;
13627|      0|          }
13628|      0|        }
13629|       |
13630|  13.1k|        input_position++;
13631|  13.1k|        break;
13632|  13.1k|      }
13633|  2.88k|      case state::PATH: {
  ------------------
  |  Branch (13633:7): [True: 2.88k, False: 134k]
  ------------------
13634|  2.88k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13635|       |        // Path, query, and fragment are structurally always valid: the
13636|       |        // parser would just percent-encode whatever is there. When we are
13637|       |        // not storing values (can_parse), we can return immediately since
13638|       |        // no subsequent state can invalidate the URL.
13639|  2.88k|        if constexpr (!store_values) {
13640|  2.88k|          return url;
13641|  2.88k|        }
13642|      0|        std::string_view view = url_data.substr(input_position);
13643|       |
13644|       |        // Most time, we do not need percent encoding.
13645|       |        // Furthermore, we can immediately locate the '?'.
13646|  2.88k|        size_t locofquestionmark = view.find('?');
13647|  2.88k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13647:13): [True: 0, False: 2.88k]
  ------------------
13648|      0|          state = state::QUERY;
13649|      0|          view.remove_suffix(view.size() - locofquestionmark);
13650|      0|          input_position += locofquestionmark + 1;
13651|  2.88k|        } else {
13652|  2.88k|          input_position = input_size + 1;
13653|  2.88k|        }
13654|       |        if constexpr (store_values) {
13655|       |          if constexpr (result_type_is_ada_url) {
13656|       |            helpers::parse_prepared_path(view, url.type, url.path);
13657|       |          } else {
13658|       |            url.consume_prepared_path(view);
13659|       |            ADA_ASSERT_TRUE(url.validate());
13660|       |          }
13661|       |        }
13662|  2.88k|        break;
13663|  13.1k|      }
13664|  2.30k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13664:7): [True: 2.30k, False: 134k]
  ------------------
13665|  2.30k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13666|       |
13667|       |        // If c is U+002F (/) or U+005C (\), then:
13668|  2.30k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13668:13): [True: 2.23k, False: 70]
  ------------------
13669|  2.23k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13669:14): [True: 2.18k, False: 49]
  ------------------
13670|  2.19k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13670:14): [True: 6, False: 43]
  ------------------
13671|  2.19k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13672|       |          // Set state to file host state.
13673|  2.19k|          state = state::FILE_HOST;
13674|  2.19k|          input_position++;
13675|  2.19k|        } else {
13676|    113|          ada_log("FILE_SLASH otherwise");
13677|       |          // If base is non-null and base's scheme is "file", then:
13678|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13679|       |          // base_url_has_value() is true.
13680|    113|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13680:15): [True: 89, False: 24]
  |  Branch (13680:38): [True: 87, False: 2]
  ------------------
13681|       |            // Set url's host to base's host.
13682|       |            if constexpr (result_type_is_ada_url) {
13683|       |              url.host = base_url->host;
13684|     87|            } else {
13685|     87|              url.update_host_to_base_host(base_url->get_host());
13686|     87|            }
13687|       |            // If the code point substring from pointer to the end of input does
13688|       |            // not start with a Windows drive letter and base's path[0] is a
13689|       |            // normalized Windows drive letter, then append base's path[0] to
13690|       |            // url's path.
13691|     87|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13691:17): [True: 0, False: 87]
  ------------------
13692|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13692:19): [True: 0, False: 0]
  ------------------
13693|      0|                      url_data.substr(input_position))) {
13694|      0|                std::string_view first_base_url_path =
13695|      0|                    base_url->get_pathname().substr(1);
13696|      0|                size_t loc = first_base_url_path.find('/');
13697|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13697:21): [True: 0, False: 0]
  ------------------
13698|      0|                  helpers::resize(first_base_url_path, loc);
13699|      0|                }
13700|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13700:21): [True: 0, False: 0]
  ------------------
13701|      0|                        first_base_url_path)) {
13702|       |                  if constexpr (result_type_is_ada_url) {
13703|       |                    url.path += '/';
13704|       |                    url.path += first_base_url_path;
13705|      0|                  } else {
13706|      0|                    url.append_base_pathname(
13707|      0|                        helpers::concat("/", first_base_url_path));
13708|      0|                  }
13709|      0|                }
13710|      0|              }
13711|      0|            }
13712|     87|          }
13713|       |
13714|       |          // Set state to path state, and decrease pointer by 1.
13715|    113|          state = state::PATH;
13716|    113|        }
13717|       |
13718|  2.30k|        break;
13719|  13.1k|      }
13720|  2.19k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13720:7): [True: 2.19k, False: 134k]
  ------------------
13721|  2.19k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13722|  2.19k|        std::string_view view = url_data.substr(input_position);
13723|       |
13724|  2.19k|        size_t location = view.find_first_of("/\\?");
13725|  2.19k|        std::string_view file_host_buffer = view.substr(
13726|  2.19k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13726:16): [True: 943, False: 1.25k]
  ------------------
13727|       |
13728|  2.19k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13728:13): [True: 12, False: 2.18k]
  ------------------
13729|     12|          state = state::PATH;
13730|  2.18k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13730:20): [True: 286, False: 1.89k]
  ------------------
13731|       |          // Set url's host to the empty string.
13732|       |          if constexpr (result_type_is_ada_url) {
13733|       |            url.host = "";
13734|    286|          } else {
13735|    286|            url.update_base_hostname("");
13736|    286|          }
13737|       |          // Set state to path start state.
13738|    286|          state = state::PATH_START;
13739|  1.89k|        } else {
13740|  1.89k|          size_t consumed_bytes = file_host_buffer.size();
13741|  1.89k|          input_position += consumed_bytes;
13742|       |          // Let host be the result of host parsing buffer with url is not
13743|       |          // special.
13744|  1.89k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13744:15): [True: 199, False: 1.69k]
  ------------------
13745|    199|            return url;
13746|    199|          }
13747|       |
13748|       |          if constexpr (result_type_is_ada_url) {
13749|       |            // If host is "localhost", then set host to the empty string.
13750|       |            if (url.host.has_value() && url.host.value() == "localhost") {
13751|       |              url.host = "";
13752|       |            }
13753|  1.69k|          } else {
13754|  1.69k|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (13754:17): [True: 11, False: 1.68k]
  ------------------
13755|     11|              url.update_base_hostname("");
13756|     11|            }
13757|  1.69k|          }
13758|       |
13759|       |          // Set buffer to the empty string and state to path start state.
13760|  1.69k|          state = state::PATH_START;
13761|  1.69k|        }
13762|       |
13763|  1.99k|        break;
13764|  2.19k|      }
13765|  2.98k|      case state::FILE: {
  ------------------
  |  Branch (13765:7): [True: 2.98k, False: 134k]
  ------------------
13766|  2.98k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13767|  2.98k|        std::string_view file_view = url_data.substr(input_position);
13768|       |
13769|  2.98k|        url.set_protocol_as_file();
13770|       |        if constexpr (result_type_is_ada_url) {
13771|       |          // Set url's host to the empty string.
13772|       |          url.host = "";
13773|  2.98k|        } else {
13774|  2.98k|          url.update_base_hostname("");
13775|  2.98k|        }
13776|       |        // If c is U+002F (/) or U+005C (\), then:
13777|  2.98k|        if (input_position != input_size &&
  ------------------
  |  Branch (13777:13): [True: 2.86k, False: 124]
  ------------------
13778|  2.86k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13778:14): [True: 2.30k, False: 562]
  ------------------
13779|  2.30k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13779:14): [True: 6, False: 556]
  ------------------
13780|  2.30k|          ada_log("FILE c is U+002F or U+005C");
13781|       |          // Set state to file slash state.
13782|  2.30k|          state = state::FILE_SLASH;
13783|  2.30k|        }
13784|       |        // Otherwise, if base is non-null and base's scheme is "file":
13785|    680|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13785:18): [True: 252, False: 428]
  |  Branch (13785:41): [True: 178, False: 74]
  ------------------
13786|       |          // Set url's host to base's host, url's path to a clone of base's
13787|       |          // path, and url's query to base's query.
13788|    178|          ada_log("FILE base non-null");
13789|       |          if constexpr (result_type_is_ada_url) {
13790|       |            url.host = base_url->host;
13791|       |            url.path = base_url->path;
13792|       |            url.query = base_url->query;
13793|    178|          } else {
13794|    178|            url.update_host_to_base_host(base_url->get_hostname());
13795|    178|            url.update_base_pathname(base_url->get_pathname());
13796|    178|            if (base_url->has_search()) {
  ------------------
  |  Branch (13796:17): [True: 0, False: 178]
  ------------------
13797|       |              // get_search() returns "" for an empty query string (URL ends
13798|       |              // with '?'). update_base_search("") would incorrectly clear the
13799|       |              // query, so pass "?" to preserve the empty query distinction.
13800|      0|              auto s = base_url->get_search();
13801|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13801:38): [True: 0, False: 0]
  ------------------
13802|      0|            }
13803|    178|          }
13804|    178|          url.has_opaque_path = base_url->has_opaque_path;
13805|       |
13806|       |          // If c is U+003F (?), then set url's query to the empty string and
13807|       |          // state to query state.
13808|    178|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13808:15): [True: 124, False: 54]
  |  Branch (13808:47): [True: 8, False: 116]
  ------------------
13809|      8|            state = state::QUERY;
13810|      8|          }
13811|       |          // Otherwise, if c is not the EOF code point:
13812|    170|          else if (input_position != input_size) {
  ------------------
  |  Branch (13812:20): [True: 116, False: 54]
  ------------------
13813|       |            // Set url's query to null.
13814|    116|            url.clear_search();
13815|       |            // If the code point substring from pointer to the end of input does
13816|       |            // not start with a Windows drive letter, then shorten url's path.
13817|    116|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13817:17): [True: 97, False: 19]
  ------------------
13818|       |              if constexpr (result_type_is_ada_url) {
13819|       |                helpers::shorten_path(url.path, url.type);
13820|     97|              } else {
13821|     97|                std::string_view path = url.get_pathname();
13822|     97|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13822:21): [True: 0, False: 97]
  ------------------
13823|      0|                  url.update_base_pathname(std::move(std::string(path)));
13824|      0|                }
13825|     97|              }
13826|     97|            }
13827|       |            // Otherwise:
13828|     19|            else {
13829|       |              // Set url's path to an empty list.
13830|     19|              url.clear_pathname();
13831|     19|              url.has_opaque_path = true;
13832|     19|            }
13833|       |
13834|       |            // Set state to path state and decrease pointer by 1.
13835|    116|            state = state::PATH;
13836|    116|            break;
13837|    116|          }
13838|    178|        }
13839|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13840|    502|        else {
13841|    502|          ada_log("FILE go to path");
13842|    502|          state = state::PATH;
13843|    502|          break;
13844|    502|        }
13845|       |
13846|  2.36k|        input_position++;
13847|  2.36k|        break;
13848|  2.98k|      }
13849|      0|      default:
  ------------------
  |  Branch (13849:7): [True: 0, False: 137k]
  ------------------
13850|      0|        unreachable();
13851|   137k|    }
13852|   137k|  }
13853|       |  if constexpr (store_values) {
13854|       |    if (fragment.has_value()) {
13855|       |      url.update_unencoded_base_hash(*fragment);
13856|       |    }
13857|       |  }
13858|    236|  enforce_max_input_length();
13859|    236|  return url;
13860|  31.8k|}
_ZNK3ada14url_aggregator8get_hostEv:
14857|    174|    ada_lifetime_bound {
14858|    174|  ada_log("url_aggregator::get_host");
14859|       |  // Technically, we should check if there is a hostname, but
14860|       |  // the code below works even if there isn't.
14861|       |  // if(!has_hostname()) { return ""; }
14862|    174|  size_t start = components.host_start;
14863|    174|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (14863:7): [True: 40, False: 134]
  ------------------
14864|     40|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (14864:7): [True: 0, False: 40]
  ------------------
14865|      0|    start++;
14866|      0|  }
14867|       |  // if we have an empty host, then the space between components.host_end and
14868|       |  // components.pathname_start may be occupied by /.
14869|    174|  if (start == components.host_end) {
  ------------------
  |  Branch (14869:7): [True: 134, False: 40]
  ------------------
14870|    134|    return {};
14871|    134|  }
14872|     40|  return helpers::substring(buffer, start, components.pathname_start);
14873|    174|}
_ZNK3ada14url_aggregator12get_hostnameEv:
14876|  19.6k|    ada_lifetime_bound {
14877|  19.6k|  ada_log("url_aggregator::get_hostname");
14878|       |  // Technically, we should check if there is a hostname, but
14879|       |  // the code below works even if there isn't.
14880|       |  // if(!has_hostname()) { return ""; }
14881|  19.6k|  size_t start = components.host_start;
14882|       |  // So host_start is not where the host begins.
14883|  19.6k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (14883:7): [True: 5.27k, False: 14.4k]
  ------------------
14884|  5.27k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (14884:7): [True: 226, False: 5.05k]
  ------------------
14885|    226|    start++;
14886|    226|  }
14887|  19.6k|  return helpers::substring(buffer, start, components.host_end);
14888|  19.6k|}
_ZNK3ada14url_aggregator10get_searchEv:
14891|    170|    ada_lifetime_bound {
14892|    170|  ada_log("url_aggregator::get_search");
14893|       |  // If this's URL's query is either null or the empty string, then return the
14894|       |  // empty string. Return U+003F (?), followed by this's URL's query.
14895|    170|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (14895:7): [True: 0, False: 170]
  ------------------
14896|      0|    return "";
14897|      0|  }
14898|    170|  auto ending_index = uint32_t(buffer.size());
14899|    170|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (14899:7): [True: 44, False: 126]
  ------------------
14900|     44|    ending_index = components.hash_start;
14901|     44|  }
14902|    170|  if (ending_index - components.search_start <= 1) {
  ------------------
  |  Branch (14902:7): [True: 65, False: 105]
  ------------------
14903|     65|    return "";
14904|     65|  }
14905|    105|  return helpers::substring(buffer, components.search_start, ending_index);
14906|    170|}
_ZNK3ada14url_aggregator12get_protocolEv:
14909|  2.10k|    ada_lifetime_bound {
14910|  2.10k|  ada_log("url_aggregator::get_protocol");
14911|  2.10k|  return helpers::substring(buffer, 0, components.protocol_end);
14912|  2.10k|}
_ZN3ada14url_aggregator10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
15021|  3.92k|bool url_aggregator::parse_ipv4(std::string_view input, bool in_place) {
15022|  3.92k|  ada_log("parse_ipv4 ", input, " [", input.size(),
15023|  3.92k|          " bytes], overlaps with buffer: ",
15024|  3.92k|          helpers::overlaps(input, buffer) ? "yes" : "no");
15025|  3.92k|  ADA_ASSERT_TRUE(validate());
15026|  3.92k|  if (input.empty()) {
  ------------------
  |  Branch (15026:7): [True: 0, False: 3.92k]
  ------------------
15027|      0|    return is_valid = false;
15028|      0|  }
15029|  3.92k|  const bool trailing_dot = (input.back() == '.');
15030|  3.92k|  if (trailing_dot) {
  ------------------
  |  Branch (15030:7): [True: 138, False: 3.78k]
  ------------------
15031|    138|    input.remove_suffix(1);
15032|    138|    if (input.empty()) {
  ------------------
  |  Branch (15032:9): [True: 0, False: 138]
  ------------------
15033|      0|      return is_valid = false;
15034|      0|    }
15035|    138|  }
15036|       |
15037|  3.92k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
15038|  3.92k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (15038:7): [True: 46, False: 3.88k]
  ------------------
15039|       |    // Pure decimal: keep the buffer when it already holds the address.
15040|       |    // Otherwise write the (trailing-dot-stripped) input.
15041|     46|    if (!(in_place && !trailing_dot)) {
  ------------------
  |  Branch (15041:11): [True: 0, False: 46]
  |  Branch (15041:23): [True: 0, False: 0]
  ------------------
15042|     46|      if (helpers::overlaps(input, buffer)) {
  ------------------
  |  Branch (15042:11): [True: 0, False: 46]
  ------------------
15043|      0|        update_base_hostname(std::string(input));
15044|     46|      } else {
15045|     46|        update_base_hostname(input);
15046|     46|      }
15047|     46|    }
15048|     46|    host_type = IPV4;
15049|     46|    ADA_ASSERT_TRUE(validate());
15050|     46|    return true;
15051|     46|  }
15052|       |
15053|  3.88k|  const char* p = input.data();
15054|  3.88k|  const char* end = p + input.size();
15055|  3.88k|  uint64_t ipv4 = 0;
15056|  3.88k|  int digit_count = 0;
15057|  3.88k|  int pure_decimal_count = 0;
15058|       |
15059|  4.83k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (15059:10): [True: 4.80k, False: 22]
  |  Branch (15059:29): [True: 4.80k, False: 0]
  ------------------
15060|  4.80k|    uint64_t segment = 0;
15061|  4.80k|    bool pure = false;
15062|  4.80k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (15062:9): [True: 666, False: 4.14k]
  ------------------
15063|    666|      return is_valid = false;
15064|    666|    }
15065|  4.14k|    if (pure) {
  ------------------
  |  Branch (15065:9): [True: 3.14k, False: 1.00k]
  ------------------
15066|  3.14k|      ++pure_decimal_count;
15067|  3.14k|    }
15068|  4.14k|    if (p >= end) {
  ------------------
  |  Branch (15068:9): [True: 3.13k, False: 1.00k]
  ------------------
15069|  3.13k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
15070|  3.13k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (15070:11): [True: 44, False: 3.09k]
  ------------------
15071|     44|        return is_valid = false;
15072|     44|      }
15073|  3.09k|      ipv4 = (ipv4 << shift) | segment;
15074|  3.09k|      goto ipv4_done;
15075|  3.13k|    }
15076|  1.00k|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (15076:9): [True: 58, False: 950]
  |  Branch (15076:26): [True: 0, False: 950]
  ------------------
15077|     58|      return is_valid = false;
15078|     58|    }
15079|    950|    ipv4 = (ipv4 << 8) | segment;
15080|    950|    ++p;
15081|    950|  }
15082|     22|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (15082:7): [True: 0, False: 22]
  |  Branch (15082:27): [True: 22, False: 0]
  ------------------
15083|     22|    return is_valid = false;
15084|     22|  }
15085|  3.09k|ipv4_done:
15086|  3.09k|  ada_log("url_aggregator::parse_ipv4 completed ", get_href(),
15087|  3.09k|          " host: ", get_host());
15088|  3.09k|  if (in_place && pure_decimal_count == 4 && !trailing_dot) {
  ------------------
  |  Branch (15088:7): [True: 0, False: 3.09k]
  |  Branch (15088:19): [True: 0, False: 0]
  |  Branch (15088:46): [True: 0, False: 0]
  ------------------
15089|      0|    ada_log(
15090|      0|        "url_aggregator::parse_ipv4 completed and was already correct in the "
15091|      0|        "buffer");
15092|  3.09k|  } else {
15093|  3.09k|    update_base_hostname(ada::serializers::ipv4(ipv4));
15094|  3.09k|  }
15095|  3.09k|  host_type = IPV4;
15096|  3.09k|  ADA_ASSERT_TRUE(validate());
15097|  3.09k|  return true;
15098|     22|}
_ZN3ada14url_aggregator10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15100|  1.07k|bool url_aggregator::parse_ipv6(std::string_view input) {
15101|  1.07k|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
15102|  1.07k|  ADA_ASSERT_TRUE(validate());
15103|  1.07k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15104|  1.07k|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (15104:7): [True: 32, False: 1.04k]
  |  Branch (15104:24): [True: 32, False: 1.01k]
  ------------------
15105|     64|    return is_valid = false;
15106|     64|  }
15107|  1.01k|  std::array<uint16_t, 8> address{};
15108|       |#if defined(ADA_AVX512_IPV6)
15109|       |  if (bool simd_valid; detail::try_parse_ipv6_avx512(input.data(), input.size(),
15110|       |                                                     address, simd_valid)) {
15111|       |    if (!simd_valid) [[unlikely]] {
15112|       |      return is_valid = false;
15113|       |    }
15114|       |    const std::string serialized = ada::serializers::ipv6(address);
15115|       |    if (get_hostname() != serialized) {
15116|       |      update_base_hostname(serialized);
15117|       |    }
15118|       |    host_type = IPV6;
15119|       |    return true;
15120|       |  }
15121|       |  address = {};
15122|       |#endif
15123|  1.01k|  const char* pointer = input.data();
15124|  1.01k|  const char* const end = pointer + input.size();
15125|  1.01k|  int piece_index = 0;
15126|  1.01k|  int compress = -1;
15127|       |
15128|  1.01k|  if (*pointer == ':') {
  ------------------
  |  Branch (15128:7): [True: 384, False: 628]
  ------------------
15129|    384|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (15129:9): [True: 6, False: 378]
  |  Branch (15129:30): [True: 12, False: 366]
  ------------------
15130|     18|      return is_valid = false;
15131|     18|    }
15132|    366|    pointer += 2;
15133|    366|    compress = ++piece_index;
15134|    366|  }
15135|       |
15136|  2.72k|  while (pointer != end) {
  ------------------
  |  Branch (15136:10): [True: 2.06k, False: 666]
  ------------------
15137|  2.06k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (15137:9): [True: 6, False: 2.05k]
  ------------------
15138|      6|      return is_valid = false;
15139|      6|    }
15140|  2.05k|    if (*pointer == ':') {
  ------------------
  |  Branch (15140:9): [True: 284, False: 1.77k]
  ------------------
15141|    284|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (15141:11): [True: 6, False: 278]
  ------------------
15142|      6|        return is_valid = false;
15143|      6|      }
15144|    278|      ++pointer;
15145|    278|      compress = ++piece_index;
15146|    278|      continue;
15147|    284|    }
15148|       |
15149|  1.77k|    uint16_t value = 0;
15150|  1.77k|    const int length = detail::parse_hex_piece(pointer, end, value);
15151|       |
15152|  1.77k|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (15152:9): [True: 1.44k, False: 330]
  |  Branch (15152:27): [True: 198, False: 1.24k]
  ------------------
15153|    198|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (15153:11): [True: 6, False: 192]
  ------------------
15154|      6|        return is_valid = false;
15155|      6|      }
15156|    192|      pointer -= length;
15157|    192|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (15157:11): [True: 6, False: 186]
  ------------------
15158|      6|        return is_valid = false;
15159|      6|      }
15160|       |
15161|    186|      int numbers_seen = 0;
15162|    552|      while (pointer != end) {
  ------------------
  |  Branch (15162:14): [True: 492, False: 60]
  ------------------
15163|    492|        int ipv4_piece = -1;
15164|    492|        if (numbers_seen > 0) {
  ------------------
  |  Branch (15164:13): [True: 306, False: 186]
  ------------------
15165|    306|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (15165:15): [True: 254, False: 52]
  |  Branch (15165:34): [True: 244, False: 10]
  ------------------
15166|    244|            ++pointer;
15167|    244|          } else {
15168|     62|            return is_valid = false;
15169|     62|          }
15170|    306|        }
15171|    430|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (15171:13): [True: 24, False: 406]
  |  Branch (15171:31): [True: 12, False: 394]
  |  Branch (15171:49): [True: 12, False: 382]
  ------------------
15172|     48|          return is_valid = false;
15173|     48|        }
15174|    382|        ipv4_piece = *pointer - '0';
15175|    382|        ++pointer;
15176|    382|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (15176:13): [True: 346, False: 36]
  |  Branch (15176:31): [True: 122, False: 224]
  |  Branch (15176:50): [True: 110, False: 12]
  ------------------
15177|    110|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (15177:15): [True: 6, False: 104]
  ------------------
15178|      6|            return is_valid = false;
15179|      6|          }
15180|    104|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
15181|    104|          ++pointer;
15182|    104|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (15182:15): [True: 92, False: 12]
  |  Branch (15182:33): [True: 54, False: 38]
  |  Branch (15182:52): [True: 46, False: 8]
  ------------------
15183|     46|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
15184|     46|            ++pointer;
15185|     46|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (15185:17): [True: 10, False: 36]
  ------------------
15186|     10|              return is_valid = false;
15187|     10|            }
15188|     46|          }
15189|    104|        }
15190|    366|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
15191|    366|            address[static_cast<size_t>(piece_index)] * 0x100 +
15192|    366|            static_cast<uint16_t>(ipv4_piece));
15193|    366|        ++numbers_seen;
15194|    366|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (15194:13): [True: 114, False: 252]
  |  Branch (15194:34): [True: 38, False: 214]
  ------------------
15195|    152|          ++piece_index;
15196|    152|        }
15197|    366|      }
15198|     60|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (15198:11): [True: 36, False: 24]
  ------------------
15199|     36|        return is_valid = false;
15200|     36|      }
15201|     24|      break;
15202|     60|    }
15203|       |
15204|  1.57k|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (15204:9): [True: 94, False: 1.47k]
  ------------------
15205|     94|      return is_valid = false;
15206|     94|    }
15207|       |
15208|  1.47k|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (15208:9): [True: 1.14k, False: 330]
  |  Branch (15208:27): [True: 1.13k, False: 18]
  ------------------
15209|  1.13k|      ++pointer;
15210|  1.13k|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (15210:11): [True: 6, False: 1.12k]
  ------------------
15211|      6|        return is_valid = false;
15212|      6|      }
15213|  1.13k|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (15213:16): [True: 18, False: 330]
  ------------------
15214|     18|      return is_valid = false;
15215|     18|    }
15216|       |
15217|  1.45k|    address[static_cast<size_t>(piece_index)] = value;
15218|  1.45k|    ++piece_index;
15219|  1.45k|  }
15220|       |
15221|    690|  if (compress != -1) {
  ------------------
  |  Branch (15221:7): [True: 624, False: 66]
  ------------------
15222|    624|    const int right = piece_index - compress;
15223|    624|    if (right > 0) {
  ------------------
  |  Branch (15223:9): [True: 288, False: 336]
  ------------------
15224|    288|      const size_t dest = static_cast<size_t>(8 - right);
15225|    288|      const size_t src = static_cast<size_t>(compress);
15226|    288|      if (dest != src) {
  ------------------
  |  Branch (15226:11): [True: 274, False: 14]
  ------------------
15227|    796|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (15227:53): [True: 522, False: 274]
  ------------------
15228|    522|          address[dest + i] = address[src + i];
15229|    522|          address[src + i] = 0;
15230|    522|        }
15231|    274|      }
15232|    288|    }
15233|    624|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (15233:14): [True: 42, False: 24]
  ------------------
15234|     42|    return is_valid = false;
15235|     42|  }
15236|       |
15237|       |  // Serialize once; skip rewrite when hostname is already canonical.
15238|    648|  const std::string serialized = ada::serializers::ipv6(address);
15239|    648|  const std::string_view current = get_hostname();
15240|    648|  if (current.size() == serialized.size() &&
  ------------------
  |  Branch (15240:7): [True: 0, False: 648]
  ------------------
15241|      0|      std::memcmp(current.data(), serialized.data(), serialized.size()) == 0) {
  ------------------
  |  Branch (15241:7): [True: 0, False: 0]
  ------------------
15242|      0|    ada_log("parse_ipv6 in-place canonical match");
15243|    648|  } else {
15244|    648|    update_base_hostname(serialized);
15245|    648|  }
15246|    648|  ada_log("parse_ipv6 ", get_hostname());
15247|    648|  ADA_ASSERT_TRUE(validate());
15248|    648|  host_type = IPV6;
15249|    648|  return true;
15250|    690|}
_ZN3ada14url_aggregator17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15252|  1.23k|bool url_aggregator::parse_opaque_host(std::string_view input) {
15253|  1.23k|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
15254|  1.23k|  ADA_ASSERT_TRUE(validate());
15255|  1.23k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15256|  1.23k|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (15256:7): [True: 24, False: 1.20k]
  ------------------
15257|     24|    return is_valid = false;
15258|     24|  }
15259|       |
15260|       |  // Return the result of running UTF-8 percent-encode on input using the C0
15261|       |  // control percent-encode set.
15262|  1.20k|  size_t idx = ada::unicode::percent_encode_index(
15263|  1.20k|      input, character_sets::C0_CONTROL_PERCENT_ENCODE);
15264|  1.20k|  if (idx == input.size()) {
  ------------------
  |  Branch (15264:7): [True: 692, False: 514]
  ------------------
15265|    692|    update_base_hostname(input);
15266|    692|  } else {
15267|       |    // We only create a temporary string if we need to.
15268|    514|    update_base_hostname(ada::unicode::percent_encode(
15269|    514|        input, character_sets::C0_CONTROL_PERCENT_ENCODE, idx));
15270|    514|  }
15271|  1.20k|  ADA_ASSERT_TRUE(validate());
15272|  1.20k|  return true;
15273|  1.23k|}
_ZN3ada14url_aggregator15delete_dash_dotEv:
15432|      4|void url_aggregator::delete_dash_dot() {
15433|      4|  ada_log("url_aggregator::delete_dash_dot");
15434|      4|  ADA_ASSERT_TRUE(validate());
15435|      4|  ADA_ASSERT_TRUE(has_dash_dot());
15436|      4|  buffer.erase(components.host_end, 2);
15437|      4|  components.pathname_start -= 2;
15438|      4|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (15438:7): [True: 0, False: 4]
  ------------------
15439|      0|    components.search_start -= 2;
15440|      0|  }
15441|      4|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15441:7): [True: 0, False: 4]
  ------------------
15442|      0|    components.hash_start -= 2;
15443|      0|  }
15444|      4|  ADA_ASSERT_TRUE(validate());
15445|      4|  ADA_ASSERT_TRUE(!has_dash_dot());
15446|      4|}
can_parse.cc:_ZZN3ada4idna22utf32_length_from_utf8EPKcmENK3$_0clEa:
  274|   425k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  275|       |    // -65 is 0b10111111, anything larger in two-complement's
  276|       |    // should start a new code point.
  277|   425k|    return c > -65;
  278|   425k|  });
_ZN3ada4idna7deflate22make_fixed_litlen_huffEv:
  480|      2|inline Huff make_fixed_litlen_huff() {
  481|      2|  Huff h;
  482|      2|  uint8_t lens[288];
  483|       |  // Fixed Huffman: 0-143:8, 144-255:9, 256-279:7, 280-287:8
  484|    290|  for (int i = 0; i <= 143; i++) lens[i] = 8;
  ------------------
  |  Branch (484:19): [True: 288, False: 2]
  ------------------
  485|    226|  for (int i = 144; i <= 255; i++) lens[i] = 9;
  ------------------
  |  Branch (485:21): [True: 224, False: 2]
  ------------------
  486|     50|  for (int i = 256; i <= 279; i++) lens[i] = 7;
  ------------------
  |  Branch (486:21): [True: 48, False: 2]
  ------------------
  487|     18|  for (int i = 280; i <= 287; i++) lens[i] = 8;
  ------------------
  |  Branch (487:21): [True: 16, False: 2]
  ------------------
  488|      2|  h.build(lens, 288);
  489|      2|  return h;
  490|      2|}
_ZN3ada4idna7deflate4Huff5buildEPKhi:
  429|     16|  bool build(const uint8_t* lens, int n) {
  430|     16|    std::memset(counts, 0, sizeof(counts));
  431|     16|    std::memset(first_code, 0, sizeof(first_code));
  432|     16|    std::memset(base_idx, 0, sizeof(base_idx));
  433|     16|    nsymbols = n;
  434|     16|    max_bits = 0;
  435|  1.99k|    for (int i = 0; i < n; i++) {
  ------------------
  |  Branch (435:21): [True: 1.97k, False: 16]
  ------------------
  436|  1.97k|      lengths[i] = lens[i];
  437|  1.97k|      if (lens[i]) {
  ------------------
  |  Branch (437:11): [True: 1.93k, False: 42]
  ------------------
  438|  1.93k|        counts[lens[i]]++;
  439|  1.93k|        if (lens[i] > max_bits) max_bits = lens[i];
  ------------------
  |  Branch (439:13): [True: 45, False: 1.88k]
  ------------------
  440|  1.93k|      }
  441|  1.97k|    }
  442|       |    // Canonical first code for each bit length (RFC 1951).
  443|     16|    int code = 0;
  444|    256|    for (int bits = 1; bits <= MAXBITS; bits++) {
  ------------------
  |  Branch (444:24): [True: 240, False: 16]
  ------------------
  445|    240|      code = (code + counts[bits - 1]) << 1;
  446|    240|      first_code[bits] = code;
  447|    240|    }
  448|     16|    int idx = 0;
  449|    159|    for (int bits = 1; bits <= max_bits; bits++) {
  ------------------
  |  Branch (449:24): [True: 143, False: 16]
  ------------------
  450|    143|      base_idx[bits] = idx;
  451|       |      // assign symbols in symbol order for codes of this length
  452|  21.8k|      for (int sym = 0; sym < n; sym++) {
  ------------------
  |  Branch (452:25): [True: 21.6k, False: 143]
  ------------------
  453|  21.6k|        if (lengths[sym] == bits) {
  ------------------
  |  Branch (453:13): [True: 1.93k, False: 19.7k]
  ------------------
  454|  1.93k|          symbols[idx++] = static_cast<int16_t>(sym);
  455|  1.93k|        }
  456|  21.6k|      }
  457|    143|    }
  458|     16|    return true;
  459|     16|  }
_ZN3ada4idna7deflate20make_fixed_dist_huffEv:
  492|      2|inline Huff make_fixed_dist_huff() {
  493|      2|  Huff h;
  494|      2|  uint8_t lens[32];
  495|     66|  for (int i = 0; i < 32; i++) lens[i] = 5;
  ------------------
  |  Branch (495:19): [True: 64, False: 2]
  ------------------
  496|      2|  h.build(lens, 32);
  497|      2|  return h;
  498|      2|}
can_parse.cc:_ZZN3ada4idna9ascii_mapEPcmENK3$_0clEh:
 5339|  19.4k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5340|  19.4k|    return 0x101010101010101ull * v;
 5341|  19.4k|  };
_ZN3ada4idna13ensure_tablesEv:
 5085|  67.1k|[[nodiscard]] inline bool ensure_tables() noexcept {
 5086|  67.1k|  uint8_t state = tables_init_state.load(std::memory_order_acquire);
 5087|  67.1k|  if (state == kTablesReady) {
  ------------------
  |  Branch (5087:7): [True: 67.1k, False: 1]
  ------------------
 5088|  67.1k|    return true;
 5089|  67.1k|  }
 5090|      1|  if (state == kTablesFailed) {
  ------------------
  |  Branch (5090:7): [True: 0, False: 1]
  ------------------
 5091|      0|    return false;
 5092|      0|  }
 5093|       |
 5094|      1|  uint8_t expected = kTablesUninit;
 5095|      1|  if (tables_init_state.compare_exchange_strong(expected, kTablesInProgress,
  ------------------
  |  Branch (5095:7): [True: 1, False: 0]
  ------------------
 5096|      1|                                                std::memory_order_acq_rel,
 5097|      1|                                                std::memory_order_acquire)) {
 5098|      1|    uint8_t* buffer = new (std::nothrow) uint8_t[table_blob::uncompressed_size];
 5099|      1|    if (buffer == nullptr) {
  ------------------
  |  Branch (5099:9): [True: 0, False: 1]
  ------------------
 5100|      0|      tables_init_state.store(kTablesFailed, std::memory_order_release);
 5101|      0|      return false;
 5102|      0|    }
 5103|       |
 5104|      1|    const size_t n = deflate::inflate_raw(table_blob::compressed,
 5105|      1|                                          table_blob::compressed_size, buffer,
 5106|      1|                                          table_blob::uncompressed_size);
 5107|      1|    if (n != table_blob::uncompressed_size ||
  ------------------
  |  Branch (5107:9): [True: 0, False: 1]
  ------------------
 5108|      1|        crc32_ieee(buffer, n) != table_blob::uncompressed_crc32) {
  ------------------
  |  Branch (5108:9): [True: 0, False: 1]
  ------------------
 5109|      0|      delete[] buffer;
 5110|      0|      tables_init_state.store(kTablesFailed, std::memory_order_release);
 5111|      0|      return false;
 5112|      0|    }
 5113|       |
 5114|       |    // LE payload verified; convert multi-byte fields for big-endian hosts.
 5115|      1|    detail::convert_table_blob_to_host_endian(buffer);
 5116|       |
 5117|      1|    auto at = [&](size_t off) noexcept -> const uint8_t* {
 5118|      1|      return buffer + off;
 5119|      1|    };
 5120|       |
 5121|       |    // Publish all non-atomic pointers before READY. Waiters synchronize via
 5122|       |    // acquire on tables_init_state and then observe these writes.
 5123|      1|    idna_stage1 =
 5124|      1|        reinterpret_cast<const uint16_t*>(at(table_blob::off_idna_stage1));
 5125|      1|    idna_stage2 =
 5126|      1|        reinterpret_cast<const uint16_t*>(at(table_blob::off_idna_stage2));
 5127|      1|    idna_bool_blocks =
 5128|      1|        reinterpret_cast<const uint64_t*>(at(table_blob::off_idna_bool_blocks));
 5129|      1|    idna_utf8_mappings = at(table_blob::off_idna_utf8_mappings);
 5130|       |
 5131|      1|    decomposition_index = at(table_blob::off_decomposition_index);
 5132|      1|    decomposition_block_flat = reinterpret_cast<const uint16_t*>(
 5133|      1|        at(table_blob::off_decomposition_block));
 5134|      1|    decomposition_data = reinterpret_cast<const char32_t*>(
 5135|      1|        at(table_blob::off_decomposition_data));
 5136|      1|    ccc_index = at(table_blob::off_ccc_index);
 5137|      1|    ccc_block_flat = at(table_blob::off_ccc_block);
 5138|      1|    composition_index = at(table_blob::off_composition_index);
 5139|      1|    composition_block_flat = reinterpret_cast<const uint16_t*>(
 5140|      1|        at(table_blob::off_composition_block));
 5141|      1|    composition_data =
 5142|      1|        reinterpret_cast<const char32_t*>(at(table_blob::off_composition_data));
 5143|       |
 5144|      1|    id_continue =
 5145|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_id_continue_flat));
 5146|      1|    id_start =
 5147|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_id_start_flat));
 5148|       |
 5149|      1|    dir_start =
 5150|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_dir_start));
 5151|      1|    dir_final =
 5152|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_dir_final));
 5153|      1|    dir_value = at(table_blob::off_dir_value);
 5154|      1|    combining_ranges =
 5155|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_combining_flat));
 5156|      1|    joining_start =
 5157|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_joining_start));
 5158|      1|    joining_final =
 5159|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_joining_final));
 5160|      1|    joining_value = at(table_blob::off_joining_value);
 5161|       |
 5162|      1|    tables_buffer = buffer;
 5163|      1|    tables_init_state.store(kTablesReady, std::memory_order_release);
 5164|      1|    return true;
 5165|      1|  }
 5166|       |
 5167|       |  // Another thread owns init (or finished between our loads).
 5168|      0|  for (uint64_t spins = 0; spins < kTablesSpinLimit; ++spins) {
  ------------------
  |  Branch (5168:28): [True: 0, False: 0]
  ------------------
 5169|      0|    state = tables_init_state.load(std::memory_order_acquire);
 5170|      0|    if (state == kTablesReady) {
  ------------------
  |  Branch (5170:9): [True: 0, False: 0]
  ------------------
 5171|      0|      return true;
 5172|      0|    }
 5173|      0|    if (state == kTablesFailed) {
  ------------------
  |  Branch (5173:9): [True: 0, False: 0]
  ------------------
 5174|      0|      return false;
 5175|      0|    }
 5176|      0|#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
 5177|      0|    defined(_M_IX86)
 5178|      0|#if defined(__GNUC__) || defined(__clang__)
 5179|      0|    __builtin_ia32_pause();
 5180|      0|#endif
 5181|       |#elif defined(__aarch64__) || defined(_M_ARM64)
 5182|       |#if defined(__GNUC__) || defined(__clang__)
 5183|       |    asm volatile("yield" ::: "memory");
 5184|       |#endif
 5185|       |#endif
 5186|      0|  }
 5187|       |  // Timed out waiting for a peer - treat as failure rather than hang.
 5188|      0|  return false;
 5189|      0|}
_ZN3ada4idna7deflate11inflate_rawEPKhmPhm:
  525|      1|                          size_t dst_cap) {
  526|      1|  BitReader br(src, src_len);
  527|      1|  size_t out = 0;
  528|      4|  for (;;) {
  529|      4|    uint32_t bfinal = br.get(1);
  530|      4|    uint32_t btype = br.get(2);
  531|      4|    if (bfinal == UINT32_MAX || btype == UINT32_MAX) return 0;
  ------------------
  |  Branch (531:9): [True: 0, False: 4]
  |  Branch (531:33): [True: 0, False: 4]
  ------------------
  532|      4|    if (btype == 0) {
  ------------------
  |  Branch (532:9): [True: 0, False: 4]
  ------------------
  533|       |      // stored
  534|      0|      br.align_byte();
  535|      0|      if (!br.ensure(16)) return 0;
  ------------------
  |  Branch (535:11): [True: 0, False: 0]
  ------------------
  536|       |      // read 16+16 from bit buffer awkwardly - use byte path
  537|       |      // Align: bits is multiple of 8
  538|       |      // Get len from remaining bits then bytes
  539|      0|      uint32_t len = br.get(16);
  540|      0|      uint32_t nlen = br.get(16);
  541|      0|      if (len == UINT32_MAX || nlen == UINT32_MAX) return 0;
  ------------------
  |  Branch (541:11): [True: 0, False: 0]
  |  Branch (541:32): [True: 0, False: 0]
  ------------------
  542|      0|      if ((len ^ 0xFFFF) != nlen) return 0;
  ------------------
  |  Branch (542:11): [True: 0, False: 0]
  ------------------
  543|       |      // After get, may have bits in acc from previous - for stored, should be
  544|       |      // byte aligned Copy len bytes from p
  545|      0|      if (br.bits != 0) {
  ------------------
  |  Branch (545:11): [True: 0, False: 0]
  ------------------
  546|       |        // leftover bits should be 0 if aligned
  547|      0|      }
  548|      0|      if (size_t(br.end - br.p) < len) return 0;
  ------------------
  |  Branch (548:11): [True: 0, False: 0]
  ------------------
  549|      0|      if (out + len > dst_cap) return 0;
  ------------------
  |  Branch (549:11): [True: 0, False: 0]
  ------------------
  550|      0|      std::memcpy(dst + out, br.p, len);
  551|      0|      br.p += len;
  552|      0|      out += len;
  553|      4|    } else if (btype == 1 || btype == 2) {
  ------------------
  |  Branch (553:16): [True: 0, False: 4]
  |  Branch (553:30): [True: 4, False: 0]
  ------------------
  554|      4|      Huff lit, dist;
  555|      4|      if (btype == 1) {
  ------------------
  |  Branch (555:11): [True: 0, False: 4]
  ------------------
  556|       |        // use fixed via functions
  557|      4|      } else {
  558|       |        // dynamic
  559|      4|        uint32_t hlit = br.get(5);
  560|      4|        if (hlit == UINT32_MAX) return 0;
  ------------------
  |  Branch (560:13): [True: 0, False: 4]
  ------------------
  561|      4|        hlit += 257;
  562|      4|        uint32_t hdist = br.get(5);
  563|      4|        if (hdist == UINT32_MAX) return 0;
  ------------------
  |  Branch (563:13): [True: 0, False: 4]
  ------------------
  564|      4|        hdist += 1;
  565|      4|        uint32_t hclen = br.get(4);
  566|      4|        if (hclen == UINT32_MAX) return 0;
  ------------------
  |  Branch (566:13): [True: 0, False: 4]
  ------------------
  567|      4|        hclen += 4;
  568|      4|        static const int order[19] = {16, 17, 18, 0, 8,  7, 9,  6, 10, 5,
  569|      4|                                      11, 4,  12, 3, 13, 2, 14, 1, 15};
  570|      4|        uint8_t clen[19]{};
  571|     69|        for (uint32_t i = 0; i < hclen; i++) {
  ------------------
  |  Branch (571:30): [True: 65, False: 4]
  ------------------
  572|     65|          uint32_t v = br.get(3);
  573|     65|          if (v == UINT32_MAX) return 0;
  ------------------
  |  Branch (573:15): [True: 0, False: 65]
  ------------------
  574|     65|          clen[order[i]] = uint8_t(v);
  575|     65|        }
  576|      4|        Huff cl;
  577|      4|        if (!cl.build(clen, 19)) return 0;
  ------------------
  |  Branch (577:13): [True: 0, False: 4]
  ------------------
  578|      4|        uint8_t lens[288 + 32]{};
  579|      4|        uint32_t n = hlit + hdist;
  580|  1.04k|        for (uint32_t i = 0; i < n;) {
  ------------------
  |  Branch (580:30): [True: 1.03k, False: 4]
  ------------------
  581|  1.03k|          int sym = cl.decode(br);
  582|  1.03k|          if (sym < 0) return 0;
  ------------------
  |  Branch (582:15): [True: 0, False: 1.03k]
  ------------------
  583|  1.03k|          if (sym < 16) {
  ------------------
  |  Branch (583:15): [True: 973, False: 63]
  ------------------
  584|    973|            lens[i++] = uint8_t(sym);
  585|    973|          } else if (sym == 16) {
  ------------------
  |  Branch (585:22): [True: 63, False: 0]
  ------------------
  586|     63|            uint32_t rep = br.get(2);
  587|     63|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (587:17): [True: 0, False: 63]
  ------------------
  588|     63|            rep += 3;
  589|     63|            if (i == 0) return 0;
  ------------------
  |  Branch (589:17): [True: 0, False: 63]
  ------------------
  590|     63|            uint8_t v = lens[i - 1];
  591|    350|            while (rep--) lens[i++] = v;
  ------------------
  |  Branch (591:20): [True: 287, False: 63]
  ------------------
  592|     63|          } else if (sym == 17) {
  ------------------
  |  Branch (592:22): [True: 0, False: 0]
  ------------------
  593|      0|            uint32_t rep = br.get(3);
  594|      0|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (594:17): [True: 0, False: 0]
  ------------------
  595|      0|            rep += 3;
  596|      0|            while (rep--) lens[i++] = 0;
  ------------------
  |  Branch (596:20): [True: 0, False: 0]
  ------------------
  597|      0|          } else if (sym == 18) {
  ------------------
  |  Branch (597:22): [True: 0, False: 0]
  ------------------
  598|      0|            uint32_t rep = br.get(7);
  599|      0|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (599:17): [True: 0, False: 0]
  ------------------
  600|      0|            rep += 11;
  601|      0|            while (rep--) lens[i++] = 0;
  ------------------
  |  Branch (601:20): [True: 0, False: 0]
  ------------------
  602|      0|          } else
  603|      0|            return 0;
  604|  1.03k|        }
  605|      4|        if (!lit.build(lens, int(hlit))) return 0;
  ------------------
  |  Branch (605:13): [True: 0, False: 4]
  ------------------
  606|      4|        if (!dist.build(lens + hlit, int(hdist))) return 0;
  ------------------
  |  Branch (606:13): [True: 0, False: 4]
  ------------------
  607|      4|      }
  608|  54.4k|      for (;;) {
  609|  54.4k|        int sym;
  610|  54.4k|        if (btype == 1)
  ------------------
  |  Branch (610:13): [True: 0, False: 54.4k]
  ------------------
  611|      0|          sym = fixed_litlen(br);
  612|  54.4k|        else
  613|  54.4k|          sym = lit.decode(br);
  614|  54.4k|        if (sym < 0) return 0;
  ------------------
  |  Branch (614:13): [True: 0, False: 54.4k]
  ------------------
  615|  54.4k|        if (sym < 256) {
  ------------------
  |  Branch (615:13): [True: 38.5k, False: 15.9k]
  ------------------
  616|  38.5k|          if (out >= dst_cap) return 0;
  ------------------
  |  Branch (616:15): [True: 0, False: 38.5k]
  ------------------
  617|  38.5k|          dst[out++] = uint8_t(sym);
  618|  38.5k|        } else if (sym == 256) {
  ------------------
  |  Branch (618:20): [True: 4, False: 15.9k]
  ------------------
  619|      4|          break;
  620|  15.9k|        } else {
  621|  15.9k|          int len_code = sym - 257;
  622|  15.9k|          if (len_code < 0 || len_code > 28) return 0;
  ------------------
  |  Branch (622:15): [True: 0, False: 15.9k]
  |  Branch (622:31): [True: 0, False: 15.9k]
  ------------------
  623|  15.9k|          uint32_t extra = br.get(len_extra[len_code]);
  624|  15.9k|          if (len_extra[len_code] && extra == UINT32_MAX) return 0;
  ------------------
  |  Branch (624:15): [True: 2.24k, False: 13.6k]
  |  Branch (624:38): [True: 0, False: 2.24k]
  ------------------
  625|  15.9k|          uint32_t length =
  626|  15.9k|              len_base[len_code] + (len_extra[len_code] ? extra : 0);
  ------------------
  |  Branch (626:37): [True: 2.24k, False: 13.6k]
  ------------------
  627|  15.9k|          int dsym;
  628|  15.9k|          if (btype == 1)
  ------------------
  |  Branch (628:15): [True: 0, False: 15.9k]
  ------------------
  629|      0|            dsym = fixed_dist(br);
  630|  15.9k|          else
  631|  15.9k|            dsym = dist.decode(br);
  632|  15.9k|          if (dsym < 0 || dsym > 29) return 0;
  ------------------
  |  Branch (632:15): [True: 0, False: 15.9k]
  |  Branch (632:27): [True: 0, False: 15.9k]
  ------------------
  633|  15.9k|          uint32_t dextra = br.get(dist_extra[dsym]);
  634|  15.9k|          if (dist_extra[dsym] && dextra == UINT32_MAX) return 0;
  ------------------
  |  Branch (634:15): [True: 10.7k, False: 5.16k]
  |  Branch (634:35): [True: 0, False: 10.7k]
  ------------------
  635|  15.9k|          uint32_t distance = dist_base[dsym] + (dist_extra[dsym] ? dextra : 0);
  ------------------
  |  Branch (635:50): [True: 10.7k, False: 5.16k]
  ------------------
  636|  15.9k|          if (distance > out || out + length > dst_cap) return 0;
  ------------------
  |  Branch (636:15): [True: 0, False: 15.9k]
  |  Branch (636:33): [True: 0, False: 15.9k]
  ------------------
  637|   193k|          for (uint32_t k = 0; k < length; k++) {
  ------------------
  |  Branch (637:32): [True: 177k, False: 15.9k]
  ------------------
  638|   177k|            dst[out] = dst[out - distance];
  639|   177k|            out++;
  640|   177k|          }
  641|  15.9k|        }
  642|  54.4k|      }
  643|      4|    } else {
  644|      0|      return 0;  // reserved
  645|      0|    }
  646|      4|    if (bfinal) break;
  ------------------
  |  Branch (646:9): [True: 1, False: 3]
  ------------------
  647|      4|  }
  648|      1|  return out;
  649|      1|}
_ZN3ada4idna7deflate9BitReaderC2EPKhm:
  392|      1|      : p(data), end(data + len) {}
_ZN3ada4idna7deflate9BitReader3getEi:
  402|   475k|  uint32_t get(int n) {
  403|   475k|    if (!ensure(n)) return UINT32_MAX;
  ------------------
  |  Branch (403:9): [True: 0, False: 475k]
  ------------------
  404|   475k|    uint32_t v = acc & ((1u << n) - 1);
  405|   475k|    acc >>= n;
  406|   475k|    bits -= n;
  407|   475k|    return v;
  408|   475k|  }
_ZN3ada4idna7deflate9BitReader6ensureEi:
  394|   475k|  bool ensure(int n) {
  395|   540k|    while (bits < n) {
  ------------------
  |  Branch (395:12): [True: 65.3k, False: 475k]
  ------------------
  396|  65.3k|      if (p >= end) return false;
  ------------------
  |  Branch (396:11): [True: 0, False: 65.3k]
  ------------------
  397|  65.3k|      acc |= uint32_t(*p++) << bits;
  398|  65.3k|      bits += 8;
  399|  65.3k|    }
  400|   475k|    return true;
  401|   475k|  }
_ZNK3ada4idna7deflate4Huff6decodeERNS1_9BitReaderE:
  461|  71.4k|  int decode(BitReader& br) const {
  462|  71.4k|    if (max_bits == 0) return -1;
  ------------------
  |  Branch (462:9): [True: 0, False: 71.4k]
  ------------------
  463|  71.4k|    int code = 0;
  464|   442k|    for (int len = 1; len <= max_bits; len++) {
  ------------------
  |  Branch (464:23): [True: 442k, False: 0]
  ------------------
  465|   442k|      uint32_t b = br.get(1);
  466|   442k|      if (b == UINT32_MAX) return -1;
  ------------------
  |  Branch (466:11): [True: 0, False: 442k]
  ------------------
  467|   442k|      code = (code << 1) | int(b);
  468|   442k|      int first = first_code[len];
  469|   442k|      int cnt = counts[len];
  470|   442k|      if (cnt && code - first < cnt) {
  ------------------
  |  Branch (470:11): [True: 276k, False: 166k]
  |  Branch (470:18): [True: 71.4k, False: 205k]
  ------------------
  471|  71.4k|        return symbols[base_idx[len] + (code - first)];
  472|  71.4k|      }
  473|   442k|    }
  474|      0|    return -1;
  475|  71.4k|  }
_ZN3ada4idna10crc32_ieeeEPKhm:
 5068|      1|                                         size_t len) noexcept {
 5069|      1|  uint32_t c = 0xFFFFFFFFu;
 5070|   216k|  for (size_t i = 0; i < len; ++i) {
  ------------------
  |  Branch (5070:22): [True: 216k, False: 1]
  ------------------
 5071|   216k|    c ^= data[i];
 5072|  1.94M|    for (int k = 0; k < 8; ++k) {
  ------------------
  |  Branch (5072:21): [True: 1.72M, False: 216k]
  ------------------
 5073|  1.72M|      const uint32_t mask = 0u - (c & 1u);
 5074|  1.72M|      c = (c >> 1) ^ (0xEDB88320u & mask);
 5075|  1.72M|    }
 5076|   216k|  }
 5077|      1|  return ~c;
 5078|      1|}
_ZN3ada4idna6detail33convert_table_blob_to_host_endianEPh:
 4873|      1|inline void convert_table_blob_to_host_endian(uint8_t* buffer) noexcept {
 4874|      1|  if constexpr (std::endian::native == std::endian::little) {
 4875|      1|    (void)buffer;
 4876|      1|    return;
 4877|      1|  }
 4878|      0|  auto u16 = [&](size_t off, size_t count) {
 4879|      1|    bswap_inplace(reinterpret_cast<uint16_t*>(buffer + off), count);
 4880|      1|  };
 4881|      1|  auto u32 = [&](size_t off, size_t count) {
 4882|      1|    bswap_inplace(reinterpret_cast<uint32_t*>(buffer + off), count);
 4883|      1|  };
 4884|      1|  auto u64 = [&](size_t off, size_t count) {
 4885|      1|    bswap_inplace(reinterpret_cast<uint64_t*>(buffer + off), count);
 4886|      1|  };
 4887|       |
 4888|      1|  u16(table_blob::off_idna_stage1, table_blob::count_idna_stage1);
 4889|      1|  u16(table_blob::off_idna_stage2, table_blob::count_idna_stage2);
 4890|      1|  u64(table_blob::off_idna_bool_blocks, table_blob::count_idna_bool_blocks);
 4891|      1|  u16(table_blob::off_decomposition_block,
 4892|      1|      table_blob::count_decomposition_block);
 4893|      1|  u32(table_blob::off_decomposition_data, table_blob::count_decomposition_data);
 4894|      1|  u16(table_blob::off_composition_block, table_blob::count_composition_block);
 4895|      1|  u32(table_blob::off_composition_data, table_blob::count_composition_data);
 4896|      1|  u32(table_blob::off_id_continue_flat, table_blob::count_id_continue_flat);
 4897|      1|  u32(table_blob::off_id_start_flat, table_blob::count_id_start_flat);
 4898|      1|  u32(table_blob::off_dir_start, table_blob::count_dir_start);
 4899|      1|  u32(table_blob::off_dir_final, table_blob::count_dir_final);
 4900|      1|  u32(table_blob::off_combining_flat, table_blob::count_combining_flat);
 4901|      1|  u32(table_blob::off_joining_start, table_blob::count_joining_start);
 4902|      1|  u32(table_blob::off_joining_final, table_blob::count_joining_final);
 4903|      1|}
_ZZN3ada4idna13ensure_tablesEvENKUlmE_clEm:
 5117|     21|    auto at = [&](size_t off) noexcept -> const uint8_t* {
 5118|     21|      return buffer + off;
 5119|     21|    };
can_parse.cc:_ZN3ada4idnaL11idna_lookupEj:
 5277|   700k|static uint16_t idna_lookup(uint32_t cp) noexcept {
 5278|   700k|  if (cp < IDNA_LOW_RANGE_END) {
  ------------------
  |  Branch (5278:7): [True: 698k, False: 2.40k]
  ------------------
 5279|   698k|    uint16_t ref = idna_stage1[cp >> IDNA_BLOCK_BITS];
 5280|   698k|    if (ref & IDNA_BOOL_FLAG) {
  ------------------
  |  Branch (5280:9): [True: 298k, False: 399k]
  ------------------
 5281|   298k|      uint32_t bit_idx =
 5282|   298k|          static_cast<uint32_t>(ref & ~IDNA_BOOL_FLAG) * IDNA_BLOCK_SIZE +
 5283|   298k|          (cp & IDNA_BLOCK_MASK);
 5284|   298k|      bool is_valid = (idna_bool_blocks[bit_idx >> 6] >> (bit_idx & 63u)) & 1u;
 5285|   298k|      return is_valid ? IDNA_VALID : IDNA_DISALLOWED;
  ------------------
  |  Branch (5285:14): [True: 298k, False: 30]
  ------------------
 5286|   298k|    }
 5287|   399k|    return idna_stage2[ref + (cp & IDNA_BLOCK_MASK)];
 5288|   698k|  }
 5289|       |
 5290|  2.40k|  if (cp >= IDNA_HIGH_IGNORED_START && cp < IDNA_HIGH_IGNORED_END) {
  ------------------
  |  Branch (5290:7): [True: 2.32k, False: 78]
  |  Branch (5290:40): [True: 2.01k, False: 315]
  ------------------
 5291|  2.01k|    return IDNA_IGNORED;
 5292|  2.01k|  }
 5293|       |
 5294|    393|  return IDNA_DISALLOWED;
 5295|  2.40k|}
can_parse.cc:_ZN3ada4idnaL21utf8_count_codepointsEPKh:
 5320|   106k|static size_t utf8_count_codepoints(const uint8_t* ptr) noexcept {
 5321|   106k|  size_t n = 0;
 5322|   517k|  while (*ptr != 0) {
  ------------------
  |  Branch (5322:10): [True: 411k, False: 106k]
  ------------------
 5323|   411k|    ++n;
 5324|   411k|    if (*ptr < 0x80u) {
  ------------------
  |  Branch (5324:9): [True: 110k, False: 300k]
  ------------------
 5325|   110k|      ++ptr;
 5326|   300k|    } else if (*ptr < 0xE0u) {
  ------------------
  |  Branch (5326:16): [True: 175k, False: 124k]
  ------------------
 5327|   175k|      ptr += 2;
 5328|   175k|    } else if (*ptr < 0xF0u) {
  ------------------
  |  Branch (5328:16): [True: 123k, False: 1.67k]
  ------------------
 5329|   123k|      ptr += 3;
 5330|   123k|    } else {
 5331|  1.67k|      ptr += 4;
 5332|  1.67k|    }
 5333|   411k|  }
 5334|   106k|  return n;
 5335|   106k|}
can_parse.cc:_ZN3ada4idnaL9utf8_nextERPKh:
 5298|   409k|static char32_t utf8_next(const uint8_t*& ptr) noexcept {
 5299|   409k|  uint8_t b0 = *ptr++;
 5300|   409k|  if (b0 < 0x80u) return static_cast<char32_t>(b0);
  ------------------
  |  Branch (5300:7): [True: 110k, False: 298k]
  ------------------
 5301|   298k|  if (b0 < 0xE0u) {
  ------------------
  |  Branch (5301:7): [True: 175k, False: 123k]
  ------------------
 5302|   175k|    uint32_t cp = (b0 & 0x1Fu) << 6;
 5303|   175k|    cp |= (*ptr++ & 0x3Fu);
 5304|   175k|    return static_cast<char32_t>(cp);
 5305|   175k|  }
 5306|   123k|  if (b0 < 0xF0u) {
  ------------------
  |  Branch (5306:7): [True: 121k, False: 1.67k]
  ------------------
 5307|   121k|    uint32_t cp = (b0 & 0x0Fu) << 12;
 5308|   121k|    cp |= ((*ptr++ & 0x3Fu) << 6);
 5309|   121k|    cp |= (*ptr++ & 0x3Fu);
 5310|   121k|    return static_cast<char32_t>(cp);
 5311|   121k|  }
 5312|  1.67k|  uint32_t cp = (b0 & 0x07u) << 18;
 5313|  1.67k|  cp |= ((*ptr++ & 0x3Fu) << 12);
 5314|  1.67k|  cp |= ((*ptr++ & 0x3Fu) << 6);
 5315|  1.67k|  cp |= (*ptr++ & 0x3Fu);
 5316|  1.67k|  return static_cast<char32_t>(cp);
 5317|   123k|}
_ZN3ada4idna23decomposition_block_rowEh:
 5194|  1.11M|inline const uint16_t* decomposition_block_row(uint8_t bi) noexcept {
 5195|  1.11M|  if (bi >= kDecompBlockRows) {
  ------------------
  |  Branch (5195:7): [True: 0, False: 1.11M]
  ------------------
 5196|      0|    bi = 0;
 5197|      0|  }
 5198|  1.11M|  return decomposition_block_flat + static_cast<size_t>(bi) * kDecompBlockCols;
 5199|  1.11M|}
_ZN3ada4idna13ccc_block_rowEh:
 5201|  1.62M|inline const uint8_t* ccc_block_row(uint8_t bi) noexcept {
 5202|  1.62M|  if (bi >= kCccBlockRows) {
  ------------------
  |  Branch (5202:7): [True: 0, False: 1.62M]
  ------------------
 5203|      0|    bi = 0;
 5204|      0|  }
 5205|  1.62M|  return ccc_block_flat + static_cast<size_t>(bi) * kCccBlockCols;
 5206|  1.62M|}
_ZN3ada4idna16tables_are_readyEv:
 5080|  21.0k|[[nodiscard]] inline bool tables_are_ready() noexcept {
 5081|  21.0k|  return tables_init_state.load(std::memory_order_acquire) == kTablesReady;
 5082|  21.0k|}
can_parse.cc:_ZN3ada4idnaL24decomposition_is_not_nfcEDiDi:
 5707|   499k|static bool decomposition_is_not_nfc(char32_t previous, char32_t c) noexcept {
 5708|   499k|  if (c >= hangul_sbase && c < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5708:7): [True: 23.3k, False: 476k]
  |  Branch (5708:28): [True: 11.2k, False: 12.1k]
  ------------------
 5709|  11.2k|    return false;
 5710|  11.2k|  }
 5711|   488k|  if (c >= 0x110000) {
  ------------------
  |  Branch (5711:7): [True: 0, False: 488k]
  ------------------
 5712|      0|    return false;
 5713|      0|  }
 5714|   488k|  const uint16_t* const decomposition =
 5715|   488k|      decomposition_block_row(decomposition_index[c >> 8]) + (c % 256);
 5716|   488k|  const size_t length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5717|   488k|  if (length == 0 || (decomposition[0] & 1)) {
  ------------------
  |  Branch (5717:7): [True: 456k, False: 32.3k]
  |  Branch (5717:22): [True: 0, False: 32.3k]
  ------------------
 5718|   456k|    return false;  // no canonical decomposition (or compatibility-only)
 5719|   456k|  }
 5720|  32.3k|  if (length == 1) {
  ------------------
  |  Branch (5720:7): [True: 0, False: 32.3k]
  ------------------
 5721|      0|    return true;  // singleton
 5722|      0|  }
 5723|  32.3k|  const size_t base = decomposition[0] >> 2;
 5724|  32.3k|  if (base + length > decomposition_data_size) {
  ------------------
  |  Branch (5724:7): [True: 0, False: 32.3k]
  ------------------
 5725|      0|    return false;
 5726|      0|  }
 5727|       |  // Recompose the canonical decomposition; if it does not collapse back to c,
 5728|       |  // then c is an exclusion / non-starter decomposition and is not NFC.
 5729|  32.3k|  std::u32string decomposed(decomposition_data + base,
 5730|  32.3k|                            decomposition_data + base + length);
 5731|  32.3k|  compose(decomposed);
 5732|  32.3k|  if (!(decomposed.size() == 1 && decomposed[0] == c)) {
  ------------------
  |  Branch (5732:9): [True: 32.3k, False: 0]
  |  Branch (5732:35): [True: 32.3k, False: 0]
  ------------------
 5733|      0|    return true;
 5734|      0|  }
 5735|  32.3k|  return previous < 0x110000 &&
  ------------------
  |  Branch (5735:10): [True: 24.5k, False: 7.79k]
  ------------------
 5736|  24.5k|         has_composition(
  ------------------
  |  Branch (5736:10): [True: 24, False: 24.5k]
  ------------------
 5737|  24.5k|             composition_block_row(composition_index[previous >> 8]) +
 5738|  24.5k|                 (previous % 256),
 5739|  24.5k|             decomposition_data[base]);
 5740|  32.3k|}
can_parse.cc:_ZN3ada4idnaL15has_compositionEPKtDi:
 5611|   121k|                            char32_t second) noexcept {
 5612|   121k|  int left = composition[0];
 5613|   121k|  int right = composition[1];
 5614|   121k|  if (left < 0 || right <= left ||
  ------------------
  |  Branch (5614:7): [True: 0, False: 121k]
  |  Branch (5614:19): [True: 17.9k, False: 103k]
  ------------------
 5615|   103k|      static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5615:7): [True: 0, False: 103k]
  ------------------
 5616|  17.9k|    return false;
 5617|  17.9k|  }
 5618|   293k|  while (left + 2 < right) {
  ------------------
  |  Branch (5618:10): [True: 190k, False: 103k]
  ------------------
 5619|   190k|    int middle = left + (((right - left) >> 1) & ~1);
 5620|   190k|    if (composition_data[static_cast<size_t>(middle)] <= second) {
  ------------------
  |  Branch (5620:9): [True: 12.3k, False: 177k]
  ------------------
 5621|  12.3k|      left = middle;
 5622|  12.3k|    }
 5623|   190k|    if (composition_data[static_cast<size_t>(middle)] >= second) {
  ------------------
  |  Branch (5623:9): [True: 179k, False: 10.7k]
  ------------------
 5624|   179k|      right = middle;
 5625|   179k|    }
 5626|   190k|  }
 5627|   103k|  return static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5627:10): [True: 103k, False: 0]
  ------------------
 5628|   103k|         composition_data[static_cast<size_t>(left)] == second;
  ------------------
  |  Branch (5628:10): [True: 2.07k, False: 101k]
  ------------------
 5629|   121k|}
can_parse.cc:_ZN3ada4idnaL12trailing_cccEDi:
 5684|   473k|static uint8_t trailing_ccc(char32_t c) noexcept {
 5685|   473k|  const size_t length = canonical_decomp_length(c);
 5686|   473k|  if (length == 0) {
  ------------------
  |  Branch (5686:7): [True: 444k, False: 28.6k]
  ------------------
 5687|   444k|    return get_ccc(c);
 5688|   444k|  }
 5689|  28.6k|  const uint16_t* const decomposition =
 5690|  28.6k|      decomposition_block_row(decomposition_index[c >> 8]) + (c % 256);
 5691|  28.6k|  const size_t base = decomposition[0] >> 2;
 5692|  28.6k|  return get_ccc(decomposition_data[base + length - 1]);
 5693|   473k|}
can_parse.cc:_ZN3ada4idnaL23canonical_decomp_lengthEDi:
 5478|   473k|static size_t canonical_decomp_length(char32_t current_character) noexcept {
 5479|   473k|  if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5479:7): [True: 18.4k, False: 454k]
  ------------------
 5480|  18.4k|      current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5480:7): [True: 7.99k, False: 10.4k]
  ------------------
 5481|       |    // Hangul precomposed syllables are NFC.
 5482|  7.99k|    return 0;
 5483|  7.99k|  }
 5484|   465k|  if (current_character >= 0x110000) {
  ------------------
  |  Branch (5484:7): [True: 0, False: 465k]
  ------------------
 5485|      0|    return 0;
 5486|      0|  }
 5487|   465k|  const uint8_t di = decomposition_index[current_character >> 8];
 5488|   465k|  const uint16_t* const decomposition =
 5489|   465k|      decomposition_block_row(di) + (current_character % 256);
 5490|   465k|  size_t decomposition_length =
 5491|   465k|      (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5492|   465k|  if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5492:7): [True: 28.6k, False: 436k]
  |  Branch (5492:37): [True: 0, False: 28.6k]
  ------------------
 5493|      0|    decomposition_length = 0;  // compatibility-only: ignored for NFC
 5494|      0|  }
 5495|   465k|  return decomposition_length;
 5496|   465k|}
can_parse.cc:_ZN3ada4idnaL13would_composeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5632|  12.7k|static bool would_compose(std::u32string_view input) noexcept {
 5633|   421k|  for (size_t input_count = 0; input_count < input.size();) {
  ------------------
  |  Branch (5633:32): [True: 411k, False: 10.3k]
  ------------------
 5634|   411k|    const char32_t current = input[input_count];
 5635|   411k|    if (current >= hangul_lbase && current < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5635:9): [True: 145k, False: 265k]
  |  Branch (5635:36): [True: 6.78k, False: 138k]
  ------------------
 5636|  6.78k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5636:11): [True: 6.42k, False: 357]
  ------------------
 5637|  6.42k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5637:11): [True: 1.65k, False: 4.76k]
  ------------------
 5638|  1.65k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5638:11): [True: 294, False: 1.36k]
  ------------------
 5639|    294|        return true;
 5640|    294|      }
 5641|  6.48k|      ++input_count;
 5642|  6.48k|      continue;
 5643|  6.78k|    }
 5644|   404k|    if (current >= hangul_sbase && current < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5644:9): [True: 10.0k, False: 394k]
  |  Branch (5644:36): [True: 4.31k, False: 5.70k]
  ------------------
 5645|  4.31k|      if ((current - hangul_sbase) % hangul_tcount == 0 &&
  ------------------
  |  Branch (5645:11): [True: 2.50k, False: 1.80k]
  ------------------
 5646|  2.50k|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (5646:11): [True: 2.40k, False: 105]
  ------------------
 5647|  2.40k|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5647:11): [True: 1.74k, False: 657]
  ------------------
 5648|  1.74k|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5648:11): [True: 96, False: 1.65k]
  ------------------
 5649|     96|        return true;
 5650|     96|      }
 5651|  4.21k|      ++input_count;
 5652|  4.21k|      continue;
 5653|  4.31k|    }
 5654|   400k|    if (current < 0x110000) {
  ------------------
  |  Branch (5654:9): [True: 400k, False: 0]
  ------------------
 5655|   400k|      const uint16_t* composition =
 5656|   400k|          composition_block_row(composition_index[current >> 8]) +
 5657|   400k|          (current % 256);
 5658|   400k|      int32_t previous_ccc = -1;
 5659|   400k|      size_t j = input_count;
 5660|   408k|      for (; j + 1 < input.size(); ++j) {
  ------------------
  |  Branch (5660:14): [True: 398k, False: 9.71k]
  ------------------
 5661|   398k|        uint8_t ccc = get_ccc(input[j + 1]);
 5662|   398k|        if (composition[1] != composition[0] && previous_ccc < ccc &&
  ------------------
  |  Branch (5662:13): [True: 100k, False: 297k]
  |  Branch (5662:49): [True: 97.2k, False: 3.69k]
  ------------------
 5663|  97.2k|            has_composition(composition, input[j + 1])) {
  ------------------
  |  Branch (5663:13): [True: 2.04k, False: 95.1k]
  ------------------
 5664|  2.04k|          return true;
 5665|  2.04k|        }
 5666|   396k|        if (ccc == 0) {
  ------------------
  |  Branch (5666:13): [True: 388k, False: 8.42k]
  ------------------
 5667|   388k|          break;
 5668|   388k|        }
 5669|  8.42k|        previous_ccc = ccc;
 5670|  8.42k|      }
 5671|   398k|      input_count = j + 1;
 5672|   398k|      continue;
 5673|   400k|    }
 5674|      0|    ++input_count;
 5675|      0|  }
 5676|  10.3k|  return false;
 5677|  12.7k|}
_ZN3ada4idna21composition_block_rowEh:
 5208|   576k|inline const uint16_t* composition_block_row(uint8_t bi) noexcept {
 5209|   576k|  if (bi >= kCompBlockRows) {
  ------------------
  |  Branch (5209:7): [True: 0, False: 576k]
  ------------------
 5210|      0|    bi = 0;
 5211|      0|  }
 5212|   576k|  return composition_block_flat + static_cast<size_t>(bi) * kCompBlockCols;
 5213|   576k|}
can_parse.cc:_ZN3ada4idnaL19char_to_digit_valueEc:
 5913|  94.0k|static constexpr int32_t char_to_digit_value(char value) {
 5914|  94.0k|  if (value >= 'a' && value <= 'z') return value - 'a';
  ------------------
  |  Branch (5914:7): [True: 83.5k, False: 10.4k]
  |  Branch (5914:23): [True: 83.5k, False: 15]
  ------------------
 5915|  10.4k|  if (value >= '0' && value <= '9') return value - '0' + 26;
  ------------------
  |  Branch (5915:7): [True: 10.3k, False: 54]
  |  Branch (5915:23): [True: 10.3k, False: 18]
  ------------------
 5916|     72|  return -1;
 5917|  10.4k|}
can_parse.cc:_ZN3ada4idnaL5adaptEiib:
 5923|   208k|static constexpr int32_t adapt(int32_t d, int32_t n, bool firsttime) {
 5924|   208k|  if (firsttime) {
  ------------------
  |  Branch (5924:7): [True: 42.0k, False: 166k]
  ------------------
 5925|  42.0k|    d = d / damp;
 5926|   166k|  } else {
 5927|   166k|    d = d / 2;
 5928|   166k|  }
 5929|   208k|  d += d / n;
 5930|   208k|  int32_t k = 0;
 5931|   297k|  while (d > ((base - tmin) * tmax) / 2) {
  ------------------
  |  Branch (5931:10): [True: 89.4k, False: 208k]
  ------------------
 5932|  89.4k|    d /= base - tmin;
 5933|  89.4k|    k += base;
 5934|  89.4k|  }
 5935|   208k|  return k + (((base - tmin + 1) * d) / (d + skew));
 5936|   208k|}
can_parse.cc:_ZN3ada4idnaL13digit_to_charEi:
 5919|   414k|static constexpr char digit_to_char(int32_t digit) {
 5920|   414k|  return digit < 26 ? char(digit + 97) : char(digit + 22);
  ------------------
  |  Branch (5920:10): [True: 281k, False: 133k]
  ------------------
 5921|   414k|}
can_parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_0clIA2_jEEDaRKT_:
 6327|   346k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
can_parse.cc:_ZN3ada4idnaL17is_contextj_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6232|  42.0k|inline static bool is_contextj_valid(std::u32string_view label) noexcept {
 6233|  42.0k|  constexpr uint8_t virama_ccc = 9;
 6234|   302k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (6234:22): [True: 260k, False: 41.5k]
  ------------------
 6235|   260k|    if (label[i] != 0x200C && label[i] != 0x200D) {
  ------------------
  |  Branch (6235:9): [True: 259k, False: 1.58k]
  |  Branch (6235:31): [True: 258k, False: 363]
  ------------------
 6236|   258k|      continue;
 6237|   258k|    }
 6238|       |    // If Canonical_Combining_Class(Before(cp)) .eq. Virama Then True;
 6239|  1.94k|    if (i > 0 && get_ccc(label[i - 1]) == virama_ccc) {
  ------------------
  |  Branch (6239:9): [True: 1.92k, False: 18]
  |  Branch (6239:18): [True: 381, False: 1.54k]
  ------------------
 6240|    381|      continue;
 6241|    381|    }
 6242|  1.56k|    if (label[i] == 0x200D) {
  ------------------
  |  Branch (6242:9): [True: 42, False: 1.52k]
  ------------------
 6243|     42|      return false;
 6244|     42|    }
 6245|       |    // If RegExpMatch((Joining_Type:{L,D})(Joining_Type:T)*\u200C
 6246|       |    //    (Joining_Type:T)*(Joining_Type:{R,D})) Then True;
 6247|  1.52k|    joining_type before = joining_type::U;
 6248|  2.31k|    for (size_t j = i; j > 0;) {
  ------------------
  |  Branch (6248:24): [True: 2.31k, False: 9]
  ------------------
 6249|  2.31k|      before = find_joining_type(label[--j]);
 6250|  2.31k|      if (before != joining_type::T) {
  ------------------
  |  Branch (6250:11): [True: 1.51k, False: 798]
  ------------------
 6251|  1.51k|        break;
 6252|  1.51k|      }
 6253|  2.31k|    }
 6254|  1.52k|    if (before != joining_type::L && before != joining_type::D) {
  ------------------
  |  Branch (6254:9): [True: 1.48k, False: 39]
  |  Branch (6254:38): [True: 180, False: 1.30k]
  ------------------
 6255|    180|      return false;
 6256|    180|    }
 6257|  1.34k|    joining_type after = joining_type::U;
 6258|  2.57k|    for (size_t j = i + 1; j < label.size(); j++) {
  ------------------
  |  Branch (6258:28): [True: 2.47k, False: 102]
  ------------------
 6259|  2.47k|      after = find_joining_type(label[j]);
 6260|  2.47k|      if (after != joining_type::T) {
  ------------------
  |  Branch (6260:11): [True: 1.23k, False: 1.23k]
  ------------------
 6261|  1.23k|        break;
 6262|  1.23k|      }
 6263|  2.47k|    }
 6264|  1.34k|    if (after != joining_type::R && after != joining_type::D) {
  ------------------
  |  Branch (6264:9): [True: 1.00k, False: 339]
  |  Branch (6264:37): [True: 246, False: 756]
  ------------------
 6265|    246|      return false;
 6266|    246|    }
 6267|  1.34k|  }
 6268|  41.5k|  return true;
 6269|  42.0k|}
can_parse.cc:_ZN3ada4idnaL17find_joining_typeEj:
 6224|  4.78k|inline static joining_type find_joining_type(uint32_t code_point) noexcept {
 6225|  4.78k|  size_t i =
 6226|  4.78k|      find_range(joining_start, joining_final, joining_table_count, code_point);
 6227|  4.78k|  return i == joining_table_count ? joining_type::U
  ------------------
  |  Branch (6227:10): [True: 312, False: 4.47k]
  ------------------
 6228|  4.78k|                                  : static_cast<joining_type>(joining_value[i]);
 6229|  4.78k|}
can_parse.cc:_ZN3ada4idnaL10find_rangeEPKjS2_mj:
 6203|   359k|                                size_t count, uint32_t code_point) noexcept {
 6204|   359k|  size_t lo = 0, hi = count;
 6205|  4.23M|  while (lo < hi) {
  ------------------
  |  Branch (6205:10): [True: 3.87M, False: 359k]
  ------------------
 6206|  3.87M|    size_t mid = lo + (hi - lo) / 2;
 6207|  3.87M|    if (last[mid] < code_point) {
  ------------------
  |  Branch (6207:9): [True: 1.41M, False: 2.45M]
  ------------------
 6208|  1.41M|      lo = mid + 1;
 6209|  2.45M|    } else {
 6210|  2.45M|      hi = mid;
 6211|  2.45M|    }
 6212|  3.87M|  }
 6213|   359k|  return lo < count && code_point >= start[lo] ? lo : count;
  ------------------
  |  Branch (6213:10): [True: 359k, False: 0]
  |  Branch (6213:24): [True: 359k, False: 312]
  ------------------
 6214|   359k|}
can_parse.cc:_ZN3ada4idnaL20find_last_not_of_nsmENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6272|  41.5k|    const std::u32string_view label) noexcept {
 6273|  49.3k|  for (int i = static_cast<int>(label.size() - 1); i >= 0; i--)
  ------------------
  |  Branch (6273:52): [True: 49.3k, False: 0]
  ------------------
 6274|  49.3k|    if (find_direction(label[i]) != direction::NSM) return i;
  ------------------
  |  Branch (6274:9): [True: 41.5k, False: 7.75k]
  ------------------
 6275|       |
 6276|      0|  return std::u32string_view::npos;
 6277|  41.5k|}
can_parse.cc:_ZN3ada4idnaL12is_rtl_labelENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6281|  41.5k|inline static bool is_rtl_label(const std::u32string_view label) noexcept {
 6282|  41.5k|  const size_t mask =
 6283|  41.5k|      (1u << direction::R) | (1u << direction::AL) | (1u << direction::AN);
 6284|       |
 6285|  41.5k|  size_t directions = 0;
 6286|   296k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (6286:22): [True: 254k, False: 41.5k]
  ------------------
 6287|   254k|    directions |= 1u << find_direction(label[i]);
 6288|   254k|  }
 6289|  41.5k|  return (directions & mask) != 0;
 6290|  41.5k|}
can_parse.cc:_ZN3ada4idnaL14find_directionEj:
 6217|   354k|inline static direction find_direction(uint32_t code_point) noexcept {
 6218|   354k|  size_t i = find_range(dir_start, dir_final, dir_table_count, code_point);
 6219|   354k|  return i == dir_table_count ? direction::NONE
  ------------------
  |  Branch (6219:10): [True: 0, False: 354k]
  ------------------
 6220|   354k|                              : static_cast<direction>(dir_value[i]);
 6221|   354k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6552|  20.0k|bool constexpr is_ascii(std::string_view view) {
 6553|   472k|  for (uint8_t c : view) {
  ------------------
  |  Branch (6553:18): [True: 472k, False: 6.48k]
  ------------------
 6554|   472k|    if (c >= 0x80) {
  ------------------
  |  Branch (6554:9): [True: 13.6k, False: 459k]
  ------------------
 6555|  13.6k|      return false;
 6556|  13.6k|    }
 6557|   472k|  }
 6558|  6.48k|  return true;
 6559|  20.0k|}
can_parse.cc:_ZN3ada4idnaL19from_ascii_to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6589|  6.48k|static void from_ascii_to_ascii(std::string_view ut8_string, std::string& out) {
 6590|  6.48k|  out.assign(ut8_string);
 6591|  6.48k|  ascii_map(out.data(), out.size());
 6592|  6.48k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6543|  67.7k|bool constexpr is_ascii(std::u32string_view view) {
 6544|   245k|  for (uint32_t c : view) {
  ------------------
  |  Branch (6544:19): [True: 245k, False: 8.63k]
  ------------------
 6545|   245k|    if (c >= 0x80) {
  ------------------
  |  Branch (6545:9): [True: 59.1k, False: 186k]
  ------------------
 6546|  59.1k|      return false;
 6547|  59.1k|    }
 6548|   245k|  }
 6549|  8.63k|  return true;
 6550|  67.7k|}
can_parse.cc:_ZN3ada4idnaL13is_ace_prefixENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6605|  49.6k|static bool is_ace_prefix(std::u32string_view label) noexcept {
 6606|  49.6k|  return label.size() >= 4 && label[0] == U'x' && label[1] == U'n' &&
  ------------------
  |  Branch (6606:10): [True: 38.2k, False: 11.3k]
  |  Branch (6606:31): [True: 9.43k, False: 28.7k]
  |  Branch (6606:51): [True: 8.67k, False: 762]
  ------------------
 6607|  8.67k|         label[2] == U'-' && label[3] == U'-';
  ------------------
  |  Branch (6607:10): [True: 8.13k, False: 534]
  |  Branch (6607:30): [True: 7.63k, False: 504]
  ------------------
 6608|  49.6k|}
can_parse.cc:_ZN3ada4idnaL18append_ascii_labelERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS1_17basic_string_viewIDiNS3_IDiEEEE:
 6595|  13.0k|static void append_ascii_label(std::string& out, std::u32string_view label) {
 6596|  13.0k|  const size_t old = out.size();
 6597|  13.0k|  out.resize(old + label.size());
 6598|  13.0k|  char* dest = out.data() + old;
 6599|   175k|  for (char32_t c : label) {
  ------------------
  |  Branch (6599:19): [True: 175k, False: 13.0k]
  ------------------
 6600|   175k|    *dest++ = static_cast<char>(c);
 6601|   175k|  }
 6602|  13.0k|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7621|  20.5k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7622|  20.5k|    return character_sets::bit_at(character_set, c);
 7623|  20.5k|  });
_ZZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7621|  60.2k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7622|  60.2k|    return character_sets::bit_at(character_set, c);
 7623|  60.2k|  });
_ZN3ada7unicode18is_ascii_hex_digitEc:
 7355|  22.9k|ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept {
 7356|  22.9k|  return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
  ------------------
  |  Branch (7356:11): [True: 14.2k, False: 8.67k]
  |  Branch (7356:23): [True: 8.97k, False: 5.26k]
  |  Branch (7356:37): [True: 5.20k, False: 8.73k]
  |  Branch (7356:49): [True: 2.82k, False: 2.38k]
  ------------------
 7357|  11.1k|         (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7357:11): [True: 2.27k, False: 8.83k]
  |  Branch (7357:23): [True: 2.02k, False: 252]
  ------------------
 7358|  22.9k|}
_ZN3ada7unicode21convert_hex_to_binaryEc:
 7445|  11.1k|unsigned constexpr convert_hex_to_binary(const char c) noexcept {
 7446|  11.1k|  return hex_to_binary_table[c - '0'];
 7447|  11.1k|}
can_parse.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
 7589|   105k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7590|   105k|    return character_sets::bit_at(character_set, c);
 7591|   105k|  });
_ZN3ada7unicode36contains_forbidden_domain_code_pointEPKcm:
 7282|  15.1k|    const char* input, size_t length) noexcept {
 7283|  15.1k|  size_t i = 0;
 7284|  15.1k|  uint8_t accumulator{};
 7285|   283k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7285:10): [True: 268k, False: 15.1k]
  ------------------
 7286|   268k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7287|   268k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 1])];
 7288|   268k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 2])];
 7289|   268k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 3])];
 7290|   268k|  }
 7291|  38.7k|  for (; i < length; i++) {
  ------------------
  |  Branch (7291:10): [True: 23.6k, False: 15.1k]
  ------------------
 7292|  23.6k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7293|  23.6k|  }
 7294|  15.1k|  return accumulator;
 7295|  15.1k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_122percent_encode_to_wideEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7975|    850|                            const uint8_t character_set[], std::string& out) {
 7976|       |  // Worst case every byte becomes %XX. Avoids realloc while walking windows.
 7977|    850|  out.reserve(out.size() + static_cast<size_t>(end - p) * 3);
 7978|    850|#if ADA_UNICODE_SSSE3
 7979|    850|  const ssse3_percent_tables tables = load_ssse3_percent_tables(character_set);
 7980|    850|  percent_encode_to_ssse3(p, end, character_set, tables, out);
 7981|       |#elif ADA_NEON
 7982|       |  percent_encode_to_neon(p, end, character_set,
 7983|       |                         load_neon_percent_table(character_set), out);
 7984|       |#elif ADA_RVV
 7985|       |  percent_encode_to_rvv(p, end, character_set, out);
 7986|       |#endif
 7987|    850|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_125load_ssse3_percent_tablesEPKh:
 7802|    850|load_ssse3_percent_tables(const uint8_t character_set[]) noexcept {
 7803|    850|  ssse3_percent_tables t{};
 7804|    850|  t.cs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set));
 7805|    850|  t.cs_hi =
 7806|    850|      _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set + 16));
 7807|       |  // 1 << (0..7), duplicated so pshufb(index & 7) works for every lane.
 7808|    850|  t.pow2 =
 7809|    850|      _mm_setr_epi8(1, 2, 4, 8, 16, 32, 64, -128, 1, 2, 4, 8, 16, 32, 64, -128);
 7810|    850|  t.mask_0f = _mm_set1_epi8(0x0F);
 7811|    850|  t.mask_07 = _mm_set1_epi8(0x07);
 7812|    850|  t.zero = _mm_setzero_si128();
 7813|    850|  return t;
 7814|    850|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_123percent_encode_to_ssse3EPKcS3_PKhRKNS1_20ssse3_percent_tablesERNSt3__112basic_stringIcNS9_11char_traitsIcEENS9_9allocatorIcEEEE:
 7833|    850|    const ssse3_percent_tables& tables, std::string& out) {
 7834|       |  // Pair 16-byte windows so a fully clean 32-byte run is one append.
 7835|  2.80k|  while (p + 32 <= end) {
  ------------------
  |  Branch (7835:10): [True: 1.95k, False: 850]
  ------------------
 7836|  1.95k|    const __m128i word0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7837|  1.95k|    const __m128i word1 =
 7838|  1.95k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
 7839|  1.95k|    const int mask0 = ssse3_percent_mask(word0, tables);
 7840|  1.95k|    const int mask1 = ssse3_percent_mask(word1, tables);
 7841|  1.95k|    if ((mask0 | mask1) == 0) {
  ------------------
  |  Branch (7841:9): [True: 127, False: 1.82k]
  ------------------
 7842|    127|      out.append(p, 32);
 7843|  1.82k|    } else {
 7844|  1.82k|      if (mask0 == 0) {
  ------------------
  |  Branch (7844:11): [True: 106, False: 1.71k]
  ------------------
 7845|    106|        out.append(p, 16);
 7846|  1.71k|      } else {
 7847|  1.71k|        encode_mask_window(p, static_cast<uint32_t>(mask0), 16, out);
 7848|  1.71k|      }
 7849|  1.82k|      if (mask1 == 0) {
  ------------------
  |  Branch (7849:11): [True: 178, False: 1.64k]
  ------------------
 7850|    178|        out.append(p + 16, 16);
 7851|  1.64k|      } else {
 7852|  1.64k|        encode_mask_window(p + 16, static_cast<uint32_t>(mask1), 16, out);
 7853|  1.64k|      }
 7854|  1.82k|    }
 7855|  1.95k|    p += 32;
 7856|  1.95k|  }
 7857|    850|  if (p + 16 <= end) {
  ------------------
  |  Branch (7857:7): [True: 578, False: 272]
  ------------------
 7858|    578|    const __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7859|    578|    const int mask = ssse3_percent_mask(word, tables);
 7860|    578|    if (mask == 0) {
  ------------------
  |  Branch (7860:9): [True: 62, False: 516]
  ------------------
 7861|     62|      out.append(p, 16);
 7862|    516|    } else {
 7863|    516|      encode_mask_window(p, static_cast<uint32_t>(mask), 16, out);
 7864|    516|    }
 7865|    578|    p += 16;
 7866|    578|  }
 7867|    850|  percent_encode_to_scalar(p, end, character_set, out);
 7868|    850|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_118ssse3_percent_maskEDv2_xRKNS1_20ssse3_percent_tablesE:
 7817|  4.47k|    __m128i word, const ssse3_percent_tables& tables) noexcept {
 7818|  4.47k|  const __m128i idx = _mm_and_si128(_mm_srli_epi16(word, 3), tables.mask_0f);
 7819|  4.47k|  const __m128i lo = _mm_shuffle_epi8(tables.cs_lo, idx);
 7820|  4.47k|  const __m128i hi = _mm_shuffle_epi8(tables.cs_hi, idx);
 7821|       |  // Bytes 0x80-0xFF are signed-negative; select the high half of the bitmap.
 7822|  4.47k|  const __m128i high_byte = _mm_cmpgt_epi8(tables.zero, word);
 7823|  4.47k|  const __m128i cs_byte = _mm_or_si128(_mm_and_si128(hi, high_byte),
 7824|  4.47k|                                       _mm_andnot_si128(high_byte, lo));
 7825|  4.47k|  const __m128i bits =
 7826|  4.47k|      _mm_shuffle_epi8(tables.pow2, _mm_and_si128(word, tables.mask_07));
 7827|  4.47k|  const __m128i hits = _mm_and_si128(cs_byte, bits);
 7828|  4.47k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hits, tables.zero)) ^ 0xFFFF;
 7829|  4.47k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_118encode_mask_windowEPKcjmRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE:
 7757|  3.87k|                                          size_t width, std::string& out) {
 7758|  3.87k|  uint64_t bits = mask;
 7759|  3.87k|  size_t off = 0;
 7760|  52.6k|  while (bits != 0) {
  ------------------
  |  Branch (7760:10): [True: 48.7k, False: 3.87k]
  ------------------
 7761|  48.7k|    const int zero_run = trailing_zeroes32(static_cast<uint32_t>(bits));
 7762|  48.7k|    if (zero_run != 0) {
  ------------------
  |  Branch (7762:9): [True: 2.89k, False: 45.8k]
  ------------------
 7763|  2.89k|      out.append(p + off, static_cast<size_t>(zero_run));
 7764|  2.89k|    }
 7765|  48.7k|    off += static_cast<size_t>(zero_run);
 7766|  48.7k|    out.append(character_sets::hex + uint8_t(p[off]) * 4, 3);
 7767|  48.7k|    ++off;
 7768|  48.7k|    bits >>= static_cast<unsigned>(zero_run + 1);
 7769|  48.7k|  }
 7770|  3.87k|  if (off < width) {
  ------------------
  |  Branch (7770:7): [True: 930, False: 2.94k]
  ------------------
 7771|    930|    out.append(p + off, width - off);
 7772|    930|  }
 7773|  3.87k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_117trailing_zeroes32Ej:
 7745|  48.7k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
 7746|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 7747|       |  unsigned long ret;
 7748|       |  _BitScanForward(&ret, input_num);
 7749|       |  return static_cast<int>(ret);
 7750|       |#else
 7751|  48.7k|  return __builtin_ctz(input_num);
 7752|  48.7k|#endif
 7753|  48.7k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_124percent_encode_to_scalarEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7778|    850|                                                std::string& out) {
 7779|  3.54k|  for (; p != end; ++p) {
  ------------------
  |  Branch (7779:10): [True: 2.69k, False: 850]
  ------------------
 7780|  2.69k|    if (character_sets::bit_at(character_set, *p)) {
  ------------------
  |  Branch (7780:9): [True: 2.00k, False: 689]
  ------------------
 7781|  2.00k|      out.append(character_sets::hex + uint8_t(*p) * 4, 3);
 7782|  2.00k|    } else {
 7783|    689|      out += *p;
 7784|    689|    }
 7785|  2.69k|  }
 7786|    850|}
can_parse.cc:_ZN3ada11serializers12_GLOBAL__N_113write_hex_u16EPct:
 8048|  2.02k|ada_really_inline char* write_hex_u16(char* p, uint16_t v) noexcept {
 8049|  2.02k|  static constexpr char kHex[] = "0123456789abcdef";
 8050|  2.02k|  if (v >= 0x1000) {
  ------------------
  |  Branch (8050:7): [True: 213, False: 1.81k]
  ------------------
 8051|    213|    *p++ = kHex[(v >> 12) & 0xf];
 8052|    213|    *p++ = kHex[(v >> 8) & 0xf];
 8053|    213|    *p++ = kHex[(v >> 4) & 0xf];
 8054|    213|    *p++ = kHex[v & 0xf];
 8055|  1.81k|  } else if (v >= 0x100) {
  ------------------
  |  Branch (8055:14): [True: 243, False: 1.56k]
  ------------------
 8056|    243|    *p++ = kHex[(v >> 8) & 0xf];
 8057|    243|    *p++ = kHex[(v >> 4) & 0xf];
 8058|    243|    *p++ = kHex[v & 0xf];
 8059|  1.56k|  } else if (v >= 0x10) {
  ------------------
  |  Branch (8059:14): [True: 240, False: 1.32k]
  ------------------
 8060|    240|    *p++ = kHex[(v >> 4) & 0xf];
 8061|    240|    *p++ = kHex[v & 0xf];
 8062|  1.32k|  } else {
 8063|  1.32k|    *p++ = kHex[v & 0xf];
 8064|  1.32k|  }
 8065|  2.02k|  return p;
 8066|  2.02k|}
can_parse.cc:_ZN3ada11serializers12_GLOBAL__N_18write_u8EPch:
 8034|  18.5k|ada_really_inline char* write_u8(char* p, uint8_t v) noexcept {
 8035|  18.5k|  if (v < 10) {
  ------------------
  |  Branch (8035:7): [True: 15.5k, False: 2.97k]
  ------------------
 8036|  15.5k|    *p++ = static_cast<char>('0' + v);
 8037|  15.5k|  } else if (v < 100) {
  ------------------
  |  Branch (8037:14): [True: 1.30k, False: 1.67k]
  ------------------
 8038|  1.30k|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v) * 2], 2);
 8039|  1.30k|    p += 2;
 8040|  1.67k|  } else {
 8041|  1.67k|    *p++ = static_cast<char>('0' + v / 100);
 8042|  1.67k|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v % 100) * 2], 2);
 8043|  1.67k|    p += 2;
 8044|  1.67k|  }
 8045|  18.5k|  return p;
 8046|  18.5k|}
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7131|   134k|    std::string_view user_input) noexcept {
 7132|       |  // first check for short strings in which case we do it naively.
 7133|   134k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (7133:7): [True: 101k, False: 33.4k]
  ------------------
 7134|   101k|    return std::ranges::any_of(user_input, is_tabs_or_newline);
 7135|   101k|  }
 7136|       |  // fast path for long strings (expected to be common)
 7137|  33.4k|  size_t i = 0;
 7138|  33.4k|  const __m128i mask1 = _mm_set1_epi8('\r');
 7139|  33.4k|  const __m128i mask2 = _mm_set1_epi8('\n');
 7140|  33.4k|  const __m128i mask3 = _mm_set1_epi8('\t');
 7141|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
 7142|  33.4k|  __m128i running{0};
 7143|   166k|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (7143:10): [True: 132k, False: 33.4k]
  ------------------
 7144|   132k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
 7145|   132k|    running = _mm_or_si128(
 7146|   132k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 7147|   132k|                                           _mm_cmpeq_epi8(word, mask2))),
 7148|   132k|        _mm_cmpeq_epi8(word, mask3));
 7149|   132k|  }
 7150|  33.4k|  if (i < user_input.size()) {
  ------------------
  |  Branch (7150:7): [True: 29.3k, False: 4.11k]
  ------------------
 7151|  29.3k|    __m128i word = _mm_loadu_si128(
 7152|  29.3k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
 7153|  29.3k|    running = _mm_or_si128(
 7154|  29.3k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 7155|  29.3k|                                           _mm_cmpeq_epi8(word, mask2))),
 7156|  29.3k|        _mm_cmpeq_epi8(word, mask3));
 7157|  29.3k|  }
 7158|  33.4k|  return _mm_movemask_epi8(running) != 0;
 7159|   134k|}
_ZN3ada7unicode18is_tabs_or_newlineEc:
 7016|   358k|constexpr bool is_tabs_or_newline(char c) noexcept {
 7017|   358k|  return c == '\r' || c == '\n' || c == '\t';
  ------------------
  |  Branch (7017:10): [True: 384, False: 358k]
  |  Branch (7017:23): [True: 332, False: 358k]
  |  Branch (7017:36): [True: 388, False: 357k]
  ------------------
 7018|   358k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_124try_can_parse_clean_httpENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8228|  34.2k|std::optional<bool> try_can_parse_clean_http(std::string_view input) noexcept {
 8229|  34.2k|  const auto* bytes = reinterpret_cast<const uint8_t*>(input.data());
 8230|  34.2k|  const size_t length = input.size();
 8231|  34.2k|  if (length < 7) {
  ------------------
  |  Branch (8231:7): [True: 16.1k, False: 18.1k]
  ------------------
 8232|  16.1k|    return std::nullopt;
 8233|  16.1k|  }
 8234|       |
 8235|  18.1k|  size_t authority_start;
 8236|  18.1k|  if constexpr (k_can_parse_little_endian) {
 8237|  18.1k|    if (length >= 8) {
  ------------------
  |  Branch (8237:9): [True: 16.7k, False: 1.46k]
  ------------------
 8238|  16.7k|      uint64_t first8 = 0;
 8239|  16.7k|      std::memcpy(&first8, bytes, 8);
 8240|  16.7k|      if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (8240:11): [True: 12, False: 16.6k]
  ------------------
 8241|     12|        authority_start = 8;
 8242|  16.6k|      } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (8242:18): [True: 413, False: 16.2k]
  ------------------
 8243|  16.6k|                 0x002f2f3a70747468ull) {  // "http://"
 8244|    413|        authority_start = 7;
 8245|  16.2k|      } else {
 8246|  16.2k|        return std::nullopt;
 8247|  16.2k|      }
 8248|  16.7k|    } else if (bytes[0] == 'h' && bytes[1] == 't' && bytes[2] == 't' &&
  ------------------
  |  Branch (8248:16): [True: 97, False: 1.36k]
  |  Branch (8248:35): [True: 76, False: 21]
  |  Branch (8248:54): [True: 62, False: 14]
  ------------------
 8249|     62|               bytes[3] == 'p' && bytes[4] == ':' && bytes[5] == '/' &&
  ------------------
  |  Branch (8249:16): [True: 46, False: 16]
  |  Branch (8249:35): [True: 30, False: 16]
  |  Branch (8249:54): [True: 12, False: 18]
  ------------------
 8250|     12|               bytes[6] == '/') {
  ------------------
  |  Branch (8250:16): [True: 2, False: 10]
  ------------------
 8251|      2|      authority_start = 7;
 8252|  1.46k|    } else {
 8253|  1.46k|      return std::nullopt;
 8254|  1.46k|    }
 8255|       |  } else {
 8256|       |    uint32_t first4{};
 8257|       |    std::memcpy(&first4, bytes, 4);
 8258|       |    if (first4 != 0x68747470u) {  // "http"
 8259|       |      return std::nullopt;
 8260|       |    }
 8261|       |    if (length >= 8 && bytes[4] == 's' && bytes[5] == ':' && bytes[6] == '/' &&
 8262|       |        bytes[7] == '/') {
 8263|       |      authority_start = 8;
 8264|       |    } else if (bytes[4] == ':' && bytes[5] == '/' && bytes[6] == '/') {
 8265|       |      authority_start = 7;
 8266|       |    } else {
 8267|       |      return std::nullopt;
 8268|       |    }
 8269|       |  }
 8270|       |
 8271|  18.1k|  if (authority_start >= length) {
  ------------------
  |  Branch (8271:7): [True: 2, False: 18.1k]
  ------------------
 8272|      2|    return false;
 8273|      2|  }
 8274|       |
 8275|  18.1k|  size_t cursor = authority_start;
 8276|  19.4k|  while (cursor + 8 <= length && eight_clean_http_host_bytes(bytes + cursor)) {
  ------------------
  |  Branch (8276:10): [True: 1.45k, False: 17.9k]
  |  Branch (8276:34): [True: 1.24k, False: 211]
  ------------------
 8277|  1.24k|    cursor += 8;
 8278|  1.24k|  }
 8279|  18.8k|  while (cursor < length) {
  ------------------
  |  Branch (8279:10): [True: 1.11k, False: 17.7k]
  ------------------
 8280|  1.11k|    const uint8_t c = bytes[cursor];
 8281|  1.11k|    if (c == '/' || c == '?' || c == '#') {
  ------------------
  |  Branch (8281:9): [True: 251, False: 864]
  |  Branch (8281:21): [True: 3, False: 861]
  |  Branch (8281:33): [True: 1, False: 860]
  ------------------
 8282|    255|      break;
 8283|    255|    }
 8284|    860|    if (!clean_http_host_byte[c]) {
  ------------------
  |  Branch (8284:9): [True: 136, False: 724]
  ------------------
 8285|    136|      return std::nullopt;
 8286|    136|    }
 8287|    724|    ++cursor;
 8288|    724|  }
 8289|       |
 8290|  18.0k|  const size_t host_length = cursor - authority_start;
 8291|  18.0k|  if (host_length == 0) {
  ------------------
  |  Branch (8291:7): [True: 3, False: 18.0k]
  ------------------
 8292|       |    // Extra special-scheme slashes are normalization, not an empty host.
 8293|      3|    if (bytes[authority_start] == '/' || bytes[authority_start] == '\\') {
  ------------------
  |  Branch (8293:9): [True: 1, False: 2]
  |  Branch (8293:42): [True: 0, False: 2]
  ------------------
 8294|      1|      return std::nullopt;
 8295|      1|    }
 8296|      2|    return false;
 8297|      3|  }
 8298|  18.0k|  if (host_length > 253 || bytes[cursor - 1] == '.') {
  ------------------
  |  Branch (8298:7): [True: 17.7k, False: 275]
  |  Branch (8298:28): [True: 33, False: 242]
  ------------------
 8299|     44|    return std::nullopt;
 8300|     44|  }
 8301|       |
 8302|  17.9k|  const std::string_view host(input.data() + authority_start, host_length);
 8303|  17.9k|  if (checkers::last_label_may_be_a_number(host)) {
  ------------------
  |  Branch (8303:7): [True: 19, False: 17.9k]
  ------------------
 8304|     19|    return std::nullopt;
 8305|     19|  }
 8306|  17.9k|  if (host.find('x') != std::string_view::npos &&
  ------------------
  |  Branch (8306:7): [True: 106, False: 17.8k]
  ------------------
 8307|    106|      host.find("xn-") != std::string_view::npos) {
  ------------------
  |  Branch (8307:7): [True: 29, False: 77]
  ------------------
 8308|     29|    return std::nullopt;
 8309|     29|  }
 8310|       |
 8311|       |  // Once a special URL has a valid host, path/query/fragment bytes cannot make
 8312|       |  // it structurally invalid: the full parser percent-encodes them as needed.
 8313|  17.9k|  return true;
 8314|  17.9k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_127eight_clean_http_host_bytesEPKh:
 8204|  1.45k|    const uint8_t* input) noexcept {
 8205|  1.45k|  uint64_t w = 0;
 8206|  1.45k|  std::memcpy(&w, input, 8);
 8207|  1.45k|  constexpr uint64_t k_high = 0x8080808080808080ull;
 8208|  1.45k|  if ((w & k_high) != 0) {
  ------------------
  |  Branch (8208:7): [True: 13, False: 1.44k]
  ------------------
 8209|     13|    return false;
 8210|     13|  }
 8211|  1.44k|  const uint64_t ok = swar_in_range(w, 'a', 'z') | swar_in_range(w, '0', '9') |
 8212|  1.44k|                      swar_in_range(w, '-', '-') | swar_in_range(w, '.', '.') |
 8213|  1.44k|                      swar_in_range(w, '_', '_') | swar_in_range(w, '~', '~');
 8214|  1.44k|  return ok == k_high;
 8215|  1.45k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_113swar_in_rangeEmhh:
 8195|  8.67k|                                         uint8_t hi) noexcept {
 8196|  8.67k|  constexpr uint64_t k_ones = 0x0101010101010101ull;
 8197|  8.67k|  constexpr uint64_t k_high = 0x8080808080808080ull;
 8198|  8.67k|  const uint64_t ge_lo = (w | k_high) - (k_ones * lo);
 8199|  8.67k|  const uint64_t le_hi = ((k_ones * hi) | k_high) - w;
 8200|  8.67k|  return ge_lo & le_hi & k_high;
 8201|  8.67k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_127try_can_parse_absolute_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8329|  34.0k|    std::string_view input) noexcept {
 8330|  34.0k|  const uint8_t* b = reinterpret_cast<const uint8_t*>(input.data());
 8331|  34.0k|  size_t len = input.size();
 8332|       |
 8333|       |  // -- Inline C0 whitespace trim (no allocation) --------------------------
 8334|       |  // Note: \t (0x09), \n (0x0a), \r (0x0d) are all <= 0x20, so any
 8335|       |  // leading/trailing tabs or newlines are correctly stripped here, matching
 8336|       |  // the WHATWG spec's "remove leading/trailing C0 control and space" step.
 8337|  35.4k|  while (len > 0 && b[0] <= 0x20) {
  ------------------
  |  Branch (8337:10): [True: 22.2k, False: 13.1k]
  |  Branch (8337:21): [True: 1.34k, False: 20.9k]
  ------------------
 8338|  1.34k|    b++;
 8339|  1.34k|    len--;
 8340|  1.34k|  }
 8341|  35.5k|  while (len > 0 && b[len - 1] <= 0x20) {
  ------------------
  |  Branch (8341:10): [True: 22.3k, False: 13.1k]
  |  Branch (8341:21): [True: 1.46k, False: 20.9k]
  ------------------
 8342|  1.46k|    len--;
 8343|  1.46k|  }
 8344|  34.0k|  if (len == 0) return false;
  ------------------
  |  Branch (8344:7): [True: 13.1k, False: 20.9k]
  ------------------
 8345|       |
 8346|       |  // -- Scheme detection -----------------------------------------------------
 8347|       |  // Fast path for HTTP and HTTPS (covers ~90%+ of real-world URLs).
 8348|       |  // Avoids the general scheme loop, buffer copy, and perfect hash lookup.
 8349|       |  // We know HTTP and HTTPS are special non-file schemes, so no further
 8350|       |  // scheme_type checks are needed on the fast path -- only `pos` matters.
 8351|  20.9k|  size_t pos;
 8352|       |
 8353|  20.9k|  if (len >= 7 && (b[0] | 0x20) == 'h' && (b[1] | 0x20) == 't' &&
  ------------------
  |  Branch (8353:7): [True: 17.8k, False: 3.02k]
  |  Branch (8353:19): [True: 582, False: 17.3k]
  |  Branch (8353:43): [True: 444, False: 138]
  ------------------
 8354|    444|      (b[2] | 0x20) == 't' && (b[3] | 0x20) == 'p') {
  ------------------
  |  Branch (8354:7): [True: 422, False: 22]
  |  Branch (8354:31): [True: 393, False: 29]
  ------------------
 8355|    393|    if (b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (8355:9): [True: 343, False: 50]
  |  Branch (8355:24): [True: 232, False: 111]
  |  Branch (8355:39): [True: 222, False: 10]
  ------------------
 8356|    222|      pos = 7;
 8357|    222|      goto skip_extra_slashes;
 8358|    222|    }
 8359|    171|    if (len >= 8 && (b[4] | 0x20) == 's' && b[5] == ':' && b[6] == '/' &&
  ------------------
  |  Branch (8359:9): [True: 130, False: 41]
  |  Branch (8359:21): [True: 27, False: 103]
  |  Branch (8359:45): [True: 15, False: 12]
  |  Branch (8359:60): [True: 11, False: 4]
  ------------------
 8360|     11|        b[7] == '/') {
  ------------------
  |  Branch (8360:9): [True: 9, False: 2]
  ------------------
 8361|      9|      pos = 8;
 8362|      9|      goto skip_extra_slashes;
 8363|      9|    }
 8364|       |    // Fall through: could be "httpe://", tabs in scheme, etc.
 8365|    171|  }
 8366|       |
 8367|  20.6k|  {
 8368|       |    // General scheme detection for ws, wss, ftp, and edge cases.
 8369|  20.6k|    if (!checkers::is_alpha(static_cast<char>(b[0]))) return false;
  ------------------
  |  Branch (8369:9): [True: 983, False: 19.7k]
  ------------------
 8370|       |
 8371|       |    // Scan for ':' within the first 7 bytes. All special schemes are <= 5
 8372|       |    // chars ("https"), so any URL whose first ':' is beyond byte 6 is either
 8373|       |    // non-special or relative -- both require the full parser.
 8374|  19.7k|    size_t colon_pos = 0;
 8375|  42.4k|    for (size_t i = 1;; ++i) {
 8376|  42.4k|      if (i >= 7 || i >= len) return std::nullopt;
  ------------------
  |  Branch (8376:11): [True: 97, False: 42.3k]
  |  Branch (8376:21): [True: 212, False: 42.1k]
  ------------------
 8377|  42.1k|      const char c = static_cast<char>(b[i]);
 8378|  42.1k|      if (c == ':') {
  ------------------
  |  Branch (8378:11): [True: 19.0k, False: 23.0k]
  ------------------
 8379|  19.0k|        colon_pos = i;
 8380|  19.0k|        break;
 8381|  19.0k|      }
 8382|       |      // Tabs/newlines in the scheme require the full parser to strip them.
 8383|  23.0k|      if (c == '\t' || c == '\n' || c == '\r') return std::nullopt;
  ------------------
  |  Branch (8383:11): [True: 12, False: 23.0k]
  |  Branch (8383:24): [True: 7, False: 23.0k]
  |  Branch (8383:37): [True: 8, False: 22.9k]
  ------------------
 8384|  22.9k|      if (!unicode::is_alnum_plus(c)) return false;
  ------------------
  |  Branch (8384:11): [True: 284, False: 22.7k]
  ------------------
 8385|  22.9k|    }
 8386|       |
 8387|       |    // Lowercase scheme bytes inline and classify via the existing perfect
 8388|       |    // hash.
 8389|  19.0k|    char scheme_buf[6];
 8390|  19.0k|    scheme_buf[0] = static_cast<char>(b[0] | 0x20);
 8391|  40.9k|    for (size_t i = 1; i < colon_pos; ++i)
  ------------------
  |  Branch (8391:24): [True: 21.9k, False: 19.0k]
  ------------------
 8392|  21.9k|      scheme_buf[i] = static_cast<char>(b[i] | 0x20);
 8393|       |
 8394|  19.0k|    const ada::scheme::type scheme_type =
 8395|  19.0k|        ada::scheme::get_scheme_type({scheme_buf, colon_pos});
 8396|       |
 8397|       |    // Only handle special, non-file schemes.
 8398|  19.0k|    if (scheme_type == ada::scheme::NOT_SPECIAL) return std::nullopt;
  ------------------
  |  Branch (8398:9): [True: 2.77k, False: 16.3k]
  ------------------
 8399|  16.3k|    if (scheme_type == ada::scheme::FILE) return std::nullopt;
  ------------------
  |  Branch (8399:9): [True: 1.93k, False: 14.3k]
  ------------------
 8400|       |
 8401|       |    // Per WHATWG, special URLs don't require "//": "http:example.com" is valid
 8402|       |    // (SPECIAL_AUTHORITY_IGNORE_SLASHES just skips leading slashes and
 8403|       |    // proceeds to AUTHORITY).  Defer to the inline fallback for any input
 8404|       |    // without "://".
 8405|  14.3k|    pos = colon_pos + 1;
 8406|  14.3k|    if (pos + 2 > len || b[pos] != '/' || b[pos + 1] != '/') {
  ------------------
  |  Branch (8406:9): [True: 163, False: 14.2k]
  |  Branch (8406:26): [True: 6.26k, False: 7.95k]
  |  Branch (8406:43): [True: 61, False: 7.89k]
  ------------------
 8407|  6.48k|      return std::nullopt;
 8408|  6.48k|    }
 8409|  7.89k|    pos += 2;
 8410|  7.89k|  }
 8411|       |
 8412|  8.12k|skip_extra_slashes:
 8413|       |  // SPECIAL_AUTHORITY_IGNORE_SLASHES: the full parser skips any additional
 8414|       |  // leading '/' or '\' after the initial "//".  Mirror that here so we don't
 8415|       |  // mis-identify the host as empty when there are extra slashes.
 8416|  8.91k|  while (pos < len && (b[pos] == '/' || b[pos] == '\\')) {
  ------------------
  |  Branch (8416:10): [True: 8.88k, False: 27]
  |  Branch (8416:24): [True: 610, False: 8.27k]
  |  Branch (8416:41): [True: 181, False: 8.09k]
  ------------------
 8417|    791|    ++pos;
 8418|    791|  }
 8419|       |
 8420|       |  // Early IPv6 bail-out: if the authority starts with '[', it's an IPv6
 8421|       |  // literal which requires the full parser.  Checking here avoids scanning
 8422|       |  // the entire bracketed address only to bail out afterward.
 8423|  8.12k|  if (pos < len && b[pos] == '[') return std::nullopt;
  ------------------
  |  Branch (8423:7): [True: 8.09k, False: 27]
  |  Branch (8423:20): [True: 98, False: 7.99k]
  ------------------
 8424|       |
 8425|       |  // -- Merged authority + host scan ------------------------------------------
 8426|       |  // A single forward pass over the authority bytes that simultaneously:
 8427|       |  //   - finds the authority end and port colon
 8428|       |  //   - validates host characters (forbidden domain code points)
 8429|       |  //   - tracks IPv4 indicators (all-decimal-dots, last non-dot char)
 8430|       |  //   - detects xn-- prefixes (IDNA punycode)
 8431|       |  //   - detects tabs/newlines (which require the full parser to strip)
 8432|       |  // This replaces 4 separate scans over the host bytes.
 8433|  8.02k|  const size_t auth_start = pos;
 8434|  8.02k|  size_t auth_end = pos;
 8435|  8.02k|  size_t port_colon = SIZE_MAX;
 8436|  8.02k|  bool all_dec_dots = true;
 8437|       |
 8438|  51.1k|  for (; auth_end < len; ++auth_end) {
  ------------------
  |  Branch (8438:10): [True: 50.8k, False: 323]
  ------------------
 8439|  50.8k|    const uint8_t c = b[auth_end];
 8440|       |
 8441|       |    // Non-ASCII -> needs IDNA processing -> full parser.
 8442|  50.8k|    if (c >= 0x80) return std::nullopt;
  ------------------
  |  Branch (8442:9): [True: 35, False: 50.7k]
  ------------------
 8443|       |
 8444|       |    // Authority delimiters.
 8445|  50.7k|    if (c == '/' || c == '?' || c == '#' || c == '\\') break;
  ------------------
  |  Branch (8445:9): [True: 5.65k, False: 45.1k]
  |  Branch (8445:21): [True: 151, False: 44.9k]
  |  Branch (8445:33): [True: 114, False: 44.8k]
  |  Branch (8445:45): [True: 2, False: 44.8k]
  ------------------
 8446|       |
 8447|       |    // Port separator.
 8448|  44.8k|    if (c == ':') {
  ------------------
  |  Branch (8448:9): [True: 2.19k, False: 42.6k]
  ------------------
 8449|  2.19k|      if (port_colon == SIZE_MAX) port_colon = auth_end;
  ------------------
  |  Branch (8449:11): [True: 1.99k, False: 202]
  ------------------
 8450|  2.19k|      continue;
 8451|  2.19k|    }
 8452|       |
 8453|       |    // Credentials or percent-encoding -> full parser.
 8454|  42.6k|    if (c == '@' || c == '%') return std::nullopt;
  ------------------
  |  Branch (8454:9): [True: 125, False: 42.5k]
  |  Branch (8454:21): [True: 171, False: 42.3k]
  ------------------
 8455|       |
 8456|       |    // Tabs/newlines anywhere in the authority require the full parser to
 8457|       |    // strip them before validation.  Without this, a tab in the port (e.g.
 8458|       |    // "http://host:8\t0/") would be mis-rejected by port validation.
 8459|  42.3k|    if (c == '\t' || c == '\n' || c == '\r') return std::nullopt;
  ------------------
  |  Branch (8459:9): [True: 1, False: 42.3k]
  |  Branch (8459:22): [True: 1, False: 42.3k]
  |  Branch (8459:35): [True: 2, False: 42.3k]
  ------------------
 8460|       |
 8461|       |    // Skip remaining host-specific checks for port bytes.  Port digits are
 8462|       |    // validated separately below, and no forbidden-domain-code-point check
 8463|       |    // is needed on port characters.
 8464|  42.3k|    if (port_colon != SIZE_MAX) continue;
  ------------------
  |  Branch (8464:9): [True: 2.65k, False: 39.7k]
  ------------------
 8465|       |
 8466|       |    // -- Host byte validation (inlined) ------------------------------------
 8467|       |    // Forbidden domain code points that are not already caught above:
 8468|       |    //   C0 controls and space (0x00-0x20), DEL (0x7F), <, >, [, ], ^, |.
 8469|       |    // At this stage, the input may still be userinfo or be normalized later
 8470|       |    // (e.g., percent-encoded), so we do not reject here and defer to the
 8471|       |    // parser. Characters already caught: >= 0x80 (non-ASCII), '/' '?' '#' '\\'
 8472|       |    // (delimiters), ':' (port), '@' '%' (bail), '\t' '\n' '\r' (bail).
 8473|  39.7k|    if (c <= 0x20 || c == 0x7F || c == '<' || c == '>' || c == '[' ||
  ------------------
  |  Branch (8473:9): [True: 9, False: 39.7k]
  |  Branch (8473:22): [True: 1, False: 39.7k]
  |  Branch (8473:35): [True: 1, False: 39.7k]
  |  Branch (8473:47): [True: 1, False: 39.7k]
  |  Branch (8473:59): [True: 2, False: 39.7k]
  ------------------
 8474|  39.7k|        c == ']' || c == '^' || c == '|') {
  ------------------
  |  Branch (8474:9): [True: 1, False: 39.7k]
  |  Branch (8474:21): [True: 1, False: 39.7k]
  |  Branch (8474:33): [True: 1, False: 39.7k]
  ------------------
 8475|     17|      return std::nullopt;
 8476|     17|    }
 8477|       |
 8478|       |    // Track whether host is all decimal digits and dots (potential IPv4).
 8479|  39.7k|    if (c != '.' && (c < '0' || c > '9')) all_dec_dots = false;
  ------------------
  |  Branch (8479:9): [True: 35.4k, False: 4.27k]
  |  Branch (8479:22): [True: 2.31k, False: 33.1k]
  |  Branch (8479:33): [True: 25.2k, False: 7.90k]
  ------------------
 8480|       |
 8481|       |    // Detect xn-- prefix inline (IDNA punycode -> needs full parser).
 8482|       |    // Checking at every position mirrors the original behavior: any
 8483|       |    // occurrence of "xn--" in the host (not just at label boundaries)
 8484|       |    // triggers a bail-out to the full IDNA validator.
 8485|  39.7k|    if ((c | 0x20) == 'x' && auth_end + 4 <= len &&
  ------------------
  |  Branch (8485:9): [True: 6.50k, False: 33.2k]
  |  Branch (8485:30): [True: 5.99k, False: 508]
  ------------------
 8486|  5.99k|        (b[auth_end + 1] | 0x20) == 'n' && b[auth_end + 2] == '-' &&
  ------------------
  |  Branch (8486:9): [True: 1.99k, False: 4.00k]
  |  Branch (8486:44): [True: 1.72k, False: 270]
  ------------------
 8487|  1.72k|        b[auth_end + 3] == '-') {
  ------------------
  |  Branch (8487:9): [True: 1.42k, False: 304]
  ------------------
 8488|  1.42k|      return std::nullopt;
 8489|  1.42k|    }
 8490|  39.7k|  }
 8491|       |
 8492|  6.24k|  const size_t host_end = (port_colon != SIZE_MAX) ? port_colon : auth_end;
  ------------------
  |  Branch (8492:27): [True: 1.92k, False: 4.32k]
  ------------------
 8493|       |
 8494|       |  // Empty host is invalid for special URLs.
 8495|  6.24k|  if (auth_start == host_end) return false;
  ------------------
  |  Branch (8495:7): [True: 39, False: 6.20k]
  ------------------
 8496|       |
 8497|       |  // -- IPv4 handling ---------------------------------------------------------
 8498|  6.20k|  const char* host_ptr = reinterpret_cast<const char*>(b + auth_start);
 8499|  6.20k|  const size_t host_len = host_end - auth_start;
 8500|       |
 8501|  6.20k|  if (all_dec_dots) {
  ------------------
  |  Branch (8501:7): [True: 1.36k, False: 4.84k]
  ------------------
 8502|       |    // Host is all decimal digits and dots -> try the fast IPv4 parser.
 8503|  1.36k|    if (checkers::try_parse_ipv4_fast({host_ptr, host_len}) !=
  ------------------
  |  Branch (8503:9): [True: 676, False: 685]
  ------------------
 8504|  1.36k|        checkers::ipv4_fast_fail) {
 8505|       |      // Valid decimal IPv4 host.  Do NOT return true yet: the port still
 8506|       |      // needs to be validated below before we can declare the URL valid.
 8507|    676|      goto validate_port;
 8508|    676|    }
 8509|       |    // Fast IPv4 parsing failed (e.g. host is ".", "..", "1.2.3.500").
 8510|       |    // Such hosts may still be valid domain names; defer to the full parser.
 8511|    685|    return std::nullopt;
 8512|  1.36k|  }
 8513|       |
 8514|  4.84k|  if (checkers::last_label_may_be_a_number({host_ptr, host_len})) {
  ------------------
  |  Branch (8514:7): [True: 281, False: 4.56k]
  ------------------
 8515|    281|    return std::nullopt;
 8516|    281|  }
 8517|       |
 8518|       |  // -- Port validation -------------------------------------------------------
 8519|  5.24k|validate_port:
 8520|  5.24k|  if (port_colon != SIZE_MAX) {
  ------------------
  |  Branch (8520:7): [True: 1.67k, False: 3.56k]
  ------------------
 8521|  1.67k|    const uint8_t* pp = b + port_colon + 1;
 8522|  1.67k|    size_t pl = auth_end - port_colon - 1;
 8523|  1.67k|    if (pl > 0) {
  ------------------
  |  Branch (8523:9): [True: 1.20k, False: 475]
  ------------------
 8524|       |      // Strip leading zeros: "0000001" == 1, "0000000000000" == 0, both valid.
 8525|       |      // Only the significant digits count toward the 5-digit maximum.
 8526|  1.70k|      while (pl > 0 && *pp == '0') {
  ------------------
  |  Branch (8526:14): [True: 1.38k, False: 328]
  |  Branch (8526:24): [True: 508, False: 873]
  ------------------
 8527|    508|        ++pp;
 8528|    508|        --pl;
 8529|    508|      }
 8530|  1.20k|      if (pl > 5) return false;  // significant digits > 99999
  ------------------
  |  Branch (8530:11): [True: 5, False: 1.19k]
  ------------------
 8531|  1.19k|      uint32_t pv = 0;
 8532|  2.47k|      for (size_t i = 0; i < pl; ++i) {
  ------------------
  |  Branch (8532:26): [True: 1.29k, False: 1.17k]
  ------------------
 8533|  1.29k|        if (pp[i] < '0' || pp[i] > '9') return false;
  ------------------
  |  Branch (8533:13): [True: 8, False: 1.28k]
  |  Branch (8533:28): [True: 13, False: 1.27k]
  ------------------
 8534|  1.27k|        pv = pv * 10 + (pp[i] - '0');
 8535|  1.27k|      }
 8536|  1.17k|      if (pv > 65535) return false;
  ------------------
  |  Branch (8536:11): [True: 3, False: 1.17k]
  ------------------
 8537|  1.17k|    }
 8538|  1.67k|  }
 8539|       |
 8540|       |  // Path, query, and fragment are structurally always valid for can_parse --
 8541|       |  // the parser would encode whatever is there.
 8542|  5.21k|  return true;
 8543|  5.24k|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 8844|  1.13k|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
 8845|       |  // Fast path: inputs with no tab/newline (the common case) avoid the
 8846|       |  // per-character erase_if scan. has_tabs_or_newline uses SIMD for inputs
 8847|       |  // >= 16 bytes, making this check much cheaper than erase_if's scalar
 8848|       |  // predicate walk when there is nothing to remove.
 8849|  1.13k|  if (!ada::unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (8849:7): [True: 0, False: 1.13k]
  ------------------
 8850|      0|    return;
 8851|      0|  }
 8852|  1.13k|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
 8853|  1.13k|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
 7376|  48.2k|    const char c) noexcept {
 7377|  48.2k|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (7377:10): [True: 1.92k, False: 46.3k]
  |  Branch (7377:23): [True: 2.33k, False: 43.9k]
  |  Branch (7377:36): [True: 840, False: 43.1k]
  ------------------
 7378|  48.2k|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
 7371|   156k|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
 7372|   156k|  return (unsigned char)c <= ' ';
 7373|   156k|}
_ZN3ada7helpers19parse_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 9551|  10.7k|                                           std::string& path) {
 9552|  10.7k|  ada_log("parse_prepared_path ", input);
 9553|  10.7k|  uint8_t accumulator = checkers::path_signature(input);
 9554|       |  // Let us first detect a trivial case.
 9555|       |  // If it is special, we check that we have no dot, no %,  no \ and no
 9556|       |  // character needing percent encoding. Otherwise, we check that we have no %,
 9557|       |  // no dot, and no character needing percent encoding.
 9558|  10.7k|  constexpr uint8_t need_encoding = 1;
 9559|  10.7k|  constexpr uint8_t backslash_char = 2;
 9560|  10.7k|  constexpr uint8_t dot_char = 4;
 9561|  10.7k|  constexpr uint8_t percent_char = 8;
 9562|  10.7k|  bool special = type != ada::scheme::NOT_SPECIAL;
 9563|  10.7k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (9563:39): [True: 1.68k, False: 9.03k]
  ------------------
 9564|  1.68k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (9564:39): [True: 135, False: 1.54k]
  ------------------
 9565|  10.7k|  bool trivial_path =
 9566|  10.7k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (9566:7): [True: 4.94k, False: 5.77k]
  |  Branch (9566:8): [True: 9.15k, False: 1.56k]
  ------------------
 9567|  10.7k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
 9568|  1.56k|                  0)) &&
 9569|  4.94k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (9569:7): [True: 4.85k, False: 97]
  ------------------
 9570|  10.7k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (9570:7): [True: 1.59k, False: 9.12k]
  |  Branch (9570:34): [True: 1.57k, False: 23]
  ------------------
 9571|       |    // '4' means that we have at least one dot, but nothing that requires
 9572|       |    // percent encoding or decoding. The only part that is not trivial is
 9573|       |    // that we may have single dots and double dots path segments.
 9574|       |    // If we have such segments, then we either have a path that begins
 9575|       |    // with '.' (easy to check), or we have the sequence './'.
 9576|       |    // Note: input cannot be empty, it must at least contain one character ('.')
 9577|       |    // Note: we know that '\' is not present.
 9578|  1.57k|    if (input[0] != '.') {
  ------------------
  |  Branch (9578:9): [True: 812, False: 758]
  ------------------
 9579|    812|      size_t slashdot = 0;
 9580|    812|      bool dot_is_file = true;
 9581|  5.59k|      for (;;) {
 9582|  5.59k|        slashdot = input.find("/.", slashdot);
 9583|  5.59k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (9583:13): [True: 812, False: 4.78k]
  ------------------
 9584|    812|          break;
 9585|  4.78k|        } else {  // uncommon
 9586|       |          // only three cases matter: /./, /.. or a final /
 9587|  4.78k|          slashdot += 2;
 9588|  4.78k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (9588:28): [True: 222, False: 4.56k]
  |  Branch (9588:56): [True: 2.92k, False: 1.64k]
  ------------------
 9589|  1.64k|                           input[slashdot] == '/');
  ------------------
  |  Branch (9589:28): [True: 1.20k, False: 439]
  ------------------
 9590|  4.78k|        }
 9591|  5.59k|      }
 9592|    812|      trivial_path = dot_is_file;
 9593|    812|    }
 9594|  1.57k|  }
 9595|  10.7k|  if (trivial_path) {
  ------------------
  |  Branch (9595:7): [True: 5.04k, False: 5.67k]
  ------------------
 9596|  5.04k|    ada_log("parse_path trivial");
 9597|  5.04k|    path += '/';
 9598|  5.04k|    path += input;
 9599|  5.04k|    return;
 9600|  5.04k|  }
 9601|       |  // We are going to need to look a bit at the path, but let us see if we can
 9602|       |  // ignore percent encoding *and* backslashes *and* percent characters.
 9603|       |  // Except for the trivial case, this is likely to capture 99% of paths out
 9604|       |  // there.
 9605|  5.67k|  bool fast_path =
 9606|  5.67k|      (special &&
  ------------------
  |  Branch (9606:8): [True: 4.52k, False: 1.14k]
  ------------------
 9607|  4.52k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (9607:8): [True: 1.32k, False: 3.20k]
  ------------------
 9608|  1.32k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (9608:7): [True: 1.14k, False: 185]
  ------------------
 9609|  5.67k|  if (fast_path) {
  ------------------
  |  Branch (9609:7): [True: 1.14k, False: 4.52k]
  ------------------
 9610|  1.14k|    ada_log("parse_prepared_path fast");
 9611|       |    // Here we don't need to worry about \ or percent encoding.
 9612|       |    // We also do not have a file protocol. We might have dots, however,
 9613|       |    // but dots must as appear as '.', and they cannot be encoded because
 9614|       |    // the symbol '%' is not present.
 9615|  1.14k|    size_t previous_location = 0;  // We start at 0.
 9616|  14.9k|    do {
 9617|  14.9k|      size_t new_location = input.find('/', previous_location);
 9618|       |      // std::string_view path_view = input;
 9619|       |      //  We process the last segment separately:
 9620|  14.9k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (9620:11): [True: 1.14k, False: 13.8k]
  ------------------
 9621|  1.14k|        std::string_view path_view = input.substr(previous_location);
 9622|  1.14k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (9622:13): [True: 125, False: 1.01k]
  ------------------
 9623|       |          // e.g., if you receive ".." with an empty path, you go to "/".
 9624|    125|          if (path.empty()) {
  ------------------
  |  Branch (9624:15): [True: 52, False: 73]
  ------------------
 9625|     52|            path = '/';
 9626|     52|            return;
 9627|     52|          }
 9628|       |          // Fast case where we have nothing to do:
 9629|     73|          if (path.back() == '/') {
  ------------------
  |  Branch (9629:15): [True: 22, False: 51]
  ------------------
 9630|     22|            return;
 9631|     22|          }
 9632|       |          // If you have the path "/joe/myfriend",
 9633|       |          // then you delete 'myfriend'.
 9634|     51|          path.resize(path.rfind('/') + 1);
 9635|     51|          return;
 9636|     73|        }
 9637|  1.01k|        path += '/';
 9638|  1.01k|        if (path_view != ".") {
  ------------------
  |  Branch (9638:13): [True: 741, False: 278]
  ------------------
 9639|    741|          path.append(path_view);
 9640|    741|        }
 9641|  1.01k|        return;
 9642|  13.8k|      } else {
 9643|       |        // This is a non-final segment.
 9644|  13.8k|        std::string_view path_view =
 9645|  13.8k|            input.substr(previous_location, new_location - previous_location);
 9646|  13.8k|        previous_location = new_location + 1;
 9647|  13.8k|        if (path_view == "..") {
  ------------------
  |  Branch (9647:13): [True: 3.13k, False: 10.7k]
  ------------------
 9648|  3.13k|          size_t last_delimiter = path.rfind('/');
 9649|  3.13k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (9649:15): [True: 1.18k, False: 1.94k]
  ------------------
 9650|  1.18k|            path.erase(last_delimiter);
 9651|  1.18k|          }
 9652|  10.7k|        } else if (path_view != ".") {
  ------------------
  |  Branch (9652:20): [True: 8.67k, False: 2.03k]
  ------------------
 9653|  8.67k|          path += '/';
 9654|  8.67k|          path.append(path_view);
 9655|  8.67k|        }
 9656|  13.8k|      }
 9657|  14.9k|    } while (true);
  ------------------
  |  Branch (9657:14): [True: 13.8k, Folded]
  ------------------
 9658|  4.52k|  } else {
 9659|  4.52k|    ada_log("parse_path slow");
 9660|       |    // we have reached the general case
 9661|  4.52k|    bool needs_percent_encoding = (accumulator & 1);
 9662|  4.52k|    std::string path_buffer_tmp;
 9663|  30.0k|    do {
 9664|  30.0k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (9664:26): [True: 23.1k, False: 6.97k]
  |  Branch (9664:37): [True: 1.96k, False: 21.1k]
  ------------------
 9665|  30.0k|                            ? input.find_first_of("/\\")
 9666|  30.0k|                            : input.find('/');
 9667|  30.0k|      std::string_view path_view = input;
 9668|  30.0k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (9668:11): [True: 25.5k, False: 4.52k]
  ------------------
 9669|  25.5k|        path_view.remove_suffix(path_view.size() - location);
 9670|  25.5k|        input.remove_prefix(location + 1);
 9671|  25.5k|      }
 9672|       |      // path_buffer is either path_view or it might point at a percent encoded
 9673|       |      // temporary file.
 9674|  30.0k|      std::string_view path_buffer =
 9675|  30.0k|          (needs_percent_encoding &&
  ------------------
  |  Branch (9675:12): [True: 16.3k, False: 13.7k]
  ------------------
 9676|  16.3k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (9676:12): [True: 5.89k, False: 10.4k]
  ------------------
 9677|  16.3k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
 9678|  30.0k|              ? path_buffer_tmp
 9679|  30.0k|              : path_view;
 9680|  30.0k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (9680:11): [True: 4.06k, False: 26.0k]
  ------------------
 9681|  4.06k|        helpers::shorten_path(path, type);
 9682|  4.06k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9682:13): [True: 255, False: 3.81k]
  ------------------
 9683|    255|          path += '/';
 9684|    255|        }
 9685|  26.0k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (9685:18): [True: 1.44k, False: 24.5k]
  ------------------
 9686|  1.44k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (9686:18): [True: 133, False: 1.31k]
  ------------------
 9687|    133|        path += '/';
 9688|    133|      }
 9689|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
 9690|  25.8k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (9690:16): [True: 24.5k, False: 1.31k]
  ------------------
 9691|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
 9692|       |        // Windows drive letter, then replace the second code point in
 9693|       |        // path_buffer with U+003A (:).
 9694|  24.5k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (9694:13): [True: 2.37k, False: 22.2k]
  |  Branch (9694:48): [True: 1.03k, False: 1.33k]
  ------------------
 9695|  1.03k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (9695:13): [True: 157, False: 877]
  ------------------
 9696|    157|          path += '/';
 9697|    157|          path += path_buffer[0];
 9698|    157|          path += ':';
 9699|    157|          path_buffer.remove_prefix(2);
 9700|    157|          path.append(path_buffer);
 9701|  24.4k|        } else {
 9702|       |          // Append path_buffer to url's path.
 9703|  24.4k|          path += '/';
 9704|  24.4k|          path.append(path_buffer);
 9705|  24.4k|        }
 9706|  24.5k|      }
 9707|  30.0k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (9707:11): [True: 4.52k, False: 25.5k]
  ------------------
 9708|  4.52k|        return;
 9709|  4.52k|      }
 9710|  30.0k|    } while (true);
  ------------------
  |  Branch (9710:14): [True: 25.5k, Folded]
  ------------------
 9711|  4.52k|  }
 9712|  5.67k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   77|  21.4k|    std::string_view input) noexcept {
   78|       |  // The path percent-encode set is the query percent-encode set and U+003F (?),
   79|       |  // U+0060 (`), U+007B ({), and U+007D (}). The query percent-encode set is the
   80|       |  // C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+0023 (#),
   81|       |  // U+003C (<), and U+003E (>). The C0 control percent-encode set are the C0
   82|       |  // controls and all code points greater than U+007E (~).
   83|  21.4k|  size_t i = 0;
   84|  21.4k|  uint8_t accumulator{};
   85|  72.2k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (85:10): [True: 50.8k, False: 21.4k]
  ------------------
   86|  50.8k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   87|  50.8k|                           path_signature_table[uint8_t(input[i + 1])] |
   88|  50.8k|                           path_signature_table[uint8_t(input[i + 2])] |
   89|  50.8k|                           path_signature_table[uint8_t(input[i + 3])] |
   90|  50.8k|                           path_signature_table[uint8_t(input[i + 4])] |
   91|  50.8k|                           path_signature_table[uint8_t(input[i + 5])] |
   92|  50.8k|                           path_signature_table[uint8_t(input[i + 6])] |
   93|  50.8k|                           path_signature_table[uint8_t(input[i + 7])]);
   94|  50.8k|  }
   95|  58.5k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (95:10): [True: 37.0k, False: 21.4k]
  ------------------
   96|  37.0k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
   97|  37.0k|  }
   98|  21.4k|  return accumulator;
   99|  21.4k|}
_ZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7384|  60.2k|    std::string_view input) noexcept {
 7385|       |  // This will catch most cases:
 7386|       |  // The length must be 2,4 or 6.
 7387|       |  // We divide by two and require
 7388|       |  // that the result be between 1 and 3 inclusively.
 7389|  60.2k|  uint64_t half_length = uint64_t(input.size()) / 2;
 7390|  60.2k|  if (half_length - 1 > 2) {
  ------------------
  |  Branch (7390:7): [True: 31.6k, False: 28.5k]
  ------------------
 7391|  31.6k|    return false;
 7392|  31.6k|  }
 7393|       |  // We have a string of length 2, 4 or 6.
 7394|       |  // We now check the first character:
 7395|  28.5k|  if ((input[0] != '.') && (input[0] != '%')) {
  ------------------
  |  Branch (7395:7): [True: 15.9k, False: 12.6k]
  |  Branch (7395:28): [True: 4.35k, False: 11.6k]
  ------------------
 7396|  4.35k|    return false;
 7397|  4.35k|  }
 7398|       |  // We are unlikely the get beyond this point.
 7399|  24.2k|  int hash_value = (input.size() + (unsigned)(input[0])) & 3;
 7400|  24.2k|  const std::string_view target = table_is_double_dot_path_segment[hash_value];
 7401|  24.2k|  if (target.size() != input.size()) {
  ------------------
  |  Branch (7401:7): [True: 10.5k, False: 13.7k]
  ------------------
 7402|  10.5k|    return false;
 7403|  10.5k|  }
 7404|       |  // We almost never get here.
 7405|       |  // Optimizing the rest is relatively unimportant.
 7406|  13.7k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7407|  13.7k|    uint16_t A, B;
 7408|  13.7k|    memcpy(&A, a.data(), sizeof(A));
 7409|  13.7k|    memcpy(&B, b.data(), sizeof(B));
 7410|  13.7k|    return A == B;
 7411|  13.7k|  };
 7412|  13.7k|  if (!prefix_equal_unsafe(input, target)) {
  ------------------
  |  Branch (7412:7): [True: 2.51k, False: 11.2k]
  ------------------
 7413|  2.51k|    return false;
 7414|  2.51k|  }
 7415|  16.1k|  for (size_t i = 2; i < input.size(); i++) {
  ------------------
  |  Branch (7415:22): [True: 8.04k, False: 8.13k]
  ------------------
 7416|  8.04k|    char c = input[i];
 7417|  8.04k|    if ((uint8_t((c | 0x20) - 0x61) <= 25 ? (c | 0x20) : c) != target[i]) {
  ------------------
  |  Branch (7417:9): [True: 3.07k, False: 4.97k]
  |  Branch (7417:10): [True: 2.33k, False: 5.71k]
  ------------------
 7418|  3.07k|      return false;
 7419|  3.07k|    }
 7420|  8.04k|  }
 7421|  8.13k|  return true;
 7422|       |  // The above code might be a bit better than the code below. Compilers
 7423|       |  // are not stupid and may use the fact that these strings have length 2,4 and
 7424|       |  // 6 and other tricks.
 7425|       |  // return input == ".." ||
 7426|       |  //  input == ".%2e" || input == ".%2E" ||
 7427|       |  //  input == "%2e." || input == "%2E." ||
 7428|       |  //  input == "%2e%2e" || input == "%2E%2E" || input == "%2E%2e" || input ==
 7429|       |  //  "%2e%2E";
 7430|  11.2k|}
_ZZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_S5_E_clES5_S5_:
 7406|  13.7k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7407|  13.7k|    uint16_t A, B;
 7408|  13.7k|    memcpy(&A, a.data(), sizeof(A));
 7409|  13.7k|    memcpy(&B, b.data(), sizeof(B));
 7410|  13.7k|    return A == B;
 7411|  13.7k|  };
_ZN3ada7unicode26is_single_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7433|   103k|    std::string_view input) noexcept {
 7434|   103k|  return input == "." || input == "%2e" || input == "%2E";
  ------------------
  |  Branch (7434:10): [True: 4.03k, False: 99.8k]
  |  Branch (7434:26): [True: 1.48k, False: 98.4k]
  |  Branch (7434:44): [True: 0, False: 98.4k]
  ------------------
 7435|   103k|}
_ZN3ada7unicode28is_forbidden_host_code_pointEc:
 7254|  69.4k|    const char c) noexcept {
 7255|  69.4k|  return is_forbidden_host_code_point_table[uint8_t(c)];
 7256|  69.4k|}
_ZN3ada6detail17parse_ipv4_numberERPKcS2_RmRb:
 9836|  7.21k|                              bool& is_pure_decimal) noexcept {
 9837|  7.21k|  is_pure_decimal = false;
 9838|  7.21k|  if (p >= end) [[unlikely]] {
  ------------------
  |  Branch (9838:7): [True: 0, False: 7.21k]
  ------------------
 9839|      0|    return false;
 9840|      0|  }
 9841|  7.21k|  if (end - p >= 2 && p[0] == '0' && ((p[1] | 0x20) == 'x')) {
  ------------------
  |  Branch (9841:7): [True: 4.50k, False: 2.70k]
  |  Branch (9841:23): [True: 2.23k, False: 2.26k]
  |  Branch (9841:38): [True: 1.40k, False: 837]
  ------------------
 9842|  1.40k|    p += 2;
 9843|  1.40k|    if (p == end || *p == '.') {
  ------------------
  |  Branch (9843:9): [True: 264, False: 1.13k]
  |  Branch (9843:21): [True: 48, False: 1.08k]
  ------------------
 9844|    312|      value = 0;
 9845|    312|      return true;
 9846|    312|    }
 9847|  1.08k|    uint64_t v = 0;
 9848|  1.08k|    int digits = 0;
 9849|  7.01k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9849:12): [True: 6.29k, False: 714]
  |  Branch (9849:23): [True: 6.20k, False: 96]
  ------------------
 9850|  6.20k|      const uint8_t nib = hex_nibble[static_cast<unsigned char>(*p)];
 9851|  6.20k|      if (nib == 0xff) [[unlikely]] {
  ------------------
  |  Branch (9851:11): [True: 24, False: 6.17k]
  ------------------
 9852|     24|        return false;
 9853|     24|      }
 9854|  6.17k|      if (v > (0xFFFFFFFFULL >> 4)) [[unlikely]] {
  ------------------
  |  Branch (9854:11): [True: 255, False: 5.92k]
  ------------------
 9855|    255|        return false;
 9856|    255|      }
 9857|  5.92k|      v = (v << 4) | nib;
 9858|  5.92k|      ++p;
 9859|  5.92k|      ++digits;
 9860|  5.92k|    }
 9861|    810|    if (digits == 0) [[unlikely]] {
  ------------------
  |  Branch (9861:9): [True: 0, False: 810]
  ------------------
 9862|      0|      return false;
 9863|      0|    }
 9864|    810|    value = v;
 9865|    810|    return true;
 9866|    810|  }
 9867|  5.81k|  if (end - p >= 2 && p[0] == '0' && p[1] >= '0' && p[1] <= '9') {
  ------------------
  |  Branch (9867:7): [True: 3.10k, False: 2.70k]
  |  Branch (9867:23): [True: 837, False: 2.26k]
  |  Branch (9867:38): [True: 477, False: 360]
  |  Branch (9867:53): [True: 453, False: 24]
  ------------------
 9868|    453|    ++p;
 9869|    453|    uint64_t v = 0;
 9870|  3.30k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9870:12): [True: 3.04k, False: 258]
  |  Branch (9870:23): [True: 2.92k, False: 120]
  ------------------
 9871|  2.92k|      const char c = *p;
 9872|  2.92k|      if (c < '0' || c > '7') [[unlikely]] {
  ------------------
  |  Branch (9872:11): [True: 12, False: 2.91k]
  |  Branch (9872:22): [True: 45, False: 2.87k]
  ------------------
 9873|     57|        return false;
 9874|     57|      }
 9875|  2.87k|      if (v > (0xFFFFFFFFULL >> 3)) [[unlikely]] {
  ------------------
  |  Branch (9875:11): [True: 18, False: 2.85k]
  ------------------
 9876|     18|        return false;
 9877|     18|      }
 9878|  2.85k|      v = (v << 3) | static_cast<uint64_t>(c - '0');
 9879|  2.85k|      ++p;
 9880|  2.85k|    }
 9881|    378|    value = v;
 9882|    378|    return true;
 9883|    453|  }
 9884|  5.35k|  if (*p < '0' || *p > '9') [[unlikely]] {
  ------------------
  |  Branch (9884:7): [True: 144, False: 5.21k]
  |  Branch (9884:19): [True: 294, False: 4.92k]
  ------------------
 9885|    438|    return false;
 9886|    438|  }
 9887|  4.92k|  is_pure_decimal = true;
 9888|  4.92k|  uint64_t v = static_cast<uint64_t>(*p - '0');
 9889|  4.92k|  ++p;
 9890|  9.63k|  while (p < end && *p != '.') {
  ------------------
  |  Branch (9890:10): [True: 6.17k, False: 3.46k]
  |  Branch (9890:21): [True: 4.92k, False: 1.24k]
  ------------------
 9891|  4.92k|    const char c = *p;
 9892|  4.92k|    if (c < '0' || c > '9') [[unlikely]] {
  ------------------
  |  Branch (9892:9): [True: 27, False: 4.89k]
  |  Branch (9892:20): [True: 69, False: 4.82k]
  ------------------
 9893|     96|      return false;
 9894|     96|    }
 9895|  4.82k|    if (v > 429496729ULL) [[unlikely]] {
  ------------------
  |  Branch (9895:9): [True: 99, False: 4.72k]
  ------------------
 9896|     99|      return false;
 9897|     99|    }
 9898|  4.72k|    v = v * 10u + static_cast<uint64_t>(c - '0');
 9899|  4.72k|    if (v > 0xFFFFFFFFULL) [[unlikely]] {
  ------------------
  |  Branch (9899:9): [True: 12, False: 4.71k]
  ------------------
 9900|     12|      return false;
 9901|     12|    }
 9902|  4.71k|    ++p;
 9903|  4.71k|  }
 9904|  4.71k|  value = v;
 9905|  4.71k|  return true;
 9906|  4.92k|}
_ZN3ada6detail15parse_hex_pieceERPKcS2_Rt:
 9910|  2.65k|                           uint16_t& value) noexcept {
 9911|  2.65k|  if (pointer == end) {
  ------------------
  |  Branch (9911:7): [True: 0, False: 2.65k]
  ------------------
 9912|      0|    return 0;
 9913|      0|  }
 9914|  2.65k|  const uint8_t n0 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9915|  2.65k|  if (n0 == 0xff) {
  ------------------
  |  Branch (9915:7): [True: 150, False: 2.50k]
  ------------------
 9916|    150|    return 0;
 9917|    150|  }
 9918|  2.50k|  uint32_t v = n0;
 9919|  2.50k|  ++pointer;
 9920|  2.50k|  int length = 1;
 9921|  2.50k|  if (pointer != end) {
  ------------------
  |  Branch (9921:7): [True: 2.25k, False: 252]
  ------------------
 9922|  2.25k|    const uint8_t n1 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9923|  2.25k|    if (n1 != 0xff) {
  ------------------
  |  Branch (9923:9): [True: 804, False: 1.44k]
  ------------------
 9924|    804|      v = (v << 4) | n1;
 9925|    804|      ++pointer;
 9926|    804|      ++length;
 9927|    804|      if (pointer != end) {
  ------------------
  |  Branch (9927:11): [True: 744, False: 60]
  ------------------
 9928|    744|        const uint8_t n2 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9929|    744|        if (n2 != 0xff) {
  ------------------
  |  Branch (9929:13): [True: 501, False: 243]
  ------------------
 9930|    501|          v = (v << 4) | n2;
 9931|    501|          ++pointer;
 9932|    501|          ++length;
 9933|    501|          if (pointer != end) {
  ------------------
  |  Branch (9933:15): [True: 426, False: 75]
  ------------------
 9934|    426|            const uint8_t n3 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9935|    426|            if (n3 != 0xff) {
  ------------------
  |  Branch (9935:17): [True: 219, False: 207]
  ------------------
 9936|    219|              v = (v << 4) | n3;
 9937|    219|              ++pointer;
 9938|    219|              ++length;
 9939|    219|            }
 9940|    426|          }
 9941|    501|        }
 9942|    744|      }
 9943|    804|    }
 9944|  2.25k|  }
 9945|  2.50k|  value = static_cast<uint16_t>(v);
 9946|  2.50k|  return length;
 9947|  2.65k|}
_ZN3ada3url10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10522|  2.35k|ada_really_inline void url::parse_path(std::string_view input) {
10523|  2.35k|  ada_log("parse_path ", input);
10524|  2.35k|  std::string tmp_buffer;
10525|  2.35k|  std::string_view internal_input;
10526|  2.35k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (10526:7): [True: 0, False: 2.35k]
  ------------------
10527|      0|    tmp_buffer = input;
10528|       |    // Optimization opportunity: Instead of copying and then pruning, we could
10529|       |    // just directly build the string from user_input.
10530|      0|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
10531|      0|    internal_input = tmp_buffer;
10532|  2.35k|  } else {
10533|  2.35k|    internal_input = input;
10534|  2.35k|  }
10535|       |
10536|       |  // If url is special, then:
10537|  2.35k|  if (is_special()) {
  ------------------
  |  Branch (10537:7): [True: 2.35k, False: 0]
  ------------------
10538|  2.35k|    if (internal_input.empty()) {
  ------------------
  |  Branch (10538:9): [True: 140, False: 2.21k]
  ------------------
10539|    140|      path = "/";
10540|  2.21k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (10540:16): [True: 2.21k, False: 0]
  |  Branch (10540:46): [True: 0, False: 0]
  ------------------
10541|  2.21k|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
10542|  2.21k|    } else {
10543|      0|      helpers::parse_prepared_path(internal_input, type, path);
10544|      0|    }
10545|  2.35k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (10545:14): [True: 0, False: 0]
  ------------------
10546|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (10546:9): [True: 0, False: 0]
  ------------------
10547|      0|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
10548|      0|    } else {
10549|      0|      helpers::parse_prepared_path(internal_input, type, path);
10550|      0|    }
10551|      0|  } else {
10552|      0|    if (!host.has_value()) {
  ------------------
  |  Branch (10552:9): [True: 0, False: 0]
  ------------------
10553|      0|      path = "/";
10554|      0|    }
10555|      0|  }
10556|  2.35k|}
_ZN3ada7unicode13is_alnum_plusEc:
 7348|   184k|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
 7349|   184k|  return is_alnum_plus_table[uint8_t(c)];
 7350|       |  // A table is almost surely much faster than the
 7351|       |  // following under most compilers: return
 7352|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
 7353|   184k|}
_ZN3ada7unicode14to_lower_asciiEPcm:
 7024|  40.9k|constexpr bool to_lower_ascii(char* input, size_t length) noexcept {
 7025|  40.9k|  uint64_t broadcast_80 = broadcast(0x80);
 7026|  40.9k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 7027|  40.9k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 7028|  40.9k|  uint64_t non_ascii = 0;
 7029|  40.9k|  size_t i = 0;
 7030|       |
 7031|  50.4k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (7031:10): [True: 9.55k, False: 40.9k]
  ------------------
 7032|  9.55k|    uint64_t word{};
 7033|  9.55k|    memcpy(&word, input + i, sizeof(word));
 7034|  9.55k|    non_ascii |= (word & broadcast_80);
 7035|  9.55k|    word ^=
 7036|  9.55k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 7037|  9.55k|    memcpy(input + i, &word, sizeof(word));
 7038|  9.55k|  }
 7039|  40.9k|  if (i < length) {
  ------------------
  |  Branch (7039:7): [True: 39.9k, False: 913]
  ------------------
 7040|  39.9k|    uint64_t word{};
 7041|  39.9k|    memcpy(&word, input + i, length - i);
 7042|  39.9k|    non_ascii |= (word & broadcast_80);
 7043|  39.9k|    word ^=
 7044|  39.9k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 7045|  39.9k|    memcpy(input + i, &word, length - i);
 7046|  39.9k|  }
 7047|  40.9k|  return non_ascii == 0;
 7048|  40.9k|}
_ZN3ada7unicode9broadcastEh:
 7020|   122k|constexpr uint64_t broadcast(uint8_t v) noexcept {
 7021|   122k|  return 0x101010101010101ull * v;
 7022|   122k|}
_ZZN3ada6parser25try_parse_simple_absoluteINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_ENKUlvE_clEv:
12545|  1.21k|    auto apply_query_and_hash = [&]() {
12546|  1.21k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12546:11): [True: 99, False: 1.11k]
  ------------------
12547|     99|        const size_t q_end =
12548|     99|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12548:13): [True: 31, False: 68]
  ------------------
12549|     99|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12550|     99|                                                q_end - query_start - 1),
12551|     99|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12552|     99|      }
12553|  1.21k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12553:11): [True: 99, False: 1.11k]
  ------------------
12554|     99|        out.update_unencoded_base_hash(std::string_view(
12555|     99|            input.data() + hash_start + 1, len - hash_start - 1));
12556|     99|      }
12557|  1.21k|    };
_ZZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_ENKUlvE_clEv:
12545|  1.21k|    auto apply_query_and_hash = [&]() {
12546|  1.21k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12546:11): [True: 99, False: 1.11k]
  ------------------
12547|     99|        const size_t q_end =
12548|     99|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12548:13): [True: 31, False: 68]
  ------------------
12549|     99|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12550|     99|                                                q_end - query_start - 1),
12551|     99|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12552|     99|      }
12553|  1.21k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12553:11): [True: 99, False: 1.11k]
  ------------------
12554|     99|        out.update_unencoded_base_hash(std::string_view(
12555|     99|            input.data() + hash_start + 1, len - hash_start - 1));
12556|     99|      }
12557|  1.21k|    };
_ZZN3ada6parser32finish_simple_absolute_with_portINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmbENKUlvE_clEv:
12145|  1.13k|    auto apply_query_and_hash = [&]() {
12146|  1.13k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12146:11): [True: 111, False: 1.02k]
  ------------------
12147|    111|        const size_t q_end =
12148|    111|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12148:13): [True: 26, False: 85]
  ------------------
12149|    111|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12150|    111|                                                q_end - query_start - 1),
12151|    111|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12152|    111|      }
12153|  1.13k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12153:11): [True: 95, False: 1.04k]
  ------------------
12154|     95|        out.update_unencoded_base_hash(std::string_view(
12155|     95|            input.data() + hash_start + 1, len - hash_start - 1));
12156|     95|      }
12157|  1.13k|    };
_ZZN3ada6parser32finish_simple_absolute_with_portINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmbENKUlvE_clEv:
12145|  1.13k|    auto apply_query_and_hash = [&]() {
12146|  1.13k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12146:11): [True: 111, False: 1.02k]
  ------------------
12147|    111|        const size_t q_end =
12148|    111|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12148:13): [True: 26, False: 85]
  ------------------
12149|    111|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12150|    111|                                                q_end - query_start - 1),
12151|    111|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12152|    111|      }
12153|  1.13k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12153:11): [True: 95, False: 1.04k]
  ------------------
12154|     95|        out.update_unencoded_base_hash(std::string_view(
12155|     95|            input.data() + hash_start + 1, len - hash_start - 1));
12156|     95|      }
12157|  1.13k|    };
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8784|   123k|    std::string_view& input) noexcept {
 8785|       |  // compiles down to 20--30 instructions including a class to memchr (C
 8786|       |  // function). this function should be quite fast.
 8787|   123k|  size_t location_of_first = input.find('#');
 8788|   123k|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (8788:7): [True: 120k, False: 3.82k]
  ------------------
 8789|   120k|    return std::nullopt;
 8790|   120k|  }
 8791|  3.82k|  std::string_view hash = input;
 8792|  3.82k|  hash.remove_prefix(location_of_first + 1);
 8793|  3.82k|  input.remove_suffix(input.size() - location_of_first);
 8794|  3.82k|  return hash;
 8795|   123k|}
_ZZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12868|  19.6k|  const auto enforce_max_input_length = [&]() {
12869|  19.6k|    if constexpr (store_values) {
12870|  19.6k|      if (url.is_valid) {
  ------------------
  |  Branch (12870:11): [True: 19.6k, False: 0]
  ------------------
12871|       |        if constexpr (result_type_is_ada_url_aggregator) {
12872|       |          if (url.buffer.size() > max_input_length) [[unlikely]] {
12873|       |            url.is_valid = false;
12874|       |          }
12875|  19.6k|        } else {
12876|  19.6k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12876:15): [True: 0, False: 19.6k]
  ------------------
12877|      0|            url.is_valid = false;
12878|      0|          }
12879|  19.6k|        }
12880|  19.6k|      }
12881|  19.6k|    }
12882|  19.6k|  };
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9747|  22.1k|find_authority_delimiter_special(std::string_view view) noexcept {
 9748|       |  // performance note: we might be able to gain further performance
 9749|       |  // with SIMD intrinsics.
 9750|   139k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9750:33): [True: 137k, False: 2.09k]
  ------------------
 9751|   137k|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (9751:9): [True: 20.0k, False: 117k]
  ------------------
 9752|  20.0k|      return pos - view.begin();
 9753|  20.0k|    }
 9754|   137k|  }
 9755|  2.09k|  return size_t(view.size());
 9756|  22.1k|}
_ZN3ada7helpers24find_authority_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9768|  2.59k|find_authority_delimiter(std::string_view view) noexcept {
 9769|       |  // performance note: we might be able to gain further performance
 9770|       |  // with SIMD instrinsics.
 9771|  40.0k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9771:33): [True: 39.6k, False: 369]
  ------------------
 9772|  39.6k|    if (authority_delimiter[(uint8_t)*pos]) {
  ------------------
  |  Branch (9772:9): [True: 2.22k, False: 37.4k]
  ------------------
 9773|  2.22k|      return pos - view.begin();
 9774|  2.22k|    }
 9775|  39.6k|  }
 9776|    369|  return size_t(view.size());
 9777|  2.59k|}
_ZN3ada7helpers12shorten_pathERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_6scheme4typeE:
 8797|  8.75k|ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) {
 8798|       |  // Let path be url's path.
 8799|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
 8800|       |  // Windows drive letter, then return.
 8801|  8.75k|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8801:7): [True: 3.26k, False: 5.48k]
  ------------------
 8802|  3.26k|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (8802:7): [True: 2.45k, False: 803]
  |  Branch (8802:54): [True: 1.71k, False: 746]
  ------------------
 8803|  1.71k|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (8803:9): [True: 740, False: 972]
  ------------------
 8804|  1.71k|            helpers::substring(path, 1))) {
 8805|    740|      return false;
 8806|    740|    }
 8807|  1.71k|  }
 8808|       |
 8809|       |  // Remove path's last item, if any.
 8810|  8.01k|  size_t last_delimiter = path.rfind('/');
 8811|  8.01k|  if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (8811:7): [True: 4.09k, False: 3.91k]
  ------------------
 8812|  4.09k|    path.erase(last_delimiter);
 8813|  4.09k|    return true;
 8814|  4.09k|  }
 8815|       |
 8816|  3.91k|  return false;
 8817|  8.01k|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9460|  42.1k|    const bool is_special, std::string_view& view) noexcept {
 9461|       |  /**
 9462|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
 9463|       |   * compute a variable called insideBrackets but this variable is only used
 9464|       |   * once, to check whether a ':' character was found outside brackets. Exact
 9465|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
 9466|       |   * It is conceptually simpler and arguably more efficient to just return a
 9467|       |   * Boolean indicating whether ':' was found outside brackets.
 9468|       |   */
 9469|  42.1k|  const size_t view_size = view.size();
 9470|  42.1k|  size_t location = 0;
 9471|  42.1k|  bool found_colon = false;
 9472|       |  /**
 9473|       |   * Performance analysis:
 9474|       |   *
 9475|       |   * We are basically seeking the end of the hostname which can be indicated
 9476|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
 9477|       |   * (where '\\' is only applicable for special URLs). However, these must
 9478|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
 9479|       |   * '?' does not count.
 9480|       |   *
 9481|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
 9482|       |   * the set of delimiters, and that we handle it first.
 9483|       |   *
 9484|       |   * So the trick is to have a fast function that locates the next delimiter.
 9485|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
 9486|       |   * function would be provided by the C++ standard library, but it seems that
 9487|       |   * find_first_of is not very fast, so we are forced to roll our own.
 9488|       |   *
 9489|       |   * We do not break into two loops for speed, but for clarity.
 9490|       |   */
 9491|  42.1k|  if (is_special) {
  ------------------
  |  Branch (9491:7): [True: 39.4k, False: 2.66k]
  ------------------
 9492|       |    // We move to the next delimiter.
 9493|  39.4k|    location = find_next_host_delimiter_special(view, location);
 9494|       |    // Unless we find '[' then we are going only going to have to call
 9495|       |    // find_next_host_delimiter_special once.
 9496|  42.8k|    for (; location < view_size;
  ------------------
  |  Branch (9496:12): [True: 22.7k, False: 20.1k]
  ------------------
 9497|  39.4k|         location = find_next_host_delimiter_special(view, location)) {
 9498|  22.7k|      if (view[location] == '[') {
  ------------------
  |  Branch (9498:11): [True: 3.58k, False: 19.1k]
  ------------------
 9499|  3.58k|        location = view.find(']', location);
 9500|  3.58k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9500:13): [True: 180, False: 3.40k]
  ------------------
 9501|       |          // performance: view.find might get translated to a memchr, which
 9502|       |          // has no notion of std::string_view::npos, so the code does not
 9503|       |          // reflect the assembly.
 9504|    180|          location = view_size;
 9505|    180|          break;
 9506|    180|        }
 9507|  19.1k|      } else {
 9508|  19.1k|        found_colon = view[location] == ':';
 9509|  19.1k|        break;
 9510|  19.1k|      }
 9511|  22.7k|    }
 9512|  39.4k|  } else {
 9513|       |    // We move to the next delimiter.
 9514|  2.66k|    location = find_next_host_delimiter(view, location);
 9515|       |    // Unless we find '[' then we are going only going to have to call
 9516|       |    // find_next_host_delimiter_special once.
 9517|  4.37k|    for (; location < view_size;
  ------------------
  |  Branch (9517:12): [True: 2.60k, False: 1.77k]
  ------------------
 9518|  2.66k|         location = find_next_host_delimiter(view, location)) {
 9519|  2.60k|      if (view[location] == '[') {
  ------------------
  |  Branch (9519:11): [True: 1.77k, False: 831]
  ------------------
 9520|  1.77k|        location = view.find(']', location);
 9521|  1.77k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9521:13): [True: 60, False: 1.71k]
  ------------------
 9522|       |          // performance: view.find might get translated to a memchr, which
 9523|       |          // has no notion of std::string_view::npos, so the code does not
 9524|       |          // reflect the assembly.
 9525|     60|          location = view_size;
 9526|     60|          break;
 9527|     60|        }
 9528|  1.77k|      } else {
 9529|    831|        found_colon = view[location] == ':';
 9530|    831|        break;
 9531|    831|      }
 9532|  2.60k|    }
 9533|  2.66k|  }
 9534|       |  // performance: remove_suffix may translate into a single instruction.
 9535|  42.1k|  view.remove_suffix(view_size - location);
 9536|  42.1k|  return {location, found_colon};
 9537|  42.1k|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 9050|  42.8k|    std::string_view view, size_t location) noexcept {
 9051|       |  // first check for short strings in which case we do it naively.
 9052|  42.8k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (9052:7): [True: 29.2k, False: 13.6k]
  ------------------
 9053|   153k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (9053:31): [True: 138k, False: 15.0k]
  ------------------
 9054|   138k|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (9054:11): [True: 5.29k, False: 133k]
  |  Branch (9054:29): [True: 6.49k, False: 126k]
  |  Branch (9054:47): [True: 35, False: 126k]
  ------------------
 9055|   126k|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (9055:11): [True: 569, False: 126k]
  |  Branch (9055:29): [True: 1.71k, False: 124k]
  ------------------
 9056|  14.1k|        return i;
 9057|  14.1k|      }
 9058|   138k|    }
 9059|  15.0k|    return size_t(view.size());
 9060|  29.2k|  }
 9061|       |  // fast path for long strings (expected to be common)
 9062|  13.6k|  size_t i = location;
 9063|  13.6k|  const __m128i mask1 = _mm_set1_epi8(':');
 9064|  13.6k|  const __m128i mask2 = _mm_set1_epi8('/');
 9065|  13.6k|  const __m128i mask3 = _mm_set1_epi8('\\');
 9066|  13.6k|  const __m128i mask4 = _mm_set1_epi8('?');
 9067|  13.6k|  const __m128i mask5 = _mm_set1_epi8('[');
 9068|       |
 9069|  52.6k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (9069:10): [True: 44.5k, False: 8.07k]
  ------------------
 9070|  44.5k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 9071|  44.5k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9072|  44.5k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9073|  44.5k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 9074|  44.5k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9075|  44.5k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9076|  44.5k|    __m128i m = _mm_or_si128(
 9077|  44.5k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 9078|  44.5k|    int mask = _mm_movemask_epi8(m);
 9079|  44.5k|    if (mask != 0) {
  ------------------
  |  Branch (9079:9): [True: 5.57k, False: 39.0k]
  ------------------
 9080|  5.57k|      return i + trailing_zeroes(mask);
 9081|  5.57k|    }
 9082|  44.5k|  }
 9083|  8.07k|  if (i < view.size()) {
  ------------------
  |  Branch (9083:7): [True: 7.45k, False: 621]
  ------------------
 9084|  7.45k|    __m128i word =
 9085|  7.45k|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 9086|  7.45k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9087|  7.45k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9088|  7.45k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 9089|  7.45k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9090|  7.45k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9091|  7.45k|    __m128i m = _mm_or_si128(
 9092|  7.45k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 9093|  7.45k|    int mask = _mm_movemask_epi8(m);
 9094|  7.45k|    if (mask != 0) {
  ------------------
  |  Branch (9094:9): [True: 3.05k, False: 4.40k]
  ------------------
 9095|  3.05k|      return view.length() - 16 + trailing_zeroes(mask);
 9096|  3.05k|    }
 9097|  7.45k|  }
 9098|  5.02k|  return size_t(view.length());
 9099|  8.07k|}
_ZN3ada7helpers15trailing_zeroesEj:
 8870|  10.0k|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
 8871|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 8872|       |  unsigned long ret;
 8873|       |  // Search the mask data from least significant bit (LSB)
 8874|       |  // to the most significant bit (MSB) for a set bit (1).
 8875|       |  _BitScanForward(&ret, input_num);
 8876|       |  return (int)ret;
 8877|       |#else   // ADA_REGULAR_VISUAL_STUDIO
 8878|  10.0k|  return __builtin_ctzl(input_num);
 8879|  10.0k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 8880|  10.0k|}
_ZN3ada7helpers24find_next_host_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 9326|  4.37k|                                                  size_t location) noexcept {
 9327|       |  // first check for short strings in which case we do it naively.
 9328|  4.37k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (9328:7): [True: 2.25k, False: 2.12k]
  ------------------
 9329|  6.09k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (9329:31): [True: 4.99k, False: 1.09k]
  ------------------
 9330|  4.99k|      if (view[i] == ':' || view[i] == '/' || view[i] == '?' ||
  ------------------
  |  Branch (9330:11): [True: 279, False: 4.71k]
  |  Branch (9330:29): [True: 177, False: 4.53k]
  |  Branch (9330:47): [True: 141, False: 4.39k]
  ------------------
 9331|  4.39k|          view[i] == '[') {
  ------------------
  |  Branch (9331:11): [True: 558, False: 3.83k]
  ------------------
 9332|  1.15k|        return i;
 9333|  1.15k|      }
 9334|  4.99k|    }
 9335|  1.09k|    return size_t(view.size());
 9336|  2.25k|  }
 9337|       |  // fast path for long strings (expected to be common)
 9338|  2.12k|  size_t i = location;
 9339|  2.12k|  const __m128i mask1 = _mm_set1_epi8(':');
 9340|  2.12k|  const __m128i mask2 = _mm_set1_epi8('/');
 9341|  2.12k|  const __m128i mask4 = _mm_set1_epi8('?');
 9342|  2.12k|  const __m128i mask5 = _mm_set1_epi8('[');
 9343|       |
 9344|  6.32k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (9344:10): [True: 5.55k, False: 768]
  ------------------
 9345|  5.55k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 9346|  5.55k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9347|  5.55k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9348|  5.55k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9349|  5.55k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9350|  5.55k|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 9351|  5.55k|    int mask = _mm_movemask_epi8(m);
 9352|  5.55k|    if (mask != 0) {
  ------------------
  |  Branch (9352:9): [True: 1.35k, False: 4.20k]
  ------------------
 9353|  1.35k|      return i + trailing_zeroes(mask);
 9354|  1.35k|    }
 9355|  5.55k|  }
 9356|    768|  if (i < view.size()) {
  ------------------
  |  Branch (9356:7): [True: 486, False: 282]
  ------------------
 9357|    486|    __m128i word =
 9358|    486|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 9359|    486|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9360|    486|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9361|    486|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9362|    486|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9363|    486|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 9364|    486|    int mask = _mm_movemask_epi8(m);
 9365|    486|    if (mask != 0) {
  ------------------
  |  Branch (9365:9): [True: 93, False: 393]
  ------------------
 9366|     93|      return view.length() - 16 + trailing_zeroes(mask);
 9367|     93|    }
 9368|    486|  }
 9369|    675|  return size_t(view.length());
 9370|    768|}
_ZN3ada3url10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10413|  15.7k|ada_really_inline bool url::parse_host(std::string_view input) {
10414|  15.7k|  ada_log("parse_host ", input, " [", input.size(), " bytes]");
10415|  15.7k|  if (input.empty()) {
  ------------------
  |  Branch (10415:7): [True: 25, False: 15.6k]
  ------------------
10416|     25|    return is_valid = false;
10417|     25|  }  // technically unnecessary.
10418|       |  // If input starts with U+005B ([), then:
10419|  15.6k|  if (input[0] == '[') {
  ------------------
  |  Branch (10419:7): [True: 698, False: 14.9k]
  ------------------
10420|       |    // If input does not end with U+005D (]), validation error, return failure.
10421|    698|    if (input.back() != ']') {
  ------------------
  |  Branch (10421:9): [True: 160, False: 538]
  ------------------
10422|    160|      return is_valid = false;
10423|    160|    }
10424|    538|    ada_log("parse_host ipv6");
10425|       |
10426|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
10427|       |    // trailing U+005D (]) removed.
10428|    538|    input.remove_prefix(1);
10429|    538|    input.remove_suffix(1);
10430|    538|    return parse_ipv6(input);
10431|    698|  }
10432|       |
10433|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
10434|       |  // input.
10435|  14.9k|  if (!is_special()) {
  ------------------
  |  Branch (10435:7): [True: 615, False: 14.3k]
  ------------------
10436|    615|    return parse_opaque_host(input);
10437|    615|  }
10438|       |
10439|       |  // Fast path: try to parse as pure decimal IPv4 first.
10440|  14.3k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
10441|  14.3k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (10441:7): [True: 953, False: 13.4k]
  ------------------
10442|    953|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (10442:9): [True: 953, False: 0]
  |  Branch (10442:27): [True: 22, False: 931]
  ------------------
10443|     22|      host = input.substr(0, input.size() - 1);
10444|    931|    } else {
10445|    931|      host = input;
10446|    931|    }
10447|    953|    host_type = IPV4;
10448|    953|    is_valid = true;
10449|    953|    ada_log("parse_host fast path decimal ipv4");
10450|    953|    return true;
10451|    953|  }
10452|       |  // Let domain be the result of running UTF-8 decode without BOM on the
10453|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
10454|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
10455|       |  // which case we do not need to call the expensive 'to_ascii' if a few
10456|       |  // conditions are met: no '%' and no 'xn-' subsequence.
10457|  13.4k|  const uint8_t forbidden_or_upper =
10458|  13.4k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
10459|  13.4k|                                                             input.size());
10460|  13.4k|  static constexpr std::string_view xn_dash{"xn-", 3};
10461|  13.4k|  if ((forbidden_or_upper & 1) == 0) {
  ------------------
  |  Branch (10461:7): [True: 8.01k, False: 5.40k]
  ------------------
10462|  8.01k|    host = std::string(input);
10463|  8.01k|    if ((forbidden_or_upper & 2) != 0) {
  ------------------
  |  Branch (10463:9): [True: 1.41k, False: 6.60k]
  ------------------
10464|  1.41k|      unicode::to_lower_ascii(host->data(), host->size());
10465|  1.41k|    }
10466|  8.01k|    if (host->find('-') == std::string_view::npos ||
  ------------------
  |  Branch (10466:9): [True: 5.72k, False: 2.28k]
  ------------------
10467|  6.06k|        host->find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (10467:9): [True: 332, False: 1.95k]
  ------------------
10468|  6.06k|      if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10468:11): [True: 1.50k, False: 4.55k]
  ------------------
10469|  1.50k|        ada_log("parse_host fast path ipv4");
10470|  1.50k|        return parse_ipv4(*host);
10471|  1.50k|      }
10472|  4.55k|      ada_log("parse_host fast path ", *host);
10473|  4.55k|      is_valid = true;
10474|  4.55k|      return true;
10475|  6.06k|    }
10476|  8.01k|  } else if (const size_t first_percent = input.find('%');
10477|  5.40k|             first_percent != std::string_view::npos) {
  ------------------
  |  Branch (10477:14): [True: 1.02k, False: 4.37k]
  ------------------
10478|  1.02k|    std::string decoded = unicode::percent_decode(input, first_percent);
10479|  1.02k|    const uint8_t decoded_flags =
10480|  1.02k|        unicode::contains_forbidden_domain_code_point_or_upper(decoded.data(),
10481|  1.02k|                                                               decoded.size());
10482|  1.02k|    if ((decoded_flags & 1) == 0) {
  ------------------
  |  Branch (10482:9): [True: 670, False: 359]
  ------------------
10483|    670|      if ((decoded_flags & 2) != 0) {
  ------------------
  |  Branch (10483:11): [True: 297, False: 373]
  ------------------
10484|    297|        unicode::to_lower_ascii(decoded.data(), decoded.size());
10485|    297|      }
10486|    670|      if (decoded.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (10486:11): [True: 572, False: 98]
  ------------------
10487|    623|          decoded.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (10487:11): [True: 51, False: 47]
  ------------------
10488|    623|        host = std::move(decoded);
10489|    623|        if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10489:13): [True: 266, False: 357]
  ------------------
10490|    266|          return parse_ipv4(*host);
10491|    266|        }
10492|    357|        is_valid = true;
10493|    357|        return true;
10494|    623|      }
10495|    670|    }
10496|  1.02k|  }
10497|  6.74k|  ada_log("parse_host calling to_ascii");
10498|  6.74k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
10499|  6.74k|  if (!is_valid || !host.has_value()) {
  ------------------
  |  Branch (10499:7): [True: 2.03k, False: 4.70k]
  |  Branch (10499:20): [True: 0, False: 4.70k]
  ------------------
10500|  2.03k|    ada_log("parse_host to_ascii returns false");
10501|  2.03k|    return is_valid = false;
10502|  2.03k|  }
10503|  4.70k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
10504|  4.70k|          " bytes]");
10505|       |
10506|  4.70k|  if (std::any_of(host->begin(), host->end(),
  ------------------
  |  Branch (10506:7): [True: 0, False: 4.70k]
  ------------------
10507|  4.70k|                  ada::unicode::is_forbidden_domain_code_point)) {
10508|      0|    host = std::nullopt;
10509|      0|    return is_valid = false;
10510|      0|  }
10511|       |
10512|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
10513|       |  // asciiDomain.
10514|  4.70k|  if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10514:7): [True: 194, False: 4.51k]
  ------------------
10515|    194|    ada_log("parse_host got ipv4 ", *host);
10516|    194|    return parse_ipv4(*host);
10517|    194|  }
10518|       |
10519|  4.51k|  return true;
10520|  4.70k|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
 7318|  43.9k|                                              size_t length) noexcept {
 7319|  43.9k|  size_t i = 0;
 7320|  43.9k|  uint8_t accumulator{};
 7321|   276k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7321:10): [True: 232k, False: 43.9k]
  ------------------
 7322|   232k|    accumulator |=
 7323|   232k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7324|   232k|    accumulator |=
 7325|   232k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
 7326|   232k|    accumulator |=
 7327|   232k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
 7328|   232k|    accumulator |=
 7329|   232k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
 7330|   232k|  }
 7331|   108k|  for (; i < length; i++) {
  ------------------
  |  Branch (7331:10): [True: 64.1k, False: 43.9k]
  ------------------
 7332|  64.1k|    accumulator |=
 7333|  64.1k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7334|  64.1k|  }
 7335|  43.9k|  return accumulator;
 7336|  43.9k|}
_ZN3ada7unicode30is_forbidden_domain_code_pointEc:
 7277|  1.05M|    const char c) noexcept {
 7278|  1.05M|  return is_forbidden_domain_code_point_table[uint8_t(c)];
 7279|  1.05M|}
_ZN3ada7helpers6resizeERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8863|     22|ada_really_inline void resize(std::string_view& input, size_t pos) noexcept {
 8864|     22|  ADA_ASSERT_TRUE(pos <= input.size());
 8865|     22|  input.remove_suffix(input.size() - pos);
 8866|     22|}
_ZZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12868|  19.6k|  const auto enforce_max_input_length = [&]() {
12869|  19.6k|    if constexpr (store_values) {
12870|  19.6k|      if (url.is_valid) {
  ------------------
  |  Branch (12870:11): [True: 19.6k, False: 0]
  ------------------
12871|  19.6k|        if constexpr (result_type_is_ada_url_aggregator) {
12872|  19.6k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12872:15): [True: 0, False: 19.6k]
  ------------------
12873|      0|            url.is_valid = false;
12874|      0|          }
12875|       |        } else {
12876|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
12877|       |            url.is_valid = false;
12878|       |          }
12879|       |        }
12880|  19.6k|      }
12881|  19.6k|    }
12882|  19.6k|  };
_ZN3ada7helpers12shorten_pathERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeE:
 8820|  1.41k|                                    ada::scheme::type type) {
 8821|       |  // Let path be url's path.
 8822|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
 8823|       |  // Windows drive letter, then return.
 8824|  1.41k|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8824:7): [True: 194, False: 1.22k]
  ------------------
 8825|    194|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (8825:7): [True: 175, False: 19]
  |  Branch (8825:54): [True: 78, False: 97]
  ------------------
 8826|     78|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (8826:9): [True: 4, False: 74]
  ------------------
 8827|     78|            helpers::substring(path, 1))) {
 8828|      4|      return false;
 8829|      4|    }
 8830|     78|  }
 8831|       |
 8832|       |  // Remove path's last item, if any.
 8833|  1.41k|  if (!path.empty()) {
  ------------------
  |  Branch (8833:7): [True: 605, False: 805]
  ------------------
 8834|    605|    size_t slash_loc = path.rfind('/');
 8835|    605|    if (slash_loc != std::string_view::npos) {
  ------------------
  |  Branch (8835:9): [True: 605, False: 0]
  ------------------
 8836|    605|      path.remove_suffix(path.size() - slash_loc);
 8837|    605|      return true;
 8838|    605|    }
 8839|    605|  }
 8840|       |
 8841|    805|  return false;
 8842|  1.41k|}
_ZZN3ada6parser14parse_url_implINS_14url_aggregatorELb0EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12868|    447|  const auto enforce_max_input_length = [&]() {
12869|       |    if constexpr (store_values) {
12870|       |      if (url.is_valid) {
12871|       |        if constexpr (result_type_is_ada_url_aggregator) {
12872|       |          if (url.buffer.size() > max_input_length) [[unlikely]] {
12873|       |            url.is_valid = false;
12874|       |          }
12875|       |        } else {
12876|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
12877|       |            url.is_valid = false;
12878|       |          }
12879|       |        }
12880|       |      }
12881|       |    }
12882|    447|  };
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_124port_decimal_digit_countEj:
11184|  2.32k|ada_really_inline uint32_t port_decimal_digit_count(uint32_t port) noexcept {
11185|  2.32k|  uint32_t digits = 1;
11186|  3.13k|  while (port >= 10) {
  ------------------
  |  Branch (11186:10): [True: 806, False: 2.32k]
  ------------------
11187|    806|    port /= 10;
11188|    806|    ++digits;
11189|    806|  }
11190|  2.32k|  return digits;
11191|  2.32k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_121append_canonical_portERNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEj:
11194|    670|                                             uint32_t port) {
11195|    670|  buffer += ':';
11196|    670|  char port_buf[5];
11197|    670|  auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, port);
11198|    670|  (void)ec;
11199|    670|  buffer.append(port_buf, static_cast<size_t>(ptr - port_buf));
11200|    670|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_116authority_has_atEPKhmm:
11342|  4.89k|                                        size_t n) noexcept {
11343|  4.89k|  const size_t rem = n - host_start;
11344|  4.89k|  if (rem == 0) {
  ------------------
  |  Branch (11344:7): [True: 0, False: 4.89k]
  ------------------
11345|      0|    return false;
11346|      0|  }
11347|       |#if ADA_NEON
11348|       |  if (rem >= 16) {
11349|       |    const uint8x16_t w = vld1q_u8(p + host_start);
11350|       |    const uint64_t at = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('@')));
11351|       |    if (at == 0) {
11352|       |      return false;
11353|       |    }
11354|       |    const uint64_t delim = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('/'))) |
11355|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('?'))) |
11356|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('#')));
11357|       |    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
11358|       |  }
11359|       |#elif ADA_SSE2
11360|  4.89k|  if (rem >= 16) {
  ------------------
  |  Branch (11360:7): [True: 1.43k, False: 3.46k]
  ------------------
11361|  1.43k|    const __m128i w =
11362|  1.43k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + host_start));
11363|  1.43k|    const uint16_t at = static_cast<uint16_t>(
11364|  1.43k|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('@'))));
11365|  1.43k|    if (at == 0) {
  ------------------
  |  Branch (11365:9): [True: 1.35k, False: 76]
  ------------------
11366|  1.35k|      return false;
11367|  1.35k|    }
11368|     76|    const uint16_t delim = static_cast<uint16_t>(
11369|     76|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) |
11370|     76|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('?'))) |
11371|     76|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('#'))));
11372|     76|    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
  ------------------
  |  Branch (11372:12): [True: 16, False: 60]
  |  Branch (11372:26): [True: 4, False: 56]
  ------------------
11373|  1.43k|  }
11374|  3.46k|#endif
11375|  3.46k|  const size_t lim = host_start + rem;
11376|  18.1k|  for (size_t i = host_start; i < lim; ++i) {
  ------------------
  |  Branch (11376:31): [True: 16.4k, False: 1.62k]
  ------------------
11377|  16.4k|    const uint8_t c = p[i];
11378|  16.4k|    if (c == '@') {
  ------------------
  |  Branch (11378:9): [True: 20, False: 16.4k]
  ------------------
11379|     20|      return true;
11380|     20|    }
11381|  16.4k|    if (c == '/' || c == '?' || c == '#') {
  ------------------
  |  Branch (11381:9): [True: 1.78k, False: 14.6k]
  |  Branch (11381:21): [True: 22, False: 14.6k]
  |  Branch (11381:33): [True: 20, False: 14.6k]
  ------------------
11382|  1.82k|      return false;
11383|  1.82k|    }
11384|  16.4k|  }
11385|  1.62k|  return false;
11386|  3.46k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_:
11407|  14.8k|                                     bool& has_x) noexcept {
11408|  14.8k|  has_upper = false;
11409|  14.8k|  has_x = false;
11410|  14.8k|  size_t i = start;
11411|  14.8k|#if ADA_PARSER_SSSE3
11412|  14.8k|  if (parser_use_ssse3() && (len - start >= 16)) {
  ------------------
  |  Branch (11412:7): [True: 14.8k, False: 0]
  |  Branch (11412:29): [True: 7.44k, False: 7.45k]
  ------------------
11413|  7.44k|    const __m128i lo_tbl = nibble_load(k_host_nibbles.low);
11414|  7.44k|    const __m128i hi_tbl = nibble_load(k_host_nibbles.high);
11415|  7.44k|    const __m128i x_splat = _mm_set1_epi8('x');
11416|  7.44k|    auto visit = [&](size_t at) noexcept -> bool {
11417|  7.44k|      const __m128i w =
11418|  7.44k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11419|  7.44k|      const int up = sse2_uppercase(w);
11420|  7.44k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11421|  7.44k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11422|  7.44k|      if (mask == 0) {
11423|  7.44k|        if (up != 0) {
11424|  7.44k|          has_upper = true;
11425|  7.44k|        }
11426|  7.44k|        if (xs != 0) {
11427|  7.44k|          has_x = true;
11428|  7.44k|        }
11429|  7.44k|        return false;
11430|  7.44k|      }
11431|  7.44k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11432|  7.44k|      const int valid = (1 << hit) - 1;
11433|  7.44k|      if ((up & valid) != 0) {
11434|  7.44k|        has_upper = true;
11435|  7.44k|      }
11436|  7.44k|      if ((xs & valid) != 0) {
11437|  7.44k|        has_x = true;
11438|  7.44k|      }
11439|  7.44k|      end = at + static_cast<size_t>(hit);
11440|  7.44k|      return true;
11441|  7.44k|    };
11442|  12.9k|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11442:12): [True: 8.87k, False: 4.05k]
  ------------------
11443|  8.87k|      if (visit(i) || visit(i + 16)) {
  ------------------
  |  Branch (11443:11): [True: 3.18k, False: 5.68k]
  |  Branch (11443:23): [True: 202, False: 5.48k]
  ------------------
11444|  3.38k|        return k_host_class[b[end]] == 1;
11445|  3.38k|      }
11446|  8.87k|    }
11447|  5.40k|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11447:12): [True: 3.46k, False: 1.94k]
  ------------------
11448|  3.46k|      if (visit(i)) {
  ------------------
  |  Branch (11448:11): [True: 2.11k, False: 1.35k]
  ------------------
11449|  2.11k|        return k_host_class[b[end]] == 1;
11450|  2.11k|      }
11451|  3.46k|    }
11452|       |    // Overlapping tail only after a full 16-byte step so the window cannot
11453|       |    // start before `start` (a prior '/' would otherwise look like a host stop).
11454|  1.94k|    if (i > start && i < len) {
  ------------------
  |  Branch (11454:9): [True: 1.94k, False: 0]
  |  Branch (11454:22): [True: 1.85k, False: 86]
  ------------------
11455|  1.85k|      if (visit(len - 16) && end >= i) {
  ------------------
  |  Branch (11455:11): [True: 1.79k, False: 60]
  |  Branch (11455:30): [True: 1.79k, False: 0]
  ------------------
11456|  1.79k|        return k_host_class[b[end]] == 1;
11457|  1.79k|      }
11458|     60|      end = len;
11459|     60|      return true;
11460|  1.85k|    }
11461|  1.94k|  }
11462|       |#elif ADA_SSE2
11463|       |  const __m128i x_splat = _mm_set1_epi8('x');
11464|       |  for (; i + 32 <= len; i += 32) {
11465|       |    for (size_t off = 0; off < 32; off += 16) {
11466|       |      const __m128i w =
11467|       |          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + off));
11468|       |      const int up = sse2_uppercase(w);
11469|       |      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11470|       |      const int mask = sse2_host_stop(w);
11471|       |      if (mask != 0) {
11472|       |        const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11473|       |        const int valid = (1 << hit) - 1;
11474|       |        if ((up & valid) != 0) {
11475|       |          has_upper = true;
11476|       |        }
11477|       |        if ((xs & valid) != 0) {
11478|       |          has_x = true;
11479|       |        }
11480|       |        end = i + off + static_cast<size_t>(hit);
11481|       |        return k_host_class[b[end]] == 1;
11482|       |      }
11483|       |      if (up != 0) {
11484|       |        has_upper = true;
11485|       |      }
11486|       |      if (xs != 0) {
11487|       |        has_x = true;
11488|       |      }
11489|       |    }
11490|       |  }
11491|       |  for (; i + 16 <= len; i += 16) {
11492|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11493|       |    const int up = sse2_uppercase(w);
11494|       |    const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11495|       |    const int mask = sse2_host_stop(w);
11496|       |    if (mask != 0) {
11497|       |      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11498|       |      const int valid = (1 << hit) - 1;
11499|       |      if ((up & valid) != 0) {
11500|       |        has_upper = true;
11501|       |      }
11502|       |      if ((xs & valid) != 0) {
11503|       |        has_x = true;
11504|       |      }
11505|       |      end = i + static_cast<size_t>(hit);
11506|       |      return k_host_class[b[end]] == 1;
11507|       |    }
11508|       |    if (up != 0) {
11509|       |      has_upper = true;
11510|       |    }
11511|       |    if (xs != 0) {
11512|       |      has_x = true;
11513|       |    }
11514|       |  }
11515|       |#elif ADA_NEON
11516|       |  if (len - start >= 16) {
11517|       |    const uint8x16_t lo_tbl = vld1q_u8(k_host_nibbles.low);
11518|       |    const uint8x16_t hi_tbl = vld1q_u8(k_host_nibbles.high);
11519|       |    const uint8x16_t x_splat = vdupq_n_u8('x');
11520|       |    auto visit = [&](size_t at) noexcept -> bool {
11521|       |      const uint8x16_t w = vld1q_u8(b + at);
11522|       |      const uint64_t up = neon_uppercase(w);
11523|       |      const uint64_t xs = neon_nibble_bits(vceqq_u8(w, x_splat));
11524|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11525|       |      if (bits == 0) {
11526|       |        if (up != 0) {
11527|       |          has_upper = true;
11528|       |        }
11529|       |        if (xs != 0) {
11530|       |          has_x = true;
11531|       |        }
11532|       |        return false;
11533|       |      }
11534|       |      const size_t hit = static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11535|       |      const uint64_t valid = (hit == 0) ? 0 : (uint64_t{1} << (hit * 4)) - 1;
11536|       |      if ((up & valid) != 0) {
11537|       |        has_upper = true;
11538|       |      }
11539|       |      if ((xs & valid) != 0) {
11540|       |        has_x = true;
11541|       |      }
11542|       |      end = at + hit;
11543|       |      return true;
11544|       |    };
11545|       |    for (; i + 32 <= len; i += 32) {
11546|       |      if (visit(i) || visit(i + 16)) {
11547|       |        return k_host_class[b[end]] == 1;
11548|       |      }
11549|       |    }
11550|       |    for (; i + 16 <= len; i += 16) {
11551|       |      if (visit(i)) {
11552|       |        return k_host_class[b[end]] == 1;
11553|       |      }
11554|       |    }
11555|       |    if (i > start && i < len) {
11556|       |      if (visit(len - 16) && end >= i) {
11557|       |        return k_host_class[b[end]] == 1;
11558|       |      }
11559|       |      end = len;
11560|       |      return true;
11561|       |    }
11562|       |  }
11563|       |#endif
11564|  30.0k|  for (; i < len; ++i) {
  ------------------
  |  Branch (11564:10): [True: 29.7k, False: 360]
  ------------------
11565|  29.7k|    const uint8_t c = b[i];
11566|  29.7k|    const uint8_t cls = k_host_class[c];
11567|  29.7k|    if (cls == 1) {
  ------------------
  |  Branch (11567:9): [True: 6.98k, False: 22.7k]
  ------------------
11568|  6.98k|      end = i;
11569|  6.98k|      return true;
11570|  6.98k|    }
11571|  22.7k|    if (cls == 2) {
  ------------------
  |  Branch (11571:9): [True: 194, False: 22.5k]
  ------------------
11572|    194|      return false;
11573|    194|    }
11574|  22.5k|    if (c >= 'A' && c <= 'Z') {
  ------------------
  |  Branch (11574:9): [True: 13.8k, False: 8.67k]
  |  Branch (11574:21): [True: 798, False: 13.0k]
  ------------------
11575|    798|      has_upper = true;
11576|  21.7k|    } else if (c == 'x') {
  ------------------
  |  Branch (11576:16): [True: 3.15k, False: 18.5k]
  ------------------
11577|  3.15k|      has_x = true;
11578|  3.15k|    }
11579|  22.5k|  }
11580|    360|  end = len;
11581|    360|  return true;
11582|  7.54k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_116parser_use_ssse3Ev:
11392|  29.0k|ada_really_inline bool parser_use_ssse3() noexcept {
11393|  29.0k|#ifdef ADA_PARSER_NEED_SSSE3_TARGET
11394|  29.0k|  static const bool supported = __builtin_cpu_supports("ssse3") != 0;
11395|  29.0k|  return supported;
11396|       |#else
11397|       |  return true;
11398|       |#endif
11399|  29.0k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_111nibble_loadEPKh:
11223|  27.9k|ADA_PARSER_SIMD __m128i nibble_load(const uint8_t* t) noexcept {
11224|  27.9k|  return _mm_load_si128(reinterpret_cast<const __m128i*>(t));
11225|  27.9k|}
can_parse.cc:_ZZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_ENKUlmE_clEm:
11416|  19.8k|    auto visit = [&](size_t at) noexcept -> bool {
11417|  19.8k|      const __m128i w =
11418|  19.8k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11419|  19.8k|      const int up = sse2_uppercase(w);
11420|  19.8k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11421|  19.8k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11422|  19.8k|      if (mask == 0) {
  ------------------
  |  Branch (11422:11): [True: 12.5k, False: 7.29k]
  ------------------
11423|  12.5k|        if (up != 0) {
  ------------------
  |  Branch (11423:13): [True: 794, False: 11.7k]
  ------------------
11424|    794|          has_upper = true;
11425|    794|        }
11426|  12.5k|        if (xs != 0) {
  ------------------
  |  Branch (11426:13): [True: 9.53k, False: 3.04k]
  ------------------
11427|  9.53k|          has_x = true;
11428|  9.53k|        }
11429|  12.5k|        return false;
11430|  12.5k|      }
11431|  7.29k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11432|  7.29k|      const int valid = (1 << hit) - 1;
11433|  7.29k|      if ((up & valid) != 0) {
  ------------------
  |  Branch (11433:11): [True: 678, False: 6.61k]
  ------------------
11434|    678|        has_upper = true;
11435|    678|      }
11436|  7.29k|      if ((xs & valid) != 0) {
  ------------------
  |  Branch (11436:11): [True: 1.32k, False: 5.97k]
  ------------------
11437|  1.32k|        has_x = true;
11438|  1.32k|      }
11439|  7.29k|      end = at + static_cast<size_t>(hit);
11440|  7.29k|      return true;
11441|  19.8k|    };
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_114sse2_uppercaseEDv2_x:
11310|  19.8k|ada_really_inline int sse2_uppercase(__m128i w) noexcept {
11311|  19.8k|  return _mm_movemask_epi8(_mm_and_si128(
11312|  19.8k|      _mm_cmpgt_epi8(w, _mm_set1_epi8(static_cast<char>('A' - 1))),
11313|  19.8k|      _mm_cmplt_epi8(w, _mm_set1_epi8(static_cast<char>('Z' + 1)))));
11314|  19.8k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_117ssse3_nibble_maskEDv2_xS2_S2_:
11230|  37.8k|                                          __m128i hi_tbl) noexcept {
11231|  37.8k|  const __m128i nibble = _mm_set1_epi8(0x0F);
11232|  37.8k|  const __m128i lo = _mm_and_si128(w, nibble);
11233|  37.8k|  const __m128i hi = _mm_and_si128(_mm_srli_epi16(w, 4), nibble);
11234|  37.8k|  const __m128i hit =
11235|  37.8k|      _mm_and_si128(_mm_shuffle_epi8(lo_tbl, lo), _mm_shuffle_epi8(hi_tbl, hi));
11236|  37.8k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hit, _mm_setzero_si128())) ^ 0xFFFF;
11237|  37.8k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_117trailing_zeroes32Ej:
11202|  11.7k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
11203|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
11204|       |  unsigned long ret;
11205|       |  _BitScanForward(&ret, input_num);
11206|       |  return static_cast<int>(ret);
11207|       |#else
11208|  11.7k|  return __builtin_ctz(input_num);
11209|  11.7k|#endif
11210|  11.7k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_113scan_path_runEPKhRmmRb:
11642|  10.9k|                                   bool& maybe_dot_segment) noexcept {
11643|  10.9k|  const size_t run_start = i;
11644|  10.9k|#if ADA_PARSER_SSSE3
11645|  10.9k|  if (parser_use_ssse3() && (i + 16 <= len)) {
  ------------------
  |  Branch (11645:7): [True: 10.9k, False: 0]
  |  Branch (11645:29): [True: 4.71k, False: 6.28k]
  ------------------
11646|  4.71k|    const __m128i lo_tbl = nibble_load(k_path_nibbles.low);
11647|  4.71k|    const __m128i hi_tbl = nibble_load(k_path_nibbles.high);
11648|  7.80k|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11648:12): [True: 5.07k, False: 2.73k]
  ------------------
11649|  5.07k|      const __m128i w0 =
11650|  5.07k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11651|  5.07k|      const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);
11652|  5.07k|      if (m0 != 0) {
  ------------------
  |  Branch (11652:11): [True: 1.43k, False: 3.63k]
  ------------------
11653|  1.43k|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m0));
11654|  1.43k|        note_dots_in_window(b, i, run_start, w0, (1 << hit_bit) - 1,
11655|  1.43k|                            maybe_dot_segment);
11656|  1.43k|        i += static_cast<size_t>(hit_bit);
11657|  1.43k|        return;
11658|  1.43k|      }
11659|  3.63k|      note_dots_in_window(b, i, run_start, w0, 0xFFFF, maybe_dot_segment);
11660|  3.63k|      const __m128i w1 =
11661|  3.63k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));
11662|  3.63k|      const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);
11663|  3.63k|      if (m1 != 0) {
  ------------------
  |  Branch (11663:11): [True: 542, False: 3.09k]
  ------------------
11664|    542|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m1));
11665|    542|        note_dots_in_window(b, i + 16, run_start, w1, (1 << hit_bit) - 1,
11666|    542|                            maybe_dot_segment);
11667|    542|        i += 16 + static_cast<size_t>(hit_bit);
11668|    542|        return;
11669|    542|      }
11670|  3.09k|      note_dots_in_window(b, i + 16, run_start, w1, 0xFFFF, maybe_dot_segment);
11671|  3.09k|    }
11672|  3.94k|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11672:12): [True: 2.10k, False: 1.83k]
  ------------------
11673|  2.10k|      const __m128i w =
11674|  2.10k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11675|  2.10k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11676|  2.10k|      if (mask != 0) {
  ------------------
  |  Branch (11676:11): [True: 902, False: 1.20k]
  ------------------
11677|    902|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11678|    902|        note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11679|    902|                            maybe_dot_segment);
11680|    902|        i += static_cast<size_t>(hit_bit);
11681|    902|        return;
11682|    902|      }
11683|  1.20k|      note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11684|  1.20k|    }
11685|  1.83k|    if (i > run_start && i < len) {
  ------------------
  |  Branch (11685:9): [True: 1.83k, False: 0]
  |  Branch (11685:26): [True: 1.11k, False: 718]
  ------------------
11686|  1.11k|      const __m128i w =
11687|  1.11k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16));
11688|  1.11k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11689|  1.11k|      if (mask != 0) {
  ------------------
  |  Branch (11689:11): [True: 462, False: 652]
  ------------------
11690|    462|        const size_t hit =
11691|    462|            len - 16 +
11692|    462|            static_cast<size_t>(trailing_zeroes32(static_cast<uint32_t>(mask)));
11693|    462|        if (hit >= i) {
  ------------------
  |  Branch (11693:13): [True: 462, False: 0]
  ------------------
11694|  2.21k|          for (size_t j = i; j < hit; ++j) {
  ------------------
  |  Branch (11694:30): [True: 1.74k, False: 462]
  ------------------
11695|  1.74k|            if (b[j] == '.') {
  ------------------
  |  Branch (11695:17): [True: 588, False: 1.16k]
  ------------------
11696|    588|              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11697|    588|            }
11698|  1.74k|          }
11699|    462|          i = hit;
11700|    462|          return;
11701|    462|        }
11702|    462|      }
11703|  4.82k|      for (size_t j = i; j < len; ++j) {
  ------------------
  |  Branch (11703:26): [True: 4.17k, False: 652]
  ------------------
11704|  4.17k|        if (b[j] == '.') {
  ------------------
  |  Branch (11704:13): [True: 1.24k, False: 2.93k]
  ------------------
11705|  1.24k|          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11706|  1.24k|        }
11707|  4.17k|      }
11708|    652|      i = len;
11709|    652|      return;
11710|  1.11k|    }
11711|  1.83k|  }
11712|       |#elif ADA_SSE2
11713|       |  for (; i + 16 <= len; i += 16) {
11714|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11715|       |    const int mask = sse2_path_stop(w);
11716|       |    if (mask != 0) {
11717|       |      const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11718|       |      note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11719|       |                          maybe_dot_segment);
11720|       |      i += static_cast<size_t>(hit_bit);
11721|       |      return;
11722|       |    }
11723|       |    note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11724|       |  }
11725|       |#elif ADA_NEON
11726|       |  if (i + 16 <= len) {
11727|       |    const uint8x16_t lo_tbl = vld1q_u8(k_path_nibbles.low);
11728|       |    const uint8x16_t hi_tbl = vld1q_u8(k_path_nibbles.high);
11729|       |    for (; i + 32 <= len; i += 32) {
11730|       |      const uint8x16_t w0 = vld1q_u8(b + i);
11731|       |      const uint64_t bits0 = neon_table_stop(w0, lo_tbl, hi_tbl);
11732|       |      if (bits0 != 0) {
11733|       |        const size_t hit_off =
11734|       |            static_cast<size_t>(trailing_zeroes64(bits0)) >> 2;
11735|       |        const uint64_t valid =
11736|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11737|       |        note_dots_in_window_neon(b, i, run_start, w0, valid, maybe_dot_segment);
11738|       |        i += hit_off;
11739|       |        return;
11740|       |      }
11741|       |      note_dots_in_window_neon(b, i, run_start, w0, ~uint64_t{0},
11742|       |                               maybe_dot_segment);
11743|       |      const uint8x16_t w1 = vld1q_u8(b + i + 16);
11744|       |      const uint64_t bits1 = neon_table_stop(w1, lo_tbl, hi_tbl);
11745|       |      if (bits1 != 0) {
11746|       |        const size_t hit_off =
11747|       |            static_cast<size_t>(trailing_zeroes64(bits1)) >> 2;
11748|       |        const uint64_t valid =
11749|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11750|       |        note_dots_in_window_neon(b, i + 16, run_start, w1, valid,
11751|       |                                 maybe_dot_segment);
11752|       |        i += 16 + hit_off;
11753|       |        return;
11754|       |      }
11755|       |      note_dots_in_window_neon(b, i + 16, run_start, w1, ~uint64_t{0},
11756|       |                               maybe_dot_segment);
11757|       |    }
11758|       |    for (; i + 16 <= len; i += 16) {
11759|       |      const uint8x16_t w = vld1q_u8(b + i);
11760|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11761|       |      if (bits != 0) {
11762|       |        const size_t hit_off =
11763|       |            static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11764|       |        const uint64_t valid =
11765|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11766|       |        note_dots_in_window_neon(b, i, run_start, w, valid, maybe_dot_segment);
11767|       |        i += hit_off;
11768|       |        return;
11769|       |      }
11770|       |      note_dots_in_window_neon(b, i, run_start, w, ~uint64_t{0},
11771|       |                               maybe_dot_segment);
11772|       |    }
11773|       |    if (i > run_start && i < len) {
11774|       |      const uint8x16_t w = vld1q_u8(b + len - 16);
11775|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11776|       |      if (bits != 0) {
11777|       |        const size_t hit =
11778|       |            len - 16 + (static_cast<size_t>(trailing_zeroes64(bits)) >> 2);
11779|       |        if (hit >= i) {
11780|       |          for (size_t j = i; j < hit; ++j) {
11781|       |            if (b[j] == '.') {
11782|       |              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11783|       |            }
11784|       |          }
11785|       |          i = hit;
11786|       |          return;
11787|       |        }
11788|       |      }
11789|       |      for (size_t j = i; j < len; ++j) {
11790|       |        if (b[j] == '.') {
11791|       |          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11792|       |        }
11793|       |      }
11794|       |      i = len;
11795|       |      return;
11796|       |    }
11797|       |  }
11798|       |#endif
11799|  15.1k|  for (; i < len; ++i) {
  ------------------
  |  Branch (11799:10): [True: 10.1k, False: 4.92k]
  ------------------
11800|  10.1k|    const uint8_t c = b[i];
11801|  10.1k|    const uint8_t cls = k_path[c];
11802|  10.1k|    if (cls != 0) {
  ------------------
  |  Branch (11802:9): [True: 2.08k, False: 8.11k]
  ------------------
11803|  2.08k|      return;
11804|  2.08k|    }
11805|  8.11k|    if (c == '.') {
  ------------------
  |  Branch (11805:9): [True: 2.46k, False: 5.64k]
  ------------------
11806|  2.46k|      note_possible_dot_segment(b, i, run_start, maybe_dot_segment);
11807|  2.46k|    }
11808|  8.11k|  }
11809|  7.00k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_119note_dots_in_windowEPKhmmDv2_xiRb:
11599|  10.8k|                                           bool& maybe_dot_segment) noexcept {
11600|  10.8k|  if (maybe_dot_segment || valid_mask == 0) {
  ------------------
  |  Branch (11600:7): [True: 2.40k, False: 8.41k]
  |  Branch (11600:28): [True: 1.44k, False: 6.97k]
  ------------------
11601|  3.84k|    return;
11602|  3.84k|  }
11603|  6.97k|  const int dots =
11604|  6.97k|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('.'))) & valid_mask;
11605|  6.97k|  if (dots == 0) {
  ------------------
  |  Branch (11605:7): [True: 2.73k, False: 4.23k]
  ------------------
11606|  2.73k|    return;
11607|  2.73k|  }
11608|  4.23k|  const int slashes =
11609|  4.23k|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) & valid_mask;
11610|  4.23k|  if (((dots & 1) != 0 && (i == run_start || b[i - 1] == '/')) ||
  ------------------
  |  Branch (11610:8): [True: 1.96k, False: 2.27k]
  |  Branch (11610:28): [True: 632, False: 1.33k]
  |  Branch (11610:46): [True: 176, False: 1.15k]
  ------------------
11611|  3.43k|      (dots & (slashes << 1)) != 0) {
  ------------------
  |  Branch (11611:7): [True: 764, False: 2.66k]
  ------------------
11612|  1.57k|    maybe_dot_segment = true;
11613|  1.57k|  }
11614|  4.23k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_125note_possible_dot_segmentEPKhmmRb:
11586|  4.30k|    bool& maybe_dot_segment) noexcept {
11587|  4.30k|  if (pos == run_start || b[pos - 1] == '/') {
  ------------------
  |  Branch (11587:7): [True: 680, False: 3.62k]
  |  Branch (11587:27): [True: 1.57k, False: 2.04k]
  ------------------
11588|  2.25k|    maybe_dot_segment = true;
11589|  2.25k|  }
11590|  4.30k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_114scan_query_runEPKhRmm:
11952|  1.60k|                                    size_t len) noexcept {
11953|  1.60k|  ADA_SCAN_STOP_RUN(k_query_nibbles, k_query, sse2_query_stop);
  ------------------
  |  |11815|  1.60k|  do {                                                                       \
  |  |11816|  1.60k|    const size_t scan_start = i;                                             \
  |  |11817|  1.60k|    if (parser_use_ssse3() && (i + 16 <= len)) {                             \
  |  |  ------------------
  |  |  |  Branch (11817:9): [True: 1.60k, False: 0]
  |  |  |  Branch (11817:31): [True: 934, False: 670]
  |  |  ------------------
  |  |11818|    934|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11819|    934|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11820|  1.87k|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11820:14): [True: 1.26k, False: 610]
  |  |  ------------------
  |  |11821|  1.26k|        const __m128i w0 =                                                   \
  |  |11822|  1.26k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11823|  1.26k|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11824|  1.26k|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11824:13): [True: 218, False: 1.05k]
  |  |  ------------------
  |  |11825|    218|          i += static_cast<size_t>(                                          \
  |  |11826|    218|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11827|    218|          return;                                                            \
  |  |11828|    218|        }                                                                    \
  |  |11829|  1.26k|        const __m128i w1 =                                                   \
  |  |11830|  1.05k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11831|  1.05k|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11832|  1.05k|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11832:13): [True: 106, False: 944]
  |  |  ------------------
  |  |11833|    106|          i += 16 + static_cast<size_t>(                                     \
  |  |11834|    106|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11835|    106|          return;                                                            \
  |  |11836|    106|        }                                                                    \
  |  |11837|  1.05k|      }                                                                      \
  |  |11838|    934|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11838:14): [True: 486, False: 416]
  |  |  ------------------
  |  |11839|    486|        const __m128i w =                                                    \
  |  |11840|    486|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11841|    486|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11842|    486|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11842:13): [True: 194, False: 292]
  |  |  ------------------
  |  |11843|    194|          i += static_cast<size_t>(                                          \
  |  |11844|    194|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11845|    194|          return;                                                            \
  |  |11846|    194|        }                                                                    \
  |  |11847|    486|      }                                                                      \
  |  |11848|    610|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11848:11): [True: 416, False: 0]
  |  |  |  Branch (11848:29): [True: 312, False: 104]
  |  |  ------------------
  |  |11849|    312|        const __m128i w =                                                    \
  |  |11850|    312|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11851|    312|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11852|    312|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11852:13): [True: 122, False: 190]
  |  |  ------------------
  |  |11853|    122|          const size_t hit = len - 16 +                                      \
  |  |11854|    122|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11855|    122|                                 static_cast<uint32_t>(mask)));              \
  |  |11856|    122|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11856:15): [True: 122, False: 0]
  |  |  ------------------
  |  |11857|    122|            i = hit;                                                         \
  |  |11858|    122|            return;                                                          \
  |  |11859|    122|          }                                                                  \
  |  |11860|    122|        }                                                                    \
  |  |11861|    312|        i = len;                                                             \
  |  |11862|    190|        return;                                                              \
  |  |11863|    312|      }                                                                      \
  |  |11864|    416|    }                                                                        \
  |  |11865|  1.94k|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11865:12): [True: 1.42k, False: 518]
  |  |  ------------------
  |  |11866|  1.42k|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11866:11): [True: 256, False: 1.16k]
  |  |  ------------------
  |  |11867|    256|        return;                                                              \
  |  |11868|    256|      }                                                                      \
  |  |11869|  1.42k|    }                                                                        \
  |  |11870|    774|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11870:12): [Folded, False: 518]
  |  |  ------------------
  ------------------
11954|  1.60k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_113scan_hash_runEPKhRmm:
11957|  1.50k|                                   size_t len) noexcept {
11958|  1.50k|  ADA_SCAN_STOP_RUN(k_hash_nibbles, k_hash, sse2_hash_stop);
  ------------------
  |  |11815|  1.50k|  do {                                                                       \
  |  |11816|  1.50k|    const size_t scan_start = i;                                             \
  |  |11817|  1.50k|    if (parser_use_ssse3() && (i + 16 <= len)) {                             \
  |  |  ------------------
  |  |  |  Branch (11817:9): [True: 1.50k, False: 0]
  |  |  |  Branch (11817:31): [True: 908, False: 600]
  |  |  ------------------
  |  |11818|    908|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11819|    908|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11820|  1.70k|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11820:14): [True: 978, False: 722]
  |  |  ------------------
  |  |11821|    978|        const __m128i w0 =                                                   \
  |  |11822|    978|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11823|    978|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11824|    978|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11824:13): [True: 80, False: 898]
  |  |  ------------------
  |  |11825|     80|          i += static_cast<size_t>(                                          \
  |  |11826|     80|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11827|     80|          return;                                                            \
  |  |11828|     80|        }                                                                    \
  |  |11829|    978|        const __m128i w1 =                                                   \
  |  |11830|    898|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11831|    898|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11832|    898|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11832:13): [True: 106, False: 792]
  |  |  ------------------
  |  |11833|    106|          i += 16 + static_cast<size_t>(                                     \
  |  |11834|    106|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11835|    106|          return;                                                            \
  |  |11836|    106|        }                                                                    \
  |  |11837|    898|      }                                                                      \
  |  |11838|  1.14k|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11838:14): [True: 566, False: 574]
  |  |  ------------------
  |  |11839|    566|        const __m128i w =                                                    \
  |  |11840|    566|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11841|    566|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11842|    566|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11842:13): [True: 148, False: 418]
  |  |  ------------------
  |  |11843|    148|          i += static_cast<size_t>(                                          \
  |  |11844|    148|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11845|    148|          return;                                                            \
  |  |11846|    148|        }                                                                    \
  |  |11847|    566|      }                                                                      \
  |  |11848|    722|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11848:11): [True: 574, False: 0]
  |  |  |  Branch (11848:29): [True: 448, False: 126]
  |  |  ------------------
  |  |11849|    448|        const __m128i w =                                                    \
  |  |11850|    448|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11851|    448|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11852|    448|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11852:13): [True: 98, False: 350]
  |  |  ------------------
  |  |11853|     98|          const size_t hit = len - 16 +                                      \
  |  |11854|     98|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11855|     98|                                 static_cast<uint32_t>(mask)));              \
  |  |11856|     98|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11856:15): [True: 98, False: 0]
  |  |  ------------------
  |  |11857|     98|            i = hit;                                                         \
  |  |11858|     98|            return;                                                          \
  |  |11859|     98|          }                                                                  \
  |  |11860|     98|        }                                                                    \
  |  |11861|    448|        i = len;                                                             \
  |  |11862|    350|        return;                                                              \
  |  |11863|    448|      }                                                                      \
  |  |11864|    574|    }                                                                        \
  |  |11865|  2.55k|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11865:12): [True: 1.94k, False: 606]
  |  |  ------------------
  |  |11866|  1.94k|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11866:11): [True: 120, False: 1.82k]
  |  |  ------------------
  |  |11867|    120|        return;                                                              \
  |  |11868|    120|      }                                                                      \
  |  |11869|  1.94k|    }                                                                        \
  |  |11870|    726|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11870:12): [Folded, False: 606]
  |  |  ------------------
  ------------------
11959|  1.50k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_120path_has_dot_segmentENSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
11963|  1.84k|bool path_has_dot_segment(std::string_view path) noexcept {
11964|  1.84k|  if (path.empty()) {
  ------------------
  |  Branch (11964:7): [True: 0, False: 1.84k]
  ------------------
11965|      0|    return false;
11966|      0|  }
11967|       |  // "." / ".." at the start, with or without a leading '/'.
11968|  1.84k|  const size_t i = (path[0] == '/') ? 1 : 0;
  ------------------
  |  Branch (11968:20): [True: 1.62k, False: 228]
  ------------------
11969|  1.84k|  if (i < path.size() && path[i] == '.') {
  ------------------
  |  Branch (11969:7): [True: 1.84k, False: 0]
  |  Branch (11969:26): [True: 798, False: 1.05k]
  ------------------
11970|    798|    const size_t after = i + 1;
11971|    798|    if (after == path.size() || path[after] == '/' ||
  ------------------
  |  Branch (11971:9): [True: 62, False: 736]
  |  Branch (11971:33): [True: 234, False: 502]
  ------------------
11972|    502|        (after < path.size() && path[after] == '.' &&
  ------------------
  |  Branch (11972:10): [True: 502, False: 0]
  |  Branch (11972:33): [True: 228, False: 274]
  ------------------
11973|    422|         (after + 1 == path.size() || path[after + 1] == '/'))) {
  ------------------
  |  Branch (11973:11): [True: 34, False: 194]
  |  Branch (11973:39): [True: 92, False: 102]
  ------------------
11974|    422|      return true;
11975|    422|    }
11976|    798|  }
11977|  1.42k|  static constexpr std::string_view slash_dot{"/.", 2};
11978|  1.42k|  size_t p = i;
11979|  3.97k|  while ((p = path.find(slash_dot, p)) != std::string_view::npos) {
  ------------------
  |  Branch (11979:10): [True: 3.25k, False: 726]
  ------------------
11980|  3.25k|    const size_t after = p + 2;
11981|  3.25k|    if (after == path.size() || path[after] == '/' ||
  ------------------
  |  Branch (11981:9): [True: 268, False: 2.98k]
  |  Branch (11981:33): [True: 178, False: 2.80k]
  ------------------
11982|  2.80k|        (after < path.size() && path[after] == '.' &&
  ------------------
  |  Branch (11982:10): [True: 2.80k, False: 0]
  |  Branch (11982:33): [True: 764, False: 2.04k]
  ------------------
11983|    764|         (after + 1 == path.size() || path[after + 1] == '/'))) {
  ------------------
  |  Branch (11983:11): [True: 64, False: 700]
  |  Branch (11983:39): [True: 190, False: 510]
  ------------------
11984|    700|      return true;
11985|    700|    }
11986|  2.55k|    p = after;
11987|  2.55k|  }
11988|    726|  return false;
11989|  1.42k|}
_ZN3ada14url_aggregator11copy_schemeERKS0_:
14085|  2.10k|inline void url_aggregator::copy_scheme(const url_aggregator& u) {
14086|  2.10k|  ada_log("url_aggregator::copy_scheme ", u.buffer);
14087|  2.10k|  ADA_ASSERT_TRUE(validate());
14088|       |  // next line could overflow but unsigned arithmetic has well-defined
14089|       |  // overflows.
14090|  2.10k|  uint32_t new_difference = u.components.protocol_end - components.protocol_end;
14091|       |
14092|  2.10k|  type = u.type;
14093|  2.10k|  buffer.erase(0, components.protocol_end);
14094|  2.10k|  buffer.insert(0, u.get_protocol());
14095|  2.10k|  components.protocol_end = u.components.protocol_end;
14096|       |
14097|       |  // No need to update the components
14098|  2.10k|  if (new_difference == 0) {
  ------------------
  |  Branch (14098:7): [True: 166, False: 1.94k]
  ------------------
14099|    166|    return;
14100|    166|  }
14101|       |
14102|  1.94k|  apply_shifted_non_scheme_offsets(components, new_difference);
14103|  1.94k|  ADA_ASSERT_TRUE(validate());
14104|  1.94k|}
can_parse.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
13963|  45.8k|    ada::url_components& components, uint32_t new_difference) {
13964|  45.8k|  components.username_end += new_difference;
13965|  45.8k|  components.host_start += new_difference;
13966|  45.8k|  components.host_end += new_difference;
13967|  45.8k|  components.pathname_start += new_difference;
13968|  45.8k|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13968:7): [True: 0, False: 45.8k]
  ------------------
13969|      0|    components.search_start += new_difference;
13970|      0|  }
13971|  45.8k|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13971:7): [True: 0, False: 45.8k]
  ------------------
13972|      0|    components.hash_start += new_difference;
13973|      0|  }
13974|  45.8k|}
_ZN3ada14url_aggregator10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14333|  2.35k|ada_really_inline void url_aggregator::parse_path(std::string_view input) {
14334|  2.35k|  ada_log("url_aggregator::parse_path ", input);
14335|  2.35k|  ADA_ASSERT_TRUE(validate());
14336|  2.35k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14337|  2.35k|  std::string tmp_buffer;
14338|  2.35k|  std::string_view internal_input;
14339|  2.35k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (14339:7): [True: 0, False: 2.35k]
  ------------------
14340|      0|    tmp_buffer = input;
14341|       |    // Optimization opportunity: Instead of copying and then pruning, we could
14342|       |    // just directly build the string from user_input.
14343|      0|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
14344|      0|    internal_input = tmp_buffer;
14345|  2.35k|  } else {
14346|  2.35k|    internal_input = input;
14347|  2.35k|  }
14348|       |
14349|       |  // If url is special, then:
14350|  2.35k|  if (is_special()) {
  ------------------
  |  Branch (14350:7): [True: 2.35k, False: 0]
  ------------------
14351|  2.35k|    if (internal_input.empty()) {
  ------------------
  |  Branch (14351:9): [True: 140, False: 2.21k]
  ------------------
14352|    140|      update_base_pathname("/");
14353|  2.21k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (14353:16): [True: 2.21k, False: 0]
  |  Branch (14353:46): [True: 0, False: 0]
  ------------------
14354|  2.21k|      consume_prepared_path(internal_input.substr(1));
14355|  2.21k|    } else {
14356|      0|      consume_prepared_path(internal_input);
14357|      0|    }
14358|  2.35k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (14358:14): [True: 0, False: 0]
  ------------------
14359|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (14359:9): [True: 0, False: 0]
  ------------------
14360|      0|      consume_prepared_path(internal_input.substr(1));
14361|      0|    } else {
14362|      0|      consume_prepared_path(internal_input);
14363|      0|    }
14364|      0|  } else {
14365|       |    // Non-special URLs with an empty host can have their paths erased
14366|       |    // Path-only URLs cannot have their paths erased
14367|      0|    if (components.host_start == components.host_end && !has_authority()) {
  ------------------
  |  Branch (14367:9): [True: 0, False: 0]
  |  Branch (14367:57): [True: 0, False: 0]
  ------------------
14368|      0|      update_base_pathname("/");
14369|      0|    }
14370|      0|  }
14371|  2.35k|  ADA_ASSERT_TRUE(validate());
14372|  2.35k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14466|  31.3k|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
14467|  31.3k|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
14468|  31.3k|          " bytes]");
14469|  31.3k|  ADA_ASSERT_TRUE(validate());
14470|  31.3k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14471|  31.3k|  if (input.empty()) {
  ------------------
  |  Branch (14471:7): [True: 40, False: 31.3k]
  ------------------
14472|     40|    return is_valid = false;
14473|     40|  }  // technically unnecessary.
14474|       |  // If input starts with U+005B ([), then:
14475|  31.3k|  if (input[0] == '[') {
  ------------------
  |  Branch (14475:7): [True: 1.39k, False: 29.9k]
  ------------------
14476|       |    // If input does not end with U+005D (]), validation error, return failure.
14477|  1.39k|    if (input.back() != ']') {
  ------------------
  |  Branch (14477:9): [True: 320, False: 1.07k]
  ------------------
14478|    320|      return is_valid = false;
14479|    320|    }
14480|  1.07k|    ada_log("parse_host ipv6");
14481|       |
14482|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
14483|       |    // trailing U+005D (]) removed.
14484|  1.07k|    input.remove_prefix(1);
14485|  1.07k|    input.remove_suffix(1);
14486|  1.07k|    return parse_ipv6(input);
14487|  1.39k|  }
14488|       |
14489|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
14490|       |  // input.
14491|  29.9k|  if (!is_special()) {
  ------------------
  |  Branch (14491:7): [True: 1.23k, False: 28.7k]
  ------------------
14492|  1.23k|    return parse_opaque_host(input);
14493|  1.23k|  }
14494|       |  // Let domain be the result of running UTF-8 decode without BOM on the
14495|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
14496|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
14497|       |  // which case we do not need to call the expensive 'to_ascii' if a few
14498|       |  // conditions are met: no '%' and no 'xn-' subsequence.
14499|       |
14500|       |  // Often, the input does not contain any forbidden code points, and no upper
14501|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
14502|       |  // optimize for such a common case.
14503|       |
14504|       |  // Fast path: try to parse as pure decimal IPv4 first.
14505|  28.7k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
14506|  28.7k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (14506:7): [True: 1.23k, False: 27.4k]
  ------------------
14507|  1.23k|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (14507:9): [True: 1.23k, False: 0]
  |  Branch (14507:27): [True: 43, False: 1.18k]
  ------------------
14508|     43|      update_base_hostname(input.substr(0, input.size() - 1));
14509|  1.18k|    } else {
14510|  1.18k|      update_base_hostname(input);
14511|  1.18k|    }
14512|  1.23k|    host_type = IPV4;
14513|  1.23k|    is_valid = true;
14514|  1.23k|    ada_log("parse_host fast path decimal ipv4");
14515|  1.23k|    ADA_ASSERT_TRUE(validate());
14516|  1.23k|    return true;
14517|  1.23k|  }
14518|  27.4k|  uint8_t is_forbidden_or_upper =
14519|  27.4k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
14520|  27.4k|                                                             input.size());
14521|  27.4k|  static constexpr std::string_view xn_dash{"xn-", 3};
14522|  27.4k|  if ((is_forbidden_or_upper & 1) == 0) {
  ------------------
  |  Branch (14522:7): [True: 16.6k, False: 10.8k]
  ------------------
14523|  16.6k|    if ((is_forbidden_or_upper & 2) != 0) {
  ------------------
  |  Branch (14523:9): [True: 2.88k, False: 13.7k]
  ------------------
14524|  2.88k|      std::string lowered(input);
14525|  2.88k|      unicode::to_lower_ascii(lowered.data(), lowered.size());
14526|  2.88k|      if (lowered.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14526:11): [True: 2.53k, False: 345]
  ------------------
14527|  2.68k|          lowered.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14527:11): [True: 149, False: 196]
  ------------------
14528|       |        // Check IPv4 before mutating so failures leave the buffer untouched.
14529|       |        // This also avoids a double buffer rewrite for hex/octal IPv4.
14530|  2.68k|        if (checkers::is_ipv4(lowered)) {
  ------------------
  |  Branch (14530:13): [True: 444, False: 2.24k]
  ------------------
14531|    444|          ada_log("parse_host fast path ipv4");
14532|    444|          return parse_ipv4(lowered, false);
14533|    444|        }
14534|  2.24k|        update_base_hostname(lowered);
14535|  2.24k|        ada_log("parse_host fast path ", get_hostname());
14536|  2.24k|        is_valid = true;
14537|  2.24k|        return true;
14538|  2.68k|      }
14539|  13.7k|    } else if (input.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14539:16): [True: 9.70k, False: 4.07k]
  ------------------
14540|  10.1k|               input.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14540:16): [True: 483, False: 3.59k]
  ------------------
14541|       |      // Check IPv4 before mutating so failures leave the buffer untouched.
14542|  10.1k|      if (checkers::is_ipv4(input)) {
  ------------------
  |  Branch (14542:11): [True: 2.56k, False: 7.62k]
  ------------------
14543|  2.56k|        ada_log("parse_host fast path ipv4");
14544|  2.56k|        return parse_ipv4(input, false);
14545|  2.56k|      }
14546|  7.62k|      update_base_hostname(input);
14547|  7.62k|      ada_log("parse_host fast path ", get_hostname());
14548|  7.62k|      is_valid = true;
14549|  7.62k|      return true;
14550|  10.1k|    }
14551|  16.6k|  } else if (const size_t first_percent = input.find('%');
14552|  10.8k|             first_percent != std::string_view::npos) {
  ------------------
  |  Branch (14552:14): [True: 2.05k, False: 8.75k]
  ------------------
14553|  2.05k|    std::string decoded = unicode::percent_decode(input, first_percent);
14554|  2.05k|    const uint8_t decoded_flags =
14555|  2.05k|        unicode::contains_forbidden_domain_code_point_or_upper(decoded.data(),
14556|  2.05k|                                                               decoded.size());
14557|  2.05k|    if ((decoded_flags & 1) == 0) {
  ------------------
  |  Branch (14557:9): [True: 1.34k, False: 718]
  ------------------
14558|  1.34k|      if ((decoded_flags & 2) != 0) {
  ------------------
  |  Branch (14558:11): [True: 594, False: 746]
  ------------------
14559|    594|        unicode::to_lower_ascii(decoded.data(), decoded.size());
14560|    594|      }
14561|  1.34k|      if (decoded.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14561:11): [True: 1.14k, False: 196]
  ------------------
14562|  1.24k|          decoded.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14562:11): [True: 102, False: 94]
  ------------------
14563|       |        // Check IPv4 before mutating so failures leave the buffer untouched.
14564|  1.24k|        if (checkers::is_ipv4(decoded)) {
  ------------------
  |  Branch (14564:13): [True: 532, False: 714]
  ------------------
14565|    532|          return parse_ipv4(decoded, false);
14566|    532|        }
14567|    714|        update_base_hostname(decoded);
14568|    714|        is_valid = true;
14569|    714|        return true;
14570|  1.24k|      }
14571|  1.34k|    }
14572|  2.05k|  }
14573|       |  // We have encountered at least one forbidden code point or the input contains
14574|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
14575|       |  // conversion.
14576|       |
14577|  13.3k|  ada_log("parse_host calling to_ascii");
14578|  13.3k|  std::optional<std::string> host = std::string(get_hostname());
14579|  13.3k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
14580|  13.3k|  if (!is_valid) {
  ------------------
  |  Branch (14580:7): [True: 4.06k, False: 9.29k]
  ------------------
14581|  4.06k|    ada_log("parse_host to_ascii returns false");
14582|  4.06k|    return is_valid = false;
14583|  4.06k|  }
14584|  9.29k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
14585|  9.29k|          " bytes]");
14586|       |
14587|  9.29k|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (14587:7): [True: 0, False: 9.29k]
  ------------------
14588|  9.29k|                          ada::unicode::is_forbidden_domain_code_point)) {
14589|      0|    return is_valid = false;
14590|      0|  }
14591|       |
14592|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
14593|       |  // asciiDomain.
14594|  9.29k|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (14594:7): [True: 388, False: 8.90k]
  ------------------
14595|    388|    ada_log("parse_host got ipv4 ", *host);
14596|    388|    return parse_ipv4(host.value(), false);
14597|    388|  }
14598|       |
14599|  8.90k|  update_base_hostname(host.value());
14600|  8.90k|  ADA_ASSERT_TRUE(validate());
14601|  8.90k|  return true;
14602|  9.29k|}
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8856|  1.79k|                                                       size_t pos) {
 8857|  1.79k|  ADA_ASSERT_TRUE(pos <= input.size());
 8858|       |  // The following is safer but unneeded if we have the above line:
 8859|       |  // return pos > input.size() ? std::string_view() : input.substr(pos);
 8860|  1.79k|  return input.substr(pos);
 8861|  1.79k|}
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15448|  10.7k|inline void url_aggregator::consume_prepared_path(std::string_view input) {
15449|  10.7k|  ada_log("url_aggregator::consume_prepared_path ", input);
15450|       |  /***
15451|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
15452|       |   * unfortunate. This particular function is nearly identical, except that it
15453|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
15454|       |   * very common) merely appends to the buffer. This is the same trivial path as
15455|       |   * with helpers::parse_prepared_path, except that we have the additional check
15456|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
15457|       |   * modify it, and then insert it back into the buffer.
15458|       |   */
15459|  10.7k|  uint8_t accumulator = checkers::path_signature(input);
15460|       |  // Let us first detect a trivial case.
15461|       |  // If it is special, we check that we have no dot, no %,  no \ and no
15462|       |  // character needing percent encoding. Otherwise, we check that we have no %,
15463|       |  // no dot, and no character needing percent encoding.
15464|  10.7k|  constexpr uint8_t need_encoding = 1;
15465|  10.7k|  constexpr uint8_t backslash_char = 2;
15466|  10.7k|  constexpr uint8_t dot_char = 4;
15467|  10.7k|  constexpr uint8_t percent_char = 8;
15468|  10.7k|  bool special = type != ada::scheme::NOT_SPECIAL;
15469|  10.7k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (15469:39): [True: 1.68k, False: 9.03k]
  ------------------
15470|  1.68k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (15470:39): [True: 135, False: 1.54k]
  ------------------
15471|  10.7k|  bool trivial_path =
15472|  10.7k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (15472:7): [True: 4.94k, False: 5.77k]
  |  Branch (15472:8): [True: 9.15k, False: 1.56k]
  ------------------
15473|  10.7k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
15474|  1.56k|                  0)) &&
15475|  4.94k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (15475:7): [True: 4.85k, False: 97]
  ------------------
15476|  10.7k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (15476:7): [True: 1.59k, False: 9.12k]
  |  Branch (15476:34): [True: 1.57k, False: 23]
  ------------------
15477|       |    // '4' means that we have at least one dot, but nothing that requires
15478|       |    // percent encoding or decoding. The only part that is not trivial is
15479|       |    // that we may have single dots and double dots path segments.
15480|       |    // If we have such segments, then we either have a path that begins
15481|       |    // with '.' (easy to check), or we have the sequence './'.
15482|       |    // Note: input cannot be empty, it must at least contain one character ('.')
15483|       |    // Note: we know that '\' is not present.
15484|  1.57k|    if (input[0] != '.') {
  ------------------
  |  Branch (15484:9): [True: 812, False: 758]
  ------------------
15485|    812|      size_t slashdot = 0;
15486|    812|      bool dot_is_file = true;
15487|  5.59k|      for (;;) {
15488|  5.59k|        slashdot = input.find("/.", slashdot);
15489|  5.59k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (15489:13): [True: 812, False: 4.78k]
  ------------------
15490|    812|          break;
15491|  4.78k|        } else {  // uncommon
15492|       |          // only three cases matter: /./, /.. or a final /
15493|  4.78k|          slashdot += 2;
15494|  4.78k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (15494:28): [True: 222, False: 4.56k]
  |  Branch (15494:56): [True: 2.92k, False: 1.64k]
  ------------------
15495|  1.64k|                           input[slashdot] == '/');
  ------------------
  |  Branch (15495:28): [True: 1.20k, False: 439]
  ------------------
15496|  4.78k|        }
15497|  5.59k|      }
15498|    812|      trivial_path = dot_is_file;
15499|    812|    }
15500|  1.57k|  }
15501|  10.7k|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (15501:7): [True: 5.04k, False: 5.67k]
  |  Branch (15501:23): [True: 4.96k, False: 88]
  ------------------
15502|  4.96k|    ada_log("parse_path trivial");
15503|  4.96k|    buffer += '/';
15504|  4.96k|    buffer += input;
15505|  4.96k|    return;
15506|  4.96k|  }
15507|  5.76k|  std::string path = std::string(get_pathname());
15508|       |  // We are going to need to look a bit at the path, but let us see if we can
15509|       |  // ignore percent encoding *and* backslashes *and* percent characters.
15510|       |  // Except for the trivial case, this is likely to capture 99% of paths out
15511|       |  // there.
15512|  5.76k|  bool fast_path =
15513|  5.76k|      (special &&
  ------------------
  |  Branch (15513:8): [True: 4.59k, False: 1.17k]
  ------------------
15514|  4.59k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (15514:8): [True: 1.39k, False: 3.20k]
  ------------------
15515|  1.39k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (15515:7): [True: 1.19k, False: 199]
  ------------------
15516|  5.76k|  if (fast_path) {
  ------------------
  |  Branch (15516:7): [True: 1.19k, False: 4.56k]
  ------------------
15517|  1.19k|    ada_log("parse_prepared_path fast");
15518|       |    // Here we don't need to worry about \ or percent encoding.
15519|       |    // We also do not have a file protocol. We might have dots, however,
15520|       |    // but dots must as appear as '.', and they cannot be encoded because
15521|       |    // the symbol '%' is not present.
15522|  1.19k|    size_t previous_location = 0;  // We start at 0.
15523|  15.1k|    do {
15524|  15.1k|      size_t new_location = input.find('/', previous_location);
15525|       |      // std::string_view path_view = input;
15526|       |      //  We process the last segment separately:
15527|  15.1k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (15527:11): [True: 1.19k, False: 13.9k]
  ------------------
15528|  1.19k|        std::string_view path_view = input.substr(previous_location);
15529|  1.19k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (15529:13): [True: 125, False: 1.06k]
  ------------------
15530|       |          // e.g., if you receive ".." with an empty path, you go to "/".
15531|    125|          if (path.empty()) {
  ------------------
  |  Branch (15531:15): [True: 52, False: 73]
  ------------------
15532|     52|            path = '/';
15533|     52|            update_base_pathname(path);
15534|     52|            return;
15535|     52|          }
15536|       |          // Fast case where we have nothing to do:
15537|     73|          if (path.back() == '/') {
  ------------------
  |  Branch (15537:15): [True: 22, False: 51]
  ------------------
15538|     22|            update_base_pathname(path);
15539|     22|            return;
15540|     22|          }
15541|       |          // If you have the path "/joe/myfriend",
15542|       |          // then you delete 'myfriend'.
15543|     51|          path.resize(path.rfind('/') + 1);
15544|     51|          update_base_pathname(path);
15545|     51|          return;
15546|     73|        }
15547|  1.06k|        path += '/';
15548|  1.06k|        if (path_view != ".") {
  ------------------
  |  Branch (15548:13): [True: 789, False: 278]
  ------------------
15549|    789|          path.append(path_view);
15550|    789|        }
15551|  1.06k|        update_base_pathname(path);
15552|  1.06k|        return;
15553|  13.9k|      } else {
15554|       |        // This is a non-final segment.
15555|  13.9k|        std::string_view path_view =
15556|  13.9k|            input.substr(previous_location, new_location - previous_location);
15557|  13.9k|        previous_location = new_location + 1;
15558|  13.9k|        if (path_view == "..") {
  ------------------
  |  Branch (15558:13): [True: 3.13k, False: 10.7k]
  ------------------
15559|  3.13k|          size_t last_delimiter = path.rfind('/');
15560|  3.13k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (15560:15): [True: 1.18k, False: 1.94k]
  ------------------
15561|  1.18k|            path.erase(last_delimiter);
15562|  1.18k|          }
15563|  10.7k|        } else if (path_view != ".") {
  ------------------
  |  Branch (15563:20): [True: 8.76k, False: 2.03k]
  ------------------
15564|  8.76k|          path += '/';
15565|  8.76k|          path.append(path_view);
15566|  8.76k|        }
15567|  13.9k|      }
15568|  15.1k|    } while (true);
  ------------------
  |  Branch (15568:14): [True: 13.9k, Folded]
  ------------------
15569|  4.56k|  } else {
15570|  4.56k|    ada_log("parse_path slow");
15571|       |    // we have reached the general case
15572|  4.56k|    bool needs_percent_encoding = (accumulator & 1);
15573|  4.56k|    std::string path_buffer_tmp;
15574|  30.1k|    do {
15575|  30.1k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (15575:26): [True: 23.1k, False: 7.00k]
  |  Branch (15575:37): [True: 1.96k, False: 21.1k]
  ------------------
15576|  30.1k|                            ? input.find_first_of("/\\")
15577|  30.1k|                            : input.find('/');
15578|  30.1k|      std::string_view path_view = input;
15579|  30.1k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (15579:11): [True: 25.5k, False: 4.56k]
  ------------------
15580|  25.5k|        path_view.remove_suffix(path_view.size() - location);
15581|  25.5k|        input.remove_prefix(location + 1);
15582|  25.5k|      }
15583|       |      // path_buffer is either path_view or it might point at a percent encoded
15584|       |      // temporary string.
15585|  30.1k|      std::string_view path_buffer =
15586|  30.1k|          (needs_percent_encoding &&
  ------------------
  |  Branch (15586:12): [True: 16.3k, False: 13.8k]
  ------------------
15587|  16.3k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (15587:12): [True: 5.89k, False: 10.4k]
  ------------------
15588|  16.3k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
15589|  30.1k|              ? path_buffer_tmp
15590|  30.1k|              : path_view;
15591|  30.1k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (15591:11): [True: 4.06k, False: 26.0k]
  ------------------
15592|  4.06k|        helpers::shorten_path(path, type);
15593|  4.06k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (15593:13): [True: 255, False: 3.81k]
  ------------------
15594|    255|          path += '/';
15595|    255|        }
15596|  26.0k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (15596:18): [True: 1.44k, False: 24.6k]
  ------------------
15597|  1.44k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (15597:18): [True: 133, False: 1.31k]
  ------------------
15598|    133|        path += '/';
15599|    133|      }
15600|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
15601|  25.9k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (15601:16): [True: 24.6k, False: 1.31k]
  ------------------
15602|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
15603|       |        // Windows drive letter, then replace the second code point in
15604|       |        // path_buffer with U+003A (:).
15605|  24.6k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (15605:13): [True: 2.38k, False: 22.2k]
  |  Branch (15605:48): [True: 1.03k, False: 1.35k]
  ------------------
15606|  1.03k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (15606:13): [True: 157, False: 877]
  ------------------
15607|    157|          path += '/';
15608|    157|          path += path_buffer[0];
15609|    157|          path += ':';
15610|    157|          path_buffer.remove_prefix(2);
15611|    157|          path.append(path_buffer);
15612|  24.4k|        } else {
15613|       |          // Append path_buffer to url's path.
15614|  24.4k|          path += '/';
15615|  24.4k|          path.append(path_buffer);
15616|  24.4k|        }
15617|  24.6k|      }
15618|  30.1k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (15618:11): [True: 4.56k, False: 25.5k]
  ------------------
15619|  4.56k|        update_base_pathname(path);
15620|  4.56k|        return;
15621|  4.56k|      }
15622|  30.1k|    } while (true);
  ------------------
  |  Branch (15622:14): [True: 25.5k, Folded]
  ------------------
15623|  4.56k|  }
15624|  5.76k|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   14|  34.8k|ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept {
   15|       |  // The string is not empty and does not contain upper case ASCII characters.
   16|  34.8k|  if (!last_label_may_be_a_number(view)) {
  ------------------
  |  Branch (16:7): [True: 25.4k, False: 9.32k]
  ------------------
   17|  25.4k|    return false;
   18|  25.4k|  }
   19|       |  // If the address ends with a dot, we need to prune it (special case).
   20|  9.32k|  if (view.ends_with('.')) {
  ------------------
  |  Branch (20:7): [True: 492, False: 8.83k]
  ------------------
   21|    492|    view.remove_suffix(1);
   22|    492|  }
   23|       |  // From the last character, find the last dot.
   24|  9.32k|  size_t last_dot = view.rfind('.');
   25|  9.32k|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (25:7): [True: 2.63k, False: 6.69k]
  ------------------
   26|       |    // We have at least one dot.
   27|  2.63k|    view.remove_prefix(last_dot + 1);
   28|  2.63k|  }
   29|       |  /** Optimization opportunity: we have basically identified the last number of
   30|       |     the ipv4 if we return true here. We might as well parse it and have at
   31|       |     least one number parsed when we get to parse_ipv4. */
   32|  9.32k|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (32:7): [True: 4.63k, False: 4.68k]
  ------------------
   33|  4.63k|    return true;
   34|  4.63k|  }
   35|       |  // It could be hex (0x), but not if there is a single character.
   36|  4.68k|  if (view.size() == 1) {
  ------------------
  |  Branch (36:7): [True: 0, False: 4.68k]
  ------------------
   37|      0|    return false;
   38|      0|  }
   39|       |  // It must start with 0x.
   40|  4.68k|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (40:7): [True: 2.32k, False: 2.36k]
  ------------------
   41|  2.32k|    return false;
   42|  2.32k|  }
   43|       |  // We must allow "0x".
   44|  2.36k|  if (view.size() == 2) {
  ------------------
  |  Branch (44:7): [True: 264, False: 2.10k]
  ------------------
   45|    264|    return true;
   46|    264|  }
   47|       |  // We have 0x followed by some characters, we need to check that they are
   48|       |  // hexadecimals.
   49|  2.10k|  view.remove_prefix(2);
   50|  2.10k|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   51|  2.36k|}
_ZN3ada7unicode16is_lowercase_hexEc:
 7437|  23.1k|ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept {
 7438|  23.1k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7438:11): [True: 23.1k, False: 0]
  |  Branch (7438:23): [True: 12.0k, False: 11.1k]
  |  Branch (7438:37): [True: 11.1k, False: 0]
  |  Branch (7438:49): [True: 10.0k, False: 1.11k]
  ------------------
 7439|  23.1k|}
_ZN3ada3url12parse_schemeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
10315|  22.0k|ada_really_inline bool url::parse_scheme(const std::string_view input) {
10316|  22.0k|  auto parsed_type = ada::scheme::get_scheme_type(input);
10317|  22.0k|  bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
10318|       |  /**
10319|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
10320|       |   *http, https), in which case, we can go really fast.
10321|       |   **/
10322|  22.0k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (10322:7): [True: 10.7k, False: 11.2k]
  ------------------
10323|       |    if constexpr (has_state_override) {
10324|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
10325|       |      // then return.
10326|       |      if (is_special() != is_input_special) {
10327|       |        return false;
10328|       |      }
10329|       |
10330|       |      // If url includes credentials or has a non-null port, and buffer is
10331|       |      // "file", then return.
10332|       |      if ((has_credentials() || port.has_value()) &&
10333|       |          parsed_type == ada::scheme::type::FILE) {
10334|       |        return false;
10335|       |      }
10336|       |
10337|       |      // If url's scheme is "file" and its host is an empty host, then return.
10338|       |      // An empty host is the empty string.
10339|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
10340|       |          host->empty()) {
10341|       |        return false;
10342|       |      }
10343|       |    }
10344|       |
10345|  10.7k|    type = parsed_type;
10346|       |
10347|       |    if constexpr (has_state_override) {
10348|       |      // This is uncommon.
10349|       |      uint16_t urls_scheme_port = get_special_port();
10350|       |
10351|       |      if (urls_scheme_port) {
10352|       |        // If url's port is url's scheme's default port, then set url's port to
10353|       |        // null.
10354|       |        if (port.has_value() && *port == urls_scheme_port) {
10355|       |          port = std::nullopt;
10356|       |        }
10357|       |      }
10358|       |    }
10359|  11.2k|  } else {  // slow path
10360|  11.2k|    std::string _buffer(input);
10361|       |    // Next function is only valid if the input is ASCII and returns false
10362|       |    // otherwise, but it seems that we always have ascii content so we do not
10363|       |    // need to check the return value.
10364|       |    // bool is_ascii =
10365|  11.2k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
10366|       |
10367|       |    if constexpr (has_state_override) {
10368|       |      // The state-override validation errors below ("return" in the WHATWG URL
10369|       |      // parser) leave the URL unchanged. The setter contract is
10370|       |      // "true on success, false if the scheme is invalid" -- the fast path
10371|       |      // above already returns false here, so the slow path must agree.
10372|       |
10373|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
10374|       |      // then return. If url's scheme is not a special scheme and buffer is a
10375|       |      // special scheme, then return.
10376|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
10377|       |        return false;
10378|       |      }
10379|       |
10380|       |      // If url includes credentials or has a non-null port, and buffer is
10381|       |      // "file", then return.
10382|       |      if ((has_credentials() || port.has_value()) && _buffer == "file") {
10383|       |        return false;
10384|       |      }
10385|       |
10386|       |      // If url's scheme is "file" and its host is an empty host, then return.
10387|       |      // An empty host is the empty string.
10388|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
10389|       |          host->empty()) {
10390|       |        return false;
10391|       |      }
10392|       |    }
10393|       |
10394|  11.2k|    set_scheme(std::move(_buffer));
10395|       |
10396|       |    if constexpr (has_state_override) {
10397|       |      // This is uncommon.
10398|       |      uint16_t urls_scheme_port = get_special_port();
10399|       |
10400|       |      if (urls_scheme_port) {
10401|       |        // If url's port is url's scheme's default port, then set url's port to
10402|       |        // null.
10403|       |        if (port.has_value() && *port == urls_scheme_port) {
10404|       |          port = std::nullopt;
10405|       |        }
10406|       |      }
10407|       |    }
10408|  11.2k|  }
10409|       |
10410|  22.0k|  return true;
10411|  22.0k|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
13981|  43.9k|    const std::string_view input_with_colon) {
13982|  43.9k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
13983|  43.9k|  ADA_ASSERT_TRUE(validate());
13984|  43.9k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
13985|  43.9k|  std::string_view input{input_with_colon};
13986|  43.9k|  input.remove_suffix(1);
13987|  43.9k|  auto parsed_type = ada::scheme::get_scheme_type(input);
13988|  43.9k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
13989|       |  /**
13990|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
13991|       |   *http, https), in which case, we can go really fast.
13992|       |   **/
13993|  43.9k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (13993:7): [True: 21.5k, False: 22.4k]
  ------------------
13994|       |    if constexpr (has_state_override) {
13995|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
13996|       |      // then return.
13997|       |      if (is_special() != is_input_special) {
13998|       |        return false;
13999|       |      }
14000|       |
14001|       |      // If url includes credentials or has a non-null port, and buffer is
14002|       |      // "file", then return.
14003|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14004|       |          parsed_type == ada::scheme::type::FILE) {
14005|       |        return false;
14006|       |      }
14007|       |
14008|       |      // If url's scheme is "file" and its host is an empty host, then return.
14009|       |      // An empty host is the empty string.
14010|       |      if (type == ada::scheme::type::FILE &&
14011|       |          components.host_start == components.host_end) {
14012|       |        return false;
14013|       |      }
14014|       |    }
14015|       |
14016|  21.5k|    type = parsed_type;
14017|  21.5k|    set_scheme_from_view_with_colon(input_with_colon);
14018|       |
14019|       |    if constexpr (has_state_override) {
14020|       |      // This is uncommon.
14021|       |      uint16_t urls_scheme_port = get_special_port();
14022|       |
14023|       |      if (urls_scheme_port) {
14024|       |        // If url's port is url's scheme's default port, then set url's port to
14025|       |        // null.
14026|       |        if (components.port == urls_scheme_port) {
14027|       |          clear_port();
14028|       |        }
14029|       |      }
14030|       |    }
14031|  22.4k|  } else {  // slow path
14032|  22.4k|    std::string _buffer(input);
14033|       |    // Next function is only valid if the input is ASCII and returns false
14034|       |    // otherwise, but it seems that we always have ascii content so we do not
14035|       |    // need to check the return value.
14036|  22.4k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
14037|       |
14038|       |    if constexpr (has_state_override) {
14039|       |      // The state-override validation errors below ("return" in the WHATWG URL
14040|       |      // parser) leave the URL unchanged. The setter contract is
14041|       |      // "true on success, false if the scheme is invalid" -- the fast path
14042|       |      // above already returns false here, so the slow path must agree.
14043|       |
14044|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
14045|       |      // then return. If url's scheme is not a special scheme and buffer is a
14046|       |      // special scheme, then return.
14047|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
14048|       |        return false;
14049|       |      }
14050|       |
14051|       |      // If url includes credentials or has a non-null port, and buffer is
14052|       |      // "file", then return.
14053|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14054|       |          _buffer == "file") {
14055|       |        return false;
14056|       |      }
14057|       |
14058|       |      // If url's scheme is "file" and its host is an empty host, then return.
14059|       |      // An empty host is the empty string.
14060|       |      if (type == ada::scheme::type::FILE &&
14061|       |          components.host_start == components.host_end) {
14062|       |        return false;
14063|       |      }
14064|       |    }
14065|       |
14066|  22.4k|    set_scheme(_buffer);
14067|       |
14068|       |    if constexpr (has_state_override) {
14069|       |      // This is uncommon.
14070|       |      uint16_t urls_scheme_port = get_special_port();
14071|       |
14072|       |      if (urls_scheme_port) {
14073|       |        // If url's port is url's scheme's default port, then set url's port to
14074|       |        // null.
14075|       |        if (components.port == urls_scheme_port) {
14076|       |          clear_port();
14077|       |        }
14078|       |      }
14079|       |    }
14080|  22.4k|  }
14081|  43.9k|  ADA_ASSERT_TRUE(validate());
14082|  43.9k|  return true;
14083|  43.9k|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14107|  21.5k|    std::string_view new_scheme_with_colon) {
14108|  21.5k|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
14109|  21.5k|          new_scheme_with_colon);
14110|  21.5k|  ADA_ASSERT_TRUE(validate());
14111|  21.5k|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
14112|  21.5k|                  new_scheme_with_colon.back() == ':');
14113|       |  // next line could overflow but unsigned arithmetic has well-defined
14114|       |  // overflows.
14115|  21.5k|  uint32_t new_difference =
14116|  21.5k|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
14117|       |
14118|  21.5k|  if (buffer.empty()) {
  ------------------
  |  Branch (14118:7): [True: 21.5k, False: 0]
  ------------------
14119|  21.5k|    buffer.append(new_scheme_with_colon);
14120|  21.5k|  } else {
14121|      0|    buffer.erase(0, components.protocol_end);
14122|      0|    buffer.insert(0, new_scheme_with_colon);
14123|      0|  }
14124|  21.5k|  components.protocol_end += new_difference;
14125|       |
14126|  21.5k|  apply_shifted_non_scheme_offsets(components, new_difference);
14127|  21.5k|  ADA_ASSERT_TRUE(validate());
14128|  21.5k|}
_ZN3ada14url_aggregator10set_schemeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14130|  22.4k|inline void url_aggregator::set_scheme(std::string_view new_scheme) {
14131|  22.4k|  ada_log("url_aggregator::set_scheme ", new_scheme);
14132|  22.4k|  ADA_ASSERT_TRUE(validate());
14133|  22.4k|  ADA_ASSERT_TRUE(new_scheme.empty() || new_scheme.back() != ':');
14134|       |  // next line could overflow but unsigned arithmetic has well-defined
14135|       |  // overflows.
14136|  22.4k|  uint32_t new_difference =
14137|  22.4k|      uint32_t(new_scheme.size()) - components.protocol_end + 1;
14138|       |
14139|  22.4k|  type = ada::scheme::get_scheme_type(new_scheme);
14140|  22.4k|  if (buffer.empty()) {
  ------------------
  |  Branch (14140:7): [True: 22.4k, False: 0]
  ------------------
14141|  22.4k|    buffer.append(helpers::concat(new_scheme, ":"));
14142|  22.4k|  } else {
14143|      0|    buffer.erase(0, components.protocol_end);
14144|      0|    buffer.insert(0, helpers::concat(new_scheme, ":"));
14145|      0|  }
14146|  22.4k|  components.protocol_end = uint32_t(new_scheme.size() + 1);
14147|       |
14148|  22.4k|  apply_shifted_non_scheme_offsets(components, new_difference);
14149|  22.4k|  ADA_ASSERT_TRUE(validate());
14150|  22.4k|}

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5868|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1085|   366k|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1086|   366k|  return !!(a[i >> 3] & (1 << (i & 7)));
 1087|   366k|}
_ZN2tl10unexpectedIN3ada6errorsEEC2EOS2_:
 2215|   105k|  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3701|  26.4k|      : impl_base(unexpect, std::move(e.value())),
 3702|  26.4k|        ctor_base(detail::default_constructor_tag{}) {}
_ZNR2tl10unexpectedIN3ada6errorsEE5valueEv:
 2229|  52.8k|  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2687|  26.4k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada3urlENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3305|  51.7k|  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_:
 3787|  25.3k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3660|  25.3k|      : impl_base(in_place, std::forward<Args>(args)...),
 3661|  25.3k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2675|  25.3k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada3urlC2EOS0_:
 5107|  25.3k|  url(url&& u) noexcept = default;
_ZN3ada8url_baseD2Ev:
 1711|   213k|  virtual ~url_base() = default;
_ZN3ada3urlD2Ev:
 5110|  77.0k|  ~url() override = default;
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3701|  26.4k|      : impl_base(unexpect, std::move(e.value())),
 3702|  26.4k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2687|  26.4k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3305|  51.7k|  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_:
 3787|  25.3k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3660|  25.3k|      : impl_base(in_place, std::forward<Args>(args)...),
 3661|  25.3k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2675|  25.3k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorC2EOS0_:
 7921|  25.3k|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZN3ada14url_aggregatorD2Ev:
 7924|   136k|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6922|   118k|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6923|   118k|  if (scheme.empty()) {
  ------------------
  |  Branch (6923:7): [True: 0, False: 118k]
  ------------------
 6924|      0|    return ada::scheme::NOT_SPECIAL;
 6925|      0|  }
 6926|   118k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6927|   118k|  const std::string_view target = details::is_special_list[hash_value];
 6928|   118k|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6928:7): [True: 94.9k, False: 23.7k]
  ------------------
 6929|  94.9k|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6929:7): [True: 65.1k, False: 29.7k]
  ------------------
 6930|  94.9k|          details::scheme_keys[hash_value]) {
 6931|  65.1k|    return ada::scheme::type(hash_value);
 6932|  65.1k|  } else {
 6933|  53.4k|    return ada::scheme::NOT_SPECIAL;
 6934|  53.4k|  }
 6935|   118k|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6863|  94.9k|inline uint64_t branchless_load5(const char* p, size_t n) {
 6864|  94.9k|  uint64_t input = (uint8_t)p[0];
 6865|  94.9k|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6866|  94.9k|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6867|  94.9k|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6868|  94.9k|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6869|  94.9k|  return input;
 6870|  94.9k|}
_ZN3ada14url_aggregatorC2Ev:
 7919|   110k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 5016|   110k|  url_components() = default;
_ZN3ada14url_aggregatoraSEOS0_:
 7922|  12.8k|  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1459|  50.3k|try_parse_ipv4_fast(std::string_view input) noexcept {
 1460|  50.3k|  const size_t len = input.size();
 1461|       |  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
 1462|  50.3k|  if (len < 7 || len > 16) [[unlikely]] {
  ------------------
  |  Branch (1462:7): [True: 26.8k, False: 23.4k]
  |  Branch (1462:18): [True: 9.76k, False: 13.6k]
  ------------------
 1463|  36.6k|    return ipv4_fast_fail;
 1464|  36.6k|  }
 1465|  13.6k|  const char* data = input.data();
 1466|       |
 1467|       |#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
 1468|       |  return detail::try_parse_ipv4_avx512(data, len);
 1469|       |#else
 1470|  13.6k|  return detail::parse_ipv4_decimal_scalar(data, data + len);
 1471|  50.3k|#endif
 1472|  50.3k|}
_ZN3ada8checkers6detail25parse_ipv4_decimal_scalarEPKcS3_:
 1300|  13.6k|parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
 1301|  13.6k|  uint32_t ipv4 = 0;
 1302|  29.5k|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1302:19): [True: 26.0k, False: 3.52k]
  ------------------
 1303|  26.0k|    if (p == pend) [[unlikely]] {
  ------------------
  |  Branch (1303:9): [True: 21, False: 25.9k]
  ------------------
 1304|     21|      return ipv4_fast_fail;
 1305|     21|    }
 1306|  25.9k|    uint32_t val;
 1307|  25.9k|    char c = *p;
 1308|  25.9k|    if (c >= '0' && c <= '9') [[likely]] {
  ------------------
  |  Branch (1308:9): [True: 23.7k, False: 2.19k]
  |  Branch (1308:21): [True: 19.1k, False: 4.65k]
  ------------------
 1309|  19.1k|      val = static_cast<uint32_t>(c - '0');
 1310|  19.1k|      ++p;
 1311|  19.1k|    } else {
 1312|  6.85k|      return ipv4_fast_fail;
 1313|  6.85k|    }
 1314|  19.1k|    if (p < pend) {
  ------------------
  |  Branch (1314:9): [True: 17.2k, False: 1.88k]
  ------------------
 1315|  17.2k|      c = *p;
 1316|  17.2k|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1316:11): [True: 7.15k, False: 10.0k]
  |  Branch (1316:23): [True: 5.89k, False: 1.26k]
  ------------------
 1317|  5.89k|        if (val == 0) [[unlikely]] {
  ------------------
  |  Branch (1317:13): [True: 494, False: 5.40k]
  ------------------
 1318|    494|          return ipv4_fast_fail;
 1319|    494|        }
 1320|  5.40k|        val = val * 10u + static_cast<uint32_t>(c - '0');
 1321|  5.40k|        ++p;
 1322|  5.40k|        if (p < pend) {
  ------------------
  |  Branch (1322:13): [True: 4.75k, False: 646]
  ------------------
 1323|  4.75k|          c = *p;
 1324|  4.75k|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1324:15): [True: 3.26k, False: 1.48k]
  |  Branch (1324:27): [True: 3.13k, False: 130]
  ------------------
 1325|  3.13k|            val = val * 10u + static_cast<uint32_t>(c - '0');
 1326|  3.13k|            ++p;
 1327|  3.13k|            if (val > 255u) [[unlikely]] {
  ------------------
  |  Branch (1327:17): [True: 801, False: 2.33k]
  ------------------
 1328|    801|              return ipv4_fast_fail;
 1329|    801|            }
 1330|  3.13k|          }
 1331|  4.75k|        }
 1332|  5.40k|      }
 1333|  17.2k|    }
 1334|  17.8k|    ipv4 = (ipv4 << 8) | val;
 1335|  17.8k|    if (i < 3) {
  ------------------
  |  Branch (1335:9): [True: 14.3k, False: 3.52k]
  ------------------
 1336|  14.3k|      if (p == pend || *p != '.') [[unlikely]] {
  ------------------
  |  Branch (1336:11): [True: 381, False: 13.9k]
  |  Branch (1336:24): [True: 1.62k, False: 12.3k]
  ------------------
 1337|  2.00k|        return ipv4_fast_fail;
 1338|  2.00k|      }
 1339|  12.3k|      ++p;
 1340|  12.3k|    }
 1341|  17.8k|  }
 1342|  3.52k|  if (p != pend) {
  ------------------
  |  Branch (1342:7): [True: 658, False: 2.86k]
  ------------------
 1343|    658|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1343:9): [True: 395, False: 263]
  |  Branch (1343:26): [True: 66, False: 329]
  ------------------
 1344|     66|      return ipv4;
 1345|     66|    }
 1346|    592|    return ipv4_fast_fail;
 1347|    658|  }
 1348|  2.86k|  return ipv4;
 1349|  3.52k|}
_ZNK3ada3url15has_credentialsEv:
 7450|  22.4k|[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
 7451|  22.4k|  return !username.empty() || !password.empty();
  ------------------
  |  Branch (7451:10): [True: 2.00k, False: 20.4k]
  |  Branch (7451:31): [True: 236, False: 20.1k]
  ------------------
 7452|  22.4k|}
_ZNK3ada8url_base10is_specialEv:
 7410|   337k|    const noexcept {
 7411|   337k|  return type != ada::scheme::NOT_SPECIAL;
 7412|   337k|}
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEEcvbEv:
 4100|  29.6k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEptEv:
 4069|  20.7k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 4070|  20.7k|    TL_ASSERT(has_value());
  ------------------
  |  | 2122|  20.7k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4070:5): [True: 20.7k, False: 0]
  ------------------
 4071|  20.7k|    return valptr();
 4072|  20.7k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE6valptrEv:
 3365|  20.7k|  T* valptr() { return std::addressof(this->m_val); }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EED2Ev:
 2697|  51.7k|  ~expected_storage_base() {
 2698|  51.7k|    if (m_has_val) {
  ------------------
  |  Branch (2698:9): [True: 25.3k, False: 26.4k]
  ------------------
 2699|  25.3k|      m_val.~T();
 2700|  25.3k|    }
 2701|  51.7k|  }
_ZNK3ada3url13get_href_sizeEv:
 7708|  26.3k|[[nodiscard]] inline size_t url::get_href_size() const noexcept {
 7709|  26.3k|  size_t size = 0;
 7710|  26.3k|  if (is_special()) {
  ------------------
  |  Branch (7710:7): [True: 17.1k, False: 9.16k]
  ------------------
 7711|  17.1k|    size += ada::scheme::details::is_special_list[type].size() + 1;
 7712|  17.1k|  } else {
 7713|  9.16k|    size += non_special_scheme.size() + 1;
 7714|  9.16k|  }
 7715|  26.3k|  if (host.has_value()) {
  ------------------
  |  Branch (7715:7): [True: 18.7k, False: 7.63k]
  ------------------
 7716|  18.7k|    size += host->size();
 7717|  18.7k|    size += 2;
 7718|  18.7k|    if (has_credentials()) {
  ------------------
  |  Branch (7718:9): [True: 1.52k, False: 17.1k]
  ------------------
 7719|  1.52k|      size += username.size();
 7720|  1.52k|      if (!password.empty()) {
  ------------------
  |  Branch (7720:11): [True: 456, False: 1.06k]
  ------------------
 7721|    456|        size += 1 + password.size();
 7722|    456|      }
 7723|  1.52k|      size += 1;
 7724|  1.52k|    }
 7725|  18.7k|    if (port.has_value()) {
  ------------------
  |  Branch (7725:9): [True: 3.88k, False: 14.8k]
  ------------------
 7726|  3.88k|      size += 1;
 7727|  3.88k|      uint16_t p = *port;
 7728|  3.88k|      size += (p >= 10000)  ? 5
  ------------------
  |  Branch (7728:15): [True: 150, False: 3.73k]
  ------------------
 7729|  3.88k|              : (p >= 1000) ? 4
  ------------------
  |  Branch (7729:17): [True: 125, False: 3.60k]
  ------------------
 7730|  3.73k|              : (p >= 100)  ? 3
  ------------------
  |  Branch (7730:17): [True: 94, False: 3.51k]
  ------------------
 7731|  3.60k|              : (p >= 10)   ? 2
  ------------------
  |  Branch (7731:17): [True: 79, False: 3.43k]
  ------------------
 7732|  3.51k|                            : 1;
 7733|  3.88k|    }
 7734|  18.7k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7734:14): [True: 2.77k, False: 4.85k]
  |  Branch (7734:34): [True: 75, False: 2.70k]
  ------------------
 7735|     75|    size += 2;
 7736|     75|  }
 7737|  26.3k|  size += path.size();
 7738|  26.3k|  if (query.has_value()) {
  ------------------
  |  Branch (7738:7): [True: 2.14k, False: 24.1k]
  ------------------
 7739|  2.14k|    size += 1 + query->size();
 7740|  2.14k|  }
 7741|  26.3k|  if (hash.has_value()) {
  ------------------
  |  Branch (7741:7): [True: 1.66k, False: 24.6k]
  ------------------
 7742|  1.66k|    size += 1 + hash->size();
 7743|  1.66k|  }
 7744|  26.3k|  return size;
 7745|  26.3k|}
_ZN3ada8checkers8is_alphaEc:
 1280|   102k|constexpr bool is_alpha(char x) noexcept {
 1281|   102k|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1281:10): [True: 92.5k, False: 9.61k]
  |  Branch (1281:34): [True: 92.2k, False: 310]
  ------------------
 1282|   102k|}
_ZN3ada8checkers8to_lowerEc:
 1278|   194k|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 4082|  4.57k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 4083|  4.57k|    TL_ASSERT(has_value());
  ------------------
  |  | 2122|  4.57k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4083:5): [True: 4.57k, False: 0]
  ------------------
 4084|  4.57k|    return val();
 4085|  4.57k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3374|  4.57k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3375|  4.57k|    return this->m_val;
 3376|  4.57k|  }
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEE9has_valueEv:
 4099|  81.4k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN3ada8checkers26last_label_may_be_a_numberENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1253|  53.9k|constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
 1254|  53.9k|  if (view.empty()) {
  ------------------
  |  Branch (1254:7): [True: 0, False: 53.9k]
  ------------------
 1255|      0|    return false;
 1256|      0|  }
 1257|  53.9k|  const char* start = view.data();
 1258|  53.9k|  const char* end = start + view.size();
 1259|  53.9k|  if (end[-1] == '.') {
  ------------------
  |  Branch (1259:7): [True: 5.17k, False: 48.7k]
  ------------------
 1260|  5.17k|    --end;
 1261|  5.17k|    if (end == start) {
  ------------------
  |  Branch (1261:9): [True: 3.38k, False: 1.79k]
  ------------------
 1262|  3.38k|      return false;
 1263|  3.38k|    }
 1264|  5.17k|  }
 1265|  50.5k|  if (!is_ipv4_number_char(end[-1])) {
  ------------------
  |  Branch (1265:7): [True: 24.9k, False: 25.6k]
  ------------------
 1266|  24.9k|    return false;
 1267|  24.9k|  }
 1268|  25.6k|  const char* label = end;
 1269|   168k|  while (label != start && is_ipv4_number_char(label[-1])) {
  ------------------
  |  Branch (1269:10): [True: 156k, False: 12.3k]
  |  Branch (1269:28): [True: 142k, False: 13.2k]
  ------------------
 1270|   142k|    --label;
 1271|   142k|  }
 1272|  25.6k|  if (label != start && label[-1] != '.') {
  ------------------
  |  Branch (1272:7): [True: 13.2k, False: 12.3k]
  |  Branch (1272:25): [True: 9.70k, False: 3.54k]
  ------------------
 1273|  9.70k|    return false;
 1274|  9.70k|  }
 1275|  15.8k|  return label != end && is_digit(*label);
  ------------------
  |  Branch (1275:10): [True: 15.8k, False: 0]
  |  Branch (1275:26): [True: 9.81k, False: 6.08k]
  ------------------
 1276|  25.6k|}
_ZN3ada8checkers19is_ipv4_number_charEc:
 1247|   206k|constexpr bool is_ipv4_number_char(char x) noexcept {
 1248|   206k|  const unsigned char c = static_cast<unsigned char>(x);
 1249|   206k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
  ------------------
  |  Branch (1249:11): [True: 194k, False: 12.0k]
  |  Branch (1249:23): [True: 64.9k, False: 129k]
  |  Branch (1249:37): [True: 126k, False: 15.0k]
  |  Branch (1249:49): [True: 70.9k, False: 55.8k]
  ------------------
 1250|  70.8k|         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
  ------------------
  |  Branch (1250:11): [True: 57.8k, False: 12.9k]
  |  Branch (1250:23): [True: 841, False: 57.0k]
  |  Branch (1250:36): [True: 31.1k, False: 38.8k]
  |  Branch (1250:48): [True: 635, False: 38.2k]
  ------------------
 1251|   206k|}
_ZN3ada8checkers8is_digitEc:
 1245|  47.3k|constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
_ZN3ada6scheme16get_special_portENS0_4typeE:
 6919|  10.4k|constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
 6920|  10.4k|  return details::special_ports[int(type)];
 6921|  10.4k|}
_ZN3ada3urlaSERKS0_:
 5109|    279|  url& operator=(const url& u) = default;
_ZN3ada14url_aggregatoraSERKS0_:
 7923|    279|  url_aggregator& operator=(const url_aggregator& u) = default;
_ZN3ada14url_aggregator10clear_hashEv:
 9009|    253|inline void url_aggregator::clear_hash() {
 9010|    253|  ada_log("url_aggregator::clear_hash");
 9011|    253|  ADA_ASSERT_TRUE(validate());
 9012|    253|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (9012:7): [True: 235, False: 18]
  ------------------
 9013|    235|    return;
 9014|    235|  }
 9015|     18|  buffer.resize(components.hash_start);
 9016|     18|  components.hash_start = url_components::omitted;
 9017|       |
 9018|       |#if ADA_DEVELOPMENT_CHECKS
 9019|       |  ADA_ASSERT_EQUAL(get_hash(), "",
 9020|       |                   "hash should have been cleared on buffer=" + buffer +
 9021|       |                       " with " + components.to_string() + "\n" + to_diagram());
 9022|       |#endif
 9023|     18|  ADA_ASSERT_TRUE(validate());
 9024|     18|}
_ZN3ada14url_aggregator20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8652|  19.1k|inline void url_aggregator::update_base_pathname(const std::string_view input) {
 8653|  19.1k|  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
 8654|  19.1k|          " bytes] \n", to_diagram());
 8655|  19.1k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8656|  19.1k|  ADA_ASSERT_TRUE(validate());
 8657|       |
 8658|  19.1k|  const bool begins_with_dashdash = input.starts_with("//");
 8659|  19.1k|  if (!begins_with_dashdash && has_dash_dot()) {
  ------------------
  |  Branch (8659:7): [True: 18.4k, False: 709]
  |  Branch (8659:32): [True: 4, False: 18.4k]
  ------------------
 8660|       |    // We must delete the ./
 8661|      4|    delete_dash_dot();
 8662|      4|  }
 8663|       |
 8664|  19.1k|  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
  ------------------
  |  Branch (8664:7): [True: 709, False: 18.4k]
  |  Branch (8664:31): [True: 709, False: 0]
  |  Branch (8664:51): [True: 52, False: 657]
  ------------------
 8665|     52|      !has_dash_dot()) {
  ------------------
  |  Branch (8665:7): [True: 46, False: 6]
  ------------------
 8666|       |    // If url's host is null, url does not have an opaque path, url's path's
 8667|       |    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
 8668|       |    // output.
 8669|     46|    buffer.insert(components.pathname_start, "/.");
 8670|     46|    components.pathname_start += 2;
 8671|     46|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8671:9): [True: 0, False: 46]
  ------------------
 8672|      0|      components.search_start += 2;
 8673|      0|    }
 8674|     46|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8674:9): [True: 0, False: 46]
  ------------------
 8675|      0|      components.hash_start += 2;
 8676|      0|    }
 8677|     46|  }
 8678|       |
 8679|  19.1k|  uint32_t difference = replace_and_resize(
 8680|  19.1k|      components.pathname_start,
 8681|  19.1k|      components.pathname_start + get_pathname_length(), input);
 8682|  19.1k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8682:7): [True: 0, False: 19.1k]
  ------------------
 8683|      0|    components.search_start += difference;
 8684|      0|  }
 8685|  19.1k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8685:7): [True: 0, False: 19.1k]
  ------------------
 8686|      0|    components.hash_start += difference;
 8687|      0|  }
 8688|  19.1k|  ADA_ASSERT_TRUE(validate());
 8689|  19.1k|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8494|  53.8k|    uint32_t start, uint32_t end, std::string_view input) {
 8495|  53.8k|  uint32_t current_length = end - start;
 8496|  53.8k|  uint32_t input_size = uint32_t(input.size());
 8497|  53.8k|  uint32_t new_difference = input_size - current_length;
 8498|       |
 8499|  53.8k|  if (current_length == 0) {
  ------------------
  |  Branch (8499:7): [True: 51.8k, False: 1.99k]
  ------------------
 8500|  51.8k|    buffer.insert(start, input);
 8501|  51.8k|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8501:14): [True: 422, False: 1.57k]
  ------------------
 8502|    422|    if (input_size != 0) {
  ------------------
  |  Branch (8502:9): [True: 422, False: 0]
  ------------------
 8503|    422|      std::memmove(buffer.data() + start, input.data(), input_size);
 8504|    422|    }
 8505|  1.57k|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8505:14): [True: 680, False: 891]
  ------------------
 8506|    680|    buffer.erase(start, current_length - input_size);
 8507|    680|    buffer.replace(start, input_size, input);
 8508|    891|  } else {
 8509|    891|    buffer.replace(start, current_length, input.substr(0, current_length));
 8510|    891|    buffer.insert(start + current_length, input.substr(current_length));
 8511|    891|  }
 8512|       |
 8513|  53.8k|  return new_difference;
 8514|  53.8k|}
_ZNK3ada14url_aggregator19get_pathname_lengthEv:
 8545|  19.1k|url_aggregator::get_pathname_length() const noexcept {
 8546|  19.1k|  ada_log("url_aggregator::get_pathname_length");
 8547|  19.1k|  uint32_t ending_index = uint32_t(buffer.size());
 8548|  19.1k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8548:7): [True: 0, False: 19.1k]
  ------------------
 8549|      0|    ending_index = components.search_start;
 8550|  19.1k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8550:14): [True: 0, False: 19.1k]
  ------------------
 8551|      0|    ending_index = components.hash_start;
 8552|      0|  }
 8553|  19.1k|  return ending_index - components.pathname_start;
 8554|  19.1k|}
_ZNK3ada14url_aggregator12get_pathnameEv:
 9487|  9.90k|    ada_lifetime_bound {
 9488|  9.90k|  ada_log("url_aggregator::get_pathname pathname_start = ",
 9489|  9.90k|          components.pathname_start, " buffer.size() = ", buffer.size(),
 9490|  9.90k|          " components.search_start = ", components.search_start,
 9491|  9.90k|          " components.hash_start = ", components.hash_start);
 9492|  9.90k|  auto ending_index = uint32_t(buffer.size());
 9493|  9.90k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9493:7): [True: 194, False: 9.71k]
  ------------------
 9494|    194|    ending_index = components.search_start;
 9495|  9.71k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9495:14): [True: 69, False: 9.64k]
  ------------------
 9496|     69|    ending_index = components.hash_start;
 9497|     69|  }
 9498|  9.90k|  return helpers::substring(buffer, components.pathname_start, ending_index);
 9499|  9.90k|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8470|  1.33k|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8471|  1.33k|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8472|  1.33k|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8473|  1.33k|          " bytes] components.hash_start = ", components.hash_start);
 8474|  1.33k|  ADA_ASSERT_TRUE(validate());
 8475|  1.33k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8476|  1.33k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8476:7): [True: 8, False: 1.32k]
  ------------------
 8477|      8|    buffer.resize(components.hash_start);
 8478|      8|  }
 8479|  1.33k|  components.hash_start = uint32_t(buffer.size());
 8480|  1.33k|  buffer += "#";
 8481|  1.33k|  bool encoding_required = unicode::percent_encode<true>(
 8482|  1.33k|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8483|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8484|       |  // encoding was not deemed required.
 8485|  1.33k|  if (!encoding_required) {
  ------------------
  |  Branch (8485:7): [True: 797, False: 535]
  ------------------
 8486|    797|    buffer.append(input);
 8487|    797|  }
 8488|  1.33k|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8489|  1.33k|          buffer, "' [", buffer.size(), " bytes]");
 8490|  1.33k|  ADA_ASSERT_TRUE(validate());
 8491|  1.33k|}
_ZN3ada3urlC2Ev:
 5105|  51.7k|  url() = default;
_ZN3ada3url11copy_schemeERKS0_:
 7616|    914|constexpr void url::copy_scheme(const ada::url& u) {
 7617|    914|  non_special_scheme = u.non_special_scheme;
 7618|    914|  type = u.type;
 7619|    914|}
_ZN3ada3url26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7558|  1.18k|inline void url::update_unencoded_base_hash(std::string_view input) {
 7559|       |  // We do the percent encoding
 7560|  1.18k|  hash = unicode::percent_encode(input,
 7561|  1.18k|                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
 7562|  1.18k|}
_ZN3ada3url12clear_searchEv:
 7591|    631|constexpr void url::clear_search() { query = std::nullopt; }
_ZN3ada3url18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7565|  1.43k|                                    const uint8_t query_percent_encode_set[]) {
 7566|  1.43k|  query = ada::unicode::percent_encode(input, query_percent_encode_set);
 7567|  1.43k|}
_ZN3ada3url20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7556|    144|inline void url::update_base_hostname(std::string_view input) { host = input; }
_ZN3ada3url20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7573|  10.1k|inline void url::update_base_pathname(const std::string_view input) {
 7574|  10.1k|  path = input;
 7575|  10.1k|}
_ZN3ada3url10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 7748|  2.29k|                                         bool check_trailing_content) noexcept {
 7749|  2.29k|  ada_log("parse_port('", view, "') ", view.size());
 7750|  2.29k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (7750:7): [True: 1.61k, False: 678]
  |  Branch (7750:24): [True: 3, False: 1.61k]
  ------------------
 7751|      3|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 7752|      3|    is_valid = false;
 7753|      3|    return 0;
 7754|      3|  }
 7755|  2.29k|  uint16_t parsed_port{};
 7756|  2.29k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 7757|  2.29k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (7757:7): [True: 39, False: 2.25k]
  ------------------
 7758|     39|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 7759|     39|    is_valid = false;
 7760|     39|    return 0;
 7761|     39|  }
 7762|  2.25k|  ada_log("parse_port: ", parsed_port);
 7763|  2.25k|  const auto consumed = size_t(r.ptr - view.data());
 7764|  2.25k|  ada_log("parse_port: consumed ", consumed);
 7765|  2.25k|  if (check_trailing_content) {
  ------------------
  |  Branch (7765:7): [True: 2.25k, False: 0]
  ------------------
 7766|  2.25k|    is_valid &=
 7767|  2.25k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (7767:10): [True: 1.31k, False: 933]
  |  Branch (7767:37): [True: 754, False: 179]
  ------------------
 7768|    179|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (7768:10): [True: 95, False: 84]
  |  Branch (7768:36): [True: 80, False: 4]
  |  Branch (7768:52): [True: 1, False: 79]
  ------------------
 7769|  2.25k|  }
 7770|  2.25k|  ada_log("parse_port: is_valid = ", is_valid);
 7771|  2.25k|  if (is_valid) {
  ------------------
  |  Branch (7771:7): [True: 2.16k, False: 83]
  ------------------
 7772|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 7773|  2.16k|    auto default_port = scheme_default_port();
 7774|  2.16k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (7774:27): [True: 82, False: 2.08k]
  |  Branch (7774:48): [True: 31, False: 51]
  ------------------
 7775|  2.13k|                         (default_port != parsed_port);
  ------------------
  |  Branch (7775:26): [True: 2.12k, False: 8]
  ------------------
 7776|  2.16k|    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
  ------------------
  |  Branch (7776:13): [True: 1.37k, False: 795]
  |  Branch (7776:36): [True: 1.36k, False: 8]
  ------------------
 7777|  2.16k|                                                  : std::nullopt;
 7778|  2.16k|  }
 7779|  2.25k|  return consumed;
 7780|  2.29k|}
_ZNK3ada8url_base19scheme_default_portEv:
 7419|  6.67k|url_base::scheme_default_port() const noexcept {
 7420|  6.67k|  return scheme::get_special_port(type);
 7421|  6.67k|}
_ZNK3ada3url12get_pathnameEv:
 7476|    169|[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
 7477|    169|  return path;
 7478|    169|}
_ZN3ada8checkers23is_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1284|  12.5k|constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
 1285|  12.5k|  return input.size() >= 2 &&
  ------------------
  |  Branch (1285:10): [True: 8.58k, False: 3.95k]
  ------------------
 1286|  8.58k|         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
  ------------------
  |  Branch (1286:11): [True: 3.42k, False: 5.16k]
  |  Branch (1286:34): [True: 810, False: 2.61k]
  |  Branch (1286:55): [True: 456, False: 2.15k]
  ------------------
 1287|  1.26k|         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
  ------------------
  |  Branch (1287:11): [True: 531, False: 735]
  |  Branch (1287:35): [True: 123, False: 612]
  |  Branch (1287:54): [True: 28, False: 584]
  ------------------
 1288|    584|                                  input[2] == '?' || input[2] == '#'));
  ------------------
  |  Branch (1288:35): [True: 5, False: 579]
  |  Branch (1288:54): [True: 0, False: 579]
  ------------------
 1289|  12.5k|}
_ZN3ada8checkers34is_normalized_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1292|  1.95k|    std::string_view input) noexcept {
 1293|  1.95k|  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
  ------------------
  |  Branch (1293:10): [True: 972, False: 982]
  |  Branch (1293:32): [True: 832, False: 140]
  |  Branch (1293:54): [True: 752, False: 80]
  ------------------
 1294|  1.95k|}
_ZN3ada3url20set_protocol_as_fileEv:
 7601|  2.98k|constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
_ZN3ada3url14clear_pathnameEv:
 7589|     19|constexpr void url::clear_pathname() { path.clear(); }
_ZN3ada7helpers14leading_zeroesEj:
 2000|  46.0k|inline int leading_zeroes(uint32_t input_num) noexcept {
 2001|       |#if ADA_REGULAR_VISUAL_STUDIO
 2002|       |  unsigned long leading_zero(0);
 2003|       |  unsigned long in(input_num);
 2004|       |  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
 2005|       |#else
 2006|  46.0k|  return __builtin_clz(input_num);
 2007|  46.0k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 2008|  46.0k|}
_ZN3ada14url_aggregator7reserveEj:
 9160|  46.0k|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 9161|  46.0k|  buffer.reserve(capacity);
 9162|  46.0k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8561|    170|inline void url_aggregator::update_base_search(std::string_view input) {
 8562|    170|  ada_log("url_aggregator::update_base_search ", input);
 8563|    170|  ADA_ASSERT_TRUE(validate());
 8564|    170|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8565|    170|  if (input.empty()) {
  ------------------
  |  Branch (8565:7): [True: 0, False: 170]
  ------------------
 8566|      0|    clear_search();
 8567|      0|    return;
 8568|      0|  }
 8569|       |
 8570|    170|  if (input[0] == '?') {
  ------------------
  |  Branch (8570:7): [True: 170, False: 0]
  ------------------
 8571|    170|    input.remove_prefix(1);
 8572|    170|  }
 8573|       |
 8574|    170|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8574:7): [True: 170, False: 0]
  ------------------
 8575|    170|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8575:9): [True: 170, False: 0]
  ------------------
 8576|    170|      components.search_start = uint32_t(buffer.size());
 8577|    170|      buffer += "?";
 8578|    170|    } else {
 8579|      0|      buffer.resize(components.search_start + 1);
 8580|      0|    }
 8581|       |
 8582|    170|    buffer.append(input);
 8583|    170|  } else if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8583:14): [True: 0, False: 0]
  ------------------
 8584|      0|    const uint32_t difference = replace_and_resize(
 8585|      0|        components.search_start + 1, components.hash_start, input);
 8586|      0|    components.hash_start += difference;
 8587|      0|  } else {
 8588|      0|    components.search_start = components.hash_start;
 8589|      0|    buffer.insert(components.search_start, input.size() + 1, '?');
 8590|      0|    if (!input.empty()) {
  ------------------
  |  Branch (8590:9): [True: 0, False: 0]
  ------------------
 8591|      0|      std::memmove(buffer.data() + components.search_start + 1, input.data(),
 8592|      0|                   input.size());
 8593|      0|    }
 8594|      0|    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8595|      0|  }
 8596|       |
 8597|    170|  ADA_ASSERT_TRUE(validate());
 8598|    170|}
_ZN3ada14url_aggregator20append_base_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8878|  7.45k|inline void url_aggregator::append_base_password(const std::string_view input) {
 8879|  7.45k|  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
 8880|  7.45k|          "\n", to_diagram());
 8881|  7.45k|  ADA_ASSERT_TRUE(validate());
 8882|  7.45k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8883|       |#if ADA_DEVELOPMENT_CHECKS
 8884|       |  // computing the expected password.
 8885|       |  std::string password_expected = std::string(get_password());
 8886|       |  password_expected.append(input);
 8887|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8888|  7.45k|  add_authority_slashes_if_needed();
 8889|       |
 8890|       |  // If input is empty, do nothing.
 8891|  7.45k|  if (input.empty()) {
  ------------------
  |  Branch (8891:7): [True: 2.04k, False: 5.40k]
  ------------------
 8892|  2.04k|    return;
 8893|  2.04k|  }
 8894|       |
 8895|  5.40k|  uint32_t difference = uint32_t(input.size());
 8896|  5.40k|  if (has_password()) {
  ------------------
  |  Branch (8896:7): [True: 4.83k, False: 571]
  ------------------
 8897|  4.83k|    buffer.insert(components.host_start, input);
 8898|  4.83k|  } else {
 8899|    571|    difference++;  // Increment for ":"
 8900|    571|    buffer.insert(components.username_end, ":");
 8901|    571|    buffer.insert(components.username_end + 1, input);
 8902|    571|  }
 8903|  5.40k|  components.host_start += difference;
 8904|       |
 8905|       |  // The following line is required to add "@" to hostname. When updating
 8906|       |  // password if hostname does not start with "@", it is "append_base_password"s
 8907|       |  // responsibility to set it.
 8908|  5.40k|  if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8908:7): [True: 387, False: 5.02k]
  ------------------
 8909|    387|    buffer.insert(components.host_start, "@");
 8910|    387|    difference++;
 8911|    387|  }
 8912|       |
 8913|  5.40k|  components.host_end += difference;
 8914|  5.40k|  components.pathname_start += difference;
 8915|  5.40k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8915:7): [True: 0, False: 5.40k]
  ------------------
 8916|      0|    components.search_start += difference;
 8917|      0|  }
 8918|  5.40k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8918:7): [True: 0, False: 5.40k]
  ------------------
 8919|      0|    components.hash_start += difference;
 8920|      0|  }
 8921|       |#if ADA_DEVELOPMENT_CHECKS
 8922|       |  std::string password_after(get_password());
 8923|       |  ADA_ASSERT_EQUAL(
 8924|       |      password_expected, password_after,
 8925|       |      "append_base_password problem after inserting " + std::string(input));
 8926|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8927|  5.40k|  ADA_ASSERT_TRUE(validate());
 8928|  5.40k|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 9134|  53.7k|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 9135|  53.7k|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 9136|  53.7k|  ADA_ASSERT_TRUE(validate());
 9137|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 9138|       |  // to insert
 9139|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 9140|  53.7k|  if (has_authority()) {
  ------------------
  |  Branch (9140:7): [True: 24.5k, False: 29.2k]
  ------------------
 9141|  24.5k|    return;
 9142|  24.5k|  }
 9143|       |  // Performance: the common case is components.protocol_end == buffer.size()
 9144|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 9145|       |  // the insert could be fused with another insert.
 9146|  29.2k|  buffer.insert(components.protocol_end, "//");
 9147|  29.2k|  components.username_end += 2;
 9148|  29.2k|  components.host_start += 2;
 9149|  29.2k|  components.host_end += 2;
 9150|  29.2k|  components.pathname_start += 2;
 9151|  29.2k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9151:7): [True: 0, False: 29.2k]
  ------------------
 9152|      0|    components.search_start += 2;
 9153|      0|  }
 9154|  29.2k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9154:7): [True: 0, False: 29.2k]
  ------------------
 9155|      0|    components.hash_start += 2;
 9156|      0|  }
 9157|  29.2k|  ADA_ASSERT_TRUE(validate());
 9158|  29.2k|}
_ZN3ada14url_aggregator20append_base_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8762|  11.5k|inline void url_aggregator::append_base_username(const std::string_view input) {
 8763|  11.5k|  ada_log("url_aggregator::append_base_username ", input);
 8764|  11.5k|  ADA_ASSERT_TRUE(validate());
 8765|  11.5k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8766|       |#if ADA_DEVELOPMENT_CHECKS
 8767|       |  // computing the expected password.
 8768|       |  std::string username_expected(get_username());
 8769|       |  username_expected.append(input);
 8770|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8771|  11.5k|  add_authority_slashes_if_needed();
 8772|       |
 8773|       |  // If input is empty, do nothing.
 8774|  11.5k|  if (input.empty()) {
  ------------------
  |  Branch (8774:7): [True: 3.22k, False: 8.34k]
  ------------------
 8775|  3.22k|    return;
 8776|  3.22k|  }
 8777|       |
 8778|  8.34k|  uint32_t difference = uint32_t(input.size());
 8779|  8.34k|  buffer.insert(components.username_end, input);
 8780|  8.34k|  components.username_end += difference;
 8781|  8.34k|  components.host_start += difference;
 8782|       |
 8783|  8.34k|  if (buffer[components.host_start] != '@' &&
  ------------------
  |  Branch (8783:7): [True: 1.22k, False: 7.12k]
  ------------------
 8784|  1.22k|      components.host_start != components.host_end) {
  ------------------
  |  Branch (8784:7): [True: 1.22k, False: 0]
  ------------------
 8785|  1.22k|    buffer.insert(components.host_start, "@");
 8786|  1.22k|    difference++;
 8787|  1.22k|  }
 8788|       |
 8789|  8.34k|  components.host_end += difference;
 8790|  8.34k|  components.pathname_start += difference;
 8791|  8.34k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8791:7): [True: 0, False: 8.34k]
  ------------------
 8792|      0|    components.search_start += difference;
 8793|      0|  }
 8794|  8.34k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8794:7): [True: 0, False: 8.34k]
  ------------------
 8795|      0|    components.hash_start += difference;
 8796|      0|  }
 8797|       |#if ADA_DEVELOPMENT_CHECKS
 8798|       |  std::string username_after(get_username());
 8799|       |  ADA_ASSERT_EQUAL(
 8800|       |      username_expected, username_after,
 8801|       |      "append_base_username problem after inserting " + std::string(input));
 8802|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8803|  8.34k|  ADA_ASSERT_TRUE(validate());
 8804|  8.34k|}
_ZN3ada14url_aggregator21update_base_authorityENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS_14url_componentsE:
 8412|  1.92k|    std::string_view base_buffer, const ada::url_components& base) {
 8413|  1.92k|  std::string_view input = base_buffer.substr(
 8414|  1.92k|      base.protocol_end, base.host_start - base.protocol_end);
 8415|  1.92k|  ada_log("url_aggregator::update_base_authority ", input);
 8416|       |
 8417|  1.92k|  bool input_starts_with_dash = input.starts_with("//");
 8418|  1.92k|  uint32_t diff = components.host_start - components.protocol_end;
 8419|       |
 8420|  1.92k|  buffer.erase(components.protocol_end,
 8421|  1.92k|               components.host_start - components.protocol_end);
 8422|  1.92k|  components.username_end = components.protocol_end;
 8423|       |
 8424|  1.92k|  if (input_starts_with_dash) {
  ------------------
  |  Branch (8424:7): [True: 1.67k, False: 250]
  ------------------
 8425|  1.67k|    input.remove_prefix(2);
 8426|  1.67k|    diff += 2;  // add "//"
 8427|  1.67k|    buffer.insert(components.protocol_end, "//");
 8428|  1.67k|    components.username_end += 2;
 8429|  1.67k|  }
 8430|       |
 8431|  1.92k|  size_t password_delimiter = input.find(':');
 8432|       |
 8433|       |  // Check if input contains both username and password by checking the
 8434|       |  // delimiter: ":" A typical input that contains authority would be "user:pass"
 8435|  1.92k|  if (password_delimiter != std::string_view::npos) {
  ------------------
  |  Branch (8435:7): [True: 30, False: 1.89k]
  ------------------
 8436|       |    // Insert both username and password
 8437|     30|    std::string_view username = input.substr(0, password_delimiter);
 8438|     30|    std::string_view password = input.substr(password_delimiter + 1);
 8439|       |
 8440|     30|    buffer.insert(components.protocol_end + diff, username);
 8441|     30|    diff += uint32_t(username.size());
 8442|     30|    buffer.insert(components.protocol_end + diff, ":");
 8443|     30|    components.username_end = components.protocol_end + diff;
 8444|     30|    buffer.insert(components.protocol_end + diff + 1, password);
 8445|     30|    diff += uint32_t(password.size()) + 1;
 8446|  1.89k|  } else if (!input.empty()) {
  ------------------
  |  Branch (8446:14): [True: 36, False: 1.85k]
  ------------------
 8447|       |    // Insert only username
 8448|     36|    buffer.insert(components.protocol_end + diff, input);
 8449|     36|    components.username_end =
 8450|     36|        components.protocol_end + diff + uint32_t(input.size());
 8451|     36|    diff += uint32_t(input.size());
 8452|     36|  }
 8453|       |
 8454|  1.92k|  components.host_start += diff;
 8455|       |
 8456|  1.92k|  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
  ------------------
  |  Branch (8456:7): [True: 0, False: 1.92k]
  |  Branch (8456:42): [True: 0, False: 0]
  ------------------
 8457|      0|    buffer.insert(components.host_start, "@");
 8458|      0|    diff++;
 8459|      0|  }
 8460|  1.92k|  components.host_end += diff;
 8461|  1.92k|  components.pathname_start += diff;
 8462|  1.92k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8462:7): [True: 0, False: 1.92k]
  ------------------
 8463|      0|    components.search_start += diff;
 8464|      0|  }
 8465|  1.92k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8465:7): [True: 0, False: 1.92k]
  ------------------
 8466|      0|    components.hash_start += diff;
 8467|      0|  }
 8468|  1.92k|}
_ZNK3ada14url_aggregator8get_hrefEv:
 9238|  31.2k|    const noexcept ada_lifetime_bound {
 9239|  31.2k|  ada_log("url_aggregator::get_href");
 9240|  31.2k|  return buffer;
 9241|  31.2k|}
_ZNK3ada14url_aggregator14get_componentsEv:
 9120|  1.92k|url_aggregator::get_components() const noexcept {
 9121|  1.92k|  return components;
 9122|  1.92k|}
_ZN3ada14url_aggregator24update_host_to_base_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9506|  2.45k|void url_aggregator::update_host_to_base_host(const std::string_view input) {
 9507|  2.45k|  ada_log("url_aggregator::update_host_to_base_host ", input);
 9508|  2.45k|  ADA_ASSERT_TRUE(validate());
 9509|  2.45k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 9510|  2.45k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (9510:7): [True: 1.92k, False: 530]
  ------------------
 9511|       |    // Let host be the result of host parsing host_view with url is not special.
 9512|  1.92k|    if (input.empty() && !is_special()) {
  ------------------
  |  Branch (9512:9): [True: 270, False: 1.65k]
  |  Branch (9512:26): [True: 270, False: 0]
  ------------------
 9513|    270|      if (has_hostname()) {
  ------------------
  |  Branch (9513:11): [True: 20, False: 250]
  ------------------
 9514|     20|        clear_hostname();
 9515|    250|      } else if (has_dash_dot()) {
  ------------------
  |  Branch (9515:18): [True: 0, False: 250]
  ------------------
 9516|      0|        add_authority_slashes_if_needed();
 9517|      0|        delete_dash_dot();
 9518|      0|      }
 9519|    270|      return;
 9520|    270|    }
 9521|  1.92k|  }
 9522|  2.18k|  update_base_hostname(input);
 9523|  2.18k|  ADA_ASSERT_TRUE(validate());
 9524|  2.18k|  return;
 9525|  2.45k|}
_ZN3ada14url_aggregator14clear_hostnameEv:
 9061|     20|constexpr void url_aggregator::clear_hostname() {
 9062|     20|  ada_log("url_aggregator::clear_hostname");
 9063|     20|  ADA_ASSERT_TRUE(validate());
 9064|     20|  if (!has_authority()) {
  ------------------
  |  Branch (9064:7): [True: 0, False: 20]
  ------------------
 9065|      0|    return;
 9066|      0|  }
 9067|     20|  ADA_ASSERT_TRUE(has_authority());
 9068|       |
 9069|     20|  uint32_t hostname_length = components.host_end - components.host_start;
 9070|     20|  uint32_t start = components.host_start;
 9071|       |
 9072|       |  // If hostname starts with "@", we should not remove that character.
 9073|     20|  if (hostname_length > 0 && buffer[start] == '@') {
  ------------------
  |  Branch (9073:7): [True: 0, False: 20]
  |  Branch (9073:30): [True: 0, False: 0]
  ------------------
 9074|      0|    start++;
 9075|      0|    hostname_length--;
 9076|      0|  }
 9077|     20|  buffer.erase(start, hostname_length);
 9078|     20|  components.host_end = start;
 9079|     20|  components.pathname_start -= hostname_length;
 9080|     20|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9080:7): [True: 0, False: 20]
  ------------------
 9081|      0|    components.search_start -= hostname_length;
 9082|      0|  }
 9083|     20|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9083:7): [True: 0, False: 20]
  ------------------
 9084|      0|    components.hash_start -= hostname_length;
 9085|      0|  }
 9086|       |#if ADA_DEVELOPMENT_CHECKS
 9087|       |  ADA_ASSERT_EQUAL(get_hostname(), "",
 9088|       |                   "hostname should have been cleared on buffer=" + buffer +
 9089|       |                       " with " + components.to_string() + "\n" + to_diagram());
 9090|       |#endif
 9091|     20|  ADA_ASSERT_TRUE(has_authority());
 9092|     20|  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
 9093|     20|                   "hostname should have been cleared on buffer=" + buffer +
 9094|     20|                       " with " + components.to_string() + "\n" + to_diagram());
 9095|     20|  ADA_ASSERT_TRUE(validate());
 9096|     20|}
_ZN3ada14url_aggregator16update_base_portEj:
 8930|  4.64k|inline void url_aggregator::update_base_port(uint32_t input) {
 8931|  4.64k|  ada_log("url_aggregator::update_base_port");
 8932|  4.64k|  ADA_ASSERT_TRUE(validate());
 8933|  4.64k|  if (input == url_components::omitted) {
  ------------------
  |  Branch (8933:7): [True: 1.90k, False: 2.74k]
  ------------------
 8934|  1.90k|    clear_port();
 8935|  1.90k|    return;
 8936|  1.90k|  }
 8937|       |  // calling std::to_string(input.value()) is unfortunate given that the port
 8938|       |  // value is probably already available as a string.
 8939|  2.74k|  std::string value = helpers::concat(":", std::to_string(input));
 8940|  2.74k|  uint32_t difference = uint32_t(value.size());
 8941|       |
 8942|  2.74k|  if (components.port != url_components::omitted) {
  ------------------
  |  Branch (8942:7): [True: 0, False: 2.74k]
  ------------------
 8943|      0|    difference -= components.pathname_start - components.host_end;
 8944|      0|    buffer.erase(components.host_end,
 8945|      0|                 components.pathname_start - components.host_end);
 8946|      0|  }
 8947|       |
 8948|  2.74k|  buffer.insert(components.host_end, value);
 8949|  2.74k|  components.pathname_start += difference;
 8950|  2.74k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8950:7): [True: 0, False: 2.74k]
  ------------------
 8951|      0|    components.search_start += difference;
 8952|      0|  }
 8953|  2.74k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8953:7): [True: 0, False: 2.74k]
  ------------------
 8954|      0|    components.hash_start += difference;
 8955|      0|  }
 8956|  2.74k|  components.port = input;
 8957|  2.74k|  ADA_ASSERT_TRUE(validate());
 8958|  2.74k|}
_ZN3ada7helpers6concatIJPKcNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEESA_DpT_:
 1990|  2.74k|std::string concat(Args... args) {
 1991|  2.74k|  std::string answer;
 1992|  2.74k|  inner_concat(answer, args...);
 1993|  2.74k|  return answer;
 1994|  2.74k|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEEvRSA_T_DpT0_:
 1979|  2.74k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1980|  2.74k|  buffer.append(t);
 1981|  2.74k|  return inner_concat(buffer, args...);
 1982|  2.74k|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJEEEvRS8_T_:
 1971|  2.74k|inline void inner_concat(std::string& buffer, T t) {
 1972|  2.74k|  buffer.append(t);
 1973|  2.74k|}
_ZNK3ada14url_aggregator18retrieve_base_portEv:
 8979|  1.92k|[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
 8980|  1.92k|  ada_log("url_aggregator::retrieve_base_port");
 8981|  1.92k|  return components.port;
 8982|  1.92k|}
_ZN3ada14url_aggregator12clear_searchEv:
 8984|  1.70k|inline void url_aggregator::clear_search() {
 8985|  1.70k|  ada_log("url_aggregator::clear_search");
 8986|  1.70k|  ADA_ASSERT_TRUE(validate());
 8987|  1.70k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8987:7): [True: 1.62k, False: 82]
  ------------------
 8988|  1.62k|    return;
 8989|  1.62k|  }
 8990|       |
 8991|     82|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8991:7): [True: 82, False: 0]
  ------------------
 8992|     82|    buffer.resize(components.search_start);
 8993|     82|  } else {
 8994|      0|    buffer.erase(components.search_start,
 8995|      0|                 components.hash_start - components.search_start);
 8996|      0|    components.hash_start = components.search_start;
 8997|      0|  }
 8998|       |
 8999|     82|  components.search_start = url_components::omitted;
 9000|       |
 9001|       |#if ADA_DEVELOPMENT_CHECKS
 9002|       |  ADA_ASSERT_EQUAL(get_search(), "",
 9003|       |                   "search should have been cleared on buffer=" + buffer +
 9004|       |                       " with " + components.to_string() + "\n" + to_diagram());
 9005|       |#endif
 9006|     82|  ADA_ASSERT_TRUE(validate());
 9007|     82|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8601|  1.43k|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8602|  1.43k|  ada_log("url_aggregator::update_base_search ", input,
 8603|  1.43k|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8604|  1.43k|  ADA_ASSERT_TRUE(validate());
 8605|  1.43k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8606|       |
 8607|  1.43k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8607:7): [True: 1.43k, False: 0]
  ------------------
 8608|  1.43k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8608:9): [True: 1.41k, False: 21]
  ------------------
 8609|  1.41k|      components.search_start = uint32_t(buffer.size());
 8610|  1.41k|      buffer += "?";
 8611|  1.41k|    } else {
 8612|     21|      buffer.resize(components.search_start + 1);
 8613|     21|    }
 8614|       |
 8615|  1.43k|    bool encoding_required =
 8616|  1.43k|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8617|       |    // When encoding_required is false, then buffer is left unchanged, and
 8618|       |    // percent encoding was not deemed required.
 8619|  1.43k|    if (!encoding_required) {
  ------------------
  |  Branch (8619:9): [True: 938, False: 497]
  ------------------
 8620|    938|      buffer.append(input);
 8621|    938|    }
 8622|  1.43k|  } else {
 8623|      0|    const size_t idx =
 8624|      0|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8625|      0|    std::string encoded;
 8626|      0|    std::string_view replacement = input;
 8627|      0|    if (idx != input.size()) {
  ------------------
  |  Branch (8627:9): [True: 0, False: 0]
  ------------------
 8628|      0|      encoded =
 8629|      0|          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
 8630|      0|      replacement = encoded;
 8631|      0|    }
 8632|       |
 8633|      0|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8633:9): [True: 0, False: 0]
  ------------------
 8634|      0|      const uint32_t difference = replace_and_resize(
 8635|      0|          components.search_start + 1, components.hash_start, replacement);
 8636|      0|      components.hash_start += difference;
 8637|      0|    } else {
 8638|      0|      components.search_start = components.hash_start;
 8639|      0|      buffer.insert(components.search_start, replacement.size() + 1, '?');
 8640|      0|      if (!replacement.empty()) {
  ------------------
  |  Branch (8640:11): [True: 0, False: 0]
  ------------------
 8641|      0|        std::memmove(buffer.data() + components.search_start + 1,
 8642|      0|                     replacement.data(), replacement.size());
 8643|      0|      }
 8644|      0|      components.hash_start +=
 8645|      0|          uint32_t(replacement.size() + 1);  // Do not forget `?`
 8646|      0|    }
 8647|      0|  }
 8648|       |
 8649|  1.43k|  ADA_ASSERT_TRUE(validate());
 8650|  1.43k|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8516|  34.7k|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8517|  34.7k|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8518|  34.7k|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8519|  34.7k|  ADA_ASSERT_TRUE(validate());
 8520|  34.7k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8521|       |
 8522|       |  // This next line is required for when parsing a URL like `foo://`
 8523|  34.7k|  add_authority_slashes_if_needed();
 8524|       |
 8525|  34.7k|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8526|  34.7k|  uint32_t new_difference =
 8527|  34.7k|      replace_and_resize(components.host_start, components.host_end, input);
 8528|       |
 8529|  34.7k|  if (has_credentials) {
  ------------------
  |  Branch (8529:7): [True: 1.01k, False: 33.7k]
  ------------------
 8530|  1.01k|    buffer.insert(components.host_start, "@");
 8531|  1.01k|    new_difference++;
 8532|  1.01k|  }
 8533|  34.7k|  components.host_end += new_difference;
 8534|  34.7k|  components.pathname_start += new_difference;
 8535|  34.7k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8535:7): [True: 0, False: 34.7k]
  ------------------
 8536|      0|    components.search_start += new_difference;
 8537|      0|  }
 8538|  34.7k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8538:7): [True: 0, False: 34.7k]
  ------------------
 8539|      0|    components.hash_start += new_difference;
 8540|      0|  }
 8541|  34.7k|  ADA_ASSERT_TRUE(validate());
 8542|  34.7k|}
_ZN3ada14url_aggregator10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 9248|  4.72k|url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
 9249|  4.72k|  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
 9250|  4.72k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (9250:7): [True: 3.35k, False: 1.36k]
  |  Branch (9250:24): [True: 6, False: 3.35k]
  ------------------
 9251|      6|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 9252|      6|    is_valid = false;
 9253|      6|    return 0;
 9254|      6|  }
 9255|  4.71k|  uint16_t parsed_port{};
 9256|  4.71k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 9257|  4.71k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (9257:7): [True: 71, False: 4.64k]
  ------------------
 9258|     71|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 9259|     71|    is_valid = false;
 9260|     71|    return 0;
 9261|     71|  }
 9262|  4.64k|  ada_log("parse_port: ", parsed_port);
 9263|  4.64k|  const size_t consumed = size_t(r.ptr - view.data());
 9264|  4.64k|  ada_log("parse_port: consumed ", consumed);
 9265|  4.64k|  if (check_trailing_content) {
  ------------------
  |  Branch (9265:7): [True: 4.64k, False: 0]
  ------------------
 9266|  4.64k|    is_valid &=
 9267|  4.64k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (9267:10): [True: 2.65k, False: 1.99k]
  |  Branch (9267:37): [True: 1.64k, False: 350]
  ------------------
 9268|    350|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (9268:10): [True: 204, False: 146]
  |  Branch (9268:36): [True: 138, False: 8]
  |  Branch (9268:52): [True: 2, False: 136]
  ------------------
 9269|  4.64k|  }
 9270|  4.64k|  ada_log("parse_port: is_valid = ", is_valid);
 9271|  4.64k|  if (is_valid) {
  ------------------
  |  Branch (9271:7): [True: 4.50k, False: 144]
  ------------------
 9272|  4.50k|    ada_log("parse_port", r.ec == std::errc());
 9273|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 9274|  4.50k|    auto default_port = scheme_default_port();
 9275|  4.50k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (9275:27): [True: 164, False: 4.33k]
  |  Branch (9275:48): [True: 62, False: 102]
  ------------------
 9276|  4.44k|                         (default_port != parsed_port);
  ------------------
  |  Branch (9276:26): [True: 4.42k, False: 17]
  ------------------
 9277|  4.50k|    if (r.ec == std::errc() && is_port_valid) {
  ------------------
  |  Branch (9277:9): [True: 2.74k, False: 1.76k]
  |  Branch (9277:32): [True: 2.72k, False: 17]
  ------------------
 9278|  2.72k|      update_base_port(parsed_port);
 9279|  2.72k|    } else {
 9280|  1.77k|      clear_port();
 9281|  1.77k|    }
 9282|  4.50k|  }
 9283|  4.64k|  return consumed;
 9284|  4.71k|}
_ZN3ada14url_aggregator20append_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8691|      4|inline void url_aggregator::append_base_pathname(const std::string_view input) {
 8692|      4|  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
 8693|      4|          "\n", to_diagram());
 8694|      4|  ADA_ASSERT_TRUE(validate());
 8695|      4|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8696|       |#if ADA_DEVELOPMENT_CHECKS
 8697|       |  // computing the expected password.
 8698|       |  std::string path_expected(get_pathname());
 8699|       |  path_expected.append(input);
 8700|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8701|      4|  uint32_t ending_index = uint32_t(buffer.size());
 8702|      4|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8702:7): [True: 0, False: 4]
  ------------------
 8703|      0|    ending_index = components.search_start;
 8704|      4|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8704:14): [True: 0, False: 4]
  ------------------
 8705|      0|    ending_index = components.hash_start;
 8706|      0|  }
 8707|      4|  buffer.insert(ending_index, input);
 8708|       |
 8709|      4|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8709:7): [True: 0, False: 4]
  ------------------
 8710|      0|    components.search_start += uint32_t(input.size());
 8711|      0|  }
 8712|      4|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8712:7): [True: 0, False: 4]
  ------------------
 8713|      0|    components.hash_start += uint32_t(input.size());
 8714|      0|  }
 8715|       |#if ADA_DEVELOPMENT_CHECKS
 8716|       |  std::string path_after = std::string(get_pathname());
 8717|       |  ADA_ASSERT_EQUAL(
 8718|       |      path_expected, path_after,
 8719|       |      "append_base_pathname problem after inserting " + std::string(input));
 8720|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8721|      4|  ADA_ASSERT_TRUE(validate());
 8722|      4|}
_ZN3ada14url_aggregator20set_protocol_as_fileEv:
 9286|  5.97k|constexpr void url_aggregator::set_protocol_as_file() {
 9287|  5.97k|  ada_log("url_aggregator::set_protocol_as_file ");
 9288|  5.97k|  ADA_ASSERT_TRUE(validate());
 9289|  5.97k|  type = ada::scheme::type::FILE;
 9290|       |  // next line could overflow but unsigned arithmetic has well-defined
 9291|       |  // overflows.
 9292|  5.97k|  uint32_t new_difference = 5 - components.protocol_end;
 9293|       |
 9294|  5.97k|  if (buffer.empty()) {
  ------------------
  |  Branch (9294:7): [True: 484, False: 5.48k]
  ------------------
 9295|    484|    buffer.append("file:");
 9296|  5.48k|  } else {
 9297|  5.48k|    buffer.erase(0, components.protocol_end);
 9298|  5.48k|    buffer.insert(0, "file:");
 9299|  5.48k|  }
 9300|  5.97k|  components.protocol_end = 5;
 9301|       |
 9302|       |  // Update the rest of the components.
 9303|  5.97k|  components.username_end += new_difference;
 9304|  5.97k|  components.host_start += new_difference;
 9305|  5.97k|  components.host_end += new_difference;
 9306|  5.97k|  components.pathname_start += new_difference;
 9307|  5.97k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9307:7): [True: 0, False: 5.97k]
  ------------------
 9308|      0|    components.search_start += new_difference;
 9309|      0|  }
 9310|  5.97k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9310:7): [True: 0, False: 5.97k]
  ------------------
 9311|      0|    components.hash_start += new_difference;
 9312|      0|  }
 9313|  5.97k|  ADA_ASSERT_TRUE(validate());
 9314|  5.97k|}
_ZN3ada14url_aggregator14clear_pathnameEv:
 9026|     38|constexpr void url_aggregator::clear_pathname() {
 9027|     38|  ada_log("url_aggregator::clear_pathname");
 9028|     38|  ADA_ASSERT_TRUE(validate());
 9029|     38|  uint32_t ending_index = uint32_t(buffer.size());
 9030|     38|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9030:7): [True: 0, False: 38]
  ------------------
 9031|      0|    ending_index = components.search_start;
 9032|     38|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9032:14): [True: 0, False: 38]
  ------------------
 9033|      0|    ending_index = components.hash_start;
 9034|      0|  }
 9035|     38|  uint32_t pathname_length = ending_index - components.pathname_start;
 9036|     38|  buffer.erase(components.pathname_start, pathname_length);
 9037|     38|  uint32_t difference = pathname_length;
 9038|     38|  if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (9038:7): [True: 0, False: 38]
  ------------------
 9039|      0|      buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9039:7): [True: 0, False: 0]
  ------------------
 9040|      0|      buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (9040:7): [True: 0, False: 0]
  ------------------
 9041|      0|    components.pathname_start -= 2;
 9042|      0|    buffer.erase(components.host_end, 2);
 9043|      0|    difference += 2;
 9044|      0|  }
 9045|     38|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9045:7): [True: 0, False: 38]
  ------------------
 9046|      0|    components.search_start -= difference;
 9047|      0|  }
 9048|     38|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9048:7): [True: 0, False: 38]
  ------------------
 9049|      0|    components.hash_start -= difference;
 9050|      0|  }
 9051|     38|  ada_log("url_aggregator::clear_pathname completed, running checks...");
 9052|       |#if ADA_DEVELOPMENT_CHECKS
 9053|       |  ADA_ASSERT_EQUAL(get_pathname(), "",
 9054|       |                   "pathname should have been cleared on buffer=" + buffer +
 9055|       |                       " with " + components.to_string() + "\n" + to_diagram());
 9056|       |#endif
 9057|     38|  ADA_ASSERT_TRUE(validate());
 9058|     38|  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
 9059|     38|}
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8368|  1.20k|                                              const uint8_t character_set[]) {
 8369|  1.20k|  const char* data = input.data();
 8370|  1.20k|  const size_t size = input.size();
 8371|       |
 8372|       |  // Process 8 bytes at a time using unrolled loop
 8373|  1.20k|  size_t i = 0;
 8374|  4.23k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (8374:10): [True: 3.34k, False: 896]
  ------------------
 8375|  3.34k|    unsigned char chunk[8];
 8376|  3.34k|    std::memcpy(&chunk, data + i,
 8377|  3.34k|                8);  // entices compiler to unconditionally process 8 characters
 8378|       |
 8379|       |    // Check 8 characters at once
 8380|  27.7k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (8380:24): [True: 24.6k, False: 3.03k]
  ------------------
 8381|  24.6k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (8381:11): [True: 310, False: 24.3k]
  ------------------
 8382|    310|        return i + j;
 8383|    310|      }
 8384|  24.6k|    }
 8385|  3.34k|  }
 8386|       |
 8387|       |  // Handle remaining bytes
 8388|  2.41k|  for (; i < size; i++) {
  ------------------
  |  Branch (8388:10): [True: 1.72k, False: 692]
  ------------------
 8389|  1.72k|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (8389:9): [True: 204, False: 1.51k]
  ------------------
 8390|    204|      return i;
 8391|    204|    }
 8392|  1.72k|  }
 8393|       |
 8394|    692|  return size;
 8395|    896|}
_ZN3ada14url_aggregator10clear_portEv:
 8960|  3.68k|inline void url_aggregator::clear_port() {
 8961|  3.68k|  ada_log("url_aggregator::clear_port");
 8962|  3.68k|  ADA_ASSERT_TRUE(validate());
 8963|  3.68k|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (8963:7): [True: 3.68k, False: 0]
  ------------------
 8964|  3.68k|    return;
 8965|  3.68k|  }
 8966|      0|  uint32_t length = components.pathname_start - components.host_end;
 8967|      0|  buffer.erase(components.host_end, length);
 8968|      0|  components.pathname_start -= length;
 8969|      0|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8969:7): [True: 0, False: 0]
  ------------------
 8970|      0|    components.search_start -= length;
 8971|      0|  }
 8972|      0|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8972:7): [True: 0, False: 0]
  ------------------
 8973|      0|    components.hash_start -= length;
 8974|      0|  }
 8975|      0|  components.port = url_components::omitted;
 8976|      0|  ADA_ASSERT_TRUE(validate());
 8977|      0|}
_ZNK3ada14url_aggregator13has_authorityEv:
 9125|  54.7k|    const noexcept {
 9126|  54.7k|  ada_log("url_aggregator::has_authority");
 9127|       |  // Performance: instead of doing this potentially expensive check, we could
 9128|       |  // have a boolean in the struct.
 9129|  54.7k|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (9129:10): [True: 25.2k, False: 29.5k]
  ------------------
 9130|  25.2k|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (9130:10): [True: 25.2k, False: 0]
  ------------------
 9131|  25.2k|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (9131:10): [True: 25.2k, False: 0]
  ------------------
 9132|  54.7k|}
_ZNK3ada14url_aggregator12has_dash_dotEv:
 9205|  18.7k|[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
 9206|       |  // If url's host is null, url does not have an opaque path, url's path's size
 9207|       |  // is greater than 1, and url's path[0] is the empty string, then append
 9208|       |  // U+002F (/) followed by U+002E (.) to output.
 9209|  18.7k|  ada_log("url_aggregator::has_dash_dot");
 9210|       |#if ADA_DEVELOPMENT_CHECKS
 9211|       |  // If pathname_start and host_end are exactly two characters apart, then we
 9212|       |  // either have a one-digit port such as http://test.com:5?param=1 or else we
 9213|       |  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
 9214|       |  if (components.pathname_start == components.host_end + 2) {
 9215|       |    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
 9216|       |                     buffer[components.host_end + 1] == '.') ||
 9217|       |                    (buffer[components.host_end] == ':' &&
 9218|       |                     checkers::is_digit(buffer[components.host_end + 1])));
 9219|       |  }
 9220|       |  if (components.pathname_start == components.host_end + 2 &&
 9221|       |      buffer[components.host_end] == '/' &&
 9222|       |      buffer[components.host_end + 1] == '.') {
 9223|       |    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
 9224|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
 9225|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
 9226|       |  }
 9227|       |#endif
 9228|       |  // Performance: it should be uncommon for components.pathname_start ==
 9229|       |  // components.host_end + 2 to be true. So we put this check first in the
 9230|       |  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
 9231|       |  // more expensive, but should be uncommon.
 9232|  18.7k|  return components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (9232:10): [True: 1.26k, False: 17.4k]
  ------------------
 9233|  1.26k|         !has_opaque_path && buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9233:10): [True: 1.26k, False: 0]
  |  Branch (9233:30): [True: 10, False: 1.25k]
  ------------------
 9234|     10|         buffer[components.host_end + 1] == '.';
  ------------------
  |  Branch (9234:10): [True: 10, False: 0]
  ------------------
 9235|  18.7k|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 4100|  47.4k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 4069|  29.2k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 4070|  29.2k|    TL_ASSERT(has_value());
  ------------------
  |  | 2122|  29.2k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4070:5): [True: 29.2k, False: 0]
  ------------------
 4071|  29.2k|    return valptr();
 4072|  29.2k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3365|  29.2k|  T* valptr() { return std::addressof(this->m_val); }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 4082|  4.57k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 4083|  4.57k|    TL_ASSERT(has_value());
  ------------------
  |  | 2122|  4.57k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4083:5): [True: 4.57k, False: 0]
  ------------------
 4084|  4.57k|    return val();
 4085|  4.57k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3374|  4.57k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3375|  4.57k|    return this->m_val;
 3376|  4.57k|  }
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 4099|  94.5k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2697|  51.7k|  ~expected_storage_base() {
 2698|  51.7k|    if (m_has_val) {
  ------------------
  |  Branch (2698:9): [True: 25.3k, False: 26.4k]
  ------------------
 2699|  25.3k|      m_val.~T();
 2700|  25.3k|    }
 2701|  51.7k|  }
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1912|  31.8k|                                                       size_t pos2) {
 1913|       |#if ADA_DEVELOPMENT_CHECKS
 1914|       |  if (pos2 < pos1) {
 1915|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1916|       |              << std::endl;
 1917|       |    abort();
 1918|       |  }
 1919|       |#endif
 1920|  31.8k|  return input.substr(pos1, pos2 - pos1);
 1921|  31.8k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8557|  5.04k|    const noexcept {
 8558|  5.04k|  return buffer.size() == components.pathname_start;
 8559|  5.04k|}
_ZNK3ada14url_aggregator12has_hostnameEv:
 9194|    270|constexpr bool url_aggregator::has_hostname() const noexcept {
 9195|    270|  return has_authority();
 9196|    270|}
_ZNK3ada14url_aggregator12has_passwordEv:
 9174|  5.40k|constexpr bool url_aggregator::has_password() const noexcept {
 9175|  5.40k|  ada_log("url_aggregator::has_password");
 9176|       |  // This function does not care about the length of the password
 9177|  5.40k|  return components.host_start > components.username_end &&
  ------------------
  |  Branch (9177:10): [True: 4.83k, False: 571]
  ------------------
 9178|  4.83k|         buffer[components.username_end] == ':';
  ------------------
  |  Branch (9178:10): [True: 4.83k, False: 0]
  ------------------
 9179|  5.40k|}
_ZNK3ada3url8get_hrefEv:
 7621|  20.7k|[[nodiscard]] ada_really_inline std::string url::get_href() const {
 7622|  20.7k|  if (is_special() && host.has_value() && username.empty() &&
  ------------------
  |  Branch (7622:7): [True: 17.3k, False: 3.38k]
  |  Branch (7622:23): [True: 17.3k, False: 0]
  |  Branch (7622:43): [True: 16.8k, False: 559]
  ------------------
 7623|  16.8k|      password.empty() && !port.has_value()) [[likely]] {
  ------------------
  |  Branch (7623:7): [True: 16.7k, False: 66]
  |  Branch (7623:27): [True: 14.3k, False: 2.37k]
  ------------------
 7624|  14.3k|    const std::string_view scheme = ada::scheme::details::is_special_list[type];
 7625|  14.3k|    const size_t host_size = host->size();
 7626|  14.3k|    const size_t path_size = path.size();
 7627|  14.3k|    const size_t query_size = query.has_value() ? query->size() : 0;
  ------------------
  |  Branch (7627:31): [True: 1.02k, False: 13.3k]
  ------------------
 7628|  14.3k|    const size_t hash_size = hash.has_value() ? hash->size() : 0;
  ------------------
  |  Branch (7628:30): [True: 850, False: 13.5k]
  ------------------
 7629|  14.3k|    const size_t total = scheme.size() + 3 + host_size + path_size +
 7630|  14.3k|                         (query.has_value() ? query_size + 1 : 0) +
  ------------------
  |  Branch (7630:27): [True: 1.02k, False: 13.3k]
  ------------------
 7631|  14.3k|                         (hash.has_value() ? hash_size + 1 : 0);
  ------------------
  |  Branch (7631:27): [True: 850, False: 13.5k]
  ------------------
 7632|  14.3k|    std::string output(total, '\0');
 7633|  14.3k|    char* p = output.data();
 7634|  14.3k|    std::memcpy(p, scheme.data(), scheme.size());
 7635|  14.3k|    p += scheme.size();
 7636|  14.3k|    p[0] = ':';
 7637|  14.3k|    p[1] = '/';
 7638|  14.3k|    p[2] = '/';
 7639|  14.3k|    p += 3;
 7640|       |    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7641|  14.3k|    std::memcpy(p, host->data(), host_size);
 7642|  14.3k|    p += host_size;
 7643|  14.3k|    std::memcpy(p, path.data(), path_size);
 7644|  14.3k|    p += path_size;
 7645|  14.3k|    if (query.has_value()) {
  ------------------
  |  Branch (7645:9): [True: 1.02k, False: 13.3k]
  ------------------
 7646|  1.02k|      *p++ = '?';
 7647|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7648|  1.02k|      std::memcpy(p, query->data(), query_size);
 7649|  1.02k|      p += query_size;
 7650|  1.02k|    }
 7651|  14.3k|    if (hash.has_value()) {
  ------------------
  |  Branch (7651:9): [True: 850, False: 13.5k]
  ------------------
 7652|    850|      *p++ = '#';
 7653|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7654|    850|      std::memcpy(p, hash->data(), hash_size);
 7655|    850|    }
 7656|  14.3k|    return output;
 7657|  14.3k|  }
 7658|       |
 7659|  6.38k|  std::string output;
 7660|  6.38k|  output.reserve(get_href_size());
 7661|       |
 7662|  6.38k|  if (is_special()) {
  ------------------
  |  Branch (7662:7): [True: 3.00k, False: 3.38k]
  ------------------
 7663|  3.00k|    output.append(ada::scheme::details::is_special_list[type]);
 7664|  3.00k|    output += ':';
 7665|  3.38k|  } else {
 7666|  3.38k|    output.append(non_special_scheme);
 7667|  3.38k|    output += ':';
 7668|  3.38k|  }
 7669|       |
 7670|  6.38k|  if (host.has_value()) {
  ------------------
  |  Branch (7670:7): [True: 3.73k, False: 2.64k]
  ------------------
 7671|  3.73k|    output += '/';
 7672|  3.73k|    output += '/';
 7673|  3.73k|    if (has_credentials()) {
  ------------------
  |  Branch (7673:9): [True: 721, False: 3.01k]
  ------------------
 7674|    721|      output.append(username);
 7675|    721|      if (!password.empty()) {
  ------------------
  |  Branch (7675:11): [True: 209, False: 512]
  ------------------
 7676|    209|        output += ':';
 7677|    209|        output.append(password);
 7678|    209|      }
 7679|    721|      output += '@';
 7680|    721|    }
 7681|  3.73k|    output.append(*host);
 7682|  3.73k|    if (port.has_value()) {
  ------------------
  |  Branch (7682:9): [True: 2.50k, False: 1.23k]
  ------------------
 7683|  2.50k|      output += ':';
 7684|  2.50k|      char port_buf[5];
 7685|  2.50k|      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
 7686|  2.50k|      (void)ec;
 7687|  2.50k|      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
 7688|  2.50k|    }
 7689|  3.73k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7689:14): [True: 1.30k, False: 1.34k]
  |  Branch (7689:34): [True: 33, False: 1.27k]
  ------------------
 7690|       |    // If url's host is null, url does not have an opaque path, url's path's
 7691|       |    // size is greater than 1, and url's path[0] is the empty string, then
 7692|       |    // append U+002F (/) followed by U+002E (.) to output.
 7693|     33|    output += '/';
 7694|     33|    output += '.';
 7695|     33|  }
 7696|  6.38k|  output.append(path);
 7697|  6.38k|  if (query.has_value()) {
  ------------------
  |  Branch (7697:7): [True: 741, False: 5.64k]
  ------------------
 7698|    741|    output += '?';
 7699|    741|    output.append(*query);
 7700|    741|  }
 7701|  6.38k|  if (hash.has_value()) {
  ------------------
  |  Branch (7701:7): [True: 610, False: 5.77k]
  ------------------
 7702|    610|    output += '#';
 7703|    610|    output.append(*hash);
 7704|    610|  }
 7705|  6.38k|  return output;
 7706|  20.7k|}
_ZNK3ada14url_aggregator10has_searchEv:
 9103|  2.28k|[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
 9104|  2.28k|  ada_log("url_aggregator::has_search");
 9105|  2.28k|  return components.search_start != url_components::omitted;
 9106|  2.28k|}
_ZN3ada7helpers6concatIJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKcEEENS2_12basic_stringIcS5_NS2_9allocatorIcEEEEDpT_:
 1990|  22.4k|std::string concat(Args... args) {
 1991|  22.4k|  std::string answer;
 1992|  22.4k|  inner_concat(answer, args...);
 1993|  22.4k|  return answer;
 1994|  22.4k|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJPKcEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_DpT0_:
 1979|  22.4k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1980|  22.4k|  buffer.append(t);
 1981|  22.4k|  return inner_concat(buffer, args...);
 1982|  22.4k|}
_ZN3ada7helpers12inner_concatIPKcJEEEvRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEET_:
 1971|  22.4k|inline void inner_concat(std::string& buffer, T t) {
 1972|  22.4k|  buffer.append(t);
 1973|  22.4k|}
_ZN3ada3url10set_schemeEONSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 7603|  11.2k|inline void url::set_scheme(std::string&& new_scheme) noexcept {
 7604|  11.2k|  type = ada::scheme::get_scheme_type(new_scheme);
 7605|       |  // We only move the 'scheme' if it is non-special.
 7606|  11.2k|  if (!is_special()) {
  ------------------
  |  Branch (7606:7): [True: 5.68k, False: 5.53k]
  ------------------
 7607|  5.68k|    non_special_scheme = std::move(new_scheme);
 7608|  5.68k|  }
 7609|  11.2k|}
_ZN3ada7helpers6concatIJPKcNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEENS4_12basic_stringIcS7_NS4_9allocatorIcEEEEDpT_:
 1990|      4|std::string concat(Args... args) {
 1991|      4|  std::string answer;
 1992|      4|  inner_concat(answer, args...);
 1993|      4|  return answer;
 1994|      4|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEEvRNS4_12basic_stringIcS7_NS4_9allocatorIcEEEET_DpT0_:
 1979|      4|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1980|      4|  buffer.append(t);
 1981|      4|  return inner_concat(buffer, args...);
 1982|      4|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_:
 1971|      4|inline void inner_concat(std::string& buffer, T t) {
 1972|      4|  buffer.append(t);
 1973|      4|}

LLVMFuzzerTestOneInput:
   10|  12.8k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   11|  12.8k|  FuzzedDataProvider fdp(data, size);
   12|  12.8k|  std::string source = fdp.ConsumeRandomLengthString(256);
   13|  12.8k|  std::string base_source = fdp.ConsumeRandomLengthString(256);
   14|       |
   15|  12.8k|  bool can_parse_result = ada::can_parse(source);
   16|  12.8k|  auto parsed_agg = ada::parse<ada::url_aggregator>(source);
   17|  12.8k|  auto parsed_url = ada::parse<ada::url>(source);
   18|       |
   19|  12.8k|  if (can_parse_result != parsed_agg.has_value()) {
  ------------------
  |  Branch (19:7): [True: 0, False: 12.8k]
  ------------------
   20|      0|    printf("can_parse vs parse<url_aggregator> inconsistency for: %s\n",
   21|      0|           source.c_str());
   22|      0|    abort();
   23|      0|  }
   24|  12.8k|  if (can_parse_result != parsed_url.has_value()) {
  ------------------
  |  Branch (24:7): [True: 0, False: 12.8k]
  ------------------
   25|      0|    printf("can_parse vs parse<url> inconsistency for: %s\n", source.c_str());
   26|      0|    abort();
   27|      0|  }
   28|  12.8k|  if (parsed_url.has_value() != parsed_agg.has_value()) {
  ------------------
  |  Branch (28:7): [True: 0, False: 12.8k]
  ------------------
   29|      0|    printf("parse<url> vs parse<url_aggregator> disagreement for: %s\n",
   30|      0|           source.c_str());
   31|      0|    abort();
   32|      0|  }
   33|  12.8k|  if (parsed_url && parsed_agg &&
  ------------------
  |  Branch (33:7): [True: 8.54k, False: 4.33k]
  |  Branch (33:7): [True: 0, False: 12.8k]
  |  Branch (33:21): [True: 8.54k, False: 0]
  ------------------
   34|  8.54k|      parsed_url->get_href() != std::string(parsed_agg->get_href())) {
  ------------------
  |  Branch (34:7): [True: 0, False: 8.54k]
  ------------------
   35|      0|    printf("parse href disagreement for: %s\n", source.c_str());
   36|      0|    abort();
   37|      0|  }
   38|       |
   39|  12.8k|  auto base_source_view =
   40|  12.8k|      std::string_view(base_source.data(), base_source.length());
   41|  12.8k|  bool can_parse_with_base = ada::can_parse(source, &base_source_view);
   42|       |
   43|  12.8k|  auto base_agg = ada::parse<ada::url_aggregator>(base_source);
   44|  12.8k|  auto base_url = ada::parse<ada::url>(base_source);
   45|  12.8k|  if (base_agg.has_value() != base_url.has_value()) {
  ------------------
  |  Branch (45:7): [True: 0, False: 12.8k]
  ------------------
   46|      0|    printf("base parse type disagreement for: %s\n", base_source.c_str());
   47|      0|    abort();
   48|      0|  }
   49|       |
   50|  12.8k|  if (base_agg && base_url) {
  ------------------
  |  Branch (50:7): [True: 4.57k, False: 8.29k]
  |  Branch (50:19): [True: 4.57k, False: 0]
  ------------------
   51|  4.57k|    auto ra = ada::parse<ada::url_aggregator>(source, &*base_agg);
   52|  4.57k|    auto ru = ada::parse<ada::url>(source, &*base_url);
   53|  4.57k|    if (can_parse_with_base != ra.has_value()) {
  ------------------
  |  Branch (53:9): [True: 0, False: 4.57k]
  ------------------
   54|      0|      printf(
   55|      0|          "can_parse_with_base vs parse inconsistency for source=%s base=%s\n",
   56|      0|          source.c_str(), base_source.c_str());
   57|      0|      abort();
   58|      0|    }
   59|  4.57k|    if (ra.has_value() != ru.has_value()) {
  ------------------
  |  Branch (59:9): [True: 0, False: 4.57k]
  ------------------
   60|      0|      printf("relative parse type disagreement source=%s base=%s\n",
   61|      0|             source.c_str(), base_source.c_str());
   62|      0|      abort();
   63|      0|    }
   64|  4.57k|    if (ra && ru && ru->get_href() != std::string(ra->get_href())) {
  ------------------
  |  Branch (64:9): [True: 3.67k, False: 906]
  |  Branch (64:9): [True: 0, False: 4.57k]
  |  Branch (64:15): [True: 3.67k, False: 0]
  |  Branch (64:21): [True: 0, False: 3.67k]
  ------------------
   65|      0|      printf("relative parse href disagreement source=%s base=%s\n",
   66|      0|             source.c_str(), base_source.c_str());
   67|      0|      abort();
   68|      0|    }
   69|  8.29k|  } else if (can_parse_with_base) {
  ------------------
  |  Branch (69:14): [True: 0, False: 8.29k]
  ------------------
   70|      0|    printf("can_parse_with_base true with invalid base=%s\n",
   71|      0|           base_source.c_str());
   72|      0|    abort();
   73|      0|  }
   74|       |
   75|  12.8k|  {
   76|  12.8k|    bool empty_cp = ada::can_parse("");
   77|  12.8k|    auto empty_agg = ada::parse<ada::url_aggregator>("");
   78|  12.8k|    auto empty_url = ada::parse<ada::url>("");
   79|  12.8k|    if (empty_cp != empty_agg.has_value() ||
  ------------------
  |  Branch (79:9): [True: 0, False: 12.8k]
  ------------------
   80|  12.8k|        empty_cp != empty_url.has_value()) {
  ------------------
  |  Branch (80:9): [True: 0, False: 12.8k]
  ------------------
   81|      0|      printf("Empty string can_parse/parse disagreement\n");
   82|      0|      abort();
   83|      0|    }
   84|  12.8k|  }
   85|       |
   86|  12.8k|  if (parsed_agg) {
  ------------------
  |  Branch (86:7): [True: 8.54k, False: 4.33k]
  ------------------
   87|  8.54k|    std::string href = std::string(parsed_agg->get_href());
   88|  8.54k|    if (!ada::can_parse(href)) {
  ------------------
  |  Branch (88:9): [True: 0, False: 8.54k]
  ------------------
   89|      0|      printf("can_parse rejected normalised href: '%s'\n", href.c_str());
   90|      0|      abort();
   91|      0|    }
   92|  8.54k|    auto reparsed = ada::parse<ada::url_aggregator>(href);
   93|  8.54k|    if (!reparsed || std::string(reparsed->get_href()) != href) {
  ------------------
  |  Branch (93:9): [True: 0, False: 8.54k]
  |  Branch (93:9): [True: 0, False: 8.54k]
  |  Branch (93:22): [True: 0, False: 8.54k]
  ------------------
   94|      0|      printf("href re-parse failure for: '%s'\n", href.c_str());
   95|      0|      abort();
   96|      0|    }
   97|  8.54k|    auto reparsed_url = ada::parse<ada::url>(href);
   98|  8.54k|    if (!reparsed_url || reparsed_url->get_href() != href) {
  ------------------
  |  Branch (98:9): [True: 0, False: 8.54k]
  |  Branch (98:9): [True: 0, False: 8.54k]
  |  Branch (98:26): [True: 0, False: 8.54k]
  ------------------
   99|      0|      printf("parse<url> re-parse disagreement on href: '%s'\n", href.c_str());
  100|      0|      abort();
  101|      0|    }
  102|  8.54k|  }
  103|       |
  104|  12.8k|  return 0;
  105|  12.8k|}

