_ZN3ada4idna13utf8_to_utf32EPKcmPDi:
  162|  4.97k|size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
  163|  4.97k|  const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
  164|  4.97k|  size_t pos = 0;
  165|  4.97k|  const char32_t* start{utf32_output};
  166|  91.8k|  while (pos < len) {
  ------------------
  |  Branch (166:10): [True: 87.3k, False: 4.53k]
  ------------------
  167|       |    // try to convert the next block of 16 ASCII bytes
  168|  87.3k|    if (pos + 16 <= len) {  // if it is safe to read 16 more
  ------------------
  |  Branch (168:9): [True: 60.3k, False: 27.0k]
  ------------------
  169|       |                            // bytes, check that they are ascii
  170|  60.3k|      uint64_t v1;
  171|  60.3k|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  172|  60.3k|      uint64_t v2;
  173|  60.3k|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  174|  60.3k|      uint64_t v{v1 | v2};
  175|  60.3k|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (175:11): [True: 3.76k, False: 56.5k]
  ------------------
  176|  3.76k|        size_t final_pos = pos + 16;
  177|  63.9k|        while (pos < final_pos) {
  ------------------
  |  Branch (177:16): [True: 60.1k, False: 3.76k]
  ------------------
  178|  60.1k|          *utf32_output++ = char32_t(buf[pos]);
  179|  60.1k|          pos++;
  180|  60.1k|        }
  181|  3.76k|        continue;
  182|  3.76k|      }
  183|  60.3k|    }
  184|  83.5k|    uint8_t leading_byte = data[pos];  // leading byte
  185|  83.5k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (185:9): [True: 47.3k, False: 36.2k]
  ------------------
  186|       |      // converting one ASCII byte !!!
  187|  47.3k|      *utf32_output++ = char32_t(leading_byte);
  188|  47.3k|      pos++;
  189|  47.3k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (189:16): [True: 7.25k, False: 28.9k]
  ------------------
  190|       |      // We have a two-byte UTF-8
  191|  7.25k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (191:11): [True: 54, False: 7.20k]
  ------------------
  192|     54|        return 0;
  193|     54|      }  // minimal bound checking
  194|  7.20k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (194:11): [True: 80, False: 7.12k]
  ------------------
  195|     80|        return 0;
  196|     80|      }
  197|       |      // range check
  198|  7.12k|      uint32_t code_point =
  199|  7.12k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  200|  7.12k|      if (code_point < 0x80 || 0x7ff < code_point) {
  ------------------
  |  Branch (200:11): [True: 6, False: 7.11k]
  |  Branch (200:32): [True: 0, False: 7.11k]
  ------------------
  201|      6|        return 0;
  202|      6|      }
  203|  7.11k|      *utf32_output++ = char32_t(code_point);
  204|  7.11k|      pos += 2;
  205|  28.9k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (205:16): [True: 28.0k, False: 938]
  ------------------
  206|       |      // We have a three-byte UTF-8
  207|  28.0k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (207:11): [True: 14, False: 27.9k]
  ------------------
  208|     14|        return 0;
  209|     14|      }  // minimal bound checking
  210|       |
  211|  27.9k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (211:11): [True: 32, False: 27.9k]
  ------------------
  212|     32|        return 0;
  213|     32|      }
  214|  27.9k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (214:11): [True: 10, False: 27.9k]
  ------------------
  215|     10|        return 0;
  216|     10|      }
  217|       |      // range check
  218|  27.9k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  219|  27.9k|                            (data[pos + 1] & 0b00111111) << 6 |
  220|  27.9k|                            (data[pos + 2] & 0b00111111);
  221|  27.9k|      if (code_point < 0x800 || 0xffff < code_point ||
  ------------------
  |  Branch (221:11): [True: 16, False: 27.9k]
  |  Branch (221:33): [True: 0, False: 27.9k]
  ------------------
  222|  27.9k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (222:12): [True: 4.59k, False: 23.3k]
  |  Branch (222:35): [True: 6, False: 4.59k]
  ------------------
  223|     22|        return 0;
  224|     22|      }
  225|  27.9k|      *utf32_output++ = char32_t(code_point);
  226|  27.9k|      pos += 3;
  227|  27.9k|    } else if ((leading_byte & 0b11111000) == 0b11110000) {  // 0b11110000
  ------------------
  |  Branch (227:16): [True: 778, False: 160]
  ------------------
  228|       |      // we have a 4-byte UTF-8 word.
  229|    778|      if (pos + 3 >= len) {
  ------------------
  |  Branch (229:11): [True: 10, False: 768]
  ------------------
  230|     10|        return 0;
  231|     10|      }  // minimal bound checking
  232|    768|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (232:11): [True: 20, False: 748]
  ------------------
  233|     20|        return 0;
  234|     20|      }
  235|    748|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (235:11): [True: 10, False: 738]
  ------------------
  236|     10|        return 0;
  237|     10|      }
  238|    738|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (238:11): [True: 8, False: 730]
  ------------------
  239|      8|        return 0;
  240|      8|      }
  241|       |
  242|       |      // range check
  243|    730|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  244|    730|                            (data[pos + 1] & 0b00111111) << 12 |
  245|    730|                            (data[pos + 2] & 0b00111111) << 6 |
  246|    730|                            (data[pos + 3] & 0b00111111);
  247|    730|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (247:11): [True: 14, False: 716]
  |  Branch (247:35): [True: 8, False: 708]
  ------------------
  248|     22|        return 0;
  249|     22|      }
  250|    708|      *utf32_output++ = char32_t(code_point);
  251|    708|      pos += 4;
  252|    708|    } else {
  253|    160|      return 0;
  254|    160|    }
  255|  83.5k|  }
  256|  4.53k|  return utf32_output - start;
  257|  4.97k|}
_ZN3ada4idna22utf32_length_from_utf8EPKcm:
  272|  5.02k|size_t utf32_length_from_utf8(const char* buf, size_t len) {
  273|  5.02k|  const int8_t* p = reinterpret_cast<const int8_t*>(buf);
  274|  5.02k|  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|  5.02k|    return c > -65;
  278|  5.02k|  });
  279|  5.02k|}
_ZN3ada4idna9ascii_mapEPcm:
 5338|   122k|void ascii_map(char* input, size_t length) {
 5339|   122k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5340|   122k|    return 0x101010101010101ull * v;
 5341|   122k|  };
 5342|   122k|  uint64_t broadcast_80 = broadcast(0x80);
 5343|   122k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 5344|   122k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 5345|   122k|  size_t i = 0;
 5346|       |
 5347|   320k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (5347:10): [True: 198k, False: 122k]
  ------------------
 5348|   198k|    uint64_t word{};
 5349|   198k|    std::memcpy(&word, input + i, sizeof(word));
 5350|   198k|    word ^=
 5351|   198k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5352|   198k|    std::memcpy(input + i, &word, sizeof(word));
 5353|   198k|  }
 5354|   122k|  if (i < length) {
  ------------------
  |  Branch (5354:7): [True: 63.9k, False: 58.8k]
  ------------------
 5355|  63.9k|    uint64_t word{};
 5356|  63.9k|    std::memcpy(&word, input + i, length - i);
 5357|  63.9k|    word ^=
 5358|  63.9k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5359|  63.9k|    std::memcpy(input + i, &word, length - i);
 5360|  63.9k|  }
 5361|   122k|}
_ZN3ada4idna3mapENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIDiS4_NS1_9allocatorIDiEEEE:
 5365|  7.11k|bool map(std::u32string_view input, std::u32string& out) {
 5366|       |  //  [Map](https://www.unicode.org/reports/tr46/#ProcessingStepMap).
 5367|  7.11k|  out.clear();
 5368|  7.11k|  if (!ensure_tables()) {
  ------------------
  |  Branch (5368:7): [True: 0, False: 7.11k]
  ------------------
 5369|      0|    return false;
 5370|      0|  }
 5371|       |
 5372|       |  // Pass 1: validate and compute exact output length.
 5373|  7.11k|  size_t out_size = 0;
 5374|   161k|  for (char32_t x : input) {
  ------------------
  |  Branch (5374:19): [True: 161k, False: 6.76k]
  ------------------
 5375|   161k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5376|   161k|    if (status == IDNA_DISALLOWED) {
  ------------------
  |  Branch (5376:9): [True: 354, False: 160k]
  ------------------
 5377|    354|      return false;
 5378|    354|    }
 5379|   160k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5379:9): [True: 110k, False: 50.4k]
  ------------------
 5380|   110k|      ++out_size;
 5381|   110k|      continue;
 5382|   110k|    }
 5383|  50.4k|    if (static_cast<size_t>(status) >= idna_utf8_mappings_size) {
  ------------------
  |  Branch (5383:9): [True: 0, False: 50.4k]
  ------------------
 5384|      0|      return false;
 5385|      0|    }
 5386|  50.4k|    out_size += utf8_count_codepoints(idna_utf8_mappings + status);
 5387|  50.4k|  }
 5388|       |
 5389|       |  // Pass 2: write into a single allocation.
 5390|  6.76k|  out.resize(out_size);
 5391|  6.76k|  size_t w = 0;
 5392|   160k|  for (char32_t x : input) {
  ------------------
  |  Branch (5392:19): [True: 160k, False: 6.76k]
  ------------------
 5393|   160k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5394|   160k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5394:9): [True: 109k, False: 50.2k]
  ------------------
 5395|   109k|      out[w++] = x;
 5396|   109k|      continue;
 5397|   109k|    }
 5398|       |    // IGNORED or mapped (status already validated in pass 1).
 5399|  50.2k|    const uint8_t* ptr = idna_utf8_mappings + status;
 5400|   266k|    while (*ptr != 0) {
  ------------------
  |  Branch (5400:12): [True: 216k, False: 50.2k]
  ------------------
 5401|   216k|      out[w++] = utf8_next(ptr);
 5402|   216k|    }
 5403|  50.2k|  }
 5404|  6.76k|  return true;
 5405|  7.11k|}
_ZN3ada4idna28compute_decomposition_lengthENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5499|  1.63k|    const std::u32string_view input) noexcept {
 5500|  1.63k|  bool decomposition_needed{false};
 5501|  1.63k|  size_t additional_elements{0};
 5502|  63.1k|  for (char32_t current_character : input) {
  ------------------
  |  Branch (5502:35): [True: 63.1k, False: 1.63k]
  ------------------
 5503|  63.1k|    size_t decomposition_length{0};
 5504|       |
 5505|  63.1k|    if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5505:9): [True: 2.86k, False: 60.3k]
  ------------------
 5506|  2.86k|        current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5506:9): [True: 1.83k, False: 1.02k]
  ------------------
 5507|       |      // Full NFD-style Hangul decomp for the decompose() step.
 5508|  1.83k|      decomposition_length = 2;
 5509|  1.83k|      if ((current_character - hangul_sbase) % hangul_tcount) {
  ------------------
  |  Branch (5509:11): [True: 370, False: 1.46k]
  ------------------
 5510|    370|        decomposition_length = 3;
 5511|    370|      }
 5512|  61.3k|    } else if (current_character < 0x110000) {
  ------------------
  |  Branch (5512:16): [True: 61.3k, False: 0]
  ------------------
 5513|  61.3k|      const uint8_t di = decomposition_index[current_character >> 8];
 5514|  61.3k|      const uint16_t* const decomposition =
 5515|  61.3k|          decomposition_block_row(di) + (current_character % 256);
 5516|  61.3k|      decomposition_length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5517|  61.3k|      if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5517:11): [True: 2.75k, False: 58.6k]
  |  Branch (5517:41): [True: 0, False: 2.75k]
  ------------------
 5518|      0|        decomposition_length = 0;
 5519|      0|      }
 5520|  61.3k|    }
 5521|  63.1k|    if (decomposition_length != 0) {
  ------------------
  |  Branch (5521:9): [True: 4.58k, False: 58.6k]
  ------------------
 5522|  4.58k|      decomposition_needed = true;
 5523|  4.58k|      additional_elements += decomposition_length - 1;
 5524|  4.58k|    }
 5525|  63.1k|  }
 5526|  1.63k|  return {decomposition_needed, additional_elements};
 5527|  1.63k|}
_ZN3ada4idna9decomposeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEEm:
 5529|  1.16k|void decompose(std::u32string& input, size_t additional_elements) {
 5530|  1.16k|  input.resize(input.size() + additional_elements);
 5531|  1.16k|  for (size_t descending_idx = input.size(),
 5532|  1.16k|              input_count = descending_idx - additional_elements;
 5533|  52.2k|       input_count--;) {
  ------------------
  |  Branch (5533:8): [True: 51.0k, False: 1.16k]
  ------------------
 5534|  51.0k|    if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5534:9): [True: 2.40k, False: 48.6k]
  ------------------
 5535|  2.40k|        input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5535:9): [True: 1.83k, False: 562]
  ------------------
 5536|       |      // Hangul decomposition.
 5537|  1.83k|      char32_t s_index = input[input_count] - hangul_sbase;
 5538|  1.83k|      if (s_index % hangul_tcount != 0) {
  ------------------
  |  Branch (5538:11): [True: 370, False: 1.46k]
  ------------------
 5539|    370|        input[--descending_idx] = hangul_tbase + s_index % hangul_tcount;
 5540|    370|      }
 5541|  1.83k|      input[--descending_idx] =
 5542|  1.83k|          hangul_vbase + (s_index % hangul_ncount) / hangul_tcount;
 5543|  1.83k|      input[--descending_idx] = hangul_lbase + s_index / hangul_ncount;
 5544|  49.2k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5544:16): [True: 49.2k, False: 0]
  ------------------
 5545|  49.2k|      const uint16_t* decomposition =
 5546|  49.2k|          decomposition_block_row(
 5547|  49.2k|              decomposition_index[input[input_count] >> 8]) +
 5548|  49.2k|          (input[input_count] % 256);
 5549|  49.2k|      uint16_t decomposition_length =
 5550|  49.2k|          (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5551|  49.2k|      if (decomposition_length > 0 && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5551:11): [True: 2.75k, False: 46.4k]
  |  Branch (5551:39): [True: 0, False: 2.75k]
  ------------------
 5552|      0|        decomposition_length = 0;
 5553|      0|      }
 5554|  49.2k|      if (decomposition_length > 0) {
  ------------------
  |  Branch (5554:11): [True: 2.75k, False: 46.4k]
  ------------------
 5555|  2.75k|        const size_t base = static_cast<size_t>(decomposition[0] >> 2);
 5556|  2.75k|        if (base + decomposition_length > decomposition_data_size) {
  ------------------
  |  Branch (5556:13): [True: 0, False: 2.75k]
  ------------------
 5557|      0|          input[--descending_idx] = input[input_count];
 5558|  2.75k|        } else {
 5559|  8.58k|          while (decomposition_length-- > 0) {
  ------------------
  |  Branch (5559:18): [True: 5.83k, False: 2.75k]
  ------------------
 5560|  5.83k|            input[--descending_idx] =
 5561|  5.83k|                decomposition_data[base + decomposition_length];
 5562|  5.83k|          }
 5563|  2.75k|        }
 5564|  46.4k|      } else {
 5565|  46.4k|        input[--descending_idx] = input[input_count];
 5566|  46.4k|      }
 5567|  49.2k|    } else {
 5568|      0|      input[--descending_idx] = input[input_count];
 5569|      0|    }
 5570|  51.0k|  }
 5571|  1.16k|}
_ZN3ada4idna7get_cccEDi:
 5573|  1.06M|uint8_t get_ccc(char32_t c) noexcept {
 5574|  1.06M|  if (c >= 0x110000) {
  ------------------
  |  Branch (5574:7): [True: 0, False: 1.06M]
  ------------------
 5575|      0|    return 0;
 5576|      0|  }
 5577|  1.06M|  return ccc_block_row(ccc_index[c >> 8])[c % 256];
 5578|  1.06M|}
_ZN3ada4idna10sort_marksERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5580|  1.63k|void sort_marks(std::u32string& input) {
 5581|  68.4k|  for (size_t idx = 1; idx < input.size(); idx++) {
  ------------------
  |  Branch (5581:24): [True: 66.8k, False: 1.63k]
  ------------------
 5582|  66.8k|    uint8_t ccc = get_ccc(input[idx]);
 5583|  66.8k|    if (ccc == 0) {
  ------------------
  |  Branch (5583:9): [True: 58.6k, False: 8.18k]
  ------------------
 5584|  58.6k|      continue;
 5585|  58.6k|    }
 5586|  8.18k|    auto current_character = input[idx];
 5587|  8.18k|    size_t back_idx = idx;
 5588|  11.6k|    while (back_idx != 0 && get_ccc(input[back_idx - 1]) > ccc) {
  ------------------
  |  Branch (5588:12): [True: 11.5k, False: 130]
  |  Branch (5588:29): [True: 3.46k, False: 8.05k]
  ------------------
 5589|  3.46k|      input[back_idx] = input[back_idx - 1];
 5590|  3.46k|      back_idx--;
 5591|  3.46k|    }
 5592|  8.18k|    input[back_idx] = current_character;
 5593|  8.18k|  }
 5594|  1.63k|}
_ZN3ada4idna13decompose_nfcERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5596|  1.63k|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|  1.63k|  auto [decomposition_needed, additional_elements] =
 5602|  1.63k|      compute_decomposition_length(input);
 5603|  1.63k|  if (decomposition_needed) {
  ------------------
  |  Branch (5603:7): [True: 1.16k, False: 470]
  ------------------
 5604|  1.16k|    decompose(input, additional_elements);
 5605|  1.16k|  }
 5606|  1.63k|  sort_marks(input);
 5607|  1.63k|}
_ZN3ada4idna14is_already_nfcENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5742|  7.50k|bool is_already_nfc(std::u32string_view input) noexcept {
 5743|  7.50k|  if (input.empty()) {
  ------------------
  |  Branch (5743:7): [True: 0, False: 7.50k]
  ------------------
 5744|      0|    return true;
 5745|      0|  }
 5746|  7.50k|  if (!tables_are_ready() && !ensure_tables()) {
  ------------------
  |  Branch (5746:7): [True: 0, False: 7.50k]
  |  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|  7.50k|  char32_t previous = 0x110000;  // none
 5755|   336k|  for (char32_t c : input) {
  ------------------
  |  Branch (5755:19): [True: 336k, False: 7.48k]
  ------------------
 5756|   336k|    if (decomposition_is_not_nfc(previous, c)) {
  ------------------
  |  Branch (5756:9): [True: 20, False: 336k]
  ------------------
 5757|     20|      return false;
 5758|     20|    }
 5759|   336k|    previous = c;
 5760|   336k|  }
 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|  7.48k|  uint8_t prev_ccc = 0;
 5765|   328k|  for (char32_t c : input) {
  ------------------
  |  Branch (5765:19): [True: 328k, False: 5.42k]
  ------------------
 5766|   328k|    uint8_t ccc = get_ccc(c);
 5767|   328k|    if (ccc != 0 && prev_ccc > ccc) {
  ------------------
  |  Branch (5767:9): [True: 8.02k, False: 320k]
  |  Branch (5767:21): [True: 2.06k, False: 5.95k]
  ------------------
 5768|  2.06k|      return false;
 5769|  2.06k|    }
 5770|   326k|    prev_ccc = trailing_ccc(c);
 5771|   326k|  }
 5772|       |  // 3) No pairwise composition would apply (including Hangul L+V / LV+T).
 5773|  5.42k|  return !would_compose(input);
 5774|  7.48k|}
_ZN3ada4idna7composeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5776|  9.00k|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|  9.00k|  size_t input_count{0};
 5782|  9.00k|  size_t composition_count{0};
 5783|  74.3k|  for (; input_count < input.size(); input_count++, composition_count++) {
  ------------------
  |  Branch (5783:10): [True: 65.2k, False: 9.00k]
  ------------------
 5784|  65.2k|    input[composition_count] = input[input_count];
 5785|  65.2k|    if (input[input_count] >= hangul_lbase &&
  ------------------
  |  Branch (5785:9): [True: 8.66k, False: 56.6k]
  ------------------
 5786|  8.66k|        input[input_count] < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5786:9): [True: 2.81k, False: 5.84k]
  ------------------
 5787|  2.81k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5787:11): [True: 2.80k, False: 16]
  ------------------
 5788|  2.80k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5788:11): [True: 2.35k, False: 450]
  ------------------
 5789|  2.35k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5789:11): [True: 1.92k, False: 426]
  ------------------
 5790|  1.92k|        input[composition_count] =
 5791|  1.92k|            hangul_sbase +
 5792|  1.92k|            ((input[input_count] - hangul_lbase) * hangul_vcount +
 5793|  1.92k|             input[input_count + 1] - hangul_vbase) *
 5794|  1.92k|                hangul_tcount;
 5795|  1.92k|        input_count++;
 5796|  1.92k|        if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5796:13): [True: 1.88k, False: 42]
  ------------------
 5797|  1.88k|            input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5797:13): [True: 680, False: 1.20k]
  ------------------
 5798|    680|            input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5798:13): [True: 396, False: 284]
  ------------------
 5799|    396|          input[composition_count] += input[++input_count] - hangul_tbase;
 5800|    396|        }
 5801|  1.92k|      }
 5802|  62.4k|    } else if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5802:16): [True: 1.35k, False: 61.1k]
  ------------------
 5803|  1.35k|               input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5803:16): [True: 0, False: 1.35k]
  ------------------
 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|  62.4k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5810:16): [True: 62.4k, False: 0]
  ------------------
 5811|  62.4k|      const uint16_t* composition =
 5812|  62.4k|          composition_block_row(composition_index[input[input_count] >> 8]) +
 5813|  62.4k|          (input[input_count] % 256);
 5814|  62.4k|      size_t initial_composition_count = composition_count;
 5815|  78.8k|      for (int32_t previous_ccc = -1; input_count + 1 < input.size();
  ------------------
  |  Branch (5815:39): [True: 69.9k, False: 8.92k]
  ------------------
 5816|  69.9k|           input_count++) {
 5817|  69.9k|        uint8_t ccc = get_ccc(input[input_count + 1]);
 5818|       |
 5819|  69.9k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (5819:13): [True: 24.3k, False: 45.5k]
  |  Branch (5819:49): [True: 23.5k, False: 856]
  ------------------
 5820|  23.5k|          int left = composition[0];
 5821|  23.5k|          int right = composition[1];
 5822|  23.5k|          if (left < 0 || right < left ||
  ------------------
  |  Branch (5822:15): [True: 0, False: 23.5k]
  |  Branch (5822:27): [True: 0, False: 23.5k]
  ------------------
 5823|  23.5k|              static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5823:15): [True: 0, False: 23.5k]
  ------------------
 5824|      0|            break;
 5825|      0|          }
 5826|  50.9k|          while (left + 2 < right) {
  ------------------
  |  Branch (5826:18): [True: 27.4k, False: 23.5k]
  ------------------
 5827|  27.4k|            int middle = left + (((right - left) >> 1) & ~1);
 5828|  27.4k|            if (composition_data[static_cast<size_t>(middle)] <=
  ------------------
  |  Branch (5828:17): [True: 13.8k, False: 13.5k]
  ------------------
 5829|  27.4k|                input[input_count + 1]) {
 5830|  13.8k|              left = middle;
 5831|  13.8k|            }
 5832|  27.4k|            if (composition_data[static_cast<size_t>(middle)] >=
  ------------------
  |  Branch (5832:17): [True: 20.7k, False: 6.62k]
  ------------------
 5833|  27.4k|                input[input_count + 1]) {
 5834|  20.7k|              right = middle;
 5835|  20.7k|            }
 5836|  27.4k|          }
 5837|  23.5k|          if (static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5837:15): [True: 23.5k, False: 0]
  ------------------
 5838|  23.5k|              composition_data[static_cast<size_t>(left)] ==
  ------------------
  |  Branch (5838:15): [True: 11.7k, False: 11.7k]
  ------------------
 5839|  23.5k|                  input[input_count + 1]) {
 5840|  11.7k|            char32_t composed = composition_data[static_cast<size_t>(left + 1)];
 5841|  11.7k|            input[initial_composition_count] = composed;
 5842|  11.7k|            composition =
 5843|  11.7k|                composition_block_row(composition_index[composed >> 8]) +
 5844|  11.7k|                (composed % 256);
 5845|  11.7k|            continue;
 5846|  11.7k|          }
 5847|  23.5k|        }
 5848|       |
 5849|  58.2k|        if (ccc == 0) {
  ------------------
  |  Branch (5849:13): [True: 53.5k, False: 4.64k]
  ------------------
 5850|  53.5k|          break;
 5851|  53.5k|        }
 5852|  4.64k|        previous_ccc = ccc;
 5853|  4.64k|        input[++composition_count] = input[input_count + 1];
 5854|  4.64k|      }
 5855|  62.4k|    }
 5856|  65.2k|  }
 5857|       |
 5858|  9.00k|  if (composition_count < input_count) {
  ------------------
  |  Branch (5858:7): [True: 8.90k, False: 102]
  ------------------
 5859|  8.90k|    input.resize(composition_count);
 5860|  8.90k|  }
 5861|  9.00k|}
_ZN3ada4idna9normalizeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5863|  1.63k|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|  1.63k|  if (!ensure_tables() || decomposition_index == nullptr ||
  ------------------
  |  Branch (5868:7): [True: 0, False: 1.63k]
  |  Branch (5868:27): [True: 0, False: 1.63k]
  ------------------
 5869|  1.63k|      composition_index == nullptr) {
  ------------------
  |  Branch (5869:7): [True: 0, False: 1.63k]
  ------------------
 5870|      0|    return false;
 5871|      0|  }
 5872|  1.63k|  if (is_already_nfc(input)) {
  ------------------
  |  Branch (5872:7): [True: 0, False: 1.63k]
  ------------------
 5873|      0|    return true;
 5874|      0|  }
 5875|  1.63k|  decompose_nfc(input);
 5876|  1.63k|  compose(input);
 5877|  1.63k|  return true;
 5878|  1.63k|}
_ZN3ada4idna17punycode_to_utf32ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIDiNS3_IDiEENS1_9allocatorIDiEEEE:
 5938|  2.80k|bool punycode_to_utf32(std::string_view input, std::u32string &out) {
 5939|  2.80k|  int32_t written_out{0};
 5940|  2.80k|  out.reserve(out.size() + input.size());
 5941|  2.80k|  uint32_t n = initial_n;
 5942|  2.80k|  int32_t i = 0;
 5943|  2.80k|  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|  2.80k|  size_t end_of_ascii = input.find_last_of('-');
 5951|  2.80k|  if (end_of_ascii != std::string_view::npos && end_of_ascii > 0) {
  ------------------
  |  Branch (5951:7): [True: 668, False: 2.14k]
  |  Branch (5951:49): [True: 662, False: 6]
  ------------------
 5952|  10.4k|    for (uint8_t c : input.substr(0, end_of_ascii)) {
  ------------------
  |  Branch (5952:20): [True: 10.4k, False: 662]
  ------------------
 5953|  10.4k|      if (c >= 0x80) {
  ------------------
  |  Branch (5953:11): [True: 0, False: 10.4k]
  ------------------
 5954|      0|        return false;
 5955|      0|      }
 5956|  10.4k|      out.push_back(c);
 5957|  10.4k|      written_out++;
 5958|  10.4k|    }
 5959|    662|    input.remove_prefix(end_of_ascii + 1);
 5960|    662|  }
 5961|  23.4k|  while (!input.empty()) {
  ------------------
  |  Branch (5961:10): [True: 20.7k, False: 2.68k]
  ------------------
 5962|  20.7k|    int32_t oldi = i;
 5963|  20.7k|    int32_t w = 1;
 5964|  31.4k|    for (int32_t k = base;; k += base) {
 5965|  31.4k|      if (input.empty()) {
  ------------------
  |  Branch (5965:11): [True: 28, False: 31.4k]
  ------------------
 5966|     28|        return false;
 5967|     28|      }
 5968|  31.4k|      uint8_t code_point = input.front();
 5969|  31.4k|      input.remove_prefix(1);
 5970|  31.4k|      int32_t digit = char_to_digit_value(code_point);
 5971|  31.4k|      if (digit < 0) {
  ------------------
  |  Branch (5971:11): [True: 36, False: 31.3k]
  ------------------
 5972|     36|        return false;
 5973|     36|      }
 5974|  31.3k|      if (digit > (0x7fffffff - i) / w) {
  ------------------
  |  Branch (5974:11): [True: 10, False: 31.3k]
  ------------------
 5975|     10|        return false;
 5976|     10|      }
 5977|  31.3k|      i = i + digit * w;
 5978|  31.3k|      int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (5978:19): [True: 6.92k, False: 24.4k]
  |  Branch (5978:38): [True: 23.2k, False: 1.26k]
  ------------------
 5979|  31.3k|      if (digit < t) {
  ------------------
  |  Branch (5979:11): [True: 20.6k, False: 10.6k]
  ------------------
 5980|  20.6k|        break;
 5981|  20.6k|      }
 5982|  10.6k|      if (w > 0x7fffffff / (base - t)) {
  ------------------
  |  Branch (5982:11): [True: 0, False: 10.6k]
  ------------------
 5983|      0|        return false;
 5984|      0|      }
 5985|  10.6k|      w = w * (base - t);
 5986|  10.6k|    }
 5987|  20.6k|    bias = adapt(i - oldi, written_out + 1, oldi == 0);
 5988|  20.6k|    if (i / (written_out + 1) > int32_t(0x7fffffff - n)) {
  ------------------
  |  Branch (5988:9): [True: 54, False: 20.6k]
  ------------------
 5989|     54|      return false;
 5990|     54|    }
 5991|  20.6k|    n = n + i / (written_out + 1);
 5992|  20.6k|    i = i % (written_out + 1);
 5993|  20.6k|    if (n < 0x80) {
  ------------------
  |  Branch (5993:9): [True: 0, False: 20.6k]
  ------------------
 5994|      0|      return false;
 5995|      0|    }
 5996|  20.6k|    out.insert(out.begin() + i, n);
 5997|  20.6k|    written_out++;
 5998|  20.6k|    ++i;
 5999|  20.6k|  }
 6000|       |  // See https://github.com/whatwg/url/issues/803
 6001|       |  // Reject labels whose decoded form begins with "xn--" (double-encoded ACE).
 6002|  2.68k|  if (out.size() >= 4 && out[0] == U'x' && out[1] == U'n' && out[2] == U'-' &&
  ------------------
  |  Branch (6002:7): [True: 1.15k, False: 1.52k]
  |  Branch (6002:26): [True: 448, False: 706]
  |  Branch (6002:44): [True: 338, False: 110]
  |  Branch (6002:62): [True: 212, False: 126]
  ------------------
 6003|    212|      out[3] == U'-') {
  ------------------
  |  Branch (6003:7): [True: 6, False: 206]
  ------------------
 6004|      6|    return false;
 6005|      6|  }
 6006|  2.67k|  return true;
 6007|  2.68k|}
_ZN3ada4idna17utf32_to_punycodeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIcNS3_IcEENS1_9allocatorIcEEEE:
 6087|  20.7k|bool utf32_to_punycode(std::u32string_view input, std::string &out) {
 6088|  20.7k|  out.reserve(input.size() + out.size());
 6089|  20.7k|  uint32_t n = initial_n;
 6090|  20.7k|  int32_t d = 0;
 6091|  20.7k|  int32_t bias = initial_bias;
 6092|  20.7k|  size_t h = 0;
 6093|       |  // first push the ascii content
 6094|  97.3k|  for (uint32_t c : input) {
  ------------------
  |  Branch (6094:19): [True: 97.3k, False: 20.7k]
  ------------------
 6095|  97.3k|    if (c < 0x80) {
  ------------------
  |  Branch (6095:9): [True: 47.4k, False: 49.9k]
  ------------------
 6096|  47.4k|      ++h;
 6097|  47.4k|      out.push_back(char(c));
 6098|  47.4k|    }
 6099|  97.3k|    if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) {
  ------------------
  |  Branch (6099:9): [True: 0, False: 97.3k]
  |  Branch (6099:26): [True: 764, False: 96.6k]
  |  Branch (6099:41): [True: 0, False: 764]
  ------------------
 6100|      0|      return false;
 6101|      0|    }
 6102|  97.3k|  }
 6103|  20.7k|  size_t b = h;
 6104|  20.7k|  if (b > 0) {
  ------------------
  |  Branch (6104:7): [True: 17.8k, False: 2.90k]
  ------------------
 6105|  17.8k|    out.push_back('-');
 6106|  17.8k|  }
 6107|  61.3k|  while (h < input.size()) {
  ------------------
  |  Branch (6107:10): [True: 40.5k, False: 20.7k]
  ------------------
 6108|  40.5k|    uint32_t m = 0x10FFFF;
 6109|   284k|    for (auto code_point : input) {
  ------------------
  |  Branch (6109:26): [True: 284k, False: 40.5k]
  ------------------
 6110|   284k|      if (code_point >= n && code_point < m) m = code_point;
  ------------------
  |  Branch (6110:11): [True: 118k, False: 166k]
  |  Branch (6110:30): [True: 43.4k, False: 74.6k]
  ------------------
 6111|   284k|    }
 6112|       |
 6113|  40.5k|    if ((m - n) > (0x7fffffff - d) / (h + 1)) {
  ------------------
  |  Branch (6113:9): [True: 0, False: 40.5k]
  ------------------
 6114|      0|      return false;
 6115|      0|    }
 6116|  40.5k|    d = d + int32_t((m - n) * (h + 1));
 6117|  40.5k|    n = m;
 6118|   284k|    for (auto c : input) {
  ------------------
  |  Branch (6118:17): [True: 284k, False: 40.5k]
  ------------------
 6119|   284k|      if (c < n) {
  ------------------
  |  Branch (6119:11): [True: 166k, False: 118k]
  ------------------
 6120|   166k|        if (d == 0x7fffffff) {
  ------------------
  |  Branch (6120:13): [True: 0, False: 166k]
  ------------------
 6121|      0|          return false;
 6122|      0|        }
 6123|   166k|        ++d;
 6124|   166k|      }
 6125|   284k|      if (c == n) {
  ------------------
  |  Branch (6125:11): [True: 49.9k, False: 234k]
  ------------------
 6126|  49.9k|        int32_t q = d;
 6127|   166k|        for (int32_t k = base;; k += base) {
 6128|   166k|          int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (6128:23): [True: 43.1k, False: 122k]
  |  Branch (6128:42): [True: 104k, False: 18.7k]
  ------------------
 6129|       |
 6130|   166k|          if (q < t) {
  ------------------
  |  Branch (6130:15): [True: 49.9k, False: 116k]
  ------------------
 6131|  49.9k|            break;
 6132|  49.9k|          }
 6133|   116k|          out.push_back(digit_to_char(t + ((q - t) % (base - t))));
 6134|   116k|          q = (q - t) / (base - t);
 6135|   116k|        }
 6136|  49.9k|        out.push_back(digit_to_char(q));
 6137|  49.9k|        bias = adapt(d, int32_t(h + 1), h == b);
 6138|  49.9k|        d = 0;
 6139|  49.9k|        ++h;
 6140|  49.9k|      }
 6141|   284k|    }
 6142|  40.5k|    ++d;
 6143|  40.5k|    ++n;
 6144|  40.5k|  }
 6145|  20.7k|  return true;
 6146|  20.7k|}
_ZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6292|  23.4k|bool is_label_valid(const std::u32string_view label) {
 6293|  23.4k|  if (label.empty()) {
  ------------------
  |  Branch (6293:7): [True: 0, False: 23.4k]
  ------------------
 6294|      0|    return true;
 6295|      0|  }
 6296|  23.4k|  if (!ensure_tables() || combining_ranges == nullptr || dir_start == nullptr ||
  ------------------
  |  Branch (6296:7): [True: 0, False: 23.4k]
  |  Branch (6296:27): [True: 0, False: 23.4k]
  |  Branch (6296:58): [True: 0, False: 23.4k]
  ------------------
 6297|  23.4k|      dir_final == nullptr || dir_value == nullptr ||
  ------------------
  |  Branch (6297:7): [True: 0, False: 23.4k]
  |  Branch (6297:31): [True: 0, False: 23.4k]
  ------------------
 6298|  23.4k|      joining_start == nullptr || joining_final == nullptr ||
  ------------------
  |  Branch (6298:7): [True: 0, False: 23.4k]
  |  Branch (6298:35): [True: 0, False: 23.4k]
  ------------------
 6299|  23.4k|      joining_value == nullptr) {
  ------------------
  |  Branch (6299:7): [True: 0, False: 23.4k]
  ------------------
 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|  23.4k|  const std::span<const uint32_t[2]> comb_span{combining_ranges,
 6325|  23.4k|                                               combining_range_count};
 6326|  23.4k|  const auto comb_it = std::ranges::lower_bound(
 6327|  23.4k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
 6328|  23.4k|  if (comb_it != comb_span.end() && label.front() >= (*comb_it)[0]) {
  ------------------
  |  Branch (6328:7): [True: 23.4k, False: 0]
  |  Branch (6328:7): [True: 100, False: 23.3k]
  |  Branch (6328:37): [True: 100, False: 23.3k]
  ------------------
 6329|    100|    return false;
 6330|    100|  }
 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|  23.3k|  if (!is_contextj_valid(label)) {
  ------------------
  |  Branch (6342:7): [True: 198, False: 23.1k]
  ------------------
 6343|    198|    return false;
 6344|    198|  }
 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|  23.1k|  size_t last_non_nsm_char = find_last_not_of_nsm(label);
 6377|  23.1k|  if (last_non_nsm_char == std::u32string_view::npos) {
  ------------------
  |  Branch (6377:7): [True: 0, False: 23.1k]
  ------------------
 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|  23.1k|  if (is_rtl_label(label)) {
  ------------------
  |  Branch (6384:7): [True: 2.48k, False: 20.6k]
  ------------------
 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|  2.48k|    if (find_direction(label[0]) == direction::L) {
  ------------------
  |  Branch (6389:9): [True: 160, False: 2.32k]
  ------------------
 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|  2.10k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6394:26): [True: 2.10k, False: 0]
  ------------------
 6395|  2.10k|        const direction d = find_direction(label[i]);
 6396|  2.10k|        if (!(d == direction::L || d == direction::EN || d == direction::ES ||
  ------------------
  |  Branch (6396:15): [True: 558, False: 1.54k]
  |  Branch (6396:36): [True: 198, False: 1.34k]
  |  Branch (6396:58): [True: 198, False: 1.15k]
  ------------------
 6397|  1.15k|              d == direction::CS || d == direction::ET || d == direction::ON ||
  ------------------
  |  Branch (6397:15): [True: 204, False: 946]
  |  Branch (6397:37): [True: 194, False: 752]
  |  Branch (6397:59): [True: 208, False: 544]
  ------------------
 6398|    544|              d == direction::BN || d == direction::NSM)) {
  ------------------
  |  Branch (6398:15): [True: 194, False: 350]
  |  Branch (6398:37): [True: 190, False: 160]
  ------------------
 6399|    160|          return false;
 6400|    160|        }
 6401|  2.10k|      }
 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|  2.32k|    } 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|  2.32k|      const direction first_dir = find_direction(label[0]);
 6416|  2.32k|      if (first_dir != direction::R && first_dir != direction::AL) {
  ------------------
  |  Branch (6416:11): [True: 1.59k, False: 732]
  |  Branch (6416:40): [True: 34, False: 1.56k]
  ------------------
 6417|     34|        return false;
 6418|     34|      }
 6419|       |
 6420|  2.29k|      bool has_an = false;
 6421|  2.29k|      bool has_en = false;
 6422|  8.94k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6422:26): [True: 6.87k, False: 2.07k]
  ------------------
 6423|  6.87k|        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|  6.87k|        if ((d == direction::EN && ((has_en = true) && has_an)) ||
  ------------------
  |  Branch (6427:14): [True: 482, False: 6.39k]
  |  Branch (6427:37): [True: 482, False: 0]
  |  Branch (6427:56): [True: 6, False: 476]
  ------------------
 6428|  6.86k|            (d == direction::AN && ((has_an = true) && has_en))) {
  ------------------
  |  Branch (6428:14): [True: 392, False: 6.47k]
  |  Branch (6428:37): [True: 392, False: 0]
  |  Branch (6428:56): [True: 6, False: 386]
  ------------------
 6429|     12|          return false;
 6430|     12|        }
 6431|       |
 6432|  6.86k|        if (!(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6432:15): [True: 932, False: 5.93k]
  |  Branch (6432:36): [True: 3.19k, False: 2.74k]
  |  Branch (6432:58): [True: 386, False: 2.35k]
  ------------------
 6433|  2.35k|              d == direction::EN || d == direction::ES || d == direction::CS ||
  ------------------
  |  Branch (6433:15): [True: 476, False: 1.87k]
  |  Branch (6433:37): [True: 190, False: 1.68k]
  |  Branch (6433:59): [True: 252, False: 1.43k]
  ------------------
 6434|  1.43k|              d == direction::ET || d == direction::ON || d == direction::BN ||
  ------------------
  |  Branch (6434:15): [True: 194, False: 1.24k]
  |  Branch (6434:37): [True: 210, False: 1.03k]
  |  Branch (6434:59): [True: 566, False: 466]
  ------------------
 6435|    466|              d == direction::NSM)) {
  ------------------
  |  Branch (6435:15): [True: 330, False: 136]
  ------------------
 6436|    136|          return false;
 6437|    136|        }
 6438|       |
 6439|  6.72k|        if (i == last_non_nsm_char &&
  ------------------
  |  Branch (6439:13): [True: 2.14k, False: 4.58k]
  ------------------
 6440|  2.14k|            !(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6440:15): [True: 336, False: 1.80k]
  |  Branch (6440:36): [True: 1.33k, False: 476]
  |  Branch (6440:58): [True: 200, False: 276]
  ------------------
 6441|    276|              d == direction::EN)) {
  ------------------
  |  Branch (6441:15): [True: 204, False: 72]
  ------------------
 6442|     72|          return false;
 6443|     72|        }
 6444|  6.72k|      }
 6445|       |
 6446|  2.07k|      return true;
 6447|  2.29k|    }
 6448|  2.48k|  }
 6449|       |
 6450|  20.6k|  return true;
 6451|  23.1k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6610|   127k|[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out) {
 6611|   127k|  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|   127k|  if (is_ascii(ut8_string)) {
  ------------------
  |  Branch (6615:7): [True: 122k, False: 5.02k]
  ------------------
 6616|   122k|    from_ascii_to_ascii(ut8_string, out);
 6617|   122k|    return true;
 6618|   122k|  }
 6619|  5.02k|  if (ut8_string.size() > max_domain_input_bytes) {
  ------------------
  |  Branch (6619:7): [True: 0, False: 5.02k]
  ------------------
 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|  5.02k|  size_t utf32_length =
 6634|  5.02k|      ada::idna::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 6635|  5.02k|  if (utf32_length == 0 && !ut8_string.empty()) {
  ------------------
  |  Branch (6635:7): [True: 46, False: 4.97k]
  |  Branch (6635:28): [True: 46, False: 0]
  ------------------
 6636|     46|    return false;
 6637|     46|  }
 6638|  4.97k|  std::u32string working(utf32_length, U'\0');
 6639|  4.97k|  size_t actual_utf32_length = ada::idna::utf8_to_utf32(
 6640|  4.97k|      ut8_string.data(), ut8_string.size(), working.data());
 6641|  4.97k|#endif
 6642|  4.97k|  if (actual_utf32_length == 0 || actual_utf32_length != utf32_length) {
  ------------------
  |  Branch (6642:7): [True: 448, False: 4.53k]
  |  Branch (6642:35): [True: 0, False: 4.53k]
  ------------------
 6643|    448|    return false;
 6644|    448|  }
 6645|  4.53k|  working.resize(actual_utf32_length);
 6646|       |
 6647|       |  // Map into a second buffer with exact sizing (no growth reallocs).
 6648|  4.53k|  std::u32string mapped;
 6649|  4.53k|  if (!ada::idna::map(working, mapped)) {
  ------------------
  |  Branch (6649:7): [True: 84, False: 4.44k]
  ------------------
 6650|     84|    return false;
 6651|     84|  }
 6652|       |  // Drop UTF-32 input; reuse `working` as scratch for ACE validation below.
 6653|  4.44k|  working.clear();
 6654|       |
 6655|       |  // Skip NFC when already normalized (ASCII is a fast subset of this check).
 6656|  4.44k|  if (!is_ascii(mapped) && !is_already_nfc(mapped)) {
  ------------------
  |  Branch (6656:7): [True: 3.66k, False: 782]
  |  Branch (6656:28): [True: 656, False: 3.00k]
  ------------------
 6657|    656|    if (!normalize(mapped)) {
  ------------------
  |  Branch (6657:9): [True: 0, False: 656]
  ------------------
 6658|      0|      return false;
 6659|      0|    }
 6660|    656|  }
 6661|       |
 6662|       |  // Estimate ASCII output size (punycode may expand non-ASCII labels).
 6663|  4.44k|  out.reserve(mapped.size() + 8);
 6664|       |
 6665|       |  // Walk labels with a single pointer scan (no repeated string::find).
 6666|  4.44k|  const char32_t* p = mapped.data();
 6667|  4.44k|  const char32_t* const end = p + mapped.size();
 6668|  4.44k|  std::u32string post_map;
 6669|       |
 6670|  31.3k|  while (p < end) {
  ------------------
  |  Branch (6670:10): [True: 28.4k, False: 2.91k]
  ------------------
 6671|  28.4k|    const char32_t* label_begin = p;
 6672|   278k|    while (p < end && *p != U'.') {
  ------------------
  |  Branch (6672:12): [True: 275k, False: 3.79k]
  |  Branch (6672:23): [True: 250k, False: 24.6k]
  ------------------
 6673|   250k|      ++p;
 6674|   250k|    }
 6675|  28.4k|    const size_t label_size = static_cast<size_t>(p - label_begin);
 6676|  28.4k|    std::u32string_view label_view(label_begin, label_size);
 6677|  28.4k|    const bool is_last_label = (p == end);
 6678|  28.4k|    if (p < end) {
  ------------------
  |  Branch (6678:9): [True: 24.6k, False: 3.79k]
  ------------------
 6679|  24.6k|      ++p;  // skip dot
 6680|  24.6k|    }
 6681|       |
 6682|  28.4k|    if (label_size == 0) {
  ------------------
  |  Branch (6682:9): [True: 1.68k, False: 26.7k]
  ------------------
 6683|       |      // empty label
 6684|  26.7k|    } else if (is_ace_prefix(label_view)) {
  ------------------
  |  Branch (6684:16): [True: 2.82k, False: 23.9k]
  ------------------
 6685|  54.6k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (6685:23): [True: 54.6k, False: 2.80k]
  ------------------
 6686|  54.6k|        if (c >= 0x80) {
  ------------------
  |  Branch (6686:13): [True: 20, False: 54.5k]
  ------------------
 6687|     20|          out.clear();
 6688|     20|          return false;
 6689|     20|        }
 6690|  54.6k|      }
 6691|  2.80k|      append_ascii_label(out, label_view);
 6692|  2.80k|      std::string_view puny_segment_ascii(
 6693|  2.80k|          out.data() + (out.size() - label_size) + 4, label_size - 4);
 6694|  2.80k|      working.clear();
 6695|  2.80k|      if (!ada::idna::punycode_to_utf32(puny_segment_ascii, working)) {
  ------------------
  |  Branch (6695:11): [True: 134, False: 2.67k]
  ------------------
 6696|    134|        out.clear();
 6697|    134|        return false;
 6698|    134|      }
 6699|  2.67k|      if (is_ascii(working)) {
  ------------------
  |  Branch (6699:11): [True: 86, False: 2.58k]
  ------------------
 6700|     86|        out.clear();
 6701|     86|        return false;
 6702|     86|      }
 6703|  2.58k|      if (!ada::idna::map(working, post_map) || working != post_map) {
  ------------------
  |  Branch (6703:11): [True: 270, False: 2.31k]
  |  Branch (6703:49): [True: 112, False: 2.20k]
  ------------------
 6704|    382|        out.clear();
 6705|    382|        return false;
 6706|    382|      }
 6707|       |      // Mapping must be stable; NFC must not change the mapped form either.
 6708|  2.20k|      if (!is_ascii(post_map) && !is_already_nfc(post_map)) {
  ------------------
  |  Branch (6708:11): [True: 2.20k, False: 0]
  |  Branch (6708:34): [True: 978, False: 1.22k]
  ------------------
 6709|    978|        if (!normalize(post_map) || post_map != working) {
  ------------------
  |  Branch (6709:13): [True: 0, False: 978]
  |  Branch (6709:37): [True: 202, False: 776]
  ------------------
 6710|    202|          out.clear();
 6711|    202|          return false;
 6712|    202|        }
 6713|    978|      }
 6714|  2.00k|      if (post_map.empty() || !is_label_valid(post_map)) {
  ------------------
  |  Branch (6714:11): [True: 0, False: 2.00k]
  |  Branch (6714:31): [True: 32, False: 1.97k]
  ------------------
 6715|     32|        out.clear();
 6716|     32|        return false;
 6717|     32|      }
 6718|  23.9k|    } else if (is_ascii(label_view)) {
  ------------------
  |  Branch (6718:16): [True: 2.48k, False: 21.4k]
  ------------------
 6719|  2.48k|      append_ascii_label(out, label_view);
 6720|  21.4k|    } else {
 6721|  21.4k|      if (!is_label_valid(label_view)) {
  ------------------
  |  Branch (6721:11): [True: 680, False: 20.7k]
  ------------------
 6722|    680|        out.clear();
 6723|    680|        return false;
 6724|    680|      }
 6725|  20.7k|      out.append("xn--");
 6726|  20.7k|      if (!ada::idna::utf32_to_punycode(label_view, out)) {
  ------------------
  |  Branch (6726:11): [True: 0, False: 20.7k]
  ------------------
 6727|      0|        out.clear();
 6728|      0|        return false;
 6729|      0|      }
 6730|  20.7k|    }
 6731|  26.9k|    if (!is_last_label) {
  ------------------
  |  Branch (6731:9): [True: 24.2k, False: 2.72k]
  ------------------
 6732|  24.2k|      out.push_back('.');
 6733|  24.2k|    }
 6734|  26.9k|  }
 6735|  2.91k|  return true;
 6736|  4.44k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6738|   127k|std::string to_ascii(std::string_view ut8_string) {
 6739|   127k|  std::string out;
 6740|   127k|  if (!to_ascii(ut8_string, out)) {
  ------------------
  |  Branch (6740:7): [True: 2.11k, False: 125k]
  ------------------
 6741|  2.11k|    return {};
 6742|  2.11k|  }
 6743|   125k|  return out;
 6744|   127k|}
_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7617|  46.5k|                    std::string& out) {
 7618|  46.5k|  ada_log("percent_encode ", input, " to output string while ",
 7619|  46.5k|          append ? "appending" : "overwriting");
 7620|  46.5k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7621|  46.5k|    return character_sets::bit_at(character_set, c);
 7622|  46.5k|  });
 7623|  46.5k|  ada_log("percent_encode done checking, moved to ",
 7624|  46.5k|          std::distance(input.begin(), pointer));
 7625|       |
 7626|       |  // Optimization: Don't iterate if percent encode is not required
 7627|  46.5k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7627:7): [True: 38.9k, False: 7.65k]
  ------------------
 7628|  38.9k|    ada_log("percent_encode encoding not needed.");
 7629|  38.9k|    return false;
 7630|  38.9k|  }
 7631|       |  if constexpr (!append) {
 7632|       |    out.clear();
 7633|       |  }
 7634|  7.65k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7635|  7.65k|          " bytes");
 7636|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7637|  7.65k|  out.append(input.data(), std::distance(input.begin(), pointer));
 7638|  7.65k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7639|  7.65k|          " bytes");
 7640|   130k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7640:10): [True: 122k, False: 7.65k]
  ------------------
 7641|   122k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7641:9): [True: 95.2k, False: 27.1k]
  ------------------
 7642|  95.2k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7643|  95.2k|    } else {
 7644|  27.1k|      out += *pointer;
 7645|  27.1k|    }
 7646|   122k|  }
 7647|  7.65k|  return true;
 7648|  46.5k|}
_ZN3ada7unicode14percent_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 7448|  39.7k|std::string percent_decode(const std::string_view input, size_t first_percent) {
 7449|       |  // next line is for safety only, we expect users to avoid calling
 7450|       |  // percent_decode when first_percent is outside the range.
 7451|  39.7k|  if (first_percent == std::string_view::npos) {
  ------------------
  |  Branch (7451:7): [True: 0, False: 39.7k]
  ------------------
 7452|      0|    return std::string(input);
 7453|      0|  }
 7454|       |
 7455|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7456|  39.7k|  const char* const src = input.data();
 7457|  39.7k|  const char* const end = src + input.size();
 7458|       |
 7459|       |  // Decoding never grows the string, so a single pre-sized buffer written via
 7460|       |  // bulk memcpy of the plain runs (then shrunk to the final length) avoids the
 7461|       |  // byte-at-a-time appends of the naive version.
 7462|  39.7k|  std::string out(input.size(), '\0');
 7463|  39.7k|  char* d = out.data();
 7464|  39.7k|  char* const d0 = d;
 7465|       |
 7466|  39.7k|  std::memcpy(d, src, first_percent);
 7467|  39.7k|  d += first_percent;
 7468|       |
 7469|  39.7k|  const char* p = src + first_percent;
 7470|   126k|  while (p < end) {
  ------------------
  |  Branch (7470:10): [True: 87.0k, False: 39.7k]
  ------------------
 7471|  87.0k|    if (*p == '%') {
  ------------------
  |  Branch (7471:9): [True: 44.8k, False: 42.2k]
  ------------------
 7472|       |      // Decode runs of valid %XX tightly (common for nested/encoded URLs).
 7473|  87.7k|      while (p + 2 < end && *p == '%') {
  ------------------
  |  Branch (7473:14): [True: 86.4k, False: 1.23k]
  |  Branch (7473:29): [True: 46.9k, False: 39.4k]
  ------------------
 7474|  46.9k|        if (!is_ascii_hex_digit(p[1]) || !is_ascii_hex_digit(p[2])) {
  ------------------
  |  Branch (7474:13): [True: 1.97k, False: 45.0k]
  |  Branch (7474:42): [True: 2.12k, False: 42.9k]
  ------------------
 7475|  4.09k|          break;
 7476|  4.09k|        }
 7477|  42.9k|        *d++ = static_cast<char>(convert_hex_to_binary(p[1]) * 16 +
 7478|  42.9k|                                 convert_hex_to_binary(p[2]));
 7479|  42.9k|        p += 3;
 7480|  42.9k|      }
 7481|  44.8k|      if (p < end && *p == '%') {
  ------------------
  |  Branch (7481:11): [True: 44.0k, False: 802]
  |  Branch (7481:22): [True: 4.35k, False: 39.6k]
  ------------------
 7482|       |        // Not a valid escape (too few chars left or bad hex): copy '%'
 7483|       |        // literally and keep scanning after it.
 7484|  4.35k|        *d++ = *p++;
 7485|  4.35k|      }
 7486|  44.8k|    } else {
 7487|  42.2k|      const char* q = static_cast<const char*>(
 7488|  42.2k|          std::memchr(p, '%', static_cast<size_t>(end - p)));
 7489|  42.2k|      const char* run_end = q ? q : end;
  ------------------
  |  Branch (7489:29): [True: 3.44k, False: 38.7k]
  ------------------
 7490|  42.2k|      const size_t n = static_cast<size_t>(run_end - p);
 7491|  42.2k|      std::memcpy(d, p, n);
 7492|  42.2k|      d += n;
 7493|  42.2k|      p = run_end;
 7494|  42.2k|    }
 7495|  87.0k|  }
 7496|       |
 7497|  39.7k|  out.resize(static_cast<size_t>(d - d0));
 7498|  39.7k|  return out;
 7499|  39.7k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7587|   191k|                           const uint8_t character_set[]) {
 7588|   191k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7589|   191k|    return character_sets::bit_at(character_set, c);
 7590|   191k|  });
 7591|       |  // Optimization: Don't iterate if percent encode is not required
 7592|   191k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7592:7): [True: 188k, False: 3.01k]
  ------------------
 7593|   188k|    return std::string(input);
 7594|   188k|  }
 7595|       |
 7596|  3.01k|  std::string result;
 7597|  3.01k|  result.reserve(input.length());  // in the worst case, percent encoding might
 7598|       |                                   // produce 3 characters.
 7599|  3.01k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
 7600|  3.01k|  if (static_cast<size_t>(input.end() - pointer) >= 48) {
  ------------------
  |  Branch (7600:7): [True: 861, False: 2.15k]
  ------------------
 7601|    861|    percent_encode_suffix(&*pointer, input.data() + input.size(), character_set,
 7602|    861|                          result);
 7603|  2.15k|  } else {
 7604|  18.6k|    for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7604:12): [True: 16.4k, False: 2.15k]
  ------------------
 7605|  16.4k|      if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7605:11): [True: 11.2k, False: 5.24k]
  ------------------
 7606|  11.2k|        result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7607|  11.2k|      } else {
 7608|  5.24k|        result += *pointer;
 7609|  5.24k|      }
 7610|  16.4k|    }
 7611|  2.15k|  }
 7612|  3.01k|  return result;
 7613|   191k|}
_ZN3ada7unicode8to_asciiERNSt3__18optionalINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEEENS1_17basic_string_viewIcS5_EEm:
 7658|   127k|              size_t first_percent) {
 7659|   127k|  std::string percent_decoded_buffer;
 7660|   127k|  std::string_view input = plain;
 7661|   127k|  if (first_percent != std::string_view::npos) {
  ------------------
  |  Branch (7661:7): [True: 656, False: 127k]
  ------------------
 7662|    656|    percent_decoded_buffer = unicode::percent_decode(plain, first_percent);
 7663|    656|    input = percent_decoded_buffer;
 7664|    656|  }
 7665|       |  // input is a non-empty UTF-8 string, must be percent decoded
 7666|   127k|  std::string idna_ascii = ada::idna::to_ascii(input);
 7667|   127k|  if (idna_ascii.empty() || contains_forbidden_domain_code_point(
  ------------------
  |  Branch (7667:7): [True: 2.13k, False: 125k]
  |  Branch (7667:29): [True: 728, False: 124k]
  ------------------
 7668|   125k|                                idna_ascii.data(), idna_ascii.size())) {
 7669|  2.86k|    return false;
 7670|  2.86k|  }
 7671|   124k|  out = std::move(idna_ascii);
 7672|   124k|  return true;
 7673|   127k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhm:
 7676|     33|                           const uint8_t character_set[], size_t index) {
 7677|     33|  std::string out;
 7678|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7679|     33|  out.append(input.data(), index);
 7680|     33|  auto pointer = input.begin() + index;
 7681|     33|  if (static_cast<size_t>(input.end() - pointer) >= 48) {
  ------------------
  |  Branch (7681:7): [True: 2, False: 31]
  ------------------
 7682|      2|    percent_encode_suffix(&*pointer, input.data() + input.size(), character_set,
 7683|      2|                          out);
 7684|     31|  } else {
 7685|    460|    for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7685:12): [True: 429, False: 31]
  ------------------
 7686|    429|      if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7686:11): [True: 134, False: 295]
  ------------------
 7687|    134|        out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7688|    295|      } else {
 7689|    295|        out += *pointer;
 7690|    295|      }
 7691|    429|    }
 7692|     31|  }
 7693|     33|  return out;
 7694|     33|}
_ZN3ada7unicode21percent_encode_suffixEPKcS2_PKhRNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEE:
 7992|    863|                           const uint8_t character_set[], std::string& out) {
 7993|    863|#if ADA_UNICODE_SSSE3 || ADA_NEON || ADA_RVV
 7994|    863|#ifdef ADA_UNICODE_NEED_SSSE3_TARGET
 7995|    863|  static const bool ssse3_supported = __builtin_cpu_supports("ssse3") != 0;
 7996|    863|  const bool use_wide = ssse3_supported;
 7997|       |#else
 7998|       |  const bool use_wide = true;
 7999|       |#endif
 8000|    863|  if (use_wide && static_cast<size_t>(end - p) >= kPercentEncodeSimdMin) {
  ------------------
  |  Branch (8000:7): [True: 863, False: 0]
  |  Branch (8000:19): [True: 863, False: 0]
  ------------------
 8001|    863|    percent_encode_to_wide(p, end, character_set, out);
 8002|    863|    return;
 8003|    863|  }
 8004|      0|#endif
 8005|      0|  percent_encode_to_scalar(p, end, character_set, out);
 8006|      0|}
_ZN3ada11serializers36find_longest_sequence_of_ipv6_piecesERKNSt3__15arrayItLm8EEERmS6_:
 8071|    868|    size_t& compress_length) noexcept {
 8072|    868|  compress = 0;
 8073|    868|  compress_length = 0;
 8074|    868|  size_t i = 0;
 8075|  3.52k|  while (i < 8) {
  ------------------
  |  Branch (8075:10): [True: 2.65k, False: 868]
  ------------------
 8076|  2.65k|    if (address[i] != 0) {
  ------------------
  |  Branch (8076:9): [True: 1.53k, False: 1.12k]
  ------------------
 8077|  1.53k|      ++i;
 8078|  1.53k|      continue;
 8079|  1.53k|    }
 8080|  1.12k|    size_t next = i + 1;
 8081|  5.41k|    while (next < 8 && address[next] == 0) {
  ------------------
  |  Branch (8081:12): [True: 4.91k, False: 502]
  |  Branch (8081:24): [True: 4.29k, False: 618]
  ------------------
 8082|  4.29k|      ++next;
 8083|  4.29k|    }
 8084|  1.12k|    const size_t count = next - i;
 8085|  1.12k|    if (count > compress_length) {
  ------------------
  |  Branch (8085:9): [True: 922, False: 198]
  ------------------
 8086|    922|      compress_length = count;
 8087|    922|      compress = i;
 8088|    922|    }
 8089|  1.12k|    i = next;
 8090|  1.12k|  }
 8091|    868|}
_ZN3ada11serializers4ipv6ERKNSt3__15arrayItLm8EEE:
 8093|    868|std::string ipv6(const std::array<uint16_t, 8>& address) {
 8094|    868|  size_t compress = 0;
 8095|    868|  size_t compress_length = 0;
 8096|    868|  find_longest_sequence_of_ipv6_pieces(address, compress, compress_length);
 8097|       |
 8098|       |  // Skip compression when the longest zero run is a single piece.
 8099|    868|  if (compress_length <= 1) {
  ------------------
  |  Branch (8099:7): [True: 80, False: 788]
  ------------------
 8100|     80|    compress = compress_length = 8;
 8101|     80|  }
 8102|       |
 8103|       |  // Max: '[' + 8*4 hex + 7 ':' + ']' = 41
 8104|    868|  char output[4 * 8 + 7 + 2];
 8105|    868|  char* point = output;
 8106|    868|  *point++ = '[';
 8107|    868|  size_t piece_index = 0;
 8108|  2.34k|  while (true) {
  ------------------
  |  Branch (8108:10): [True: 2.34k, Folded]
  ------------------
 8109|  2.34k|    if (piece_index == compress) {
  ------------------
  |  Branch (8109:9): [True: 788, False: 1.55k]
  ------------------
 8110|    788|      *point++ = ':';
 8111|       |      // If we skip a value initially, we need to write '::'.
 8112|    788|      if (piece_index == 0) {
  ------------------
  |  Branch (8112:11): [True: 518, False: 270]
  ------------------
 8113|    518|        *point++ = ':';
 8114|    518|      }
 8115|    788|      piece_index += compress_length;
 8116|    788|      if (piece_index == 8) {
  ------------------
  |  Branch (8116:11): [True: 422, False: 366]
  ------------------
 8117|    422|        break;
 8118|    422|      }
 8119|    788|    }
 8120|  1.91k|    point = write_hex_u16(point, address[piece_index]);
 8121|  1.91k|    ++piece_index;
 8122|  1.91k|    if (piece_index == 8) {
  ------------------
  |  Branch (8122:9): [True: 446, False: 1.47k]
  ------------------
 8123|    446|      break;
 8124|    446|    }
 8125|  1.47k|    *point++ = ':';
 8126|  1.47k|  }
 8127|    868|  *point++ = ']';
 8128|    868|  return std::string(output, static_cast<size_t>(point - output));
 8129|    868|}
_ZN3ada11serializers4ipv4Em:
 8131|  40.9k|std::string ipv4(const uint64_t address) {
 8132|  40.9k|  std::string output(15, '\0');
 8133|  40.9k|  char* point = output.data();
 8134|  40.9k|  point = write_u8(point, static_cast<uint8_t>(address >> 24));
 8135|  40.9k|  *point++ = '.';
 8136|  40.9k|  point = write_u8(point, static_cast<uint8_t>(address >> 16));
 8137|  40.9k|  *point++ = '.';
 8138|  40.9k|  point = write_u8(point, static_cast<uint8_t>(address >> 8));
 8139|  40.9k|  *point++ = '.';
 8140|  40.9k|  point = write_u8(point, static_cast<uint8_t>(address));
 8141|  40.9k|  output.resize(static_cast<size_t>(point - output.data()));
 8142|  40.9k|  return output;
 8143|  40.9k|}
_ZN3ada5parseINS_3urlEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 8547|   623k|    std::string_view input, const result_type* base_url) {
 8548|   623k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 8549|   623k|  if (!u.is_valid) {
  ------------------
  |  Branch (8549:7): [True: 21.7k, False: 601k]
  ------------------
 8550|  21.7k|    return tl::unexpected(errors::type_error);
 8551|  21.7k|  }
 8552|   601k|  return u;
 8553|   623k|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 8547|   623k|    std::string_view input, const result_type* base_url) {
 8548|   623k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 8549|   623k|  if (!u.is_valid) {
  ------------------
  |  Branch (8549:7): [True: 21.7k, False: 601k]
  ------------------
 8550|  21.7k|    return tl::unexpected(errors::type_error);
 8551|  21.7k|  }
 8552|   601k|  return u;
 8553|   623k|}
_ZN3ada20get_max_input_lengthEv:
 8168|  1.64M|uint32_t get_max_input_length() {
 8169|  1.64M|  return max_input_length_.load(std::memory_order_relaxed);
 8170|  1.64M|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9535|   536k|void trim_c0_whitespace(std::string_view& input) noexcept {
 9536|   536k|  while (!input.empty() &&
  ------------------
  |  Branch (9536:10): [True: 536k, False: 0]
  ------------------
 9537|   536k|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (9537:10): [True: 470, False: 536k]
  ------------------
 9538|    470|    input.remove_prefix(1);
 9539|    470|  }
 9540|   539k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (9540:10): [True: 539k, False: 0]
  |  Branch (9540:28): [True: 3.36k, False: 536k]
  ------------------
 9541|  3.36k|    input.remove_suffix(1);
 9542|  3.36k|  }
 9543|   536k|}
_ZN3ada7helpers8overlapsENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 9707|  18.9k|bool overlaps(std::string_view input1, const std::string& input2) noexcept {
 9708|  18.9k|  ada_log("helpers::overlaps check if string_view '", input1, "' [",
 9709|  18.9k|          input1.size(), " bytes] is part of string '", input2, "' [",
 9710|  18.9k|          input2.size(), " bytes]");
 9711|  18.9k|  return !input1.empty() && !input2.empty() && input1.data() >= input2.data() &&
  ------------------
  |  Branch (9711:10): [True: 18.9k, False: 0]
  |  Branch (9711:29): [True: 18.9k, False: 0]
  |  Branch (9711:48): [True: 18.9k, False: 2]
  ------------------
 9712|  18.9k|         input1.data() < input2.data() + input2.size();
  ------------------
  |  Branch (9712:10): [True: 0, False: 18.9k]
  ------------------
 9713|  18.9k|}
_ZN3ada3url17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10092|    652|bool url::parse_opaque_host(std::string_view input) {
10093|    652|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
10094|    652|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (10094:7): [True: 33, False: 619]
  ------------------
10095|     33|    return is_valid = false;
10096|     33|  }
10097|       |
10098|       |  // Return the result of running UTF-8 percent-encode on input using the C0
10099|       |  // control percent-encode set.
10100|    619|  host = ada::unicode::percent_encode(
10101|    619|      input, ada::character_sets::C0_CONTROL_PERCENT_ENCODE);
10102|    619|  return true;
10103|    652|}
_ZN3ada3url10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10105|  58.6k|bool url::parse_ipv4(std::string_view input) {
10106|  58.6k|  ada_log("parse_ipv4 ", input, " [", input.size(), " bytes]");
10107|  58.6k|  if (input.empty()) {
  ------------------
  |  Branch (10107:7): [True: 0, False: 58.6k]
  ------------------
10108|      0|    return is_valid = false;
10109|      0|  }
10110|  58.6k|  std::string_view original_input = input;
10111|  58.6k|  if (original_input.back() == '.') {
  ------------------
  |  Branch (10111:7): [True: 28.3k, False: 30.2k]
  ------------------
10112|  28.3k|    original_input.remove_suffix(1);
10113|  28.3k|  }
10114|  58.6k|  if (input.back() == '.') {
  ------------------
  |  Branch (10114:7): [True: 28.3k, False: 30.2k]
  ------------------
10115|  28.3k|    input.remove_suffix(1);
10116|  28.3k|    if (input.empty()) {
  ------------------
  |  Branch (10116:9): [True: 0, False: 28.3k]
  ------------------
10117|      0|      return is_valid = false;
10118|      0|    }
10119|  28.3k|  }
10120|       |
10121|  58.6k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
10122|  58.6k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (10122:7): [True: 18.9k, False: 39.7k]
  ------------------
10123|  18.9k|    host = original_input;
10124|  18.9k|    host_type = IPV4;
10125|  18.9k|    return true;
10126|  18.9k|  }
10127|       |
10128|  39.7k|  const char* p = input.data();
10129|  39.7k|  const char* end = p + input.size();
10130|  39.7k|  uint64_t ipv4 = 0;
10131|  39.7k|  int digit_count = 0;
10132|  39.7k|  int pure_decimal_count = 0;
10133|       |
10134|  50.0k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (10134:10): [True: 50.0k, False: 16]
  |  Branch (10134:29): [True: 50.0k, False: 0]
  ------------------
10135|  50.0k|    uint64_t segment = 0;
10136|  50.0k|    bool pure = false;
10137|  50.0k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (10137:9): [True: 19.1k, False: 30.8k]
  ------------------
10138|  19.1k|      return is_valid = false;
10139|  19.1k|    }
10140|  30.8k|    if (pure) {
  ------------------
  |  Branch (10140:9): [True: 11.3k, False: 19.4k]
  ------------------
10141|  11.3k|      ++pure_decimal_count;
10142|  11.3k|    }
10143|  30.8k|    if (p >= end) {
  ------------------
  |  Branch (10143:9): [True: 20.5k, False: 10.3k]
  ------------------
10144|  20.5k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
10145|  20.5k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (10145:11): [True: 23, False: 20.4k]
  ------------------
10146|     23|        return is_valid = false;
10147|     23|      }
10148|  20.4k|      ipv4 = (ipv4 << shift) | segment;
10149|  20.4k|      host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (10149:14): [True: 0, False: 20.4k]
  ------------------
10150|  20.4k|                                       : ada::serializers::ipv4(ipv4);
10151|  20.4k|      host_type = IPV4;
10152|  20.4k|      return true;
10153|  20.5k|    }
10154|  10.3k|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (10154:9): [True: 36, False: 10.2k]
  |  Branch (10154:26): [True: 0, False: 10.2k]
  ------------------
10155|     36|      return is_valid = false;
10156|     36|    }
10157|  10.2k|    ipv4 = (ipv4 << 8) | segment;
10158|  10.2k|    ++p;
10159|  10.2k|  }
10160|     16|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (10160:7): [True: 0, False: 16]
  |  Branch (10160:27): [True: 16, False: 0]
  ------------------
10161|     16|    return is_valid = false;
10162|     16|  }
10163|      0|  host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (10163:10): [True: 0, False: 0]
  ------------------
10164|      0|                                   : ada::serializers::ipv4(ipv4);
10165|      0|  host_type = IPV4;
10166|      0|  return true;
10167|     16|}
_ZN3ada3url10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10169|    594|bool url::parse_ipv6(std::string_view input) {
10170|    594|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
10171|    594|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (10171:7): [True: 20, False: 574]
  |  Branch (10171:24): [True: 9, False: 565]
  ------------------
10172|     29|    return is_valid = false;
10173|     29|  }
10174|    565|  std::array<uint16_t, 8> address{};
10175|       |#if defined(ADA_AVX512_IPV6)
10176|       |  if (bool simd_valid; detail::try_parse_ipv6_avx512(input.data(), input.size(),
10177|       |                                                     address, simd_valid)) {
10178|       |    if (!simd_valid) [[unlikely]] {
10179|       |      return is_valid = false;
10180|       |    }
10181|       |    host = ada::serializers::ipv6(address);
10182|       |    host_type = IPV6;
10183|       |    return true;
10184|       |  }
10185|       |  address = {};
10186|       |#endif
10187|    565|  const char* pointer = input.data();
10188|    565|  const char* const end = pointer + input.size();
10189|    565|  int piece_index = 0;
10190|    565|  int compress = -1;
10191|       |
10192|    565|  if (*pointer == ':') {
  ------------------
  |  Branch (10192:7): [True: 269, False: 296]
  ------------------
10193|    269|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (10193:9): [True: 3, False: 266]
  |  Branch (10193:30): [True: 7, False: 259]
  ------------------
10194|     10|      return is_valid = false;
10195|     10|    }
10196|    259|    pointer += 2;
10197|    259|    compress = ++piece_index;
10198|    259|  }
10199|       |
10200|  1.73k|  while (pointer != end) {
  ------------------
  |  Branch (10200:10): [True: 1.28k, False: 449]
  ------------------
10201|  1.28k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (10201:9): [True: 3, False: 1.28k]
  ------------------
10202|      3|      return is_valid = false;
10203|      3|    }
10204|  1.28k|    if (*pointer == ':') {
  ------------------
  |  Branch (10204:9): [True: 162, False: 1.12k]
  ------------------
10205|    162|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (10205:11): [True: 3, False: 159]
  ------------------
10206|      3|        return is_valid = false;
10207|      3|      }
10208|    159|      ++pointer;
10209|    159|      compress = ++piece_index;
10210|    159|      continue;
10211|    162|    }
10212|       |
10213|  1.12k|    uint16_t value = 0;
10214|  1.12k|    const int length = detail::parse_hex_piece(pointer, end, value);
10215|       |
10216|  1.12k|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (10216:9): [True: 878, False: 243]
  |  Branch (10216:27): [True: 72, False: 806]
  ------------------
10217|     72|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (10217:11): [True: 3, False: 69]
  ------------------
10218|      3|        return is_valid = false;
10219|      3|      }
10220|     69|      pointer -= length;
10221|     69|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (10221:11): [True: 3, False: 66]
  ------------------
10222|      3|        return is_valid = false;
10223|      3|      }
10224|       |
10225|     66|      int numbers_seen = 0;
10226|    188|      while (pointer != end) {
  ------------------
  |  Branch (10226:14): [True: 170, False: 18]
  ------------------
10227|    170|        int ipv4_piece = -1;
10228|    170|        if (numbers_seen > 0) {
  ------------------
  |  Branch (10228:13): [True: 104, False: 66]
  ------------------
10229|    104|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (10229:15): [True: 91, False: 13]
  |  Branch (10229:34): [True: 86, False: 5]
  ------------------
10230|     86|            ++pointer;
10231|     86|          } else {
10232|     18|            return is_valid = false;
10233|     18|          }
10234|    104|        }
10235|    152|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (10235:13): [True: 14, False: 138]
  |  Branch (10235:31): [True: 4, False: 134]
  |  Branch (10235:49): [True: 3, False: 131]
  ------------------
10236|     21|          return is_valid = false;
10237|     21|        }
10238|    131|        ipv4_piece = *pointer - '0';
10239|    131|        ++pointer;
10240|    131|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (10240:13): [True: 121, False: 10]
  |  Branch (10240:31): [True: 61, False: 60]
  |  Branch (10240:50): [True: 58, False: 3]
  ------------------
10241|     58|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (10241:15): [True: 3, False: 55]
  ------------------
10242|      3|            return is_valid = false;
10243|      3|          }
10244|     55|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
10245|     55|          ++pointer;
10246|     55|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (10246:15): [True: 52, False: 3]
  |  Branch (10246:33): [True: 31, False: 21]
  |  Branch (10246:52): [True: 28, False: 3]
  ------------------
10247|     28|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
10248|     28|            ++pointer;
10249|     28|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (10249:17): [True: 6, False: 22]
  ------------------
10250|      6|              return is_valid = false;
10251|      6|            }
10252|     28|          }
10253|     55|        }
10254|    122|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
10255|    122|            address[static_cast<size_t>(piece_index)] * 0x100 +
10256|    122|            static_cast<uint16_t>(ipv4_piece));
10257|    122|        ++numbers_seen;
10258|    122|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (10258:13): [True: 36, False: 86]
  |  Branch (10258:34): [True: 12, False: 74]
  ------------------
10259|     48|          ++piece_index;
10260|     48|        }
10261|    122|      }
10262|     18|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (10262:11): [True: 12, False: 6]
  ------------------
10263|     12|        return is_valid = false;
10264|     12|      }
10265|      6|      break;
10266|     18|    }
10267|       |
10268|  1.04k|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (10268:9): [True: 22, False: 1.02k]
  ------------------
10269|     22|      return is_valid = false;
10270|     22|    }
10271|       |
10272|  1.02k|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (10272:9): [True: 784, False: 243]
  |  Branch (10272:27): [True: 781, False: 3]
  ------------------
10273|    781|      ++pointer;
10274|    781|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (10274:11): [True: 3, False: 778]
  ------------------
10275|      3|        return is_valid = false;
10276|      3|      }
10277|    781|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (10277:16): [True: 3, False: 243]
  ------------------
10278|      3|      return is_valid = false;
10279|      3|    }
10280|       |
10281|  1.02k|    address[static_cast<size_t>(piece_index)] = value;
10282|  1.02k|    ++piece_index;
10283|  1.02k|  }
10284|       |
10285|    455|  if (compress != -1) {
  ------------------
  |  Branch (10285:7): [True: 409, False: 46]
  ------------------
10286|    409|    const int right = piece_index - compress;
10287|    409|    if (right > 0) {
  ------------------
  |  Branch (10287:9): [True: 203, False: 206]
  ------------------
10288|    203|      const size_t dest = static_cast<size_t>(8 - right);
10289|    203|      const size_t src = static_cast<size_t>(compress);
10290|    203|      if (dest != src) {
  ------------------
  |  Branch (10290:11): [True: 185, False: 18]
  ------------------
10291|    508|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (10291:53): [True: 323, False: 185]
  ------------------
10292|    323|          address[dest + i] = address[src + i];
10293|    323|          address[src + i] = 0;
10294|    323|        }
10295|    185|      }
10296|    203|    }
10297|    409|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (10297:14): [True: 21, False: 25]
  ------------------
10298|     21|    return is_valid = false;
10299|     21|  }
10300|       |
10301|    434|  host = ada::serializers::ipv6(address);
10302|    434|  ada_log("parse_ipv6 ", *host);
10303|    434|  host_type = IPV6;
10304|    434|  return true;
10305|    455|}
_ZNK3ada3url12get_protocolEv:
10634|   200k|[[nodiscard]] std::string url::get_protocol() const {
10635|   200k|  if (is_special()) {
  ------------------
  |  Branch (10635:7): [True: 199k, False: 584]
  ------------------
10636|   199k|    return helpers::concat(ada::scheme::details::is_special_list[type], ":");
10637|   199k|  }
10638|       |  // We only move the 'scheme' if it is non-special.
10639|    584|  return helpers::concat(non_special_scheme, ":");
10640|   200k|}
_ZNK3ada3url8get_hostEv:
10642|   200k|[[nodiscard]] std::string url::get_host() const {
10643|       |  // If url's host is null, then return the empty string.
10644|       |  // If url's port is null, return url's host, serialized.
10645|       |  // Return url's host, serialized, followed by U+003A (:) and url's port,
10646|       |  // serialized.
10647|   200k|  if (!host.has_value()) {
  ------------------
  |  Branch (10647:7): [True: 349, False: 200k]
  ------------------
10648|    349|    return "";
10649|    349|  }
10650|   200k|  if (port.has_value()) {
  ------------------
  |  Branch (10650:7): [True: 20.9k, False: 179k]
  ------------------
10651|  20.9k|    return host.value() + ":" + get_port();
10652|  20.9k|  }
10653|   179k|  return host.value();
10654|   200k|}
_ZNK3ada3url12get_hostnameEv:
10656|   200k|[[nodiscard]] std::string url::get_hostname() const {
10657|   200k|  return host.value_or("");
10658|   200k|}
_ZNK3ada3url10get_searchEv:
10660|   200k|[[nodiscard]] std::string url::get_search() const {
10661|       |  // If this's URL's query is either null or the empty string, then return the
10662|       |  // empty string. Return U+003F (?), followed by this's URL's query.
10663|   200k|  return (!query.has_value() || (query->empty())) ? "" : "?" + query.value();
  ------------------
  |  Branch (10663:11): [True: 166k, False: 34.1k]
  |  Branch (10663:33): [True: 1.12k, False: 33.0k]
  ------------------
10664|   200k|}
_ZNK3ada3url12get_usernameEv:
10666|   200k|[[nodiscard]] const std::string& url::get_username() const noexcept {
10667|   200k|  return username;
10668|   200k|}
_ZNK3ada3url12get_passwordEv:
10670|   200k|[[nodiscard]] const std::string& url::get_password() const noexcept {
10671|   200k|  return password;
10672|   200k|}
_ZNK3ada3url8get_portEv:
10674|   221k|[[nodiscard]] std::string url::get_port() const {
10675|   221k|  return port.has_value() ? std::to_string(port.value()) : "";
  ------------------
  |  Branch (10675:10): [True: 41.9k, False: 179k]
  ------------------
10676|   221k|}
_ZNK3ada3url8get_hashEv:
10678|   200k|[[nodiscard]] std::string url::get_hash() const {
10679|       |  // If this's URL's fragment is either null or the empty string, then return
10680|       |  // the empty string. Return U+0023 (#), followed by this's URL's fragment.
10681|   200k|  return (!hash.has_value() || (hash->empty())) ? "" : "#" + hash.value();
  ------------------
  |  Branch (10681:11): [True: 175k, False: 24.6k]
  |  Branch (10681:32): [True: 1.52k, False: 23.1k]
  ------------------
10682|   200k|}
_ZN3ada3url8set_hrefENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10972|   200k|bool url::set_href(const std::string_view input) {
10973|   200k|  ada::result<ada::url> out = ada::parse<ada::url>(input);
10974|       |
10975|   200k|  if (out) {
  ------------------
  |  Branch (10975:7): [True: 200k, False: 0]
  ------------------
10976|       |    // The parser enforces get_max_input_length() on both the input and the
10977|       |    // normalized result. This is a defense-in-depth check.
10978|   200k|    if (out->get_href_size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (10978:9): [True: 0, False: 200k]
  ------------------
10979|      0|      return false;
10980|      0|    }
10981|   200k|    *this = std::move(*out);
10982|   200k|  }
10983|       |
10984|   200k|  return out.has_value();
10985|   200k|}
_ZN3ada6parser25try_parse_simple_absoluteINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
12262|   444k|                                                result_type& out) {
12263|   444k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12264|   444k|  constexpr bool is_aggregator =
12265|   444k|      std::is_same_v<result_type, ada::url_aggregator>;
12266|   444k|  static_assert(is_ada_url || is_aggregator);
12267|       |
12268|   444k|  const size_t len = input.size();
12269|       |  // Shortest accepted form is "ws://x" (6).
12270|   444k|  if (len < 6) [[unlikely]] {
  ------------------
  |  Branch (12270:7): [True: 10, False: 444k]
  ------------------
12271|     10|    return false;
12272|     10|  }
12273|   444k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12274|       |
12275|   444k|  size_t pos = 0;
12276|   444k|  ada::scheme::type scheme_type = ada::scheme::type::NOT_SPECIAL;
12277|   444k|  uint32_t protocol_end = 0;
12278|   444k|#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
12279|   444k|    defined(_M_X64) || defined(_M_IX86) || defined(_M_AMD64)
12280|       |  // One 8-byte load + integer compare is a single cmp/jcc on x86-64.
12281|       |  // Masked compares cover the shorter special schemes without extra loads.
12282|   444k|  if (len >= 8) {
  ------------------
  |  Branch (12282:7): [True: 443k, False: 211]
  ------------------
12283|   443k|    uint64_t first8 = 0;
12284|   443k|    std::memcpy(&first8, b, 8);
12285|   443k|    if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (12285:9): [True: 349k, False: 94.8k]
  ------------------
12286|   349k|      pos = 8;
12287|   349k|      scheme_type = ada::scheme::type::HTTPS;
12288|   349k|      protocol_end = 6;
12289|   349k|    } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (12289:16): [True: 88.5k, False: 6.36k]
  ------------------
12290|  94.8k|               0x002f2f3a70747468ull) {  // "http://"
12291|  88.5k|      pos = 7;
12292|  88.5k|      scheme_type = ada::scheme::type::HTTP;
12293|  88.5k|      protocol_end = 5;
12294|  88.5k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12294:16): [True: 8, False: 6.36k]
  ------------------
12295|  6.36k|               0x00002f2f3a737377ull) {  // "wss://"
12296|      8|      pos = 6;
12297|      8|      scheme_type = ada::scheme::type::WSS;
12298|      8|      protocol_end = 4;
12299|  6.36k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12299:16): [True: 81, False: 6.27k]
  ------------------
12300|  6.36k|               0x00002f2f3a707466ull) {  // "ftp://"
12301|     81|      pos = 6;
12302|     81|      scheme_type = ada::scheme::type::FTP;
12303|     81|      protocol_end = 4;
12304|  6.27k|    } else if ((first8 & 0x000000ffffffffffull) ==
  ------------------
  |  Branch (12304:16): [True: 235, False: 6.04k]
  ------------------
12305|  6.27k|               0x0000002f2f3a7377ull) {  // "ws://"
12306|    235|      pos = 5;
12307|    235|      scheme_type = ada::scheme::type::WS;
12308|    235|      protocol_end = 3;
12309|  6.04k|    } else {
12310|  6.04k|      return false;
12311|  6.04k|    }
12312|   443k|  } else if (b[0] == 'w' && b[1] == 's') {
  ------------------
  |  Branch (12312:14): [True: 50, False: 161]
  |  Branch (12312:29): [True: 29, False: 21]
  ------------------
12313|     29|    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
  ------------------
  |  Branch (12313:9): [True: 3, False: 26]
  |  Branch (12313:24): [True: 2, False: 1]
  |  Branch (12313:39): [True: 2, False: 0]
  ------------------
12314|      2|      pos = 5;
12315|      2|      scheme_type = ada::scheme::type::WS;
12316|      2|      protocol_end = 3;
12317|     27|    } else if (b[2] == 's' && b[3] == ':' && b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12317:16): [True: 7, False: 20]
  |  Branch (12317:31): [True: 0, False: 7]
  |  Branch (12317:46): [True: 0, False: 0]
  |  Branch (12317:61): [True: 0, False: 0]
  ------------------
12318|      0|      pos = 6;
12319|      0|      scheme_type = ada::scheme::type::WSS;
12320|      0|      protocol_end = 4;
12321|     27|    } else {
12322|     27|      return false;
12323|     27|    }
12324|    182|  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
  ------------------
  |  Branch (12324:14): [True: 30, False: 152]
  |  Branch (12324:29): [True: 18, False: 12]
  |  Branch (12324:44): [True: 7, False: 11]
  |  Branch (12324:59): [True: 0, False: 7]
  ------------------
12325|      0|             b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12325:14): [True: 0, False: 0]
  |  Branch (12325:29): [True: 0, False: 0]
  ------------------
12326|      0|    pos = 6;
12327|      0|    scheme_type = ada::scheme::type::FTP;
12328|      0|    protocol_end = 4;
12329|    182|  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
  ------------------
  |  Branch (12329:14): [True: 159, False: 23]
  |  Branch (12329:26): [True: 95, False: 64]
  |  Branch (12329:41): [True: 74, False: 21]
  |  Branch (12329:56): [True: 57, False: 17]
  ------------------
12330|     57|             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (12330:14): [True: 33, False: 24]
  |  Branch (12330:29): [True: 22, False: 11]
  |  Branch (12330:44): [True: 12, False: 10]
  |  Branch (12330:59): [True: 4, False: 8]
  ------------------
12331|      4|    pos = 7;
12332|      4|    scheme_type = ada::scheme::type::HTTP;
12333|      4|    protocol_end = 5;
12334|    178|  } else {
12335|    178|    return false;
12336|    178|  }
12337|       |#else
12338|       |  // Big-endian / uncommon arches: byte-wise special-scheme match.
12339|       |  if (len >= 8 && b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p' &&
12340|       |      b[4] == 's' && b[5] == ':' && b[6] == '/' && b[7] == '/') {
12341|       |    pos = 8;
12342|       |    scheme_type = ada::scheme::type::HTTPS;
12343|       |    protocol_end = 6;
12344|       |  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
12345|       |             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
12346|       |    pos = 7;
12347|       |    scheme_type = ada::scheme::type::HTTP;
12348|       |    protocol_end = 5;
12349|       |  } else if (b[0] == 'w' && b[1] == 's') {
12350|       |    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
12351|       |      pos = 5;
12352|       |      scheme_type = ada::scheme::type::WS;
12353|       |      protocol_end = 3;
12354|       |    } else if (len >= 6 && b[2] == 's' && b[3] == ':' && b[4] == '/' &&
12355|       |               b[5] == '/') {
12356|       |      pos = 6;
12357|       |      scheme_type = ada::scheme::type::WSS;
12358|       |      protocol_end = 4;
12359|       |    } else {
12360|       |      return false;
12361|       |    }
12362|       |  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
12363|       |             b[4] == '/' && b[5] == '/') {
12364|       |    pos = 6;
12365|       |    scheme_type = ada::scheme::type::FTP;
12366|       |    protocol_end = 4;
12367|       |  } else {
12368|       |    return false;
12369|       |  }
12370|       |#endif
12371|   437k|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (12371:7): [True: 437k, False: 5]
  |  Branch (12371:21): [True: 97, False: 437k]
  |  Branch (12371:38): [True: 19, False: 437k]
  ------------------
12372|    116|    return false;
12373|    116|  }
12374|       |
12375|       |  // Digit-led hosts are IPv4/numeric; '[' is IPv6. Skip before scanning.
12376|   437k|  if (pos < len && ((b[pos] >= '0' && b[pos] <= '9') || b[pos] == '[')) {
  ------------------
  |  Branch (12376:7): [True: 437k, False: 5]
  |  Branch (12376:22): [True: 435k, False: 2.07k]
  |  Branch (12376:39): [True: 46, False: 435k]
  |  Branch (12376:57): [True: 6, False: 437k]
  ------------------
12377|     52|    return false;
12378|     52|  }
12379|       |
12380|   437k|  const size_t host_start = pos;
12381|   437k|  bool has_upper = false;
12382|   437k|  bool has_x = false;
12383|   437k|  size_t host_end = pos;
12384|   437k|  if (!scan_plain_host(b, pos, len, host_end, has_upper, has_x)) {
  ------------------
  |  Branch (12384:7): [True: 2.76k, False: 434k]
  ------------------
12385|  2.76k|    return false;
12386|  2.76k|  }
12387|   434k|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (12387:7): [True: 81, False: 434k]
  ------------------
12388|     81|    return false;
12389|     81|  }
12390|   434k|  const size_t host_len = host_end - host_start;
12391|   434k|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (12391:7): [True: 288, False: 434k]
  ------------------
12392|    288|    return false;
12393|    288|  }
12394|   434k|  {
12395|   434k|    std::string_view hv(input.data() + host_start, host_len);
12396|   434k|    char host_buf[256];
12397|   434k|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (12397:9): [True: 10.1k, False: 424k]
  ------------------
12398|  10.1k|      std::memcpy(host_buf, input.data() + host_start, host_len);
12399|  10.1k|      unicode::to_lower_ascii(host_buf, host_len);
12400|  10.1k|      hv = std::string_view(host_buf, host_len);
12401|  10.1k|    }
12402|   434k|    if (ada::checkers::last_label_may_be_a_number(hv)) [[unlikely]] {
  ------------------
  |  Branch (12402:9): [True: 19.0k, False: 415k]
  ------------------
12403|  19.0k|      return false;
12404|  19.0k|    }
12405|       |    // Punycode is "xn--...". The host scan already notes a lowercase 'x';
12406|       |    // uppercase 'X' is covered by has_upper after the in-place lower.
12407|   415k|    static constexpr std::string_view xn{"xn-", 3};
12408|   415k|    if ((has_x || has_upper) && hv.find(xn) != std::string_view::npos)
  ------------------
  |  Branch (12408:10): [True: 379k, False: 36.0k]
  |  Branch (12408:19): [True: 483, False: 35.6k]
  |  Branch (12408:33): [True: 60.0k, False: 319k]
  ------------------
12409|  60.0k|        [[unlikely]] {
12410|  60.0k|      return false;
12411|  60.0k|    }
12412|   415k|  }
12413|       |
12414|   355k|  if (host_end < len && b[host_end] == ':') [[unlikely]] {
  ------------------
  |  Branch (12414:7): [True: 345k, False: 9.65k]
  |  Branch (12414:25): [True: 62.0k, False: 283k]
  ------------------
12415|  62.0k|    return finish_simple_absolute_with_port(input, out, scheme_type,
12416|  62.0k|                                            protocol_end, host_start, host_end,
12417|  62.0k|                                            host_len, has_upper);
12418|  62.0k|  }
12419|       |
12420|   293k|  size_t i = host_end;
12421|   293k|  size_t path_start = host_end;
12422|   293k|  size_t path_end = host_end;
12423|   293k|  size_t query_start = std::string_view::npos;
12424|   293k|  size_t hash_start = std::string_view::npos;
12425|   293k|  bool has_path = false;
12426|   293k|  bool maybe_dot_segment = false;
12427|   293k|  bool rest_simple = true;
12428|       |
12429|   293k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12429:7): [True: 283k, False: 9.65k]
  |  Branch (12429:18): [True: 264k, False: 19.3k]
  ------------------
12430|   264k|    has_path = true;
12431|   264k|    path_start = i;
12432|   264k|    ++i;
12433|   264k|    scan_path_run(b, i, len, maybe_dot_segment);
12434|   264k|    if (i < len) {
  ------------------
  |  Branch (12434:9): [True: 110k, False: 153k]
  ------------------
12435|   110k|      const uint8_t cls = k_path[b[i]];
12436|   110k|      if (cls == 1) {
  ------------------
  |  Branch (12436:11): [True: 98.8k, False: 12.0k]
  ------------------
12437|  98.8k|        path_end = i;
12438|  98.8k|        if (b[i] == '?') {
  ------------------
  |  Branch (12438:13): [True: 78.9k, False: 19.9k]
  ------------------
12439|  78.9k|          query_start = i;
12440|  78.9k|          ++i;
12441|  78.9k|          goto scan_query;
12442|  78.9k|        }
12443|  19.9k|        hash_start = i;
12444|  19.9k|        ++i;
12445|  19.9k|        goto scan_hash;
12446|  98.8k|      }
12447|  12.0k|      rest_simple = false;
12448|  12.0k|      hash_start = input.find('#', i);
12449|  12.0k|      query_start = input.substr(0, hash_start).find('?', i);
12450|  12.0k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12450:11): [True: 1.02k, False: 10.9k]
  ------------------
12451|  1.02k|        path_end = query_start;
12452|  10.9k|      } else {
12453|  10.9k|        path_end = hash_start != std::string_view::npos ? hash_start : len;
  ------------------
  |  Branch (12453:20): [True: 306, False: 10.6k]
  ------------------
12454|  10.9k|      }
12455|  12.0k|      goto after_rest;
12456|   110k|    }
12457|   153k|    path_end = i;
12458|   153k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12458:14): [True: 19.3k, False: 9.65k]
  |  Branch (12458:25): [True: 9.73k, False: 9.57k]
  ------------------
12459|  9.73k|    query_start = i;
12460|  9.73k|    ++i;
12461|  9.73k|    goto scan_query;
12462|  19.2k|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12462:14): [True: 9.57k, False: 9.65k]
  |  Branch (12462:25): [True: 9.57k, False: 0]
  ------------------
12463|  9.57k|    hash_start = i;
12464|  9.57k|    ++i;
12465|  9.57k|    goto scan_hash;
12466|  9.57k|  }
12467|   163k|  goto after_rest;
12468|       |
12469|   163k|scan_query:
12470|  88.7k|  scan_query_run(b, i, len);
12471|  88.7k|  if (i < len) {
  ------------------
  |  Branch (12471:7): [True: 31.0k, False: 57.6k]
  ------------------
12472|  31.0k|    if (b[i] == '#') {
  ------------------
  |  Branch (12472:9): [True: 30.9k, False: 137]
  ------------------
12473|  30.9k|      hash_start = i;
12474|  30.9k|      ++i;
12475|  30.9k|      goto scan_hash;
12476|  30.9k|    }
12477|    137|    rest_simple = false;
12478|    137|    goto scan_query_boundary;
12479|  31.0k|  }
12480|  57.6k|  goto after_rest;
12481|       |
12482|  57.6k|scan_query_boundary:
12483|    137|  hash_start = input.find('#', i);
12484|    137|  goto after_rest;
12485|       |
12486|  60.3k|scan_hash:
12487|  60.3k|  scan_hash_run(b, i, len);
12488|  60.3k|  if (i < len) {
  ------------------
  |  Branch (12488:7): [True: 128, False: 60.2k]
  ------------------
12489|    128|    rest_simple = false;
12490|    128|  }
12491|       |
12492|   293k|after_rest:
12493|   293k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12493:7): [True: 281k, False: 12.2k]
  |  Branch (12493:22): [True: 10.2k, False: 270k]
  ------------------
12494|  10.2k|    const std::string_view path_body(input.data() + path_start,
12495|  10.2k|                                     path_end - path_start);
12496|  10.2k|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12496:9): [True: 9.83k, False: 406]
  ------------------
12497|  9.83k|      rest_simple = false;
12498|  9.83k|    }
12499|  10.2k|  }
12500|       |
12501|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12502|       |  // trailing C0 control or space; this fast path does neither. A query or
12503|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12504|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12505|       |  // inputs back to the slow path.
12506|   293k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12506:7): [True: 22.1k, False: 271k]
  |  Branch (12506:24): [True: 35, False: 22.0k]
  ------------------
12507|  22.0k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12507:24): [True: 82, False: 21.9k]
  ------------------
12508|    117|    return false;
12509|    117|  }
12510|       |
12511|   293k|  out.type = scheme_type;
12512|   293k|  out.is_valid = true;
12513|   293k|  out.has_opaque_path = false;
12514|   293k|  out.host_type = DEFAULT;
12515|       |
12516|   293k|  if (!rest_simple) {
  ------------------
  |  Branch (12516:7): [True: 21.9k, False: 271k]
  ------------------
12517|       |    // Host is a plain domain. Finish path/query/hash with
12518|       |    // the regular helpers
12519|       |    // so percent-encoding and dot segments do not re-parse the authority.
12520|  21.9k|    const std::string_view path_view =
12521|  21.9k|        has_path
  ------------------
  |  Branch (12521:9): [True: 21.8k, False: 158]
  ------------------
12522|  21.9k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12523|  21.9k|            : std::string_view{};
12524|  21.9k|    auto apply_query_and_hash = [&]() {
12525|  21.9k|      if (query_start != std::string_view::npos) {
12526|  21.9k|        const size_t q_end =
12527|  21.9k|            (hash_start != std::string_view::npos) ? hash_start : len;
12528|  21.9k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12529|  21.9k|                                                q_end - query_start - 1),
12530|  21.9k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12531|  21.9k|      }
12532|  21.9k|      if (hash_start != std::string_view::npos) {
12533|  21.9k|        out.update_unencoded_base_hash(std::string_view(
12534|  21.9k|            input.data() + hash_start + 1, len - hash_start - 1));
12535|  21.9k|      }
12536|  21.9k|    };
12537|       |    if constexpr (is_aggregator) {
12538|       |      out.buffer.assign(input.substr(0, host_end));
12539|       |      if (has_upper) {
12540|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12541|       |      }
12542|       |      out.components.protocol_end = protocol_end;
12543|       |      out.components.username_end = protocol_end + 2;
12544|       |      out.components.host_start = protocol_end + 2;
12545|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12546|       |      out.components.port = url_components::omitted;
12547|       |      out.components.pathname_start = static_cast<uint32_t>(host_end);
12548|       |      out.components.search_start = url_components::omitted;
12549|       |      out.components.hash_start = url_components::omitted;
12550|       |      out.parse_path(path_view);
12551|       |      apply_query_and_hash();
12552|  21.9k|    } else {
12553|  21.9k|      std::string host_str(input.substr(host_start, host_len));
12554|  21.9k|      if (has_upper) {
  ------------------
  |  Branch (12554:11): [True: 137, False: 21.8k]
  ------------------
12555|    137|        unicode::to_lower_ascii(host_str.data(), host_str.size());
12556|    137|      }
12557|  21.9k|      out.host = std::move(host_str);
12558|  21.9k|      out.parse_path(path_view);
12559|  21.9k|      apply_query_and_hash();
12560|  21.9k|    }
12561|  21.9k|    return true;
12562|  21.9k|  }
12563|       |
12564|   271k|  const bool need_slash = !has_path;
12565|       |  if constexpr (is_aggregator) {
12566|       |    if (!need_slash) {
12567|       |      // assign copies once. resize()+memcpy would value-init then overwrite.
12568|       |      out.buffer.assign(input);
12569|       |      if (has_upper) {
12570|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12571|       |      }
12572|       |      out.components.protocol_end = protocol_end;
12573|       |      out.components.username_end = protocol_end + 2;
12574|       |      out.components.host_start = protocol_end + 2;
12575|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12576|       |      out.components.port = url_components::omitted;
12577|       |      out.components.pathname_start = static_cast<uint32_t>(path_start);
12578|       |      out.components.search_start = (query_start != std::string_view::npos)
12579|       |                                        ? static_cast<uint32_t>(query_start)
12580|       |                                        : url_components::omitted;
12581|       |      out.components.hash_start = (hash_start != std::string_view::npos)
12582|       |                                      ? static_cast<uint32_t>(hash_start)
12583|       |                                      : url_components::omitted;
12584|       |    } else {
12585|       |      out.buffer.clear();
12586|       |      out.buffer.reserve(len + 1);
12587|       |      out.buffer.append(input.substr(0, host_end));
12588|       |      out.buffer.push_back('/');
12589|       |      if (host_end < len) {
12590|       |        out.buffer.append(input.substr(host_end));
12591|       |      }
12592|       |      if (has_upper) {
12593|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12594|       |      }
12595|       |      out.components.protocol_end = protocol_end;
12596|       |      out.components.username_end = protocol_end + 2;
12597|       |      out.components.host_start = protocol_end + 2;
12598|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12599|       |      out.components.port = url_components::omitted;
12600|       |      out.components.pathname_start = static_cast<uint32_t>(host_end);
12601|       |      out.components.search_start = (query_start != std::string_view::npos)
12602|       |                                        ? static_cast<uint32_t>(query_start + 1)
12603|       |                                        : url_components::omitted;
12604|       |      out.components.hash_start = (hash_start != std::string_view::npos)
12605|       |                                      ? static_cast<uint32_t>(hash_start + 1)
12606|       |                                      : url_components::omitted;
12607|       |    }
12608|   271k|  } else {
12609|   271k|    std::string host_str(input.substr(host_start, host_len));
12610|   271k|    if (has_upper) {
  ------------------
  |  Branch (12610:9): [True: 9.59k, False: 261k]
  ------------------
12611|  9.59k|      unicode::to_lower_ascii(host_str.data(), host_str.size());
12612|  9.59k|    }
12613|   271k|    out.host = std::move(host_str);
12614|   271k|    if (need_slash) {
  ------------------
  |  Branch (12614:9): [True: 28.7k, False: 242k]
  ------------------
12615|  28.7k|      out.path = "/";
12616|   242k|    } else {
12617|   242k|      out.path.assign(input.data() + path_start, path_end - path_start);
12618|   242k|    }
12619|   271k|    if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12619:9): [True: 88.3k, False: 182k]
  ------------------
12620|  88.3k|      const size_t q_end =
12621|  88.3k|          (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12621:11): [True: 30.7k, False: 57.6k]
  ------------------
12622|  88.3k|      out.query.emplace(input.data() + query_start + 1,
12623|  88.3k|                        q_end - query_start - 1);
12624|  88.3k|    }
12625|   271k|    if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12625:9): [True: 60.0k, False: 211k]
  ------------------
12626|  60.0k|      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12627|  60.0k|    }
12628|   271k|  }
12629|   271k|  return true;
12630|   293k|}
_ZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
12262|   444k|                                                result_type& out) {
12263|   444k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12264|   444k|  constexpr bool is_aggregator =
12265|   444k|      std::is_same_v<result_type, ada::url_aggregator>;
12266|   444k|  static_assert(is_ada_url || is_aggregator);
12267|       |
12268|   444k|  const size_t len = input.size();
12269|       |  // Shortest accepted form is "ws://x" (6).
12270|   444k|  if (len < 6) [[unlikely]] {
  ------------------
  |  Branch (12270:7): [True: 10, False: 444k]
  ------------------
12271|     10|    return false;
12272|     10|  }
12273|   444k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12274|       |
12275|   444k|  size_t pos = 0;
12276|   444k|  ada::scheme::type scheme_type = ada::scheme::type::NOT_SPECIAL;
12277|   444k|  uint32_t protocol_end = 0;
12278|   444k|#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
12279|   444k|    defined(_M_X64) || defined(_M_IX86) || defined(_M_AMD64)
12280|       |  // One 8-byte load + integer compare is a single cmp/jcc on x86-64.
12281|       |  // Masked compares cover the shorter special schemes without extra loads.
12282|   444k|  if (len >= 8) {
  ------------------
  |  Branch (12282:7): [True: 443k, False: 211]
  ------------------
12283|   443k|    uint64_t first8 = 0;
12284|   443k|    std::memcpy(&first8, b, 8);
12285|   443k|    if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (12285:9): [True: 349k, False: 94.8k]
  ------------------
12286|   349k|      pos = 8;
12287|   349k|      scheme_type = ada::scheme::type::HTTPS;
12288|   349k|      protocol_end = 6;
12289|   349k|    } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (12289:16): [True: 88.5k, False: 6.36k]
  ------------------
12290|  94.8k|               0x002f2f3a70747468ull) {  // "http://"
12291|  88.5k|      pos = 7;
12292|  88.5k|      scheme_type = ada::scheme::type::HTTP;
12293|  88.5k|      protocol_end = 5;
12294|  88.5k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12294:16): [True: 8, False: 6.36k]
  ------------------
12295|  6.36k|               0x00002f2f3a737377ull) {  // "wss://"
12296|      8|      pos = 6;
12297|      8|      scheme_type = ada::scheme::type::WSS;
12298|      8|      protocol_end = 4;
12299|  6.36k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (12299:16): [True: 81, False: 6.27k]
  ------------------
12300|  6.36k|               0x00002f2f3a707466ull) {  // "ftp://"
12301|     81|      pos = 6;
12302|     81|      scheme_type = ada::scheme::type::FTP;
12303|     81|      protocol_end = 4;
12304|  6.27k|    } else if ((first8 & 0x000000ffffffffffull) ==
  ------------------
  |  Branch (12304:16): [True: 235, False: 6.04k]
  ------------------
12305|  6.27k|               0x0000002f2f3a7377ull) {  // "ws://"
12306|    235|      pos = 5;
12307|    235|      scheme_type = ada::scheme::type::WS;
12308|    235|      protocol_end = 3;
12309|  6.04k|    } else {
12310|  6.04k|      return false;
12311|  6.04k|    }
12312|   443k|  } else if (b[0] == 'w' && b[1] == 's') {
  ------------------
  |  Branch (12312:14): [True: 50, False: 161]
  |  Branch (12312:29): [True: 29, False: 21]
  ------------------
12313|     29|    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
  ------------------
  |  Branch (12313:9): [True: 3, False: 26]
  |  Branch (12313:24): [True: 2, False: 1]
  |  Branch (12313:39): [True: 2, False: 0]
  ------------------
12314|      2|      pos = 5;
12315|      2|      scheme_type = ada::scheme::type::WS;
12316|      2|      protocol_end = 3;
12317|     27|    } else if (b[2] == 's' && b[3] == ':' && b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12317:16): [True: 7, False: 20]
  |  Branch (12317:31): [True: 0, False: 7]
  |  Branch (12317:46): [True: 0, False: 0]
  |  Branch (12317:61): [True: 0, False: 0]
  ------------------
12318|      0|      pos = 6;
12319|      0|      scheme_type = ada::scheme::type::WSS;
12320|      0|      protocol_end = 4;
12321|     27|    } else {
12322|     27|      return false;
12323|     27|    }
12324|    182|  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
  ------------------
  |  Branch (12324:14): [True: 30, False: 152]
  |  Branch (12324:29): [True: 18, False: 12]
  |  Branch (12324:44): [True: 7, False: 11]
  |  Branch (12324:59): [True: 0, False: 7]
  ------------------
12325|      0|             b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12325:14): [True: 0, False: 0]
  |  Branch (12325:29): [True: 0, False: 0]
  ------------------
12326|      0|    pos = 6;
12327|      0|    scheme_type = ada::scheme::type::FTP;
12328|      0|    protocol_end = 4;
12329|    182|  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
  ------------------
  |  Branch (12329:14): [True: 159, False: 23]
  |  Branch (12329:26): [True: 95, False: 64]
  |  Branch (12329:41): [True: 74, False: 21]
  |  Branch (12329:56): [True: 57, False: 17]
  ------------------
12330|     57|             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (12330:14): [True: 33, False: 24]
  |  Branch (12330:29): [True: 22, False: 11]
  |  Branch (12330:44): [True: 12, False: 10]
  |  Branch (12330:59): [True: 4, False: 8]
  ------------------
12331|      4|    pos = 7;
12332|      4|    scheme_type = ada::scheme::type::HTTP;
12333|      4|    protocol_end = 5;
12334|    178|  } else {
12335|    178|    return false;
12336|    178|  }
12337|       |#else
12338|       |  // Big-endian / uncommon arches: byte-wise special-scheme match.
12339|       |  if (len >= 8 && b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p' &&
12340|       |      b[4] == 's' && b[5] == ':' && b[6] == '/' && b[7] == '/') {
12341|       |    pos = 8;
12342|       |    scheme_type = ada::scheme::type::HTTPS;
12343|       |    protocol_end = 6;
12344|       |  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
12345|       |             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
12346|       |    pos = 7;
12347|       |    scheme_type = ada::scheme::type::HTTP;
12348|       |    protocol_end = 5;
12349|       |  } else if (b[0] == 'w' && b[1] == 's') {
12350|       |    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
12351|       |      pos = 5;
12352|       |      scheme_type = ada::scheme::type::WS;
12353|       |      protocol_end = 3;
12354|       |    } else if (len >= 6 && b[2] == 's' && b[3] == ':' && b[4] == '/' &&
12355|       |               b[5] == '/') {
12356|       |      pos = 6;
12357|       |      scheme_type = ada::scheme::type::WSS;
12358|       |      protocol_end = 4;
12359|       |    } else {
12360|       |      return false;
12361|       |    }
12362|       |  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
12363|       |             b[4] == '/' && b[5] == '/') {
12364|       |    pos = 6;
12365|       |    scheme_type = ada::scheme::type::FTP;
12366|       |    protocol_end = 4;
12367|       |  } else {
12368|       |    return false;
12369|       |  }
12370|       |#endif
12371|   437k|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (12371:7): [True: 437k, False: 5]
  |  Branch (12371:21): [True: 97, False: 437k]
  |  Branch (12371:38): [True: 19, False: 437k]
  ------------------
12372|    116|    return false;
12373|    116|  }
12374|       |
12375|       |  // Digit-led hosts are IPv4/numeric; '[' is IPv6. Skip before scanning.
12376|   437k|  if (pos < len && ((b[pos] >= '0' && b[pos] <= '9') || b[pos] == '[')) {
  ------------------
  |  Branch (12376:7): [True: 437k, False: 5]
  |  Branch (12376:22): [True: 435k, False: 2.07k]
  |  Branch (12376:39): [True: 46, False: 435k]
  |  Branch (12376:57): [True: 6, False: 437k]
  ------------------
12377|     52|    return false;
12378|     52|  }
12379|       |
12380|   437k|  const size_t host_start = pos;
12381|   437k|  bool has_upper = false;
12382|   437k|  bool has_x = false;
12383|   437k|  size_t host_end = pos;
12384|   437k|  if (!scan_plain_host(b, pos, len, host_end, has_upper, has_x)) {
  ------------------
  |  Branch (12384:7): [True: 2.76k, False: 434k]
  ------------------
12385|  2.76k|    return false;
12386|  2.76k|  }
12387|   434k|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (12387:7): [True: 81, False: 434k]
  ------------------
12388|     81|    return false;
12389|     81|  }
12390|   434k|  const size_t host_len = host_end - host_start;
12391|   434k|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (12391:7): [True: 288, False: 434k]
  ------------------
12392|    288|    return false;
12393|    288|  }
12394|   434k|  {
12395|   434k|    std::string_view hv(input.data() + host_start, host_len);
12396|   434k|    char host_buf[256];
12397|   434k|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (12397:9): [True: 10.1k, False: 424k]
  ------------------
12398|  10.1k|      std::memcpy(host_buf, input.data() + host_start, host_len);
12399|  10.1k|      unicode::to_lower_ascii(host_buf, host_len);
12400|  10.1k|      hv = std::string_view(host_buf, host_len);
12401|  10.1k|    }
12402|   434k|    if (ada::checkers::last_label_may_be_a_number(hv)) [[unlikely]] {
  ------------------
  |  Branch (12402:9): [True: 19.0k, False: 415k]
  ------------------
12403|  19.0k|      return false;
12404|  19.0k|    }
12405|       |    // Punycode is "xn--...". The host scan already notes a lowercase 'x';
12406|       |    // uppercase 'X' is covered by has_upper after the in-place lower.
12407|   415k|    static constexpr std::string_view xn{"xn-", 3};
12408|   415k|    if ((has_x || has_upper) && hv.find(xn) != std::string_view::npos)
  ------------------
  |  Branch (12408:10): [True: 379k, False: 36.0k]
  |  Branch (12408:19): [True: 483, False: 35.6k]
  |  Branch (12408:33): [True: 60.0k, False: 319k]
  ------------------
12409|  60.0k|        [[unlikely]] {
12410|  60.0k|      return false;
12411|  60.0k|    }
12412|   415k|  }
12413|       |
12414|   355k|  if (host_end < len && b[host_end] == ':') [[unlikely]] {
  ------------------
  |  Branch (12414:7): [True: 345k, False: 9.65k]
  |  Branch (12414:25): [True: 62.0k, False: 283k]
  ------------------
12415|  62.0k|    return finish_simple_absolute_with_port(input, out, scheme_type,
12416|  62.0k|                                            protocol_end, host_start, host_end,
12417|  62.0k|                                            host_len, has_upper);
12418|  62.0k|  }
12419|       |
12420|   293k|  size_t i = host_end;
12421|   293k|  size_t path_start = host_end;
12422|   293k|  size_t path_end = host_end;
12423|   293k|  size_t query_start = std::string_view::npos;
12424|   293k|  size_t hash_start = std::string_view::npos;
12425|   293k|  bool has_path = false;
12426|   293k|  bool maybe_dot_segment = false;
12427|   293k|  bool rest_simple = true;
12428|       |
12429|   293k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12429:7): [True: 283k, False: 9.65k]
  |  Branch (12429:18): [True: 264k, False: 19.3k]
  ------------------
12430|   264k|    has_path = true;
12431|   264k|    path_start = i;
12432|   264k|    ++i;
12433|   264k|    scan_path_run(b, i, len, maybe_dot_segment);
12434|   264k|    if (i < len) {
  ------------------
  |  Branch (12434:9): [True: 110k, False: 153k]
  ------------------
12435|   110k|      const uint8_t cls = k_path[b[i]];
12436|   110k|      if (cls == 1) {
  ------------------
  |  Branch (12436:11): [True: 98.8k, False: 12.0k]
  ------------------
12437|  98.8k|        path_end = i;
12438|  98.8k|        if (b[i] == '?') {
  ------------------
  |  Branch (12438:13): [True: 78.9k, False: 19.9k]
  ------------------
12439|  78.9k|          query_start = i;
12440|  78.9k|          ++i;
12441|  78.9k|          goto scan_query;
12442|  78.9k|        }
12443|  19.9k|        hash_start = i;
12444|  19.9k|        ++i;
12445|  19.9k|        goto scan_hash;
12446|  98.8k|      }
12447|  12.0k|      rest_simple = false;
12448|  12.0k|      hash_start = input.find('#', i);
12449|  12.0k|      query_start = input.substr(0, hash_start).find('?', i);
12450|  12.0k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12450:11): [True: 1.02k, False: 10.9k]
  ------------------
12451|  1.02k|        path_end = query_start;
12452|  10.9k|      } else {
12453|  10.9k|        path_end = hash_start != std::string_view::npos ? hash_start : len;
  ------------------
  |  Branch (12453:20): [True: 306, False: 10.6k]
  ------------------
12454|  10.9k|      }
12455|  12.0k|      goto after_rest;
12456|   110k|    }
12457|   153k|    path_end = i;
12458|   153k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12458:14): [True: 19.3k, False: 9.65k]
  |  Branch (12458:25): [True: 9.73k, False: 9.57k]
  ------------------
12459|  9.73k|    query_start = i;
12460|  9.73k|    ++i;
12461|  9.73k|    goto scan_query;
12462|  19.2k|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12462:14): [True: 9.57k, False: 9.65k]
  |  Branch (12462:25): [True: 9.57k, False: 0]
  ------------------
12463|  9.57k|    hash_start = i;
12464|  9.57k|    ++i;
12465|  9.57k|    goto scan_hash;
12466|  9.57k|  }
12467|   163k|  goto after_rest;
12468|       |
12469|   163k|scan_query:
12470|  88.7k|  scan_query_run(b, i, len);
12471|  88.7k|  if (i < len) {
  ------------------
  |  Branch (12471:7): [True: 31.0k, False: 57.6k]
  ------------------
12472|  31.0k|    if (b[i] == '#') {
  ------------------
  |  Branch (12472:9): [True: 30.9k, False: 137]
  ------------------
12473|  30.9k|      hash_start = i;
12474|  30.9k|      ++i;
12475|  30.9k|      goto scan_hash;
12476|  30.9k|    }
12477|    137|    rest_simple = false;
12478|    137|    goto scan_query_boundary;
12479|  31.0k|  }
12480|  57.6k|  goto after_rest;
12481|       |
12482|  57.6k|scan_query_boundary:
12483|    137|  hash_start = input.find('#', i);
12484|    137|  goto after_rest;
12485|       |
12486|  60.3k|scan_hash:
12487|  60.3k|  scan_hash_run(b, i, len);
12488|  60.3k|  if (i < len) {
  ------------------
  |  Branch (12488:7): [True: 128, False: 60.2k]
  ------------------
12489|    128|    rest_simple = false;
12490|    128|  }
12491|       |
12492|   293k|after_rest:
12493|   293k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12493:7): [True: 281k, False: 12.2k]
  |  Branch (12493:22): [True: 10.2k, False: 270k]
  ------------------
12494|  10.2k|    const std::string_view path_body(input.data() + path_start,
12495|  10.2k|                                     path_end - path_start);
12496|  10.2k|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12496:9): [True: 9.83k, False: 406]
  ------------------
12497|  9.83k|      rest_simple = false;
12498|  9.83k|    }
12499|  10.2k|  }
12500|       |
12501|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12502|       |  // trailing C0 control or space; this fast path does neither. A query or
12503|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12504|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12505|       |  // inputs back to the slow path.
12506|   293k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12506:7): [True: 22.1k, False: 271k]
  |  Branch (12506:24): [True: 35, False: 22.0k]
  ------------------
12507|  22.0k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12507:24): [True: 82, False: 21.9k]
  ------------------
12508|    117|    return false;
12509|    117|  }
12510|       |
12511|   293k|  out.type = scheme_type;
12512|   293k|  out.is_valid = true;
12513|   293k|  out.has_opaque_path = false;
12514|   293k|  out.host_type = DEFAULT;
12515|       |
12516|   293k|  if (!rest_simple) {
  ------------------
  |  Branch (12516:7): [True: 21.9k, False: 271k]
  ------------------
12517|       |    // Host is a plain domain. Finish path/query/hash with
12518|       |    // the regular helpers
12519|       |    // so percent-encoding and dot segments do not re-parse the authority.
12520|  21.9k|    const std::string_view path_view =
12521|  21.9k|        has_path
  ------------------
  |  Branch (12521:9): [True: 21.8k, False: 158]
  ------------------
12522|  21.9k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12523|  21.9k|            : std::string_view{};
12524|  21.9k|    auto apply_query_and_hash = [&]() {
12525|  21.9k|      if (query_start != std::string_view::npos) {
12526|  21.9k|        const size_t q_end =
12527|  21.9k|            (hash_start != std::string_view::npos) ? hash_start : len;
12528|  21.9k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12529|  21.9k|                                                q_end - query_start - 1),
12530|  21.9k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12531|  21.9k|      }
12532|  21.9k|      if (hash_start != std::string_view::npos) {
12533|  21.9k|        out.update_unencoded_base_hash(std::string_view(
12534|  21.9k|            input.data() + hash_start + 1, len - hash_start - 1));
12535|  21.9k|      }
12536|  21.9k|    };
12537|  21.9k|    if constexpr (is_aggregator) {
12538|  21.9k|      out.buffer.assign(input.substr(0, host_end));
12539|  21.9k|      if (has_upper) {
  ------------------
  |  Branch (12539:11): [True: 137, False: 21.8k]
  ------------------
12540|    137|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12541|    137|      }
12542|  21.9k|      out.components.protocol_end = protocol_end;
12543|  21.9k|      out.components.username_end = protocol_end + 2;
12544|  21.9k|      out.components.host_start = protocol_end + 2;
12545|  21.9k|      out.components.host_end = static_cast<uint32_t>(host_end);
12546|  21.9k|      out.components.port = url_components::omitted;
12547|  21.9k|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12548|  21.9k|      out.components.search_start = url_components::omitted;
12549|  21.9k|      out.components.hash_start = url_components::omitted;
12550|  21.9k|      out.parse_path(path_view);
12551|  21.9k|      apply_query_and_hash();
12552|       |    } else {
12553|       |      std::string host_str(input.substr(host_start, host_len));
12554|       |      if (has_upper) {
12555|       |        unicode::to_lower_ascii(host_str.data(), host_str.size());
12556|       |      }
12557|       |      out.host = std::move(host_str);
12558|       |      out.parse_path(path_view);
12559|       |      apply_query_and_hash();
12560|       |    }
12561|  21.9k|    return true;
12562|  21.9k|  }
12563|       |
12564|   271k|  const bool need_slash = !has_path;
12565|   271k|  if constexpr (is_aggregator) {
12566|   271k|    if (!need_slash) {
  ------------------
  |  Branch (12566:9): [True: 242k, False: 28.7k]
  ------------------
12567|       |      // assign copies once. resize()+memcpy would value-init then overwrite.
12568|   242k|      out.buffer.assign(input);
12569|   242k|      if (has_upper) {
  ------------------
  |  Branch (12569:11): [True: 9.48k, False: 233k]
  ------------------
12570|  9.48k|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12571|  9.48k|      }
12572|   242k|      out.components.protocol_end = protocol_end;
12573|   242k|      out.components.username_end = protocol_end + 2;
12574|   242k|      out.components.host_start = protocol_end + 2;
12575|   242k|      out.components.host_end = static_cast<uint32_t>(host_end);
12576|   242k|      out.components.port = url_components::omitted;
12577|   242k|      out.components.pathname_start = static_cast<uint32_t>(path_start);
12578|   242k|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12578:37): [True: 78.7k, False: 163k]
  ------------------
12579|   242k|                                        ? static_cast<uint32_t>(query_start)
12580|   242k|                                        : url_components::omitted;
12581|   242k|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12581:35): [True: 50.4k, False: 192k]
  ------------------
12582|   242k|                                      ? static_cast<uint32_t>(hash_start)
12583|   242k|                                      : url_components::omitted;
12584|   242k|    } else {
12585|  28.7k|      out.buffer.clear();
12586|  28.7k|      out.buffer.reserve(len + 1);
12587|  28.7k|      out.buffer.append(input.substr(0, host_end));
12588|  28.7k|      out.buffer.push_back('/');
12589|  28.7k|      if (host_end < len) {
  ------------------
  |  Branch (12589:11): [True: 19.1k, False: 9.65k]
  ------------------
12590|  19.1k|        out.buffer.append(input.substr(host_end));
12591|  19.1k|      }
12592|  28.7k|      if (has_upper) {
  ------------------
  |  Branch (12592:11): [True: 104, False: 28.6k]
  ------------------
12593|    104|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12594|    104|      }
12595|  28.7k|      out.components.protocol_end = protocol_end;
12596|  28.7k|      out.components.username_end = protocol_end + 2;
12597|  28.7k|      out.components.host_start = protocol_end + 2;
12598|  28.7k|      out.components.host_end = static_cast<uint32_t>(host_end);
12599|  28.7k|      out.components.port = url_components::omitted;
12600|  28.7k|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12601|  28.7k|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12601:37): [True: 9.61k, False: 19.1k]
  ------------------
12602|  28.7k|                                        ? static_cast<uint32_t>(query_start + 1)
12603|  28.7k|                                        : url_components::omitted;
12604|  28.7k|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12604:35): [True: 9.62k, False: 19.1k]
  ------------------
12605|  28.7k|                                      ? static_cast<uint32_t>(hash_start + 1)
12606|  28.7k|                                      : url_components::omitted;
12607|  28.7k|    }
12608|       |  } else {
12609|       |    std::string host_str(input.substr(host_start, host_len));
12610|       |    if (has_upper) {
12611|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
12612|       |    }
12613|       |    out.host = std::move(host_str);
12614|       |    if (need_slash) {
12615|       |      out.path = "/";
12616|       |    } else {
12617|       |      out.path.assign(input.data() + path_start, path_end - path_start);
12618|       |    }
12619|       |    if (query_start != std::string_view::npos) {
12620|       |      const size_t q_end =
12621|       |          (hash_start != std::string_view::npos) ? hash_start : len;
12622|       |      out.query.emplace(input.data() + query_start + 1,
12623|       |                        q_end - query_start - 1);
12624|       |    }
12625|       |    if (hash_start != std::string_view::npos) {
12626|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12627|       |    }
12628|       |  }
12629|   271k|  return true;
12630|   293k|}
_ZN3ada6parser32finish_simple_absolute_with_portINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmb:
11990|  62.0k|    bool has_upper) {
11991|  62.0k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11992|  62.0k|  constexpr bool is_aggregator =
11993|  62.0k|      std::is_same_v<result_type, ada::url_aggregator>;
11994|  62.0k|  static_assert(is_ada_url || is_aggregator);
11995|       |
11996|  62.0k|  const size_t len = input.size();
11997|  62.0k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
11998|  62.0k|  size_t p = host_end + 1;
11999|  62.0k|  uint32_t port_value = 0;
12000|  62.0k|  bool any_digit = false;
12001|   267k|  while (p < len && b[p] >= '0' && b[p] <= '9') {
  ------------------
  |  Branch (12001:10): [True: 267k, False: 62]
  |  Branch (12001:21): [True: 205k, False: 61.6k]
  |  Branch (12001:36): [True: 205k, False: 299]
  ------------------
12002|   205k|    any_digit = true;
12003|   205k|    port_value = port_value * 10 + static_cast<uint32_t>(b[p] - '0');
12004|   205k|    if (port_value > 65535) [[unlikely]] {
  ------------------
  |  Branch (12004:9): [True: 19, False: 205k]
  ------------------
12005|     19|      return false;
12006|     19|    }
12007|   205k|    ++p;
12008|   205k|  }
12009|  62.0k|  if (p < len && b[p] != '/' && b[p] != '?' && b[p] != '#') [[unlikely]] {
  ------------------
  |  Branch (12009:7): [True: 61.9k, False: 62]
  |  Branch (12009:18): [True: 416, False: 61.5k]
  |  Branch (12009:33): [True: 283, False: 133]
  |  Branch (12009:48): [True: 205, False: 78]
  ------------------
12010|    205|    return false;
12011|    205|  }
12012|  61.8k|  uint32_t parsed_port = url_components::omitted;
12013|  61.8k|  const uint16_t default_port = ada::scheme::get_special_port(scheme_type);
12014|  61.8k|  if (any_digit && port_value != default_port) {
  ------------------
  |  Branch (12014:7): [True: 61.4k, False: 404]
  |  Branch (12014:20): [True: 61.3k, False: 7]
  ------------------
12015|  61.3k|    parsed_port = port_value;
12016|  61.3k|  }
12017|  61.8k|  const size_t authority_end = p;
12018|       |
12019|  61.8k|  size_t i = authority_end;
12020|  61.8k|  size_t path_start = host_end;
12021|  61.8k|  size_t path_end = host_end;
12022|  61.8k|  size_t query_start = std::string_view::npos;
12023|  61.8k|  size_t hash_start = std::string_view::npos;
12024|  61.8k|  bool has_path = false;
12025|  61.8k|  bool maybe_dot_segment = false;
12026|  61.8k|  bool rest_simple = true;
12027|       |
12028|  61.8k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12028:7): [True: 61.7k, False: 62]
  |  Branch (12028:18): [True: 61.5k, False: 211]
  ------------------
12029|  61.5k|    has_path = true;
12030|  61.5k|    path_start = i;
12031|  61.5k|    ++i;
12032|  61.5k|    scan_path_run(b, i, len, maybe_dot_segment);
12033|  61.5k|    if (i < len) {
  ------------------
  |  Branch (12033:9): [True: 4.25k, False: 57.2k]
  ------------------
12034|  4.25k|      const uint8_t cls = k_path[b[i]];
12035|  4.25k|      if (cls == 1) {
  ------------------
  |  Branch (12035:11): [True: 2.36k, False: 1.89k]
  ------------------
12036|  2.36k|        path_end = i;
12037|  2.36k|        if (b[i] == '?') {
  ------------------
  |  Branch (12037:13): [True: 1.89k, False: 467]
  ------------------
12038|  1.89k|          query_start = i;
12039|  1.89k|          ++i;
12040|  1.89k|          goto scan_query;
12041|  1.89k|        }
12042|    467|        hash_start = i;
12043|    467|        ++i;
12044|    467|        goto scan_hash;
12045|  2.36k|      }
12046|  1.89k|      rest_simple = false;
12047|  88.4k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12047:14): [True: 87.7k, False: 686]
  ------------------
12048|  87.7k|        if (b[i] == '?') {
  ------------------
  |  Branch (12048:13): [True: 969, False: 86.7k]
  ------------------
12049|    969|          path_end = i;
12050|    969|          query_start = i;
12051|    969|          ++i;
12052|    969|          goto scan_query_boundary;
12053|    969|        }
12054|  86.7k|        if (b[i] == '#') {
  ------------------
  |  Branch (12054:13): [True: 235, False: 86.5k]
  ------------------
12055|    235|          path_end = i;
12056|    235|          hash_start = i;
12057|    235|          ++i;
12058|    235|          goto after_rest;
12059|    235|        }
12060|  86.7k|      }
12061|    686|      path_end = i;
12062|    686|      goto after_rest;
12063|  1.89k|    }
12064|  57.2k|    path_end = i;
12065|  57.2k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12065:14): [True: 211, False: 62]
  |  Branch (12065:25): [True: 133, False: 78]
  ------------------
12066|    133|    query_start = i;
12067|    133|    ++i;
12068|    133|    goto scan_query;
12069|    140|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12069:14): [True: 78, False: 62]
  |  Branch (12069:25): [True: 78, False: 0]
  ------------------
12070|     78|    hash_start = i;
12071|     78|    ++i;
12072|     78|    goto scan_hash;
12073|     78|  }
12074|  57.3k|  goto after_rest;
12075|       |
12076|  57.3k|scan_query:
12077|  2.02k|  scan_query_run(b, i, len);
12078|  2.02k|  if (i < len) {
  ------------------
  |  Branch (12078:7): [True: 1.37k, False: 647]
  ------------------
12079|  1.37k|    if (b[i] == '#') {
  ------------------
  |  Branch (12079:9): [True: 1.28k, False: 99]
  ------------------
12080|  1.28k|      hash_start = i;
12081|  1.28k|      ++i;
12082|  1.28k|      goto scan_hash;
12083|  1.28k|    }
12084|     99|    rest_simple = false;
12085|     99|    goto scan_query_boundary;
12086|  1.37k|  }
12087|    647|  goto after_rest;
12088|       |
12089|  1.06k|scan_query_boundary:
12090|  25.9k|  for (; i < len; ++i) {
  ------------------
  |  Branch (12090:10): [True: 25.5k, False: 396]
  ------------------
12091|  25.5k|    if (b[i] == '#') {
  ------------------
  |  Branch (12091:9): [True: 672, False: 24.8k]
  ------------------
12092|    672|      hash_start = i;
12093|    672|      ++i;
12094|    672|      goto after_rest;
12095|    672|    }
12096|  25.5k|  }
12097|    396|  goto after_rest;
12098|       |
12099|  1.82k|scan_hash:
12100|  1.82k|  scan_hash_run(b, i, len);
12101|  1.82k|  if (i < len) {
  ------------------
  |  Branch (12101:7): [True: 96, False: 1.72k]
  ------------------
12102|     96|    rest_simple = false;
12103|     96|  }
12104|       |
12105|  61.8k|after_rest:
12106|  61.8k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12106:7): [True: 59.7k, False: 2.08k]
  |  Branch (12106:22): [True: 590, False: 59.1k]
  ------------------
12107|    590|    const std::string_view path_body(input.data() + path_start,
12108|    590|                                     path_end - path_start);
12109|    590|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12109:9): [True: 369, False: 221]
  ------------------
12110|    369|      rest_simple = false;
12111|    369|    }
12112|    590|  }
12113|       |
12114|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12115|       |  // trailing C0 control or space; this fast path does neither. A query or
12116|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12117|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12118|       |  // inputs back to the slow path.
12119|  61.8k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12119:7): [True: 2.45k, False: 59.3k]
  |  Branch (12119:24): [True: 45, False: 2.40k]
  ------------------
12120|  2.40k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12120:24): [True: 48, False: 2.36k]
  ------------------
12121|     93|    return false;
12122|     93|  }
12123|       |
12124|  61.7k|  out.type = scheme_type;
12125|  61.7k|  out.is_valid = true;
12126|  61.7k|  out.has_opaque_path = false;
12127|  61.7k|  out.host_type = DEFAULT;
12128|       |
12129|  61.7k|  const uint32_t port_bytes = (parsed_port != url_components::omitted)
  ------------------
  |  Branch (12129:31): [True: 61.3k, False: 365]
  ------------------
12130|  61.7k|                                  ? (1 + port_decimal_digit_count(parsed_port))
12131|  61.7k|                                  : 0;
12132|       |
12133|  61.7k|  if (!rest_simple) {
  ------------------
  |  Branch (12133:7): [True: 2.36k, False: 59.3k]
  ------------------
12134|  2.36k|    const std::string_view path_view =
12135|  2.36k|        has_path
  ------------------
  |  Branch (12135:9): [True: 2.27k, False: 91]
  ------------------
12136|  2.36k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12137|  2.36k|            : std::string_view{};
12138|  2.36k|    auto apply_query_and_hash = [&]() {
12139|  2.36k|      if (query_start != std::string_view::npos) {
12140|  2.36k|        const size_t q_end =
12141|  2.36k|            (hash_start != std::string_view::npos) ? hash_start : len;
12142|  2.36k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12143|  2.36k|                                                q_end - query_start - 1),
12144|  2.36k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12145|  2.36k|      }
12146|  2.36k|      if (hash_start != std::string_view::npos) {
12147|  2.36k|        out.update_unencoded_base_hash(std::string_view(
12148|  2.36k|            input.data() + hash_start + 1, len - hash_start - 1));
12149|  2.36k|      }
12150|  2.36k|    };
12151|       |    if constexpr (is_aggregator) {
12152|       |      out.buffer.assign(input.substr(0, host_end));
12153|       |      if (parsed_port != url_components::omitted) {
12154|       |        append_canonical_port(out.buffer, parsed_port);
12155|       |      }
12156|       |      if (has_upper) {
12157|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12158|       |      }
12159|       |      out.components.protocol_end = protocol_end;
12160|       |      out.components.username_end = protocol_end + 2;
12161|       |      out.components.host_start = protocol_end + 2;
12162|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12163|       |      out.components.port = parsed_port;
12164|       |      out.components.pathname_start = static_cast<uint32_t>(out.buffer.size());
12165|       |      out.components.search_start = url_components::omitted;
12166|       |      out.components.hash_start = url_components::omitted;
12167|       |      out.parse_path(path_view);
12168|       |      apply_query_and_hash();
12169|  2.36k|    } else {
12170|  2.36k|      std::string host_str(input.substr(host_start, host_len));
12171|  2.36k|      if (has_upper) {
  ------------------
  |  Branch (12171:11): [True: 147, False: 2.21k]
  ------------------
12172|    147|        unicode::to_lower_ascii(host_str.data(), host_str.size());
12173|    147|      }
12174|  2.36k|      out.host = std::move(host_str);
12175|  2.36k|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12175:11): [True: 2.08k, False: 280]
  ------------------
12176|  2.08k|        out.port = static_cast<uint16_t>(parsed_port);
12177|  2.08k|      }
12178|  2.36k|      out.parse_path(path_view);
12179|  2.36k|      apply_query_and_hash();
12180|  2.36k|    }
12181|  2.36k|    return true;
12182|  2.36k|  }
12183|       |
12184|  59.3k|  const bool insert_slash = !has_path;
12185|       |  if constexpr (is_aggregator) {
12186|       |    const size_t out_len =
12187|       |        host_end + port_bytes + (insert_slash ? 1 : 0) + (len - authority_end);
12188|       |    const bool omit_port_bytes = parsed_port == url_components::omitted;
12189|       |    if (out_len == len && !insert_slash && !omit_port_bytes) {
12190|       |      out.buffer.assign(input);
12191|       |    } else {
12192|       |      out.buffer.clear();
12193|       |      out.buffer.reserve(out_len);
12194|       |      out.buffer.append(input.substr(0, host_end));
12195|       |      if (parsed_port != url_components::omitted) {
12196|       |        append_canonical_port(out.buffer, parsed_port);
12197|       |      }
12198|       |      if (insert_slash) {
12199|       |        out.buffer.push_back('/');
12200|       |      }
12201|       |      if (authority_end < len) {
12202|       |        out.buffer.append(input.substr(authority_end));
12203|       |      }
12204|       |    }
12205|       |    if (has_upper) {
12206|       |      unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12207|       |    }
12208|       |    const uint32_t pathname_start =
12209|       |        static_cast<uint32_t>(host_end) + port_bytes;
12210|       |    const int32_t tail_delta = static_cast<int32_t>(pathname_start) +
12211|       |                               (insert_slash ? 1 : 0) -
12212|       |                               static_cast<int32_t>(authority_end);
12213|       |    out.components.protocol_end = protocol_end;
12214|       |    out.components.username_end = protocol_end + 2;
12215|       |    out.components.host_start = protocol_end + 2;
12216|       |    out.components.host_end = static_cast<uint32_t>(host_end);
12217|       |    out.components.port = parsed_port;
12218|       |    out.components.pathname_start = pathname_start;
12219|       |    out.components.search_start =
12220|       |        (query_start != std::string_view::npos)
12221|       |            ? static_cast<uint32_t>(static_cast<int32_t>(query_start) +
12222|       |                                    tail_delta)
12223|       |            : url_components::omitted;
12224|       |    out.components.hash_start =
12225|       |        (hash_start != std::string_view::npos)
12226|       |            ? static_cast<uint32_t>(static_cast<int32_t>(hash_start) +
12227|       |                                    tail_delta)
12228|       |            : url_components::omitted;
12229|  59.3k|  } else {
12230|  59.3k|    std::string host_str(input.substr(host_start, host_len));
12231|  59.3k|    if (has_upper) {
  ------------------
  |  Branch (12231:9): [True: 58, False: 59.2k]
  ------------------
12232|     58|      unicode::to_lower_ascii(host_str.data(), host_str.size());
12233|     58|    }
12234|  59.3k|    out.host = std::move(host_str);
12235|  59.3k|    if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12235:9): [True: 59.2k, False: 85]
  ------------------
12236|  59.2k|      out.port = static_cast<uint16_t>(parsed_port);
12237|  59.2k|    }
12238|  59.3k|    if (insert_slash) {
  ------------------
  |  Branch (12238:9): [True: 159, False: 59.1k]
  ------------------
12239|    159|      out.path = "/";
12240|  59.1k|    } else {
12241|  59.1k|      out.path.assign(input.data() + path_start, path_end - path_start);
12242|  59.1k|    }
12243|  59.3k|    if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12243:9): [True: 1.72k, False: 57.6k]
  ------------------
12244|  1.72k|      const size_t q_end =
12245|  1.72k|          (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12245:11): [True: 1.15k, False: 571]
  ------------------
12246|  1.72k|      out.query.emplace(input.data() + query_start + 1,
12247|  1.72k|                        q_end - query_start - 1);
12248|  1.72k|    }
12249|  59.3k|    if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12249:9): [True: 1.58k, False: 57.7k]
  ------------------
12250|  1.58k|      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12251|  1.58k|    }
12252|  59.3k|  }
12253|  59.3k|  return true;
12254|  61.7k|}
_ZN3ada6parser32finish_simple_absolute_with_portINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmb:
11990|  62.0k|    bool has_upper) {
11991|  62.0k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11992|  62.0k|  constexpr bool is_aggregator =
11993|  62.0k|      std::is_same_v<result_type, ada::url_aggregator>;
11994|  62.0k|  static_assert(is_ada_url || is_aggregator);
11995|       |
11996|  62.0k|  const size_t len = input.size();
11997|  62.0k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
11998|  62.0k|  size_t p = host_end + 1;
11999|  62.0k|  uint32_t port_value = 0;
12000|  62.0k|  bool any_digit = false;
12001|   267k|  while (p < len && b[p] >= '0' && b[p] <= '9') {
  ------------------
  |  Branch (12001:10): [True: 267k, False: 62]
  |  Branch (12001:21): [True: 205k, False: 61.6k]
  |  Branch (12001:36): [True: 205k, False: 299]
  ------------------
12002|   205k|    any_digit = true;
12003|   205k|    port_value = port_value * 10 + static_cast<uint32_t>(b[p] - '0');
12004|   205k|    if (port_value > 65535) [[unlikely]] {
  ------------------
  |  Branch (12004:9): [True: 19, False: 205k]
  ------------------
12005|     19|      return false;
12006|     19|    }
12007|   205k|    ++p;
12008|   205k|  }
12009|  62.0k|  if (p < len && b[p] != '/' && b[p] != '?' && b[p] != '#') [[unlikely]] {
  ------------------
  |  Branch (12009:7): [True: 61.9k, False: 62]
  |  Branch (12009:18): [True: 416, False: 61.5k]
  |  Branch (12009:33): [True: 283, False: 133]
  |  Branch (12009:48): [True: 205, False: 78]
  ------------------
12010|    205|    return false;
12011|    205|  }
12012|  61.8k|  uint32_t parsed_port = url_components::omitted;
12013|  61.8k|  const uint16_t default_port = ada::scheme::get_special_port(scheme_type);
12014|  61.8k|  if (any_digit && port_value != default_port) {
  ------------------
  |  Branch (12014:7): [True: 61.4k, False: 404]
  |  Branch (12014:20): [True: 61.3k, False: 7]
  ------------------
12015|  61.3k|    parsed_port = port_value;
12016|  61.3k|  }
12017|  61.8k|  const size_t authority_end = p;
12018|       |
12019|  61.8k|  size_t i = authority_end;
12020|  61.8k|  size_t path_start = host_end;
12021|  61.8k|  size_t path_end = host_end;
12022|  61.8k|  size_t query_start = std::string_view::npos;
12023|  61.8k|  size_t hash_start = std::string_view::npos;
12024|  61.8k|  bool has_path = false;
12025|  61.8k|  bool maybe_dot_segment = false;
12026|  61.8k|  bool rest_simple = true;
12027|       |
12028|  61.8k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12028:7): [True: 61.7k, False: 62]
  |  Branch (12028:18): [True: 61.5k, False: 211]
  ------------------
12029|  61.5k|    has_path = true;
12030|  61.5k|    path_start = i;
12031|  61.5k|    ++i;
12032|  61.5k|    scan_path_run(b, i, len, maybe_dot_segment);
12033|  61.5k|    if (i < len) {
  ------------------
  |  Branch (12033:9): [True: 4.25k, False: 57.2k]
  ------------------
12034|  4.25k|      const uint8_t cls = k_path[b[i]];
12035|  4.25k|      if (cls == 1) {
  ------------------
  |  Branch (12035:11): [True: 2.36k, False: 1.89k]
  ------------------
12036|  2.36k|        path_end = i;
12037|  2.36k|        if (b[i] == '?') {
  ------------------
  |  Branch (12037:13): [True: 1.89k, False: 467]
  ------------------
12038|  1.89k|          query_start = i;
12039|  1.89k|          ++i;
12040|  1.89k|          goto scan_query;
12041|  1.89k|        }
12042|    467|        hash_start = i;
12043|    467|        ++i;
12044|    467|        goto scan_hash;
12045|  2.36k|      }
12046|  1.89k|      rest_simple = false;
12047|  88.4k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12047:14): [True: 87.7k, False: 686]
  ------------------
12048|  87.7k|        if (b[i] == '?') {
  ------------------
  |  Branch (12048:13): [True: 969, False: 86.7k]
  ------------------
12049|    969|          path_end = i;
12050|    969|          query_start = i;
12051|    969|          ++i;
12052|    969|          goto scan_query_boundary;
12053|    969|        }
12054|  86.7k|        if (b[i] == '#') {
  ------------------
  |  Branch (12054:13): [True: 235, False: 86.5k]
  ------------------
12055|    235|          path_end = i;
12056|    235|          hash_start = i;
12057|    235|          ++i;
12058|    235|          goto after_rest;
12059|    235|        }
12060|  86.7k|      }
12061|    686|      path_end = i;
12062|    686|      goto after_rest;
12063|  1.89k|    }
12064|  57.2k|    path_end = i;
12065|  57.2k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12065:14): [True: 211, False: 62]
  |  Branch (12065:25): [True: 133, False: 78]
  ------------------
12066|    133|    query_start = i;
12067|    133|    ++i;
12068|    133|    goto scan_query;
12069|    140|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12069:14): [True: 78, False: 62]
  |  Branch (12069:25): [True: 78, False: 0]
  ------------------
12070|     78|    hash_start = i;
12071|     78|    ++i;
12072|     78|    goto scan_hash;
12073|     78|  }
12074|  57.3k|  goto after_rest;
12075|       |
12076|  57.3k|scan_query:
12077|  2.02k|  scan_query_run(b, i, len);
12078|  2.02k|  if (i < len) {
  ------------------
  |  Branch (12078:7): [True: 1.37k, False: 647]
  ------------------
12079|  1.37k|    if (b[i] == '#') {
  ------------------
  |  Branch (12079:9): [True: 1.28k, False: 99]
  ------------------
12080|  1.28k|      hash_start = i;
12081|  1.28k|      ++i;
12082|  1.28k|      goto scan_hash;
12083|  1.28k|    }
12084|     99|    rest_simple = false;
12085|     99|    goto scan_query_boundary;
12086|  1.37k|  }
12087|    647|  goto after_rest;
12088|       |
12089|  1.06k|scan_query_boundary:
12090|  25.9k|  for (; i < len; ++i) {
  ------------------
  |  Branch (12090:10): [True: 25.5k, False: 396]
  ------------------
12091|  25.5k|    if (b[i] == '#') {
  ------------------
  |  Branch (12091:9): [True: 672, False: 24.8k]
  ------------------
12092|    672|      hash_start = i;
12093|    672|      ++i;
12094|    672|      goto after_rest;
12095|    672|    }
12096|  25.5k|  }
12097|    396|  goto after_rest;
12098|       |
12099|  1.82k|scan_hash:
12100|  1.82k|  scan_hash_run(b, i, len);
12101|  1.82k|  if (i < len) {
  ------------------
  |  Branch (12101:7): [True: 96, False: 1.72k]
  ------------------
12102|     96|    rest_simple = false;
12103|     96|  }
12104|       |
12105|  61.8k|after_rest:
12106|  61.8k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12106:7): [True: 59.7k, False: 2.08k]
  |  Branch (12106:22): [True: 590, False: 59.1k]
  ------------------
12107|    590|    const std::string_view path_body(input.data() + path_start,
12108|    590|                                     path_end - path_start);
12109|    590|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12109:9): [True: 369, False: 221]
  ------------------
12110|    369|      rest_simple = false;
12111|    369|    }
12112|    590|  }
12113|       |
12114|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12115|       |  // trailing C0 control or space; this fast path does neither. A query or
12116|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12117|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12118|       |  // inputs back to the slow path.
12119|  61.8k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12119:7): [True: 2.45k, False: 59.3k]
  |  Branch (12119:24): [True: 45, False: 2.40k]
  ------------------
12120|  2.40k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12120:24): [True: 48, False: 2.36k]
  ------------------
12121|     93|    return false;
12122|     93|  }
12123|       |
12124|  61.7k|  out.type = scheme_type;
12125|  61.7k|  out.is_valid = true;
12126|  61.7k|  out.has_opaque_path = false;
12127|  61.7k|  out.host_type = DEFAULT;
12128|       |
12129|  61.7k|  const uint32_t port_bytes = (parsed_port != url_components::omitted)
  ------------------
  |  Branch (12129:31): [True: 61.3k, False: 365]
  ------------------
12130|  61.7k|                                  ? (1 + port_decimal_digit_count(parsed_port))
12131|  61.7k|                                  : 0;
12132|       |
12133|  61.7k|  if (!rest_simple) {
  ------------------
  |  Branch (12133:7): [True: 2.36k, False: 59.3k]
  ------------------
12134|  2.36k|    const std::string_view path_view =
12135|  2.36k|        has_path
  ------------------
  |  Branch (12135:9): [True: 2.27k, False: 91]
  ------------------
12136|  2.36k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12137|  2.36k|            : std::string_view{};
12138|  2.36k|    auto apply_query_and_hash = [&]() {
12139|  2.36k|      if (query_start != std::string_view::npos) {
12140|  2.36k|        const size_t q_end =
12141|  2.36k|            (hash_start != std::string_view::npos) ? hash_start : len;
12142|  2.36k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12143|  2.36k|                                                q_end - query_start - 1),
12144|  2.36k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12145|  2.36k|      }
12146|  2.36k|      if (hash_start != std::string_view::npos) {
12147|  2.36k|        out.update_unencoded_base_hash(std::string_view(
12148|  2.36k|            input.data() + hash_start + 1, len - hash_start - 1));
12149|  2.36k|      }
12150|  2.36k|    };
12151|  2.36k|    if constexpr (is_aggregator) {
12152|  2.36k|      out.buffer.assign(input.substr(0, host_end));
12153|  2.36k|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12153:11): [True: 2.08k, False: 280]
  ------------------
12154|  2.08k|        append_canonical_port(out.buffer, parsed_port);
12155|  2.08k|      }
12156|  2.36k|      if (has_upper) {
  ------------------
  |  Branch (12156:11): [True: 147, False: 2.21k]
  ------------------
12157|    147|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12158|    147|      }
12159|  2.36k|      out.components.protocol_end = protocol_end;
12160|  2.36k|      out.components.username_end = protocol_end + 2;
12161|  2.36k|      out.components.host_start = protocol_end + 2;
12162|  2.36k|      out.components.host_end = static_cast<uint32_t>(host_end);
12163|  2.36k|      out.components.port = parsed_port;
12164|  2.36k|      out.components.pathname_start = static_cast<uint32_t>(out.buffer.size());
12165|  2.36k|      out.components.search_start = url_components::omitted;
12166|  2.36k|      out.components.hash_start = url_components::omitted;
12167|  2.36k|      out.parse_path(path_view);
12168|  2.36k|      apply_query_and_hash();
12169|       |    } else {
12170|       |      std::string host_str(input.substr(host_start, host_len));
12171|       |      if (has_upper) {
12172|       |        unicode::to_lower_ascii(host_str.data(), host_str.size());
12173|       |      }
12174|       |      out.host = std::move(host_str);
12175|       |      if (parsed_port != url_components::omitted) {
12176|       |        out.port = static_cast<uint16_t>(parsed_port);
12177|       |      }
12178|       |      out.parse_path(path_view);
12179|       |      apply_query_and_hash();
12180|       |    }
12181|  2.36k|    return true;
12182|  2.36k|  }
12183|       |
12184|  59.3k|  const bool insert_slash = !has_path;
12185|  59.3k|  if constexpr (is_aggregator) {
12186|  59.3k|    const size_t out_len =
12187|  59.3k|        host_end + port_bytes + (insert_slash ? 1 : 0) + (len - authority_end);
  ------------------
  |  Branch (12187:34): [True: 159, False: 59.1k]
  ------------------
12188|  59.3k|    const bool omit_port_bytes = parsed_port == url_components::omitted;
12189|  59.3k|    if (out_len == len && !insert_slash && !omit_port_bytes) {
  ------------------
  |  Branch (12189:9): [True: 49.7k, False: 9.62k]
  |  Branch (12189:27): [True: 49.6k, False: 33]
  |  Branch (12189:44): [True: 49.6k, False: 0]
  ------------------
12190|  49.6k|      out.buffer.assign(input);
12191|  49.6k|    } else {
12192|  9.65k|      out.buffer.clear();
12193|  9.65k|      out.buffer.reserve(out_len);
12194|  9.65k|      out.buffer.append(input.substr(0, host_end));
12195|  9.65k|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (12195:11): [True: 9.57k, False: 85]
  ------------------
12196|  9.57k|        append_canonical_port(out.buffer, parsed_port);
12197|  9.57k|      }
12198|  9.65k|      if (insert_slash) {
  ------------------
  |  Branch (12198:11): [True: 159, False: 9.49k]
  ------------------
12199|    159|        out.buffer.push_back('/');
12200|    159|      }
12201|  9.65k|      if (authority_end < len) {
  ------------------
  |  Branch (12201:11): [True: 9.59k, False: 62]
  ------------------
12202|  9.59k|        out.buffer.append(input.substr(authority_end));
12203|  9.59k|      }
12204|  9.65k|    }
12205|  59.3k|    if (has_upper) {
  ------------------
  |  Branch (12205:9): [True: 58, False: 59.2k]
  ------------------
12206|     58|      unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12207|     58|    }
12208|  59.3k|    const uint32_t pathname_start =
12209|  59.3k|        static_cast<uint32_t>(host_end) + port_bytes;
12210|  59.3k|    const int32_t tail_delta = static_cast<int32_t>(pathname_start) +
12211|  59.3k|                               (insert_slash ? 1 : 0) -
  ------------------
  |  Branch (12211:33): [True: 159, False: 59.1k]
  ------------------
12212|  59.3k|                               static_cast<int32_t>(authority_end);
12213|  59.3k|    out.components.protocol_end = protocol_end;
12214|  59.3k|    out.components.username_end = protocol_end + 2;
12215|  59.3k|    out.components.host_start = protocol_end + 2;
12216|  59.3k|    out.components.host_end = static_cast<uint32_t>(host_end);
12217|  59.3k|    out.components.port = parsed_port;
12218|  59.3k|    out.components.pathname_start = pathname_start;
12219|  59.3k|    out.components.search_start =
12220|  59.3k|        (query_start != std::string_view::npos)
  ------------------
  |  Branch (12220:9): [True: 1.72k, False: 57.6k]
  ------------------
12221|  59.3k|            ? static_cast<uint32_t>(static_cast<int32_t>(query_start) +
12222|  1.72k|                                    tail_delta)
12223|  59.3k|            : url_components::omitted;
12224|  59.3k|    out.components.hash_start =
12225|  59.3k|        (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12225:9): [True: 1.58k, False: 57.7k]
  ------------------
12226|  59.3k|            ? static_cast<uint32_t>(static_cast<int32_t>(hash_start) +
12227|  1.58k|                                    tail_delta)
12228|  59.3k|            : url_components::omitted;
12229|       |  } else {
12230|       |    std::string host_str(input.substr(host_start, host_len));
12231|       |    if (has_upper) {
12232|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
12233|       |    }
12234|       |    out.host = std::move(host_str);
12235|       |    if (parsed_port != url_components::omitted) {
12236|       |      out.port = static_cast<uint16_t>(parsed_port);
12237|       |    }
12238|       |    if (insert_slash) {
12239|       |      out.path = "/";
12240|       |    } else {
12241|       |      out.path.assign(input.data() + path_start, path_end - path_start);
12242|       |    }
12243|       |    if (query_start != std::string_view::npos) {
12244|       |      const size_t q_end =
12245|       |          (hash_start != std::string_view::npos) ? hash_start : len;
12246|       |      out.query.emplace(input.data() + query_start + 1,
12247|       |                        q_end - query_start - 1);
12248|       |    }
12249|       |    if (hash_start != std::string_view::npos) {
12250|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12251|       |    }
12252|       |  }
12253|  59.3k|  return true;
12254|  61.7k|}
_ZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12819|   623k|                           const result_type* base_url) {
12820|       |  // We can specialize the implementation per type.
12821|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12822|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12823|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12824|       |  // and ada::url **do not have to support the exact same API**.
12825|   623k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12826|   623k|  constexpr bool result_type_is_ada_url_aggregator =
12827|   623k|      std::is_same_v<url_aggregator, result_type>;
12828|   623k|  static_assert(result_type_is_ada_url ||
12829|   623k|                result_type_is_ada_url_aggregator);  // We don't support
12830|       |                                                     // anything else for now.
12831|       |
12832|   623k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12833|   623k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12834|   623k|          ")");
12835|       |
12836|   623k|  result_type url{};
12837|       |
12838|   623k|  const uint32_t max_input_length = ada::get_max_input_length();
12839|       |
12840|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12841|       |  // max_input_length even when the input fit under it. The check at the end of
12842|       |  // the state machine covers URLs that run to completion, but several states
12843|       |  // return early after appending a query or fragment, so run the same check on
12844|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12845|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12846|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12847|   623k|  const auto enforce_max_input_length = [&]() {
12848|   623k|    if constexpr (store_values) {
12849|   623k|      if (url.is_valid) {
12850|   623k|        if constexpr (result_type_is_ada_url_aggregator) {
12851|   623k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12852|   623k|            url.is_valid = false;
12853|   623k|          }
12854|   623k|        } else {
12855|   623k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12856|   623k|            url.is_valid = false;
12857|   623k|          }
12858|   623k|        }
12859|   623k|      }
12860|   623k|    }
12861|   623k|  };
12862|       |
12863|       |  // We refuse to parse URL strings that exceed the maximum input length.
12864|       |  // By default, this is 4GB but can be configured via
12865|       |  // ada::set_max_input_length().
12866|   623k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12866:7): [True: 0, False: 623k]
  ------------------
12867|      0|    url.is_valid = false;
12868|      0|  }
12869|       |  // Going forward, user_input.size() is in [0,
12870|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12871|       |  // base, or the optional_url was invalid, we must return.
12872|   623k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12872:7): [True: 0, False: 623k]
  ------------------
12873|      0|    url.is_valid &= base_url->is_valid;
12874|      0|  }
12875|   623k|  if (!url.is_valid) {
  ------------------
  |  Branch (12875:7): [True: 0, False: 623k]
  ------------------
12876|      0|    return url;
12877|      0|  }
12878|       |
12879|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12880|   623k|  if constexpr (store_values) {
12881|   623k|    bool hit_fast_path = false;
12882|   623k|    if (base_url == nullptr) {
  ------------------
  |  Branch (12882:9): [True: 623k, False: 0]
  ------------------
12883|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12884|       |      // call so those URLs (including SetHref) do not pay for a miss.
12885|   623k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12886|   623k|      const size_t n = user_input.size();
12887|   623k|      size_t host_start = 0;
12888|   623k|      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
  ------------------
  |  Branch (12888:11): [True: 622k, False: 221]
  |  Branch (12888:21): [True: 238k, False: 384k]
  |  Branch (12888:36): [True: 237k, False: 889]
  |  Branch (12888:51): [True: 237k, False: 176]
  ------------------
12889|   237k|        host_start = 7;
12890|   385k|      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
  ------------------
  |  Branch (12890:18): [True: 385k, False: 242]
  |  Branch (12890:28): [True: 382k, False: 2.66k]
  |  Branch (12890:43): [True: 382k, False: 767]
  |  Branch (12890:58): [True: 382k, False: 102]
  ------------------
12891|   382k|        host_start = 8;
12892|   382k|      }
12893|   623k|      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
  ------------------
  |  Branch (12893:34): [True: 619k, False: 3.77k]
  ------------------
12894|   623k|      const bool skip_ip =
12895|   623k|          host_first == '[' || (host_first >= '0' && host_first <= '9');
  ------------------
  |  Branch (12895:11): [True: 528, False: 622k]
  |  Branch (12895:33): [True: 616k, False: 6.56k]
  |  Branch (12895:54): [True: 138k, False: 477k]
  ------------------
12896|   623k|      const bool skip_userinfo =
12897|   623k|          host_start != 0 && authority_has_at(p, host_start, n);
  ------------------
  |  Branch (12897:11): [True: 619k, False: 3.77k]
  |  Branch (12897:30): [True: 39.6k, False: 579k]
  ------------------
12898|   623k|      hit_fast_path = !skip_ip && !skip_userinfo &&
  ------------------
  |  Branch (12898:23): [True: 483k, False: 139k]
  |  Branch (12898:35): [True: 444k, False: 39.5k]
  ------------------
12899|   444k|                      try_parse_simple_absolute(user_input, url);
  ------------------
  |  Branch (12899:23): [True: 355k, False: 89.1k]
  ------------------
12900|   623k|    } else {
12901|      0|      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12902|      0|    }
12903|   623k|    if (hit_fast_path) {
  ------------------
  |  Branch (12903:9): [True: 355k, False: 268k]
  ------------------
12904|       |      if constexpr (result_type_is_ada_url_aggregator) {
12905|       |        if (url.buffer.size() > max_input_length) [[unlikely]] {
12906|       |          url.is_valid = false;
12907|       |        }
12908|   355k|      } else {
12909|       |        // For a small absolute input, even worst-case normalization cannot
12910|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12911|       |        // most 4.5x. Avoid traversing every stored component on the common
12912|       |        // default-limit path.
12913|   355k|        if ((base_url != nullptr ||
  ------------------
  |  Branch (12913:14): [True: 0, False: 355k]
  ------------------
12914|   355k|             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
  ------------------
  |  Branch (12914:14): [True: 0, False: 355k]
  ------------------
12915|      0|            url.get_href_size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12915:13): [True: 0, False: 0]
  ------------------
12916|      0|          url.is_valid = false;
12917|      0|        }
12918|   355k|      }
12919|   355k|      return url;
12920|   355k|    }
12921|   623k|  }
12922|       |
12923|   268k|  state state = state::SCHEME_START;
12924|       |
12925|   623k|  std::string tmp_buffer;
12926|   623k|  std::string_view url_data;
12927|   623k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12927:7): [True: 509, False: 622k]
  ------------------
12928|    509|    tmp_buffer = user_input;
12929|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12930|       |    // just directly build the string from user_input.
12931|    509|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12932|    509|    url_data = tmp_buffer;
12933|   622k|  } else [[likely]] {
12934|   622k|    url_data = user_input;
12935|   622k|  }
12936|       |
12937|       |  // Leading and trailing control characters are uncommon and easy to deal with
12938|       |  // (no performance concern).
12939|   623k|  helpers::trim_c0_whitespace(url_data);
12940|       |
12941|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12942|       |    // Most of the time, we just need user_input.size().
12943|       |    // In some instances, we may need a bit more.
12944|       |    ///////////////////////////
12945|       |    // This is *very* important. This line should *not* be removed
12946|       |    // hastily. There are principled reasons why reserve is important
12947|       |    // for performance. If you have a benchmark with small inputs,
12948|       |    // it may not matter, but in other instances, it could.
12949|       |    ////
12950|       |    // This rounds up to the next power of two.
12951|       |    // We know that user_input.size() is in [0,
12952|       |    // std::numeric_limits<uint32_t>::max).
12953|       |    uint32_t reserve_capacity =
12954|       |        (0xFFFFFFFF >>
12955|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12956|       |        1;
12957|       |    url.reserve(reserve_capacity);
12958|       |  }
12959|       |
12960|       |  // Optimization opportunity. Most websites do not have fragment.
12961|   623k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12962|       |  // We add it last so that an implementation like ada::url_aggregator
12963|       |  // can append it last to its internal buffer, thus improving performance.
12964|       |
12965|       |  // Here url_data no longer has its fragment.
12966|       |  // We are going to access the data from url_data (it is immutable).
12967|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12968|       |  // The input_position variable should range from 0 to input_size.
12969|       |  // It is illegal to access url_data at input_size.
12970|   623k|  size_t input_position = 0;
12971|   623k|  const size_t input_size = url_data.size();
12972|       |  // Keep running the following state machine by switching on state.
12973|       |  // If after a run pointer points to the EOF code point, go to the next step.
12974|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12975|       |  // We never decrement input_position.
12976|  2.36M|  while (input_position <= input_size) {
  ------------------
  |  Branch (12976:10): [True: 1.80M, False: 561k]
  ------------------
12977|  1.80M|    ada_log("In parsing at ", input_position, " out of ", input_size,
12978|  1.80M|            " in state ", ada::to_string(state));
12979|  1.80M|    switch (state) {
12980|   268k|      case state::SCHEME_START: {
  ------------------
  |  Branch (12980:7): [True: 268k, False: 1.53M]
  ------------------
12981|   268k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
12982|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
12983|       |        // state to scheme state.
12984|   268k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12984:13): [True: 268k, False: 1]
  ------------------
12985|   268k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (12985:13): [True: 268k, False: 131]
  ------------------
12986|   268k|          state = state::SCHEME;
12987|   268k|          input_position++;
12988|   268k|        } else {
12989|       |          // Otherwise, if state override is not given, set state to no scheme
12990|       |          // state and decrease pointer by 1.
12991|    132|          state = state::NO_SCHEME;
12992|    132|        }
12993|   268k|        break;
12994|      0|      }
12995|   268k|      case state::SCHEME: {
  ------------------
  |  Branch (12995:7): [True: 268k, False: 1.53M]
  ------------------
12996|   268k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
12997|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
12998|       |        // append c, lowercased, to buffer.
12999|  1.16M|        while ((input_position != input_size) &&
  ------------------
  |  Branch (12999:16): [True: 1.16M, False: 4]
  ------------------
13000|  1.16M|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13000:16): [True: 896k, False: 268k]
  ------------------
13001|   896k|          input_position++;
13002|   896k|        }
13003|       |        // Otherwise, if c is U+003A (:), then:
13004|   268k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13004:13): [True: 268k, False: 4]
  ------------------
13005|   268k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13005:13): [True: 267k, False: 73]
  ------------------
13006|   267k|          ada_log("SCHEME the scheme should be ",
13007|   267k|                  url_data.substr(0, input_position));
13008|   267k|          if constexpr (result_type_is_ada_url) {
13009|   267k|            if (!url.parse_scheme(url_data.substr(0, input_position))) {
  ------------------
  |  Branch (13009:17): [True: 0, False: 267k]
  ------------------
13010|      0|              return url;
13011|      0|            }
13012|       |          } else {
13013|       |            // we pass the colon along instead of painfully adding it back.
13014|       |            if (!url.parse_scheme_with_colon(
13015|       |                    url_data.substr(0, input_position + 1))) {
13016|       |              return url;
13017|       |            }
13018|       |          }
13019|   267k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13020|       |
13021|       |          // If url's scheme is "file", then:
13022|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13023|   267k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13023:15): [True: 1.41k, False: 266k]
  ------------------
13024|       |            // Set state to file state.
13025|  1.41k|            state = state::FILE;
13026|  1.41k|          }
13027|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13028|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13029|       |          // != nullptr is false.
13030|   266k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13030:20): [True: 264k, False: 1.81k]
  |  Branch (13030:40): [True: 0, False: 264k]
  ------------------
13031|      0|                   base_url->type == url.type) {
  ------------------
  |  Branch (13031:20): [True: 0, False: 0]
  ------------------
13032|       |            // Set state to special relative or authority state.
13033|      0|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13034|      0|          }
13035|       |          // Otherwise, if url is special, set state to special authority
13036|       |          // slashes state.
13037|   266k|          else if (url.is_special()) {
  ------------------
  |  Branch (13037:20): [True: 264k, False: 1.81k]
  ------------------
13038|   264k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13039|   264k|          }
13040|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13041|       |          // path or authority state and increase pointer by 1.
13042|  1.81k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13042:20): [True: 1.68k, False: 132]
  ------------------
13043|  1.68k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13043:20): [True: 1.12k, False: 564]
  ------------------
13044|  1.12k|            state = state::PATH_OR_AUTHORITY;
13045|  1.12k|            input_position++;
13046|  1.12k|          }
13047|       |          // Otherwise, set url's path to the empty string and set state to
13048|       |          // opaque path state.
13049|    696|          else {
13050|    696|            state = state::OPAQUE_PATH;
13051|    696|          }
13052|   267k|        }
13053|       |        // Otherwise, if state override is not given, set buffer to the empty
13054|       |        // string, state to no scheme state, and start over (from the first code
13055|       |        // point in input).
13056|     77|        else {
13057|     77|          state = state::NO_SCHEME;
13058|     77|          input_position = 0;
13059|     77|          break;
13060|     77|        }
13061|   267k|        input_position++;
13062|   267k|        break;
13063|   268k|      }
13064|    209|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13064:7): [True: 209, False: 1.80M]
  ------------------
13065|    209|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13066|       |        // The fragment was pruned from url_data before the state machine ran,
13067|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
13068|       |        // nothing else remains in front of it.
13069|    209|        const bool c_is_hash =
13070|    209|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (13070:13): [True: 118, False: 91]
  |  Branch (13070:37): [True: 1, False: 117]
  ------------------
13071|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13072|       |        // validation error, return failure.
13073|    209|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (13073:13): [True: 209, False: 0]
  |  Branch (13073:37): [True: 0, False: 0]
  |  Branch (13073:66): [True: 0, False: 0]
  ------------------
13074|    209|          ada_log("NO_SCHEME validation error");
13075|    209|          url.is_valid = false;
13076|    209|          return url;
13077|    209|        }
13078|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13079|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13080|       |        // query to base's query, and set state to fragment state.
13081|      0|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (13081:18): [True: 0, False: 0]
  |  Branch (13081:47): [True: 0, False: 0]
  ------------------
13082|      0|          ada_log("NO_SCHEME opaque base with fragment");
13083|      0|          url.copy_scheme(*base_url);
13084|      0|          url.has_opaque_path = base_url->has_opaque_path;
13085|       |
13086|      0|          if constexpr (result_type_is_ada_url) {
13087|      0|            url.path = base_url->path;
13088|      0|            url.query = base_url->query;
13089|       |          } else {
13090|       |            url.update_base_pathname(base_url->get_pathname());
13091|       |            if (base_url->has_search()) {
13092|       |              // get_search() returns "" for an empty query string (URL ends
13093|       |              // with '?'). update_base_search("") would incorrectly clear the
13094|       |              // query, so pass "?" to preserve the empty query distinction.
13095|       |              auto s = base_url->get_search();
13096|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13097|       |            }
13098|       |          }
13099|      0|          url.update_unencoded_base_hash(*fragment);
13100|      0|          enforce_max_input_length();
13101|      0|          return url;
13102|      0|        }
13103|       |        // Otherwise, if base's scheme is not "file", set state to relative
13104|       |        // state and decrease pointer by 1.
13105|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13106|      0|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13106:18): [True: 0, False: 0]
  ------------------
13107|      0|          ada_log("NO_SCHEME non-file relative path");
13108|      0|          state = state::RELATIVE_SCHEME;
13109|      0|        }
13110|       |        // Otherwise, set state to file state and decrease pointer by 1.
13111|      0|        else {
13112|      0|          ada_log("NO_SCHEME file base type");
13113|      0|          state = state::FILE;
13114|      0|        }
13115|      0|        break;
13116|    209|      }
13117|   265k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13117:7): [True: 265k, False: 1.54M]
  ------------------
13118|   265k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13119|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13120|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13121|       |        // worry about AUTHORITY.
13122|       |        // TODO: Instead of just collecting a bool, collect the location of the
13123|       |        // '@' and do something useful with it.
13124|       |        // TODO: We could do various processing early on, using a single pass
13125|       |        // over the string to collect information about it, e.g., telling us
13126|       |        // whether there is a @ and if so, where (or how many).
13127|       |
13128|       |        // Check if url data contains an @.
13129|   265k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13129:13): [True: 224k, False: 40.8k]
  ------------------
13130|   224k|          state = state::HOST;
13131|   224k|          break;
13132|   224k|        }
13133|  40.8k|        bool at_sign_seen{false};
13134|  40.8k|        bool password_token_seen{false};
13135|       |        /**
13136|       |         * We expect something of the sort...
13137|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13138|       |         * --------^
13139|       |         */
13140|  93.6k|        do {
13141|  93.6k|          std::string_view view = url_data.substr(input_position);
13142|       |          // The delimiters are @, /, ? \\.
13143|  93.6k|          size_t location =
13144|  93.6k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13144:15): [True: 93.4k, False: 165]
  ------------------
13145|  93.6k|                               : helpers::find_authority_delimiter(view);
13146|  93.6k|          std::string_view authority_view = view.substr(0, location);
13147|  93.6k|          size_t end_of_authority = input_position + authority_view.size();
13148|       |          // If c is U+0040 (@), then:
13149|  93.6k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13149:15): [True: 83.7k, False: 9.95k]
  ------------------
13150|  83.7k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13150:15): [True: 52.8k, False: 30.8k]
  ------------------
13151|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13152|  52.8k|            if (at_sign_seen) {
  ------------------
  |  Branch (13152:17): [True: 12.2k, False: 40.5k]
  ------------------
13153|  12.2k|              if (password_token_seen) {
  ------------------
  |  Branch (13153:19): [True: 4.59k, False: 7.68k]
  ------------------
13154|  4.59k|                if constexpr (result_type_is_ada_url) {
13155|  4.59k|                  url.password += "%40";
13156|       |                } else {
13157|       |                  url.append_base_password("%40");
13158|       |                }
13159|  7.68k|              } else {
13160|  7.68k|                if constexpr (result_type_is_ada_url) {
13161|  7.68k|                  url.username += "%40";
13162|       |                } else {
13163|       |                  url.append_base_username("%40");
13164|       |                }
13165|  7.68k|              }
13166|  12.2k|            }
13167|       |
13168|  52.8k|            at_sign_seen = true;
13169|       |
13170|  52.8k|            if (!password_token_seen) {
  ------------------
  |  Branch (13170:17): [True: 48.2k, False: 4.59k]
  ------------------
13171|  48.2k|              size_t password_token_location = authority_view.find(':');
13172|  48.2k|              password_token_seen =
13173|  48.2k|                  password_token_location != std::string_view::npos;
13174|       |
13175|  48.2k|              if constexpr (store_values) {
13176|  48.2k|                if (!password_token_seen) {
  ------------------
  |  Branch (13176:21): [True: 18.4k, False: 29.8k]
  ------------------
13177|  18.4k|                  if constexpr (result_type_is_ada_url) {
13178|  18.4k|                    url.username += unicode::percent_encode(
13179|  18.4k|                        authority_view,
13180|  18.4k|                        character_sets::USERINFO_PERCENT_ENCODE);
13181|       |                  } else {
13182|       |                    url.append_base_username(unicode::percent_encode(
13183|       |                        authority_view,
13184|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13185|       |                  }
13186|  29.8k|                } else {
13187|  29.8k|                  if constexpr (result_type_is_ada_url) {
13188|  29.8k|                    url.username += unicode::percent_encode(
13189|  29.8k|                        authority_view.substr(0, password_token_location),
13190|  29.8k|                        character_sets::USERINFO_PERCENT_ENCODE);
13191|  29.8k|                    url.password += unicode::percent_encode(
13192|  29.8k|                        authority_view.substr(password_token_location + 1),
13193|  29.8k|                        character_sets::USERINFO_PERCENT_ENCODE);
13194|       |                  } else {
13195|       |                    url.append_base_username(unicode::percent_encode(
13196|       |                        authority_view.substr(0, password_token_location),
13197|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13198|       |                    url.append_base_password(unicode::percent_encode(
13199|       |                        authority_view.substr(password_token_location + 1),
13200|       |                        character_sets::USERINFO_PERCENT_ENCODE));
13201|       |                  }
13202|  29.8k|                }
13203|  48.2k|              }
13204|  48.2k|            } else if constexpr (store_values) {
13205|  4.59k|              if constexpr (result_type_is_ada_url) {
13206|  4.59k|                url.password += unicode::percent_encode(
13207|  4.59k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13208|       |              } else {
13209|       |                url.append_base_password(unicode::percent_encode(
13210|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13211|       |              }
13212|  4.59k|            }
13213|  52.8k|          }
13214|       |          // Otherwise, if one of the following is true:
13215|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13216|       |          // - url is special and c is U+005C (\)
13217|  40.8k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13217:20): [True: 9.95k, False: 30.8k]
  ------------------
13218|  30.8k|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13218:20): [True: 30.7k, False: 144]
  ------------------
13219|    144|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13219:20): [True: 117, False: 27]
  ------------------
13220|  40.8k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13220:21): [True: 27, False: 0]
  |  Branch (13220:41): [True: 27, False: 0]
  ------------------
13221|       |            // If atSignSeen is true and authority_view is the empty string,
13222|       |            // validation error, return failure.
13223|  40.8k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13223:17): [True: 40.5k, False: 263]
  |  Branch (13223:33): [True: 249, False: 40.3k]
  ------------------
13224|    249|              url.is_valid = false;
13225|    249|              return url;
13226|    249|            }
13227|  40.5k|            state = state::HOST;
13228|  40.5k|            break;
13229|  40.8k|          }
13230|  52.8k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13230:15): [True: 0, False: 52.8k]
  ------------------
13231|      0|            if constexpr (store_values) {
13232|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13232:19): [True: 0, False: 0]
  ------------------
13233|      0|                url.update_unencoded_base_hash(*fragment);
13234|      0|              }
13235|      0|            }
13236|      0|            enforce_max_input_length();
13237|      0|            return url;
13238|      0|          }
13239|  52.8k|          input_position = end_of_authority + 1;
13240|  52.8k|        } while (true);
  ------------------
  |  Branch (13240:18): [True: 52.8k, Folded]
  ------------------
13241|       |
13242|  40.5k|        break;
13243|  40.8k|      }
13244|  40.5k|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13244:7): [True: 0, False: 1.80M]
  ------------------
13245|      0|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13246|      0|                helpers::substring(url_data, input_position));
13247|       |
13248|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13249|       |        // then set state to special authority ignore slashes state and increase
13250|       |        // pointer by 1.
13251|      0|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13251:13): [True: 0, False: 0]
  ------------------
13252|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13253|      0|          input_position += 2;
13254|      0|        } else {
13255|       |          // Otherwise, validation error, set state to relative state and
13256|       |          // decrease pointer by 1.
13257|      0|          state = state::RELATIVE_SCHEME;
13258|      0|        }
13259|       |
13260|      0|        break;
13261|  40.8k|      }
13262|  1.12k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (13262:7): [True: 1.12k, False: 1.80M]
  ------------------
13263|  1.12k|        ada_log("PATH_OR_AUTHORITY ",
13264|  1.12k|                helpers::substring(url_data, input_position));
13265|       |
13266|       |        // If c is U+002F (/), then set state to authority state.
13267|  1.12k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13267:13): [True: 1.08k, False: 33]
  ------------------
13268|  1.08k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13268:13): [True: 770, False: 318]
  ------------------
13269|    770|          state = state::AUTHORITY;
13270|    770|          input_position++;
13271|    770|        } else {
13272|       |          // Otherwise, set state to path state, and decrease pointer by 1.
13273|    351|          state = state::PATH;
13274|    351|        }
13275|       |
13276|  1.12k|        break;
13277|  40.8k|      }
13278|      0|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (13278:7): [True: 0, False: 1.80M]
  ------------------
13279|      0|        ada_log("RELATIVE_SCHEME ",
13280|      0|                helpers::substring(url_data, input_position));
13281|       |
13282|       |        // Set url's scheme to base's scheme.
13283|      0|        url.copy_scheme(*base_url);
13284|       |
13285|       |        // If c is U+002F (/), then set state to relative slash state.
13286|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13286:13): [True: 0, False: 0]
  ------------------
13287|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
13288|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13288:13): [True: 0, False: 0]
  ------------------
13289|      0|          ada_log(
13290|      0|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13291|      0|              "slash state");
13292|      0|          state = state::RELATIVE_SLASH;
13293|      0|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13293:20): [True: 0, False: 0]
  |  Branch (13293:40): [True: 0, False: 0]
  ------------------
13294|      0|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13294:20): [True: 0, False: 0]
  ------------------
13295|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13296|       |          // set state to relative slash state.
13297|      0|          ada_log(
13298|      0|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13299|      0|              "error, set state to relative slash state");
13300|      0|          state = state::RELATIVE_SLASH;
13301|      0|        } else {
13302|      0|          ada_log("RELATIVE_SCHEME otherwise");
13303|       |          // Set url's username to base's username, url's password to base's
13304|       |          // password, url's host to base's host, url's port to base's port,
13305|       |          // url's path to a clone of base's path, and url's query to base's
13306|       |          // query.
13307|      0|          if constexpr (result_type_is_ada_url) {
13308|      0|            url.username = base_url->username;
13309|      0|            url.password = base_url->password;
13310|      0|            url.host = base_url->host;
13311|      0|            url.port = base_url->port;
13312|       |            // cloning the base path includes cloning the has_opaque_path flag
13313|      0|            url.has_opaque_path = base_url->has_opaque_path;
13314|      0|            url.path = base_url->path;
13315|      0|            url.query = base_url->query;
13316|       |          } else {
13317|       |            url.update_base_authority(base_url->get_href(),
13318|       |                                      base_url->get_components());
13319|       |            url.update_host_to_base_host(base_url->get_hostname());
13320|       |            url.update_base_port(base_url->retrieve_base_port());
13321|       |            // cloning the base path includes cloning the has_opaque_path flag
13322|       |            url.has_opaque_path = base_url->has_opaque_path;
13323|       |            url.update_base_pathname(base_url->get_pathname());
13324|       |            if (base_url->has_search()) {
13325|       |              // get_search() returns "" for an empty query string (URL ends
13326|       |              // with '?'). update_base_search("") would incorrectly clear the
13327|       |              // query, so pass "?" to preserve the empty query distinction.
13328|       |              auto s = base_url->get_search();
13329|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13330|       |            }
13331|       |          }
13332|       |
13333|      0|          url.has_opaque_path = base_url->has_opaque_path;
13334|       |
13335|       |          // If c is U+003F (?), then set url's query to the empty string, and
13336|       |          // state to query state.
13337|      0|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13337:15): [True: 0, False: 0]
  ------------------
13338|      0|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13338:15): [True: 0, False: 0]
  ------------------
13339|      0|            state = state::QUERY;
13340|      0|          }
13341|       |          // Otherwise, if c is not the EOF code point:
13342|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (13342:20): [True: 0, False: 0]
  ------------------
13343|       |            // Set url's query to null.
13344|      0|            url.clear_search();
13345|      0|            if constexpr (result_type_is_ada_url) {
13346|       |              // Shorten url's path.
13347|      0|              helpers::shorten_path(url.path, url.type);
13348|       |            } else {
13349|       |              std::string_view path = url.get_pathname();
13350|       |              if (helpers::shorten_path(path, url.type)) {
13351|       |                url.update_base_pathname(std::move(std::string(path)));
13352|       |              }
13353|       |            }
13354|       |            // Set state to path state and decrease pointer by 1.
13355|      0|            state = state::PATH;
13356|      0|            break;
13357|      0|          }
13358|      0|        }
13359|      0|        input_position++;
13360|      0|        break;
13361|      0|      }
13362|      0|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13362:7): [True: 0, False: 1.80M]
  ------------------
13363|      0|        ada_log("RELATIVE_SLASH ",
13364|      0|                helpers::substring(url_data, input_position));
13365|       |
13366|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13367|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13368|      0|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13368:13): [True: 0, False: 0]
  |  Branch (13368:33): [True: 0, False: 0]
  ------------------
13369|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13369:14): [True: 0, False: 0]
  ------------------
13370|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13370:14): [True: 0, False: 0]
  ------------------
13371|       |          // Set state to special authority ignore slashes state.
13372|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13373|      0|        }
13374|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13375|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13375:18): [True: 0, False: 0]
  ------------------
13376|      0|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13376:18): [True: 0, False: 0]
  ------------------
13377|      0|          state = state::AUTHORITY;
13378|      0|        }
13379|       |        // Otherwise, set
13380|       |        // - url's username to base's username,
13381|       |        // - url's password to base's password,
13382|       |        // - url's host to base's host,
13383|       |        // - url's port to base's port,
13384|       |        // - state to path state, and then, decrease pointer by 1.
13385|      0|        else {
13386|      0|          if constexpr (result_type_is_ada_url) {
13387|      0|            url.username = base_url->username;
13388|      0|            url.password = base_url->password;
13389|      0|            url.host = base_url->host;
13390|      0|            url.port = base_url->port;
13391|       |          } else {
13392|       |            url.update_base_authority(base_url->get_href(),
13393|       |                                      base_url->get_components());
13394|       |            url.update_host_to_base_host(base_url->get_hostname());
13395|       |            url.update_base_port(base_url->retrieve_base_port());
13396|       |          }
13397|      0|          state = state::PATH;
13398|      0|          break;
13399|      0|        }
13400|       |
13401|      0|        input_position++;
13402|      0|        break;
13403|      0|      }
13404|   264k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13404:7): [True: 264k, False: 1.54M]
  ------------------
13405|   264k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13406|   264k|                helpers::substring(url_data, input_position));
13407|       |
13408|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13409|       |        // then set state to special authority ignore slashes state and increase
13410|       |        // pointer by 1.
13411|   264k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13411:13): [True: 263k, False: 1.68k]
  ------------------
13412|   263k|          input_position += 2;
13413|   263k|        }
13414|       |
13415|   264k|        [[fallthrough]];
13416|   264k|      }
13417|   264k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13417:7): [True: 0, False: 1.80M]
  ------------------
13418|   264k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13419|   264k|                helpers::substring(url_data, input_position));
13420|       |
13421|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13422|       |        // authority state and decrease pointer by 1.
13423|   266k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13423:16): [True: 266k, False: 73]
  ------------------
13424|   266k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13424:17): [True: 638, False: 265k]
  ------------------
13425|   265k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13425:17): [True: 1.25k, False: 264k]
  ------------------
13426|  1.88k|          input_position++;
13427|  1.88k|        }
13428|   264k|        state = state::AUTHORITY;
13429|       |
13430|   264k|        break;
13431|   264k|      }
13432|  9.80k|      case state::QUERY: {
  ------------------
  |  Branch (13432:7): [True: 9.80k, False: 1.79M]
  ------------------
13433|  9.80k|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13434|  9.80k|        if constexpr (store_values) {
13435|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13436|       |          // if url is special; otherwise the query percent-encode set.
13437|  9.80k|          const uint8_t* query_percent_encode_set =
13438|  9.80k|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13438:15): [True: 8.63k, False: 1.16k]
  ------------------
13439|  9.80k|                               : character_sets::QUERY_PERCENT_ENCODE;
13440|       |
13441|       |          // Percent-encode after encoding, with encoding, buffer, and
13442|       |          // queryPercentEncodeSet, and append the result to url's query.
13443|  9.80k|          url.update_base_search(url_data.substr(input_position),
13444|  9.80k|                                 query_percent_encode_set);
13445|  9.80k|          ada_log("QUERY update_base_search completed ");
13446|  9.80k|          if (fragment.has_value()) {
  ------------------
  |  Branch (13446:15): [True: 7.37k, False: 2.42k]
  ------------------
13447|  7.37k|            url.update_unencoded_base_hash(*fragment);
13448|  7.37k|          }
13449|  9.80k|        }
13450|  9.80k|        enforce_max_input_length();
13451|  9.80k|        return url;
13452|   264k|      }
13453|   265k|      case state::HOST: {
  ------------------
  |  Branch (13453:7): [True: 265k, False: 1.54M]
  ------------------
13454|   265k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13455|       |
13456|   265k|        std::string_view host_view = url_data.substr(input_position);
13457|   265k|        auto [location, found_colon] =
13458|   265k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13459|   265k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13459:26): [True: 265k, False: 0]
  ------------------
13460|   265k|                             ? input_position + location
13461|   265k|                             : input_size;
13462|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13463|       |        // Note: the 'found_colon' value is true if and only if a colon was
13464|       |        // encountered while not inside brackets.
13465|   265k|        if (found_colon) {
  ------------------
  |  Branch (13465:13): [True: 2.37k, False: 262k]
  ------------------
13466|       |          // If buffer is the empty string, validation error, return failure.
13467|       |          // Let host be the result of host parsing buffer with url is not
13468|       |          // special.
13469|  2.37k|          ada_log("HOST parsing ", host_view);
13470|  2.37k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13470:15): [True: 169, False: 2.20k]
  ------------------
13471|    169|            return url;
13472|    169|          }
13473|  2.20k|          ada_log("HOST parsing results in ", url.get_hostname());
13474|       |          // Set url's host to host, buffer to the empty string, and state to
13475|       |          // port state.
13476|  2.20k|          state = state::PORT;
13477|  2.20k|          input_position++;
13478|  2.20k|        }
13479|       |        // Otherwise, if one of the following is true:
13480|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13481|       |        // - url is special and c is U+005C (\)
13482|       |        // The get_host_delimiter_location function either brings us to
13483|       |        // the colon outside of the bracket, or to one of those characters.
13484|   262k|        else {
13485|       |          // If url is special and host_view is the empty string, validation
13486|       |          // error, return failure.
13487|   262k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13487:15): [True: 184, False: 262k]
  |  Branch (13487:36): [True: 100, False: 84]
  ------------------
13488|    100|            url.is_valid = false;
13489|    100|            return url;
13490|    100|          }
13491|   262k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13492|       |          // Let host be the result of host parsing host_view with url is not
13493|       |          // special.
13494|   262k|          if (host_view.empty()) {
  ------------------
  |  Branch (13494:15): [True: 84, False: 262k]
  ------------------
13495|     84|            url.update_base_hostname("");
13496|   262k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13496:22): [True: 20.7k, False: 242k]
  ------------------
13497|  20.7k|            return url;
13498|  20.7k|          }
13499|   242k|          ada_log("HOST parsing results in ", url.get_hostname(),
13500|   242k|                  " href=", url.get_href());
13501|       |
13502|       |          // Set url's host to host, and state to path start state.
13503|   242k|          state = state::PATH_START;
13504|   242k|        }
13505|       |
13506|   244k|        break;
13507|   265k|      }
13508|   244k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13508:7): [True: 696, False: 1.80M]
  ------------------
13509|    696|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13510|       |        // Opaque path, query, and fragment are structurally always valid:
13511|       |        // the parser would just percent-encode whatever is there. When we
13512|       |        // are not storing values (can_parse), we can return immediately.
13513|       |        // We must set has_opaque_path = true before returning so that when
13514|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13515|       |        // correctly rejects relative inputs against an opaque-path base
13516|       |        // (e.g. can_parse("", &"W:") must return false).
13517|       |        if constexpr (!store_values) {
13518|       |          url.has_opaque_path = true;
13519|       |          return url;
13520|       |        }
13521|    696|        std::string_view view = url_data.substr(input_position);
13522|       |        // If c is U+003F (?), then set url's query to the empty string and
13523|       |        // state to query state.
13524|    696|        size_t location = view.find('?');
13525|    696|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13525:13): [True: 438, False: 258]
  ------------------
13526|    438|          view.remove_suffix(view.size() - location);
13527|    438|          state = state::QUERY;
13528|    438|          input_position += location + 1;
13529|    438|        } else {
13530|    258|          input_position = input_size + 1;
13531|    258|        }
13532|    696|        url.has_opaque_path = true;
13533|       |
13534|       |        // This is a really unlikely scenario in real world. We should not seek
13535|       |        // to optimize it.
13536|    696|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13536:13): [True: 10, False: 686]
  ------------------
13537|     10|          std::string modified_view =
13538|     10|              std::string(view.substr(0, view.size() - 1)) + "%20";
13539|     10|          url.update_base_pathname(unicode::percent_encode(
13540|     10|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13541|    686|        } else {
13542|    686|          url.update_base_pathname(unicode::percent_encode(
13543|    686|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13544|    686|        }
13545|    696|        break;
13546|   265k|      }
13547|  2.20k|      case state::PORT: {
  ------------------
  |  Branch (13547:7): [True: 2.20k, False: 1.80M]
  ------------------
13548|  2.20k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13549|  2.20k|        std::string_view port_view = url_data.substr(input_position);
13550|  2.20k|        input_position += url.parse_port(port_view, true);
13551|  2.20k|        if (!url.is_valid) {
  ------------------
  |  Branch (13551:13): [True: 223, False: 1.98k]
  ------------------
13552|    223|          return url;
13553|    223|        }
13554|  1.98k|        state = state::PATH_START;
13555|  1.98k|        [[fallthrough]];
13556|  1.98k|      }
13557|   245k|      case state::PATH_START: {
  ------------------
  |  Branch (13557:7): [True: 243k, False: 1.56M]
  ------------------
13558|   245k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13559|       |        // Path, query, and fragment are structurally always valid: the
13560|       |        // parser would just percent-encode whatever is there. When we are
13561|       |        // not storing values (can_parse), we can return immediately since
13562|       |        // no subsequent state can invalidate the URL.
13563|       |        if constexpr (!store_values) {
13564|       |          return url;
13565|       |        }
13566|       |
13567|       |        // If url is special, then:
13568|   245k|        if (url.is_special()) {
  ------------------
  |  Branch (13568:13): [True: 244k, False: 705]
  ------------------
13569|       |          // Set state to path state.
13570|   244k|          state = state::PATH;
13571|       |
13572|       |          // Optimization: Avoiding going into PATH state improves the
13573|       |          // performance of urls ending with /.
13574|   244k|          if (input_position == input_size) {
  ------------------
  |  Branch (13574:15): [True: 30.5k, False: 214k]
  ------------------
13575|  30.5k|            if constexpr (store_values) {
13576|  30.5k|              url.update_base_pathname("/");
13577|  30.5k|              if (fragment.has_value()) {
  ------------------
  |  Branch (13577:19): [True: 270, False: 30.2k]
  ------------------
13578|    270|                url.update_unencoded_base_hash(*fragment);
13579|    270|              }
13580|  30.5k|            }
13581|  30.5k|            enforce_max_input_length();
13582|  30.5k|            return url;
13583|  30.5k|          }
13584|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13585|       |          // by 1. We know that (input_position == input_size) is impossible
13586|       |          // here, because of the previous if-check.
13587|   214k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13587:15): [True: 1.02k, False: 212k]
  ------------------
13588|  1.02k|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13588:15): [True: 635, False: 385]
  ------------------
13589|    635|            break;
13590|    635|          }
13591|   214k|        }
13592|       |        // Otherwise, if state override is not given and c is U+003F (?),
13593|       |        // set url's query to the empty string and state to query state.
13594|    705|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13594:18): [True: 576, False: 129]
  ------------------
13595|    576|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13595:18): [True: 117, False: 459]
  ------------------
13596|    117|          state = state::QUERY;
13597|    117|        }
13598|       |        // Otherwise, if c is not the EOF code point:
13599|    588|        else if (input_position != input_size) {
  ------------------
  |  Branch (13599:18): [True: 459, False: 129]
  ------------------
13600|       |          // Set state to path state.
13601|    459|          state = state::PATH;
13602|       |
13603|       |          // If c is not U+002F (/), then decrease pointer by 1.
13604|    459|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13604:15): [True: 0, False: 459]
  ------------------
13605|      0|            break;
13606|      0|          }
13607|    459|        }
13608|       |
13609|   214k|        input_position++;
13610|   214k|        break;
13611|   245k|      }
13612|   214k|      case state::PATH: {
  ------------------
  |  Branch (13612:7): [True: 214k, False: 1.59M]
  ------------------
13613|   214k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13614|       |        // Path, query, and fragment are structurally always valid: the
13615|       |        // parser would just percent-encode whatever is there. When we are
13616|       |        // not storing values (can_parse), we can return immediately since
13617|       |        // no subsequent state can invalidate the URL.
13618|       |        if constexpr (!store_values) {
13619|       |          return url;
13620|       |        }
13621|   214k|        std::string_view view = url_data.substr(input_position);
13622|       |
13623|       |        // Most time, we do not need percent encoding.
13624|       |        // Furthermore, we can immediately locate the '?'.
13625|   214k|        size_t locofquestionmark = view.find('?');
13626|   214k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13626:13): [True: 9.24k, False: 205k]
  ------------------
13627|  9.24k|          state = state::QUERY;
13628|  9.24k|          view.remove_suffix(view.size() - locofquestionmark);
13629|  9.24k|          input_position += locofquestionmark + 1;
13630|   205k|        } else {
13631|   205k|          input_position = input_size + 1;
13632|   205k|        }
13633|   214k|        if constexpr (store_values) {
13634|   214k|          if constexpr (result_type_is_ada_url) {
13635|   214k|            helpers::parse_prepared_path(view, url.type, url.path);
13636|       |          } else {
13637|       |            url.consume_prepared_path(view);
13638|       |            ADA_ASSERT_TRUE(url.validate());
13639|       |          }
13640|   214k|        }
13641|   214k|        break;
13642|   245k|      }
13643|  1.30k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13643:7): [True: 1.30k, False: 1.80M]
  ------------------
13644|  1.30k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13645|       |
13646|       |        // If c is U+002F (/) or U+005C (\), then:
13647|  1.30k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13647:13): [True: 1.29k, False: 1]
  ------------------
13648|  1.29k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13648:14): [True: 1.28k, False: 12]
  ------------------
13649|  1.29k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13649:14): [True: 3, False: 9]
  ------------------
13650|  1.29k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13651|       |          // Set state to file host state.
13652|  1.29k|          state = state::FILE_HOST;
13653|  1.29k|          input_position++;
13654|  1.29k|        } else {
13655|     10|          ada_log("FILE_SLASH otherwise");
13656|       |          // If base is non-null and base's scheme is "file", then:
13657|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13658|       |          // base_url_has_value() is true.
13659|     10|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13659:15): [True: 0, False: 10]
  |  Branch (13659:38): [True: 0, False: 0]
  ------------------
13660|       |            // Set url's host to base's host.
13661|      0|            if constexpr (result_type_is_ada_url) {
13662|      0|              url.host = base_url->host;
13663|       |            } else {
13664|       |              url.update_host_to_base_host(base_url->get_host());
13665|       |            }
13666|       |            // If the code point substring from pointer to the end of input does
13667|       |            // not start with a Windows drive letter and base's path[0] is a
13668|       |            // normalized Windows drive letter, then append base's path[0] to
13669|       |            // url's path.
13670|      0|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13670:17): [True: 0, False: 0]
  ------------------
13671|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13671:19): [True: 0, False: 0]
  ------------------
13672|      0|                      url_data.substr(input_position))) {
13673|      0|                std::string_view first_base_url_path =
13674|      0|                    base_url->get_pathname().substr(1);
13675|      0|                size_t loc = first_base_url_path.find('/');
13676|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13676:21): [True: 0, False: 0]
  ------------------
13677|      0|                  helpers::resize(first_base_url_path, loc);
13678|      0|                }
13679|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13679:21): [True: 0, False: 0]
  ------------------
13680|      0|                        first_base_url_path)) {
13681|      0|                  if constexpr (result_type_is_ada_url) {
13682|      0|                    url.path += '/';
13683|      0|                    url.path += first_base_url_path;
13684|       |                  } else {
13685|       |                    url.append_base_pathname(
13686|       |                        helpers::concat("/", first_base_url_path));
13687|       |                  }
13688|      0|                }
13689|      0|              }
13690|      0|            }
13691|      0|          }
13692|       |
13693|       |          // Set state to path state, and decrease pointer by 1.
13694|     10|          state = state::PATH;
13695|     10|        }
13696|       |
13697|  1.30k|        break;
13698|   245k|      }
13699|  1.29k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13699:7): [True: 1.29k, False: 1.80M]
  ------------------
13700|  1.29k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13701|  1.29k|        std::string_view view = url_data.substr(input_position);
13702|       |
13703|  1.29k|        size_t location = view.find_first_of("/\\?");
13704|  1.29k|        std::string_view file_host_buffer = view.substr(
13705|  1.29k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13705:16): [True: 1.26k, False: 29]
  ------------------
13706|       |
13707|  1.29k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13707:13): [True: 1, False: 1.28k]
  ------------------
13708|      1|          state = state::PATH;
13709|  1.28k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13709:20): [True: 261, False: 1.02k]
  ------------------
13710|       |          // Set url's host to the empty string.
13711|    261|          if constexpr (result_type_is_ada_url) {
13712|    261|            url.host = "";
13713|       |          } else {
13714|       |            url.update_base_hostname("");
13715|       |          }
13716|       |          // Set state to path start state.
13717|    261|          state = state::PATH_START;
13718|  1.02k|        } else {
13719|  1.02k|          size_t consumed_bytes = file_host_buffer.size();
13720|  1.02k|          input_position += consumed_bytes;
13721|       |          // Let host be the result of host parsing buffer with url is not
13722|       |          // special.
13723|  1.02k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13723:15): [True: 95, False: 933]
  ------------------
13724|     95|            return url;
13725|     95|          }
13726|       |
13727|    933|          if constexpr (result_type_is_ada_url) {
13728|       |            // If host is "localhost", then set host to the empty string.
13729|    933|            if (url.host.has_value() && url.host.value() == "localhost") {
  ------------------
  |  Branch (13729:17): [True: 933, False: 0]
  |  Branch (13729:41): [True: 3, False: 930]
  ------------------
13730|      3|              url.host = "";
13731|      3|            }
13732|       |          } else {
13733|       |            if (url.get_hostname() == "localhost") {
13734|       |              url.update_base_hostname("");
13735|       |            }
13736|       |          }
13737|       |
13738|       |          // Set buffer to the empty string and state to path start state.
13739|    933|          state = state::PATH_START;
13740|    933|        }
13741|       |
13742|  1.19k|        break;
13743|  1.29k|      }
13744|  1.41k|      case state::FILE: {
  ------------------
  |  Branch (13744:7): [True: 1.41k, False: 1.80M]
  ------------------
13745|  1.41k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13746|  1.41k|        std::string_view file_view = url_data.substr(input_position);
13747|       |
13748|  1.41k|        url.set_protocol_as_file();
13749|  1.41k|        if constexpr (result_type_is_ada_url) {
13750|       |          // Set url's host to the empty string.
13751|  1.41k|          url.host = "";
13752|       |        } else {
13753|       |          url.update_base_hostname("");
13754|       |        }
13755|       |        // If c is U+002F (/) or U+005C (\), then:
13756|  1.41k|        if (input_position != input_size &&
  ------------------
  |  Branch (13756:13): [True: 1.41k, False: 1]
  ------------------
13757|  1.41k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13757:14): [True: 1.29k, False: 113]
  ------------------
13758|  1.30k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13758:14): [True: 2, False: 111]
  ------------------
13759|  1.30k|          ada_log("FILE c is U+002F or U+005C");
13760|       |          // Set state to file slash state.
13761|  1.30k|          state = state::FILE_SLASH;
13762|  1.30k|        }
13763|       |        // Otherwise, if base is non-null and base's scheme is "file":
13764|    112|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13764:18): [True: 0, False: 112]
  |  Branch (13764:41): [True: 0, False: 0]
  ------------------
13765|       |          // Set url's host to base's host, url's path to a clone of base's
13766|       |          // path, and url's query to base's query.
13767|      0|          ada_log("FILE base non-null");
13768|      0|          if constexpr (result_type_is_ada_url) {
13769|      0|            url.host = base_url->host;
13770|      0|            url.path = base_url->path;
13771|      0|            url.query = base_url->query;
13772|       |          } else {
13773|       |            url.update_host_to_base_host(base_url->get_hostname());
13774|       |            url.update_base_pathname(base_url->get_pathname());
13775|       |            if (base_url->has_search()) {
13776|       |              // get_search() returns "" for an empty query string (URL ends
13777|       |              // with '?'). update_base_search("") would incorrectly clear the
13778|       |              // query, so pass "?" to preserve the empty query distinction.
13779|       |              auto s = base_url->get_search();
13780|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13781|       |            }
13782|       |          }
13783|      0|          url.has_opaque_path = base_url->has_opaque_path;
13784|       |
13785|       |          // If c is U+003F (?), then set url's query to the empty string and
13786|       |          // state to query state.
13787|      0|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13787:15): [True: 0, False: 0]
  |  Branch (13787:47): [True: 0, False: 0]
  ------------------
13788|      0|            state = state::QUERY;
13789|      0|          }
13790|       |          // Otherwise, if c is not the EOF code point:
13791|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (13791:20): [True: 0, False: 0]
  ------------------
13792|       |            // Set url's query to null.
13793|      0|            url.clear_search();
13794|       |            // If the code point substring from pointer to the end of input does
13795|       |            // not start with a Windows drive letter, then shorten url's path.
13796|      0|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13796:17): [True: 0, False: 0]
  ------------------
13797|      0|              if constexpr (result_type_is_ada_url) {
13798|      0|                helpers::shorten_path(url.path, url.type);
13799|       |              } else {
13800|       |                std::string_view path = url.get_pathname();
13801|       |                if (helpers::shorten_path(path, url.type)) {
13802|       |                  url.update_base_pathname(std::move(std::string(path)));
13803|       |                }
13804|       |              }
13805|      0|            }
13806|       |            // Otherwise:
13807|      0|            else {
13808|       |              // Set url's path to an empty list.
13809|      0|              url.clear_pathname();
13810|      0|              url.has_opaque_path = true;
13811|      0|            }
13812|       |
13813|       |            // Set state to path state and decrease pointer by 1.
13814|      0|            state = state::PATH;
13815|      0|            break;
13816|      0|          }
13817|      0|        }
13818|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13819|    112|        else {
13820|    112|          ada_log("FILE go to path");
13821|    112|          state = state::PATH;
13822|    112|          break;
13823|    112|        }
13824|       |
13825|  1.30k|        input_position++;
13826|  1.30k|        break;
13827|  1.41k|      }
13828|      0|      default:
  ------------------
  |  Branch (13828:7): [True: 0, False: 1.80M]
  ------------------
13829|      0|        unreachable();
13830|  1.80M|    }
13831|  1.80M|  }
13832|   561k|  if constexpr (store_values) {
13833|   561k|    if (fragment.has_value()) {
  ------------------
  |  Branch (13833:9): [True: 2.25k, False: 558k]
  ------------------
13834|  2.25k|      url.update_unencoded_base_hash(*fragment);
13835|  2.25k|    }
13836|   561k|  }
13837|   561k|  enforce_max_input_length();
13838|   561k|  return url;
13839|   623k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12819|   623k|                           const result_type* base_url) {
12820|       |  // We can specialize the implementation per type.
12821|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12822|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12823|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12824|       |  // and ada::url **do not have to support the exact same API**.
12825|   623k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12826|   623k|  constexpr bool result_type_is_ada_url_aggregator =
12827|   623k|      std::is_same_v<url_aggregator, result_type>;
12828|   623k|  static_assert(result_type_is_ada_url ||
12829|   623k|                result_type_is_ada_url_aggregator);  // We don't support
12830|       |                                                     // anything else for now.
12831|       |
12832|   623k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12833|   623k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12834|   623k|          ")");
12835|       |
12836|   623k|  result_type url{};
12837|       |
12838|   623k|  const uint32_t max_input_length = ada::get_max_input_length();
12839|       |
12840|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12841|       |  // max_input_length even when the input fit under it. The check at the end of
12842|       |  // the state machine covers URLs that run to completion, but several states
12843|       |  // return early after appending a query or fragment, so run the same check on
12844|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12845|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12846|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12847|   623k|  const auto enforce_max_input_length = [&]() {
12848|   623k|    if constexpr (store_values) {
12849|   623k|      if (url.is_valid) {
12850|   623k|        if constexpr (result_type_is_ada_url_aggregator) {
12851|   623k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12852|   623k|            url.is_valid = false;
12853|   623k|          }
12854|   623k|        } else {
12855|   623k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12856|   623k|            url.is_valid = false;
12857|   623k|          }
12858|   623k|        }
12859|   623k|      }
12860|   623k|    }
12861|   623k|  };
12862|       |
12863|       |  // We refuse to parse URL strings that exceed the maximum input length.
12864|       |  // By default, this is 4GB but can be configured via
12865|       |  // ada::set_max_input_length().
12866|   623k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12866:7): [True: 0, False: 623k]
  ------------------
12867|      0|    url.is_valid = false;
12868|      0|  }
12869|       |  // Going forward, user_input.size() is in [0,
12870|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12871|       |  // base, or the optional_url was invalid, we must return.
12872|   623k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12872:7): [True: 0, False: 623k]
  ------------------
12873|      0|    url.is_valid &= base_url->is_valid;
12874|      0|  }
12875|   623k|  if (!url.is_valid) {
  ------------------
  |  Branch (12875:7): [True: 0, False: 623k]
  ------------------
12876|      0|    return url;
12877|      0|  }
12878|       |
12879|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12880|   623k|  if constexpr (store_values) {
12881|   623k|    bool hit_fast_path = false;
12882|   623k|    if (base_url == nullptr) {
  ------------------
  |  Branch (12882:9): [True: 623k, False: 0]
  ------------------
12883|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12884|       |      // call so those URLs (including SetHref) do not pay for a miss.
12885|   623k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12886|   623k|      const size_t n = user_input.size();
12887|   623k|      size_t host_start = 0;
12888|   623k|      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
  ------------------
  |  Branch (12888:11): [True: 622k, False: 221]
  |  Branch (12888:21): [True: 238k, False: 384k]
  |  Branch (12888:36): [True: 237k, False: 889]
  |  Branch (12888:51): [True: 237k, False: 176]
  ------------------
12889|   237k|        host_start = 7;
12890|   385k|      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
  ------------------
  |  Branch (12890:18): [True: 385k, False: 242]
  |  Branch (12890:28): [True: 382k, False: 2.66k]
  |  Branch (12890:43): [True: 382k, False: 767]
  |  Branch (12890:58): [True: 382k, False: 102]
  ------------------
12891|   382k|        host_start = 8;
12892|   382k|      }
12893|   623k|      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
  ------------------
  |  Branch (12893:34): [True: 619k, False: 3.77k]
  ------------------
12894|   623k|      const bool skip_ip =
12895|   623k|          host_first == '[' || (host_first >= '0' && host_first <= '9');
  ------------------
  |  Branch (12895:11): [True: 528, False: 622k]
  |  Branch (12895:33): [True: 616k, False: 6.56k]
  |  Branch (12895:54): [True: 138k, False: 477k]
  ------------------
12896|   623k|      const bool skip_userinfo =
12897|   623k|          host_start != 0 && authority_has_at(p, host_start, n);
  ------------------
  |  Branch (12897:11): [True: 619k, False: 3.77k]
  |  Branch (12897:30): [True: 39.6k, False: 579k]
  ------------------
12898|   623k|      hit_fast_path = !skip_ip && !skip_userinfo &&
  ------------------
  |  Branch (12898:23): [True: 483k, False: 139k]
  |  Branch (12898:35): [True: 444k, False: 39.5k]
  ------------------
12899|   444k|                      try_parse_simple_absolute(user_input, url);
  ------------------
  |  Branch (12899:23): [True: 355k, False: 89.1k]
  ------------------
12900|   623k|    } else {
12901|      0|      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12902|      0|    }
12903|   623k|    if (hit_fast_path) {
  ------------------
  |  Branch (12903:9): [True: 355k, False: 268k]
  ------------------
12904|   355k|      if constexpr (result_type_is_ada_url_aggregator) {
12905|   355k|        if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12905:13): [True: 0, False: 355k]
  ------------------
12906|      0|          url.is_valid = false;
12907|      0|        }
12908|       |      } else {
12909|       |        // For a small absolute input, even worst-case normalization cannot
12910|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12911|       |        // most 4.5x. Avoid traversing every stored component on the common
12912|       |        // default-limit path.
12913|       |        if ((base_url != nullptr ||
12914|       |             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
12915|       |            url.get_href_size() > max_input_length) [[unlikely]] {
12916|       |          url.is_valid = false;
12917|       |        }
12918|       |      }
12919|   355k|      return url;
12920|   355k|    }
12921|   623k|  }
12922|       |
12923|   268k|  state state = state::SCHEME_START;
12924|       |
12925|   623k|  std::string tmp_buffer;
12926|   623k|  std::string_view url_data;
12927|   623k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12927:7): [True: 509, False: 622k]
  ------------------
12928|    509|    tmp_buffer = user_input;
12929|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12930|       |    // just directly build the string from user_input.
12931|    509|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12932|    509|    url_data = tmp_buffer;
12933|   622k|  } else [[likely]] {
12934|   622k|    url_data = user_input;
12935|   622k|  }
12936|       |
12937|       |  // Leading and trailing control characters are uncommon and easy to deal with
12938|       |  // (no performance concern).
12939|   623k|  helpers::trim_c0_whitespace(url_data);
12940|       |
12941|   623k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12942|       |    // Most of the time, we just need user_input.size().
12943|       |    // In some instances, we may need a bit more.
12944|       |    ///////////////////////////
12945|       |    // This is *very* important. This line should *not* be removed
12946|       |    // hastily. There are principled reasons why reserve is important
12947|       |    // for performance. If you have a benchmark with small inputs,
12948|       |    // it may not matter, but in other instances, it could.
12949|       |    ////
12950|       |    // This rounds up to the next power of two.
12951|       |    // We know that user_input.size() is in [0,
12952|       |    // std::numeric_limits<uint32_t>::max).
12953|   623k|    uint32_t reserve_capacity =
12954|   623k|        (0xFFFFFFFF >>
12955|   623k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12956|   623k|        1;
12957|   623k|    url.reserve(reserve_capacity);
12958|   623k|  }
12959|       |
12960|       |  // Optimization opportunity. Most websites do not have fragment.
12961|   623k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12962|       |  // We add it last so that an implementation like ada::url_aggregator
12963|       |  // can append it last to its internal buffer, thus improving performance.
12964|       |
12965|       |  // Here url_data no longer has its fragment.
12966|       |  // We are going to access the data from url_data (it is immutable).
12967|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12968|       |  // The input_position variable should range from 0 to input_size.
12969|       |  // It is illegal to access url_data at input_size.
12970|   623k|  size_t input_position = 0;
12971|   623k|  const size_t input_size = url_data.size();
12972|       |  // Keep running the following state machine by switching on state.
12973|       |  // If after a run pointer points to the EOF code point, go to the next step.
12974|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12975|       |  // We never decrement input_position.
12976|  2.36M|  while (input_position <= input_size) {
  ------------------
  |  Branch (12976:10): [True: 1.80M, False: 561k]
  ------------------
12977|  1.80M|    ada_log("In parsing at ", input_position, " out of ", input_size,
12978|  1.80M|            " in state ", ada::to_string(state));
12979|  1.80M|    switch (state) {
12980|   268k|      case state::SCHEME_START: {
  ------------------
  |  Branch (12980:7): [True: 268k, False: 1.53M]
  ------------------
12981|   268k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
12982|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
12983|       |        // state to scheme state.
12984|   268k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12984:13): [True: 268k, False: 1]
  ------------------
12985|   268k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (12985:13): [True: 268k, False: 131]
  ------------------
12986|   268k|          state = state::SCHEME;
12987|   268k|          input_position++;
12988|   268k|        } else {
12989|       |          // Otherwise, if state override is not given, set state to no scheme
12990|       |          // state and decrease pointer by 1.
12991|    132|          state = state::NO_SCHEME;
12992|    132|        }
12993|   268k|        break;
12994|      0|      }
12995|   268k|      case state::SCHEME: {
  ------------------
  |  Branch (12995:7): [True: 268k, False: 1.53M]
  ------------------
12996|   268k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
12997|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
12998|       |        // append c, lowercased, to buffer.
12999|  1.16M|        while ((input_position != input_size) &&
  ------------------
  |  Branch (12999:16): [True: 1.16M, False: 4]
  ------------------
13000|  1.16M|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (13000:16): [True: 896k, False: 268k]
  ------------------
13001|   896k|          input_position++;
13002|   896k|        }
13003|       |        // Otherwise, if c is U+003A (:), then:
13004|   268k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13004:13): [True: 268k, False: 4]
  ------------------
13005|   268k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (13005:13): [True: 267k, False: 73]
  ------------------
13006|   267k|          ada_log("SCHEME the scheme should be ",
13007|   267k|                  url_data.substr(0, input_position));
13008|       |          if constexpr (result_type_is_ada_url) {
13009|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
13010|       |              return url;
13011|       |            }
13012|   267k|          } else {
13013|       |            // we pass the colon along instead of painfully adding it back.
13014|   267k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (13014:17): [True: 0, False: 267k]
  ------------------
13015|   267k|                    url_data.substr(0, input_position + 1))) {
13016|      0|              return url;
13017|      0|            }
13018|   267k|          }
13019|   267k|          ada_log("SCHEME the scheme is ", url.get_protocol());
13020|       |
13021|       |          // If url's scheme is "file", then:
13022|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
13023|   267k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (13023:15): [True: 1.41k, False: 266k]
  ------------------
13024|       |            // Set state to file state.
13025|  1.41k|            state = state::FILE;
13026|  1.41k|          }
13027|       |          // Otherwise, if url is special, base is non-null, and base's scheme
13028|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
13029|       |          // != nullptr is false.
13030|   266k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (13030:20): [True: 264k, False: 1.81k]
  |  Branch (13030:40): [True: 0, False: 264k]
  ------------------
13031|      0|                   base_url->type == url.type) {
  ------------------
  |  Branch (13031:20): [True: 0, False: 0]
  ------------------
13032|       |            // Set state to special relative or authority state.
13033|      0|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
13034|      0|          }
13035|       |          // Otherwise, if url is special, set state to special authority
13036|       |          // slashes state.
13037|   266k|          else if (url.is_special()) {
  ------------------
  |  Branch (13037:20): [True: 264k, False: 1.81k]
  ------------------
13038|   264k|            state = state::SPECIAL_AUTHORITY_SLASHES;
13039|   264k|          }
13040|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
13041|       |          // path or authority state and increase pointer by 1.
13042|  1.81k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (13042:20): [True: 1.68k, False: 132]
  ------------------
13043|  1.68k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (13043:20): [True: 1.12k, False: 564]
  ------------------
13044|  1.12k|            state = state::PATH_OR_AUTHORITY;
13045|  1.12k|            input_position++;
13046|  1.12k|          }
13047|       |          // Otherwise, set url's path to the empty string and set state to
13048|       |          // opaque path state.
13049|    696|          else {
13050|    696|            state = state::OPAQUE_PATH;
13051|    696|          }
13052|   267k|        }
13053|       |        // Otherwise, if state override is not given, set buffer to the empty
13054|       |        // string, state to no scheme state, and start over (from the first code
13055|       |        // point in input).
13056|     77|        else {
13057|     77|          state = state::NO_SCHEME;
13058|     77|          input_position = 0;
13059|     77|          break;
13060|     77|        }
13061|   267k|        input_position++;
13062|   267k|        break;
13063|   268k|      }
13064|    209|      case state::NO_SCHEME: {
  ------------------
  |  Branch (13064:7): [True: 209, False: 1.80M]
  ------------------
13065|    209|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
13066|       |        // The fragment was pruned from url_data before the state machine ran,
13067|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
13068|       |        // nothing else remains in front of it.
13069|    209|        const bool c_is_hash =
13070|    209|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (13070:13): [True: 118, False: 91]
  |  Branch (13070:37): [True: 1, False: 117]
  ------------------
13071|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
13072|       |        // validation error, return failure.
13073|    209|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (13073:13): [True: 209, False: 0]
  |  Branch (13073:37): [True: 0, False: 0]
  |  Branch (13073:66): [True: 0, False: 0]
  ------------------
13074|    209|          ada_log("NO_SCHEME validation error");
13075|    209|          url.is_valid = false;
13076|    209|          return url;
13077|    209|        }
13078|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
13079|       |        // set url's scheme to base's scheme, url's path to base's path, url's
13080|       |        // query to base's query, and set state to fragment state.
13081|      0|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (13081:18): [True: 0, False: 0]
  |  Branch (13081:47): [True: 0, False: 0]
  ------------------
13082|      0|          ada_log("NO_SCHEME opaque base with fragment");
13083|      0|          url.copy_scheme(*base_url);
13084|      0|          url.has_opaque_path = base_url->has_opaque_path;
13085|       |
13086|       |          if constexpr (result_type_is_ada_url) {
13087|       |            url.path = base_url->path;
13088|       |            url.query = base_url->query;
13089|      0|          } else {
13090|      0|            url.update_base_pathname(base_url->get_pathname());
13091|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13091:17): [True: 0, False: 0]
  ------------------
13092|       |              // get_search() returns "" for an empty query string (URL ends
13093|       |              // with '?'). update_base_search("") would incorrectly clear the
13094|       |              // query, so pass "?" to preserve the empty query distinction.
13095|      0|              auto s = base_url->get_search();
13096|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13096:38): [True: 0, False: 0]
  ------------------
13097|      0|            }
13098|      0|          }
13099|      0|          url.update_unencoded_base_hash(*fragment);
13100|      0|          enforce_max_input_length();
13101|      0|          return url;
13102|      0|        }
13103|       |        // Otherwise, if base's scheme is not "file", set state to relative
13104|       |        // state and decrease pointer by 1.
13105|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13106|      0|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (13106:18): [True: 0, False: 0]
  ------------------
13107|      0|          ada_log("NO_SCHEME non-file relative path");
13108|      0|          state = state::RELATIVE_SCHEME;
13109|      0|        }
13110|       |        // Otherwise, set state to file state and decrease pointer by 1.
13111|      0|        else {
13112|      0|          ada_log("NO_SCHEME file base type");
13113|      0|          state = state::FILE;
13114|      0|        }
13115|      0|        break;
13116|    209|      }
13117|   265k|      case state::AUTHORITY: {
  ------------------
  |  Branch (13117:7): [True: 265k, False: 1.54M]
  ------------------
13118|   265k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
13119|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
13120|       |        // about AUTHORITY. Of course, we could have @ and still not have to
13121|       |        // worry about AUTHORITY.
13122|       |        // TODO: Instead of just collecting a bool, collect the location of the
13123|       |        // '@' and do something useful with it.
13124|       |        // TODO: We could do various processing early on, using a single pass
13125|       |        // over the string to collect information about it, e.g., telling us
13126|       |        // whether there is a @ and if so, where (or how many).
13127|       |
13128|       |        // Check if url data contains an @.
13129|   265k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (13129:13): [True: 224k, False: 40.8k]
  ------------------
13130|   224k|          state = state::HOST;
13131|   224k|          break;
13132|   224k|        }
13133|  40.8k|        bool at_sign_seen{false};
13134|  40.8k|        bool password_token_seen{false};
13135|       |        /**
13136|       |         * We expect something of the sort...
13137|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
13138|       |         * --------^
13139|       |         */
13140|  93.6k|        do {
13141|  93.6k|          std::string_view view = url_data.substr(input_position);
13142|       |          // The delimiters are @, /, ? \\.
13143|  93.6k|          size_t location =
13144|  93.6k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (13144:15): [True: 93.4k, False: 165]
  ------------------
13145|  93.6k|                               : helpers::find_authority_delimiter(view);
13146|  93.6k|          std::string_view authority_view = view.substr(0, location);
13147|  93.6k|          size_t end_of_authority = input_position + authority_view.size();
13148|       |          // If c is U+0040 (@), then:
13149|  93.6k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (13149:15): [True: 83.7k, False: 9.95k]
  ------------------
13150|  83.7k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (13150:15): [True: 52.8k, False: 30.8k]
  ------------------
13151|       |            // If atSignSeen is true, then prepend "%40" to buffer.
13152|  52.8k|            if (at_sign_seen) {
  ------------------
  |  Branch (13152:17): [True: 12.2k, False: 40.5k]
  ------------------
13153|  12.2k|              if (password_token_seen) {
  ------------------
  |  Branch (13153:19): [True: 4.59k, False: 7.68k]
  ------------------
13154|       |                if constexpr (result_type_is_ada_url) {
13155|       |                  url.password += "%40";
13156|  4.59k|                } else {
13157|  4.59k|                  url.append_base_password("%40");
13158|  4.59k|                }
13159|  7.68k|              } else {
13160|       |                if constexpr (result_type_is_ada_url) {
13161|       |                  url.username += "%40";
13162|  7.68k|                } else {
13163|  7.68k|                  url.append_base_username("%40");
13164|  7.68k|                }
13165|  7.68k|              }
13166|  12.2k|            }
13167|       |
13168|  52.8k|            at_sign_seen = true;
13169|       |
13170|  52.8k|            if (!password_token_seen) {
  ------------------
  |  Branch (13170:17): [True: 48.2k, False: 4.59k]
  ------------------
13171|  48.2k|              size_t password_token_location = authority_view.find(':');
13172|  48.2k|              password_token_seen =
13173|  48.2k|                  password_token_location != std::string_view::npos;
13174|       |
13175|  48.2k|              if constexpr (store_values) {
13176|  48.2k|                if (!password_token_seen) {
  ------------------
  |  Branch (13176:21): [True: 18.4k, False: 29.8k]
  ------------------
13177|       |                  if constexpr (result_type_is_ada_url) {
13178|       |                    url.username += unicode::percent_encode(
13179|       |                        authority_view,
13180|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13181|  18.4k|                  } else {
13182|  18.4k|                    url.append_base_username(unicode::percent_encode(
13183|  18.4k|                        authority_view,
13184|  18.4k|                        character_sets::USERINFO_PERCENT_ENCODE));
13185|  18.4k|                  }
13186|  29.8k|                } else {
13187|       |                  if constexpr (result_type_is_ada_url) {
13188|       |                    url.username += unicode::percent_encode(
13189|       |                        authority_view.substr(0, password_token_location),
13190|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13191|       |                    url.password += unicode::percent_encode(
13192|       |                        authority_view.substr(password_token_location + 1),
13193|       |                        character_sets::USERINFO_PERCENT_ENCODE);
13194|  29.8k|                  } else {
13195|  29.8k|                    url.append_base_username(unicode::percent_encode(
13196|  29.8k|                        authority_view.substr(0, password_token_location),
13197|  29.8k|                        character_sets::USERINFO_PERCENT_ENCODE));
13198|  29.8k|                    url.append_base_password(unicode::percent_encode(
13199|  29.8k|                        authority_view.substr(password_token_location + 1),
13200|  29.8k|                        character_sets::USERINFO_PERCENT_ENCODE));
13201|  29.8k|                  }
13202|  29.8k|                }
13203|  48.2k|              }
13204|  48.2k|            } else if constexpr (store_values) {
13205|       |              if constexpr (result_type_is_ada_url) {
13206|       |                url.password += unicode::percent_encode(
13207|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
13208|  4.59k|              } else {
13209|  4.59k|                url.append_base_password(unicode::percent_encode(
13210|  4.59k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
13211|  4.59k|              }
13212|  4.59k|            }
13213|  52.8k|          }
13214|       |          // Otherwise, if one of the following is true:
13215|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13216|       |          // - url is special and c is U+005C (\)
13217|  40.8k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (13217:20): [True: 9.95k, False: 30.8k]
  ------------------
13218|  30.8k|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (13218:20): [True: 30.7k, False: 144]
  ------------------
13219|    144|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (13219:20): [True: 117, False: 27]
  ------------------
13220|  40.8k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (13220:21): [True: 27, False: 0]
  |  Branch (13220:41): [True: 27, False: 0]
  ------------------
13221|       |            // If atSignSeen is true and authority_view is the empty string,
13222|       |            // validation error, return failure.
13223|  40.8k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (13223:17): [True: 40.5k, False: 263]
  |  Branch (13223:33): [True: 249, False: 40.3k]
  ------------------
13224|    249|              url.is_valid = false;
13225|    249|              return url;
13226|    249|            }
13227|  40.5k|            state = state::HOST;
13228|  40.5k|            break;
13229|  40.8k|          }
13230|  52.8k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (13230:15): [True: 0, False: 52.8k]
  ------------------
13231|      0|            if constexpr (store_values) {
13232|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (13232:19): [True: 0, False: 0]
  ------------------
13233|      0|                url.update_unencoded_base_hash(*fragment);
13234|      0|              }
13235|      0|            }
13236|      0|            enforce_max_input_length();
13237|      0|            return url;
13238|      0|          }
13239|  52.8k|          input_position = end_of_authority + 1;
13240|  52.8k|        } while (true);
  ------------------
  |  Branch (13240:18): [True: 52.8k, Folded]
  ------------------
13241|       |
13242|  40.5k|        break;
13243|  40.8k|      }
13244|  40.5k|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (13244:7): [True: 0, False: 1.80M]
  ------------------
13245|      0|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
13246|      0|                helpers::substring(url_data, input_position));
13247|       |
13248|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13249|       |        // then set state to special authority ignore slashes state and increase
13250|       |        // pointer by 1.
13251|      0|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13251:13): [True: 0, False: 0]
  ------------------
13252|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13253|      0|          input_position += 2;
13254|      0|        } else {
13255|       |          // Otherwise, validation error, set state to relative state and
13256|       |          // decrease pointer by 1.
13257|      0|          state = state::RELATIVE_SCHEME;
13258|      0|        }
13259|       |
13260|      0|        break;
13261|  40.8k|      }
13262|  1.12k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (13262:7): [True: 1.12k, False: 1.80M]
  ------------------
13263|  1.12k|        ada_log("PATH_OR_AUTHORITY ",
13264|  1.12k|                helpers::substring(url_data, input_position));
13265|       |
13266|       |        // If c is U+002F (/), then set state to authority state.
13267|  1.12k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13267:13): [True: 1.08k, False: 33]
  ------------------
13268|  1.08k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13268:13): [True: 770, False: 318]
  ------------------
13269|    770|          state = state::AUTHORITY;
13270|    770|          input_position++;
13271|    770|        } else {
13272|       |          // Otherwise, set state to path state, and decrease pointer by 1.
13273|    351|          state = state::PATH;
13274|    351|        }
13275|       |
13276|  1.12k|        break;
13277|  40.8k|      }
13278|      0|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (13278:7): [True: 0, False: 1.80M]
  ------------------
13279|      0|        ada_log("RELATIVE_SCHEME ",
13280|      0|                helpers::substring(url_data, input_position));
13281|       |
13282|       |        // Set url's scheme to base's scheme.
13283|      0|        url.copy_scheme(*base_url);
13284|       |
13285|       |        // If c is U+002F (/), then set state to relative slash state.
13286|      0|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13286:13): [True: 0, False: 0]
  ------------------
13287|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
13288|      0|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13288:13): [True: 0, False: 0]
  ------------------
13289|      0|          ada_log(
13290|      0|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13291|      0|              "slash state");
13292|      0|          state = state::RELATIVE_SLASH;
13293|      0|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13293:20): [True: 0, False: 0]
  |  Branch (13293:40): [True: 0, False: 0]
  ------------------
13294|      0|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13294:20): [True: 0, False: 0]
  ------------------
13295|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13296|       |          // set state to relative slash state.
13297|      0|          ada_log(
13298|      0|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13299|      0|              "error, set state to relative slash state");
13300|      0|          state = state::RELATIVE_SLASH;
13301|      0|        } else {
13302|      0|          ada_log("RELATIVE_SCHEME otherwise");
13303|       |          // Set url's username to base's username, url's password to base's
13304|       |          // password, url's host to base's host, url's port to base's port,
13305|       |          // url's path to a clone of base's path, and url's query to base's
13306|       |          // query.
13307|       |          if constexpr (result_type_is_ada_url) {
13308|       |            url.username = base_url->username;
13309|       |            url.password = base_url->password;
13310|       |            url.host = base_url->host;
13311|       |            url.port = base_url->port;
13312|       |            // cloning the base path includes cloning the has_opaque_path flag
13313|       |            url.has_opaque_path = base_url->has_opaque_path;
13314|       |            url.path = base_url->path;
13315|       |            url.query = base_url->query;
13316|      0|          } else {
13317|      0|            url.update_base_authority(base_url->get_href(),
13318|      0|                                      base_url->get_components());
13319|      0|            url.update_host_to_base_host(base_url->get_hostname());
13320|      0|            url.update_base_port(base_url->retrieve_base_port());
13321|       |            // cloning the base path includes cloning the has_opaque_path flag
13322|      0|            url.has_opaque_path = base_url->has_opaque_path;
13323|      0|            url.update_base_pathname(base_url->get_pathname());
13324|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13324:17): [True: 0, False: 0]
  ------------------
13325|       |              // get_search() returns "" for an empty query string (URL ends
13326|       |              // with '?'). update_base_search("") would incorrectly clear the
13327|       |              // query, so pass "?" to preserve the empty query distinction.
13328|      0|              auto s = base_url->get_search();
13329|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13329:38): [True: 0, False: 0]
  ------------------
13330|      0|            }
13331|      0|          }
13332|       |
13333|      0|          url.has_opaque_path = base_url->has_opaque_path;
13334|       |
13335|       |          // If c is U+003F (?), then set url's query to the empty string, and
13336|       |          // state to query state.
13337|      0|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13337:15): [True: 0, False: 0]
  ------------------
13338|      0|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13338:15): [True: 0, False: 0]
  ------------------
13339|      0|            state = state::QUERY;
13340|      0|          }
13341|       |          // Otherwise, if c is not the EOF code point:
13342|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (13342:20): [True: 0, False: 0]
  ------------------
13343|       |            // Set url's query to null.
13344|      0|            url.clear_search();
13345|       |            if constexpr (result_type_is_ada_url) {
13346|       |              // Shorten url's path.
13347|       |              helpers::shorten_path(url.path, url.type);
13348|      0|            } else {
13349|      0|              std::string_view path = url.get_pathname();
13350|      0|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13350:19): [True: 0, False: 0]
  ------------------
13351|      0|                url.update_base_pathname(std::move(std::string(path)));
13352|      0|              }
13353|      0|            }
13354|       |            // Set state to path state and decrease pointer by 1.
13355|      0|            state = state::PATH;
13356|      0|            break;
13357|      0|          }
13358|      0|        }
13359|      0|        input_position++;
13360|      0|        break;
13361|      0|      }
13362|      0|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13362:7): [True: 0, False: 1.80M]
  ------------------
13363|      0|        ada_log("RELATIVE_SLASH ",
13364|      0|                helpers::substring(url_data, input_position));
13365|       |
13366|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13367|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13368|      0|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13368:13): [True: 0, False: 0]
  |  Branch (13368:33): [True: 0, False: 0]
  ------------------
13369|      0|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13369:14): [True: 0, False: 0]
  ------------------
13370|      0|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13370:14): [True: 0, False: 0]
  ------------------
13371|       |          // Set state to special authority ignore slashes state.
13372|      0|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13373|      0|        }
13374|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13375|      0|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13375:18): [True: 0, False: 0]
  ------------------
13376|      0|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13376:18): [True: 0, False: 0]
  ------------------
13377|      0|          state = state::AUTHORITY;
13378|      0|        }
13379|       |        // Otherwise, set
13380|       |        // - url's username to base's username,
13381|       |        // - url's password to base's password,
13382|       |        // - url's host to base's host,
13383|       |        // - url's port to base's port,
13384|       |        // - state to path state, and then, decrease pointer by 1.
13385|      0|        else {
13386|       |          if constexpr (result_type_is_ada_url) {
13387|       |            url.username = base_url->username;
13388|       |            url.password = base_url->password;
13389|       |            url.host = base_url->host;
13390|       |            url.port = base_url->port;
13391|      0|          } else {
13392|      0|            url.update_base_authority(base_url->get_href(),
13393|      0|                                      base_url->get_components());
13394|      0|            url.update_host_to_base_host(base_url->get_hostname());
13395|      0|            url.update_base_port(base_url->retrieve_base_port());
13396|      0|          }
13397|      0|          state = state::PATH;
13398|      0|          break;
13399|      0|        }
13400|       |
13401|      0|        input_position++;
13402|      0|        break;
13403|      0|      }
13404|   264k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13404:7): [True: 264k, False: 1.54M]
  ------------------
13405|   264k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13406|   264k|                helpers::substring(url_data, input_position));
13407|       |
13408|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13409|       |        // then set state to special authority ignore slashes state and increase
13410|       |        // pointer by 1.
13411|   264k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13411:13): [True: 263k, False: 1.68k]
  ------------------
13412|   263k|          input_position += 2;
13413|   263k|        }
13414|       |
13415|   264k|        [[fallthrough]];
13416|   264k|      }
13417|   264k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13417:7): [True: 0, False: 1.80M]
  ------------------
13418|   264k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13419|   264k|                helpers::substring(url_data, input_position));
13420|       |
13421|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13422|       |        // authority state and decrease pointer by 1.
13423|   266k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13423:16): [True: 266k, False: 73]
  ------------------
13424|   266k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13424:17): [True: 638, False: 265k]
  ------------------
13425|   265k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13425:17): [True: 1.25k, False: 264k]
  ------------------
13426|  1.88k|          input_position++;
13427|  1.88k|        }
13428|   264k|        state = state::AUTHORITY;
13429|       |
13430|   264k|        break;
13431|   264k|      }
13432|  9.80k|      case state::QUERY: {
  ------------------
  |  Branch (13432:7): [True: 9.80k, False: 1.79M]
  ------------------
13433|  9.80k|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13434|  9.80k|        if constexpr (store_values) {
13435|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13436|       |          // if url is special; otherwise the query percent-encode set.
13437|  9.80k|          const uint8_t* query_percent_encode_set =
13438|  9.80k|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13438:15): [True: 8.63k, False: 1.16k]
  ------------------
13439|  9.80k|                               : character_sets::QUERY_PERCENT_ENCODE;
13440|       |
13441|       |          // Percent-encode after encoding, with encoding, buffer, and
13442|       |          // queryPercentEncodeSet, and append the result to url's query.
13443|  9.80k|          url.update_base_search(url_data.substr(input_position),
13444|  9.80k|                                 query_percent_encode_set);
13445|  9.80k|          ada_log("QUERY update_base_search completed ");
13446|  9.80k|          if (fragment.has_value()) {
  ------------------
  |  Branch (13446:15): [True: 7.37k, False: 2.42k]
  ------------------
13447|  7.37k|            url.update_unencoded_base_hash(*fragment);
13448|  7.37k|          }
13449|  9.80k|        }
13450|  9.80k|        enforce_max_input_length();
13451|  9.80k|        return url;
13452|   264k|      }
13453|   265k|      case state::HOST: {
  ------------------
  |  Branch (13453:7): [True: 265k, False: 1.54M]
  ------------------
13454|   265k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13455|       |
13456|   265k|        std::string_view host_view = url_data.substr(input_position);
13457|   265k|        auto [location, found_colon] =
13458|   265k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13459|   265k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13459:26): [True: 265k, False: 0]
  ------------------
13460|   265k|                             ? input_position + location
13461|   265k|                             : input_size;
13462|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13463|       |        // Note: the 'found_colon' value is true if and only if a colon was
13464|       |        // encountered while not inside brackets.
13465|   265k|        if (found_colon) {
  ------------------
  |  Branch (13465:13): [True: 2.37k, False: 262k]
  ------------------
13466|       |          // If buffer is the empty string, validation error, return failure.
13467|       |          // Let host be the result of host parsing buffer with url is not
13468|       |          // special.
13469|  2.37k|          ada_log("HOST parsing ", host_view);
13470|  2.37k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13470:15): [True: 169, False: 2.20k]
  ------------------
13471|    169|            return url;
13472|    169|          }
13473|  2.20k|          ada_log("HOST parsing results in ", url.get_hostname());
13474|       |          // Set url's host to host, buffer to the empty string, and state to
13475|       |          // port state.
13476|  2.20k|          state = state::PORT;
13477|  2.20k|          input_position++;
13478|  2.20k|        }
13479|       |        // Otherwise, if one of the following is true:
13480|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13481|       |        // - url is special and c is U+005C (\)
13482|       |        // The get_host_delimiter_location function either brings us to
13483|       |        // the colon outside of the bracket, or to one of those characters.
13484|   262k|        else {
13485|       |          // If url is special and host_view is the empty string, validation
13486|       |          // error, return failure.
13487|   262k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13487:15): [True: 184, False: 262k]
  |  Branch (13487:36): [True: 100, False: 84]
  ------------------
13488|    100|            url.is_valid = false;
13489|    100|            return url;
13490|    100|          }
13491|   262k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13492|       |          // Let host be the result of host parsing host_view with url is not
13493|       |          // special.
13494|   262k|          if (host_view.empty()) {
  ------------------
  |  Branch (13494:15): [True: 84, False: 262k]
  ------------------
13495|     84|            url.update_base_hostname("");
13496|   262k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13496:22): [True: 20.7k, False: 242k]
  ------------------
13497|  20.7k|            return url;
13498|  20.7k|          }
13499|   242k|          ada_log("HOST parsing results in ", url.get_hostname(),
13500|   242k|                  " href=", url.get_href());
13501|       |
13502|       |          // Set url's host to host, and state to path start state.
13503|   242k|          state = state::PATH_START;
13504|   242k|        }
13505|       |
13506|   244k|        break;
13507|   265k|      }
13508|   244k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13508:7): [True: 696, False: 1.80M]
  ------------------
13509|    696|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13510|       |        // Opaque path, query, and fragment are structurally always valid:
13511|       |        // the parser would just percent-encode whatever is there. When we
13512|       |        // are not storing values (can_parse), we can return immediately.
13513|       |        // We must set has_opaque_path = true before returning so that when
13514|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13515|       |        // correctly rejects relative inputs against an opaque-path base
13516|       |        // (e.g. can_parse("", &"W:") must return false).
13517|       |        if constexpr (!store_values) {
13518|       |          url.has_opaque_path = true;
13519|       |          return url;
13520|       |        }
13521|    696|        std::string_view view = url_data.substr(input_position);
13522|       |        // If c is U+003F (?), then set url's query to the empty string and
13523|       |        // state to query state.
13524|    696|        size_t location = view.find('?');
13525|    696|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13525:13): [True: 438, False: 258]
  ------------------
13526|    438|          view.remove_suffix(view.size() - location);
13527|    438|          state = state::QUERY;
13528|    438|          input_position += location + 1;
13529|    438|        } else {
13530|    258|          input_position = input_size + 1;
13531|    258|        }
13532|    696|        url.has_opaque_path = true;
13533|       |
13534|       |        // This is a really unlikely scenario in real world. We should not seek
13535|       |        // to optimize it.
13536|    696|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13536:13): [True: 10, False: 686]
  ------------------
13537|     10|          std::string modified_view =
13538|     10|              std::string(view.substr(0, view.size() - 1)) + "%20";
13539|     10|          url.update_base_pathname(unicode::percent_encode(
13540|     10|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13541|    686|        } else {
13542|    686|          url.update_base_pathname(unicode::percent_encode(
13543|    686|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13544|    686|        }
13545|    696|        break;
13546|   265k|      }
13547|  2.20k|      case state::PORT: {
  ------------------
  |  Branch (13547:7): [True: 2.20k, False: 1.80M]
  ------------------
13548|  2.20k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13549|  2.20k|        std::string_view port_view = url_data.substr(input_position);
13550|  2.20k|        input_position += url.parse_port(port_view, true);
13551|  2.20k|        if (!url.is_valid) {
  ------------------
  |  Branch (13551:13): [True: 223, False: 1.98k]
  ------------------
13552|    223|          return url;
13553|    223|        }
13554|  1.98k|        state = state::PATH_START;
13555|  1.98k|        [[fallthrough]];
13556|  1.98k|      }
13557|   245k|      case state::PATH_START: {
  ------------------
  |  Branch (13557:7): [True: 243k, False: 1.56M]
  ------------------
13558|   245k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13559|       |        // Path, query, and fragment are structurally always valid: the
13560|       |        // parser would just percent-encode whatever is there. When we are
13561|       |        // not storing values (can_parse), we can return immediately since
13562|       |        // no subsequent state can invalidate the URL.
13563|       |        if constexpr (!store_values) {
13564|       |          return url;
13565|       |        }
13566|       |
13567|       |        // If url is special, then:
13568|   245k|        if (url.is_special()) {
  ------------------
  |  Branch (13568:13): [True: 244k, False: 705]
  ------------------
13569|       |          // Set state to path state.
13570|   244k|          state = state::PATH;
13571|       |
13572|       |          // Optimization: Avoiding going into PATH state improves the
13573|       |          // performance of urls ending with /.
13574|   244k|          if (input_position == input_size) {
  ------------------
  |  Branch (13574:15): [True: 30.5k, False: 214k]
  ------------------
13575|  30.5k|            if constexpr (store_values) {
13576|  30.5k|              url.update_base_pathname("/");
13577|  30.5k|              if (fragment.has_value()) {
  ------------------
  |  Branch (13577:19): [True: 270, False: 30.2k]
  ------------------
13578|    270|                url.update_unencoded_base_hash(*fragment);
13579|    270|              }
13580|  30.5k|            }
13581|  30.5k|            enforce_max_input_length();
13582|  30.5k|            return url;
13583|  30.5k|          }
13584|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13585|       |          // by 1. We know that (input_position == input_size) is impossible
13586|       |          // here, because of the previous if-check.
13587|   214k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13587:15): [True: 1.02k, False: 212k]
  ------------------
13588|  1.02k|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13588:15): [True: 635, False: 385]
  ------------------
13589|    635|            break;
13590|    635|          }
13591|   214k|        }
13592|       |        // Otherwise, if state override is not given and c is U+003F (?),
13593|       |        // set url's query to the empty string and state to query state.
13594|    705|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13594:18): [True: 576, False: 129]
  ------------------
13595|    576|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13595:18): [True: 117, False: 459]
  ------------------
13596|    117|          state = state::QUERY;
13597|    117|        }
13598|       |        // Otherwise, if c is not the EOF code point:
13599|    588|        else if (input_position != input_size) {
  ------------------
  |  Branch (13599:18): [True: 459, False: 129]
  ------------------
13600|       |          // Set state to path state.
13601|    459|          state = state::PATH;
13602|       |
13603|       |          // If c is not U+002F (/), then decrease pointer by 1.
13604|    459|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13604:15): [True: 0, False: 459]
  ------------------
13605|      0|            break;
13606|      0|          }
13607|    459|        }
13608|       |
13609|   214k|        input_position++;
13610|   214k|        break;
13611|   245k|      }
13612|   214k|      case state::PATH: {
  ------------------
  |  Branch (13612:7): [True: 214k, False: 1.59M]
  ------------------
13613|   214k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13614|       |        // Path, query, and fragment are structurally always valid: the
13615|       |        // parser would just percent-encode whatever is there. When we are
13616|       |        // not storing values (can_parse), we can return immediately since
13617|       |        // no subsequent state can invalidate the URL.
13618|       |        if constexpr (!store_values) {
13619|       |          return url;
13620|       |        }
13621|   214k|        std::string_view view = url_data.substr(input_position);
13622|       |
13623|       |        // Most time, we do not need percent encoding.
13624|       |        // Furthermore, we can immediately locate the '?'.
13625|   214k|        size_t locofquestionmark = view.find('?');
13626|   214k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13626:13): [True: 9.24k, False: 205k]
  ------------------
13627|  9.24k|          state = state::QUERY;
13628|  9.24k|          view.remove_suffix(view.size() - locofquestionmark);
13629|  9.24k|          input_position += locofquestionmark + 1;
13630|   205k|        } else {
13631|   205k|          input_position = input_size + 1;
13632|   205k|        }
13633|   214k|        if constexpr (store_values) {
13634|       |          if constexpr (result_type_is_ada_url) {
13635|       |            helpers::parse_prepared_path(view, url.type, url.path);
13636|   214k|          } else {
13637|   214k|            url.consume_prepared_path(view);
13638|   214k|            ADA_ASSERT_TRUE(url.validate());
13639|   214k|          }
13640|   214k|        }
13641|   214k|        break;
13642|   245k|      }
13643|  1.30k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13643:7): [True: 1.30k, False: 1.80M]
  ------------------
13644|  1.30k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13645|       |
13646|       |        // If c is U+002F (/) or U+005C (\), then:
13647|  1.30k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13647:13): [True: 1.29k, False: 1]
  ------------------
13648|  1.29k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13648:14): [True: 1.28k, False: 12]
  ------------------
13649|  1.29k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13649:14): [True: 3, False: 9]
  ------------------
13650|  1.29k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13651|       |          // Set state to file host state.
13652|  1.29k|          state = state::FILE_HOST;
13653|  1.29k|          input_position++;
13654|  1.29k|        } else {
13655|     10|          ada_log("FILE_SLASH otherwise");
13656|       |          // If base is non-null and base's scheme is "file", then:
13657|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13658|       |          // base_url_has_value() is true.
13659|     10|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13659:15): [True: 0, False: 10]
  |  Branch (13659:38): [True: 0, False: 0]
  ------------------
13660|       |            // Set url's host to base's host.
13661|       |            if constexpr (result_type_is_ada_url) {
13662|       |              url.host = base_url->host;
13663|      0|            } else {
13664|      0|              url.update_host_to_base_host(base_url->get_host());
13665|      0|            }
13666|       |            // If the code point substring from pointer to the end of input does
13667|       |            // not start with a Windows drive letter and base's path[0] is a
13668|       |            // normalized Windows drive letter, then append base's path[0] to
13669|       |            // url's path.
13670|      0|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13670:17): [True: 0, False: 0]
  ------------------
13671|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13671:19): [True: 0, False: 0]
  ------------------
13672|      0|                      url_data.substr(input_position))) {
13673|      0|                std::string_view first_base_url_path =
13674|      0|                    base_url->get_pathname().substr(1);
13675|      0|                size_t loc = first_base_url_path.find('/');
13676|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13676:21): [True: 0, False: 0]
  ------------------
13677|      0|                  helpers::resize(first_base_url_path, loc);
13678|      0|                }
13679|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13679:21): [True: 0, False: 0]
  ------------------
13680|      0|                        first_base_url_path)) {
13681|       |                  if constexpr (result_type_is_ada_url) {
13682|       |                    url.path += '/';
13683|       |                    url.path += first_base_url_path;
13684|      0|                  } else {
13685|      0|                    url.append_base_pathname(
13686|      0|                        helpers::concat("/", first_base_url_path));
13687|      0|                  }
13688|      0|                }
13689|      0|              }
13690|      0|            }
13691|      0|          }
13692|       |
13693|       |          // Set state to path state, and decrease pointer by 1.
13694|     10|          state = state::PATH;
13695|     10|        }
13696|       |
13697|  1.30k|        break;
13698|   245k|      }
13699|  1.29k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13699:7): [True: 1.29k, False: 1.80M]
  ------------------
13700|  1.29k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13701|  1.29k|        std::string_view view = url_data.substr(input_position);
13702|       |
13703|  1.29k|        size_t location = view.find_first_of("/\\?");
13704|  1.29k|        std::string_view file_host_buffer = view.substr(
13705|  1.29k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13705:16): [True: 1.26k, False: 29]
  ------------------
13706|       |
13707|  1.29k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13707:13): [True: 1, False: 1.28k]
  ------------------
13708|      1|          state = state::PATH;
13709|  1.28k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13709:20): [True: 261, False: 1.02k]
  ------------------
13710|       |          // Set url's host to the empty string.
13711|       |          if constexpr (result_type_is_ada_url) {
13712|       |            url.host = "";
13713|    261|          } else {
13714|    261|            url.update_base_hostname("");
13715|    261|          }
13716|       |          // Set state to path start state.
13717|    261|          state = state::PATH_START;
13718|  1.02k|        } else {
13719|  1.02k|          size_t consumed_bytes = file_host_buffer.size();
13720|  1.02k|          input_position += consumed_bytes;
13721|       |          // Let host be the result of host parsing buffer with url is not
13722|       |          // special.
13723|  1.02k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13723:15): [True: 95, False: 933]
  ------------------
13724|     95|            return url;
13725|     95|          }
13726|       |
13727|       |          if constexpr (result_type_is_ada_url) {
13728|       |            // If host is "localhost", then set host to the empty string.
13729|       |            if (url.host.has_value() && url.host.value() == "localhost") {
13730|       |              url.host = "";
13731|       |            }
13732|    933|          } else {
13733|    933|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (13733:17): [True: 3, False: 930]
  ------------------
13734|      3|              url.update_base_hostname("");
13735|      3|            }
13736|    933|          }
13737|       |
13738|       |          // Set buffer to the empty string and state to path start state.
13739|    933|          state = state::PATH_START;
13740|    933|        }
13741|       |
13742|  1.19k|        break;
13743|  1.29k|      }
13744|  1.41k|      case state::FILE: {
  ------------------
  |  Branch (13744:7): [True: 1.41k, False: 1.80M]
  ------------------
13745|  1.41k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13746|  1.41k|        std::string_view file_view = url_data.substr(input_position);
13747|       |
13748|  1.41k|        url.set_protocol_as_file();
13749|       |        if constexpr (result_type_is_ada_url) {
13750|       |          // Set url's host to the empty string.
13751|       |          url.host = "";
13752|  1.41k|        } else {
13753|  1.41k|          url.update_base_hostname("");
13754|  1.41k|        }
13755|       |        // If c is U+002F (/) or U+005C (\), then:
13756|  1.41k|        if (input_position != input_size &&
  ------------------
  |  Branch (13756:13): [True: 1.41k, False: 1]
  ------------------
13757|  1.41k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13757:14): [True: 1.29k, False: 113]
  ------------------
13758|  1.30k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13758:14): [True: 2, False: 111]
  ------------------
13759|  1.30k|          ada_log("FILE c is U+002F or U+005C");
13760|       |          // Set state to file slash state.
13761|  1.30k|          state = state::FILE_SLASH;
13762|  1.30k|        }
13763|       |        // Otherwise, if base is non-null and base's scheme is "file":
13764|    112|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13764:18): [True: 0, False: 112]
  |  Branch (13764:41): [True: 0, False: 0]
  ------------------
13765|       |          // Set url's host to base's host, url's path to a clone of base's
13766|       |          // path, and url's query to base's query.
13767|      0|          ada_log("FILE base non-null");
13768|       |          if constexpr (result_type_is_ada_url) {
13769|       |            url.host = base_url->host;
13770|       |            url.path = base_url->path;
13771|       |            url.query = base_url->query;
13772|      0|          } else {
13773|      0|            url.update_host_to_base_host(base_url->get_hostname());
13774|      0|            url.update_base_pathname(base_url->get_pathname());
13775|      0|            if (base_url->has_search()) {
  ------------------
  |  Branch (13775:17): [True: 0, False: 0]
  ------------------
13776|       |              // get_search() returns "" for an empty query string (URL ends
13777|       |              // with '?'). update_base_search("") would incorrectly clear the
13778|       |              // query, so pass "?" to preserve the empty query distinction.
13779|      0|              auto s = base_url->get_search();
13780|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13780:38): [True: 0, False: 0]
  ------------------
13781|      0|            }
13782|      0|          }
13783|      0|          url.has_opaque_path = base_url->has_opaque_path;
13784|       |
13785|       |          // If c is U+003F (?), then set url's query to the empty string and
13786|       |          // state to query state.
13787|      0|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13787:15): [True: 0, False: 0]
  |  Branch (13787:47): [True: 0, False: 0]
  ------------------
13788|      0|            state = state::QUERY;
13789|      0|          }
13790|       |          // Otherwise, if c is not the EOF code point:
13791|      0|          else if (input_position != input_size) {
  ------------------
  |  Branch (13791:20): [True: 0, False: 0]
  ------------------
13792|       |            // Set url's query to null.
13793|      0|            url.clear_search();
13794|       |            // If the code point substring from pointer to the end of input does
13795|       |            // not start with a Windows drive letter, then shorten url's path.
13796|      0|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13796:17): [True: 0, False: 0]
  ------------------
13797|       |              if constexpr (result_type_is_ada_url) {
13798|       |                helpers::shorten_path(url.path, url.type);
13799|      0|              } else {
13800|      0|                std::string_view path = url.get_pathname();
13801|      0|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13801:21): [True: 0, False: 0]
  ------------------
13802|      0|                  url.update_base_pathname(std::move(std::string(path)));
13803|      0|                }
13804|      0|              }
13805|      0|            }
13806|       |            // Otherwise:
13807|      0|            else {
13808|       |              // Set url's path to an empty list.
13809|      0|              url.clear_pathname();
13810|      0|              url.has_opaque_path = true;
13811|      0|            }
13812|       |
13813|       |            // Set state to path state and decrease pointer by 1.
13814|      0|            state = state::PATH;
13815|      0|            break;
13816|      0|          }
13817|      0|        }
13818|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13819|    112|        else {
13820|    112|          ada_log("FILE go to path");
13821|    112|          state = state::PATH;
13822|    112|          break;
13823|    112|        }
13824|       |
13825|  1.30k|        input_position++;
13826|  1.30k|        break;
13827|  1.41k|      }
13828|      0|      default:
  ------------------
  |  Branch (13828:7): [True: 0, False: 1.80M]
  ------------------
13829|      0|        unreachable();
13830|  1.80M|    }
13831|  1.80M|  }
13832|   561k|  if constexpr (store_values) {
13833|   561k|    if (fragment.has_value()) {
  ------------------
  |  Branch (13833:9): [True: 2.25k, False: 558k]
  ------------------
13834|  2.25k|      url.update_unencoded_base_hash(*fragment);
13835|  2.25k|    }
13836|   561k|  }
13837|   561k|  enforce_max_input_length();
13838|   561k|  return url;
13839|   623k|}
_ZN3ada14url_aggregator8set_hrefENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14425|   200k|bool url_aggregator::set_href(const std::string_view input) {
14426|   200k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14427|   200k|  ada_log("url_aggregator::set_href ", input, " [", input.size(), " bytes]");
14428|   200k|  ada::result<url_aggregator> out = ada::parse<url_aggregator>(input);
14429|   200k|  ada_log("url_aggregator::set_href, success :", out.has_value());
14430|       |
14431|   200k|  if (out) {
  ------------------
  |  Branch (14431:7): [True: 200k, False: 0]
  ------------------
14432|       |    // The parser enforces get_max_input_length() on both the input and the
14433|       |    // normalized result. This is a defense-in-depth check.
14434|   200k|    if (out->buffer.size() > ada::get_max_input_length()) {
  ------------------
  |  Branch (14434:9): [True: 0, False: 200k]
  ------------------
14435|      0|      return false;
14436|      0|    }
14437|   200k|    ada_log("url_aggregator::set_href, parsed ", out->to_string());
14438|       |    // TODO: Figure out why the following line puts test to never finish.
14439|   200k|    *this = std::move(*out);
14440|   200k|  }
14441|       |
14442|   200k|  return out.has_value();
14443|   200k|}
_ZNK3ada14url_aggregator12get_usernameEv:
14792|   200k|    ada_lifetime_bound {
14793|   200k|  ada_log("url_aggregator::get_username");
14794|   200k|  if (has_non_empty_username()) {
  ------------------
  |  Branch (14794:7): [True: 10.2k, False: 190k]
  ------------------
14795|  10.2k|    return helpers::substring(buffer, components.protocol_end + 2,
14796|  10.2k|                              components.username_end);
14797|  10.2k|  }
14798|   190k|  return "";
14799|   200k|}
_ZNK3ada14url_aggregator12get_passwordEv:
14802|   200k|    ada_lifetime_bound {
14803|   200k|  ada_log("url_aggregator::get_password");
14804|   200k|  if (has_non_empty_password()) {
  ------------------
  |  Branch (14804:7): [True: 9.89k, False: 190k]
  ------------------
14805|  9.89k|    return helpers::substring(buffer, components.username_end + 1,
14806|  9.89k|                              components.host_start);
14807|  9.89k|  }
14808|   190k|  return "";
14809|   200k|}
_ZNK3ada14url_aggregator8get_portEv:
14812|   200k|    ada_lifetime_bound {
14813|   200k|  ada_log("url_aggregator::get_port");
14814|   200k|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (14814:7): [True: 179k, False: 20.9k]
  ------------------
14815|   179k|    return "";
14816|   179k|  }
14817|  20.9k|  return helpers::substring(buffer, components.host_end + 1,
14818|  20.9k|                            components.pathname_start);
14819|   200k|}
_ZNK3ada14url_aggregator8get_hashEv:
14822|   200k|    ada_lifetime_bound {
14823|   200k|  ada_log("url_aggregator::get_hash");
14824|       |  // If this's URL's fragment is either null or the empty string, then return
14825|       |  // the empty string. Return U+0023 (#), followed by this's URL's fragment.
14826|   200k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (14826:7): [True: 175k, False: 24.6k]
  ------------------
14827|   175k|    return "";
14828|   175k|  }
14829|  24.6k|  if (buffer.size() - components.hash_start <= 1) {
  ------------------
  |  Branch (14829:7): [True: 1.52k, False: 23.1k]
  ------------------
14830|  1.52k|    return "";
14831|  1.52k|  }
14832|  23.1k|  return helpers::substring(buffer, components.hash_start);
14833|  24.6k|}
_ZNK3ada14url_aggregator8get_hostEv:
14836|   200k|    ada_lifetime_bound {
14837|   200k|  ada_log("url_aggregator::get_host");
14838|       |  // Technically, we should check if there is a hostname, but
14839|       |  // the code below works even if there isn't.
14840|       |  // if(!has_hostname()) { return ""; }
14841|   200k|  size_t start = components.host_start;
14842|   200k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (14842:7): [True: 199k, False: 506]
  ------------------
14843|   199k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (14843:7): [True: 10.2k, False: 189k]
  ------------------
14844|  10.2k|    start++;
14845|  10.2k|  }
14846|       |  // if we have an empty host, then the space between components.host_end and
14847|       |  // components.pathname_start may be occupied by /.
14848|   200k|  if (start == components.host_end) {
  ------------------
  |  Branch (14848:7): [True: 506, False: 199k]
  ------------------
14849|    506|    return {};
14850|    506|  }
14851|   199k|  return helpers::substring(buffer, start, components.pathname_start);
14852|   200k|}
_ZNK3ada14url_aggregator12get_hostnameEv:
14855|   265k|    ada_lifetime_bound {
14856|   265k|  ada_log("url_aggregator::get_hostname");
14857|       |  // Technically, we should check if there is a hostname, but
14858|       |  // the code below works even if there isn't.
14859|       |  // if(!has_hostname()) { return ""; }
14860|   265k|  size_t start = components.host_start;
14861|       |  // So host_start is not where the host begins.
14862|   265k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (14862:7): [True: 201k, False: 64.6k]
  ------------------
14863|   201k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (14863:7): [True: 10.4k, False: 190k]
  ------------------
14864|  10.4k|    start++;
14865|  10.4k|  }
14866|   265k|  return helpers::substring(buffer, start, components.host_end);
14867|   265k|}
_ZNK3ada14url_aggregator10get_searchEv:
14870|   200k|    ada_lifetime_bound {
14871|   200k|  ada_log("url_aggregator::get_search");
14872|       |  // If this's URL's query is either null or the empty string, then return the
14873|       |  // empty string. Return U+003F (?), followed by this's URL's query.
14874|   200k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (14874:7): [True: 166k, False: 34.1k]
  ------------------
14875|   166k|    return "";
14876|   166k|  }
14877|  34.1k|  auto ending_index = uint32_t(buffer.size());
14878|  34.1k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (14878:7): [True: 13.6k, False: 20.5k]
  ------------------
14879|  13.6k|    ending_index = components.hash_start;
14880|  13.6k|  }
14881|  34.1k|  if (ending_index - components.search_start <= 1) {
  ------------------
  |  Branch (14881:7): [True: 1.12k, False: 33.0k]
  ------------------
14882|  1.12k|    return "";
14883|  1.12k|  }
14884|  33.0k|  return helpers::substring(buffer, components.search_start, ending_index);
14885|  34.1k|}
_ZNK3ada14url_aggregator12get_protocolEv:
14888|   200k|    ada_lifetime_bound {
14889|   200k|  ada_log("url_aggregator::get_protocol");
14890|   200k|  return helpers::substring(buffer, 0, components.protocol_end);
14891|   200k|}
_ZN3ada14url_aggregator10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
15000|  58.6k|bool url_aggregator::parse_ipv4(std::string_view input, bool in_place) {
15001|  58.6k|  ada_log("parse_ipv4 ", input, " [", input.size(),
15002|  58.6k|          " bytes], overlaps with buffer: ",
15003|  58.6k|          helpers::overlaps(input, buffer) ? "yes" : "no");
15004|  58.6k|  ADA_ASSERT_TRUE(validate());
15005|  58.6k|  if (input.empty()) {
  ------------------
  |  Branch (15005:7): [True: 0, False: 58.6k]
  ------------------
15006|      0|    return is_valid = false;
15007|      0|  }
15008|  58.6k|  const bool trailing_dot = (input.back() == '.');
15009|  58.6k|  if (trailing_dot) {
  ------------------
  |  Branch (15009:7): [True: 28.3k, False: 30.2k]
  ------------------
15010|  28.3k|    input.remove_suffix(1);
15011|  28.3k|    if (input.empty()) {
  ------------------
  |  Branch (15011:9): [True: 0, False: 28.3k]
  ------------------
15012|      0|      return is_valid = false;
15013|      0|    }
15014|  28.3k|  }
15015|       |
15016|  58.6k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
15017|  58.6k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (15017:7): [True: 18.9k, False: 39.7k]
  ------------------
15018|       |    // Pure decimal: keep the buffer when it already holds the address.
15019|       |    // Otherwise write the (trailing-dot-stripped) input.
15020|  18.9k|    if (!(in_place && !trailing_dot)) {
  ------------------
  |  Branch (15020:11): [True: 0, False: 18.9k]
  |  Branch (15020:23): [True: 0, False: 0]
  ------------------
15021|  18.9k|      if (helpers::overlaps(input, buffer)) {
  ------------------
  |  Branch (15021:11): [True: 0, False: 18.9k]
  ------------------
15022|      0|        update_base_hostname(std::string(input));
15023|  18.9k|      } else {
15024|  18.9k|        update_base_hostname(input);
15025|  18.9k|      }
15026|  18.9k|    }
15027|  18.9k|    host_type = IPV4;
15028|  18.9k|    ADA_ASSERT_TRUE(validate());
15029|  18.9k|    return true;
15030|  18.9k|  }
15031|       |
15032|  39.7k|  const char* p = input.data();
15033|  39.7k|  const char* end = p + input.size();
15034|  39.7k|  uint64_t ipv4 = 0;
15035|  39.7k|  int digit_count = 0;
15036|  39.7k|  int pure_decimal_count = 0;
15037|       |
15038|  50.0k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (15038:10): [True: 50.0k, False: 16]
  |  Branch (15038:29): [True: 50.0k, False: 0]
  ------------------
15039|  50.0k|    uint64_t segment = 0;
15040|  50.0k|    bool pure = false;
15041|  50.0k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (15041:9): [True: 19.1k, False: 30.8k]
  ------------------
15042|  19.1k|      return is_valid = false;
15043|  19.1k|    }
15044|  30.8k|    if (pure) {
  ------------------
  |  Branch (15044:9): [True: 11.3k, False: 19.4k]
  ------------------
15045|  11.3k|      ++pure_decimal_count;
15046|  11.3k|    }
15047|  30.8k|    if (p >= end) {
  ------------------
  |  Branch (15047:9): [True: 20.5k, False: 10.3k]
  ------------------
15048|  20.5k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
15049|  20.5k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (15049:11): [True: 23, False: 20.4k]
  ------------------
15050|     23|        return is_valid = false;
15051|     23|      }
15052|  20.4k|      ipv4 = (ipv4 << shift) | segment;
15053|  20.4k|      goto ipv4_done;
15054|  20.5k|    }
15055|  10.3k|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (15055:9): [True: 36, False: 10.2k]
  |  Branch (15055:26): [True: 0, False: 10.2k]
  ------------------
15056|     36|      return is_valid = false;
15057|     36|    }
15058|  10.2k|    ipv4 = (ipv4 << 8) | segment;
15059|  10.2k|    ++p;
15060|  10.2k|  }
15061|     16|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (15061:7): [True: 0, False: 16]
  |  Branch (15061:27): [True: 16, False: 0]
  ------------------
15062|     16|    return is_valid = false;
15063|     16|  }
15064|  20.4k|ipv4_done:
15065|  20.4k|  ada_log("url_aggregator::parse_ipv4 completed ", get_href(),
15066|  20.4k|          " host: ", get_host());
15067|  20.4k|  if (in_place && pure_decimal_count == 4 && !trailing_dot) {
  ------------------
  |  Branch (15067:7): [True: 0, False: 20.4k]
  |  Branch (15067:19): [True: 0, False: 0]
  |  Branch (15067:46): [True: 0, False: 0]
  ------------------
15068|      0|    ada_log(
15069|      0|        "url_aggregator::parse_ipv4 completed and was already correct in the "
15070|      0|        "buffer");
15071|  20.4k|  } else {
15072|  20.4k|    update_base_hostname(ada::serializers::ipv4(ipv4));
15073|  20.4k|  }
15074|  20.4k|  host_type = IPV4;
15075|  20.4k|  ADA_ASSERT_TRUE(validate());
15076|  20.4k|  return true;
15077|     16|}
_ZN3ada14url_aggregator10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15079|    594|bool url_aggregator::parse_ipv6(std::string_view input) {
15080|    594|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
15081|    594|  ADA_ASSERT_TRUE(validate());
15082|    594|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15083|    594|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (15083:7): [True: 20, False: 574]
  |  Branch (15083:24): [True: 9, False: 565]
  ------------------
15084|     29|    return is_valid = false;
15085|     29|  }
15086|    565|  std::array<uint16_t, 8> address{};
15087|       |#if defined(ADA_AVX512_IPV6)
15088|       |  if (bool simd_valid; detail::try_parse_ipv6_avx512(input.data(), input.size(),
15089|       |                                                     address, simd_valid)) {
15090|       |    if (!simd_valid) [[unlikely]] {
15091|       |      return is_valid = false;
15092|       |    }
15093|       |    const std::string serialized = ada::serializers::ipv6(address);
15094|       |    if (get_hostname() != serialized) {
15095|       |      update_base_hostname(serialized);
15096|       |    }
15097|       |    host_type = IPV6;
15098|       |    return true;
15099|       |  }
15100|       |  address = {};
15101|       |#endif
15102|    565|  const char* pointer = input.data();
15103|    565|  const char* const end = pointer + input.size();
15104|    565|  int piece_index = 0;
15105|    565|  int compress = -1;
15106|       |
15107|    565|  if (*pointer == ':') {
  ------------------
  |  Branch (15107:7): [True: 269, False: 296]
  ------------------
15108|    269|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (15108:9): [True: 3, False: 266]
  |  Branch (15108:30): [True: 7, False: 259]
  ------------------
15109|     10|      return is_valid = false;
15110|     10|    }
15111|    259|    pointer += 2;
15112|    259|    compress = ++piece_index;
15113|    259|  }
15114|       |
15115|  1.73k|  while (pointer != end) {
  ------------------
  |  Branch (15115:10): [True: 1.28k, False: 449]
  ------------------
15116|  1.28k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (15116:9): [True: 3, False: 1.28k]
  ------------------
15117|      3|      return is_valid = false;
15118|      3|    }
15119|  1.28k|    if (*pointer == ':') {
  ------------------
  |  Branch (15119:9): [True: 162, False: 1.12k]
  ------------------
15120|    162|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (15120:11): [True: 3, False: 159]
  ------------------
15121|      3|        return is_valid = false;
15122|      3|      }
15123|    159|      ++pointer;
15124|    159|      compress = ++piece_index;
15125|    159|      continue;
15126|    162|    }
15127|       |
15128|  1.12k|    uint16_t value = 0;
15129|  1.12k|    const int length = detail::parse_hex_piece(pointer, end, value);
15130|       |
15131|  1.12k|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (15131:9): [True: 878, False: 243]
  |  Branch (15131:27): [True: 72, False: 806]
  ------------------
15132|     72|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (15132:11): [True: 3, False: 69]
  ------------------
15133|      3|        return is_valid = false;
15134|      3|      }
15135|     69|      pointer -= length;
15136|     69|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (15136:11): [True: 3, False: 66]
  ------------------
15137|      3|        return is_valid = false;
15138|      3|      }
15139|       |
15140|     66|      int numbers_seen = 0;
15141|    188|      while (pointer != end) {
  ------------------
  |  Branch (15141:14): [True: 170, False: 18]
  ------------------
15142|    170|        int ipv4_piece = -1;
15143|    170|        if (numbers_seen > 0) {
  ------------------
  |  Branch (15143:13): [True: 104, False: 66]
  ------------------
15144|    104|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (15144:15): [True: 91, False: 13]
  |  Branch (15144:34): [True: 86, False: 5]
  ------------------
15145|     86|            ++pointer;
15146|     86|          } else {
15147|     18|            return is_valid = false;
15148|     18|          }
15149|    104|        }
15150|    152|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (15150:13): [True: 14, False: 138]
  |  Branch (15150:31): [True: 4, False: 134]
  |  Branch (15150:49): [True: 3, False: 131]
  ------------------
15151|     21|          return is_valid = false;
15152|     21|        }
15153|    131|        ipv4_piece = *pointer - '0';
15154|    131|        ++pointer;
15155|    131|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (15155:13): [True: 121, False: 10]
  |  Branch (15155:31): [True: 61, False: 60]
  |  Branch (15155:50): [True: 58, False: 3]
  ------------------
15156|     58|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (15156:15): [True: 3, False: 55]
  ------------------
15157|      3|            return is_valid = false;
15158|      3|          }
15159|     55|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
15160|     55|          ++pointer;
15161|     55|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (15161:15): [True: 52, False: 3]
  |  Branch (15161:33): [True: 31, False: 21]
  |  Branch (15161:52): [True: 28, False: 3]
  ------------------
15162|     28|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
15163|     28|            ++pointer;
15164|     28|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (15164:17): [True: 6, False: 22]
  ------------------
15165|      6|              return is_valid = false;
15166|      6|            }
15167|     28|          }
15168|     55|        }
15169|    122|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
15170|    122|            address[static_cast<size_t>(piece_index)] * 0x100 +
15171|    122|            static_cast<uint16_t>(ipv4_piece));
15172|    122|        ++numbers_seen;
15173|    122|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (15173:13): [True: 36, False: 86]
  |  Branch (15173:34): [True: 12, False: 74]
  ------------------
15174|     48|          ++piece_index;
15175|     48|        }
15176|    122|      }
15177|     18|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (15177:11): [True: 12, False: 6]
  ------------------
15178|     12|        return is_valid = false;
15179|     12|      }
15180|      6|      break;
15181|     18|    }
15182|       |
15183|  1.04k|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (15183:9): [True: 22, False: 1.02k]
  ------------------
15184|     22|      return is_valid = false;
15185|     22|    }
15186|       |
15187|  1.02k|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (15187:9): [True: 784, False: 243]
  |  Branch (15187:27): [True: 781, False: 3]
  ------------------
15188|    781|      ++pointer;
15189|    781|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (15189:11): [True: 3, False: 778]
  ------------------
15190|      3|        return is_valid = false;
15191|      3|      }
15192|    781|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (15192:16): [True: 3, False: 243]
  ------------------
15193|      3|      return is_valid = false;
15194|      3|    }
15195|       |
15196|  1.02k|    address[static_cast<size_t>(piece_index)] = value;
15197|  1.02k|    ++piece_index;
15198|  1.02k|  }
15199|       |
15200|    455|  if (compress != -1) {
  ------------------
  |  Branch (15200:7): [True: 409, False: 46]
  ------------------
15201|    409|    const int right = piece_index - compress;
15202|    409|    if (right > 0) {
  ------------------
  |  Branch (15202:9): [True: 203, False: 206]
  ------------------
15203|    203|      const size_t dest = static_cast<size_t>(8 - right);
15204|    203|      const size_t src = static_cast<size_t>(compress);
15205|    203|      if (dest != src) {
  ------------------
  |  Branch (15205:11): [True: 185, False: 18]
  ------------------
15206|    508|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (15206:53): [True: 323, False: 185]
  ------------------
15207|    323|          address[dest + i] = address[src + i];
15208|    323|          address[src + i] = 0;
15209|    323|        }
15210|    185|      }
15211|    203|    }
15212|    409|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (15212:14): [True: 21, False: 25]
  ------------------
15213|     21|    return is_valid = false;
15214|     21|  }
15215|       |
15216|       |  // Serialize once; skip rewrite when hostname is already canonical.
15217|    434|  const std::string serialized = ada::serializers::ipv6(address);
15218|    434|  const std::string_view current = get_hostname();
15219|    434|  if (current.size() == serialized.size() &&
  ------------------
  |  Branch (15219:7): [True: 0, False: 434]
  ------------------
15220|      0|      std::memcmp(current.data(), serialized.data(), serialized.size()) == 0) {
  ------------------
  |  Branch (15220:7): [True: 0, False: 0]
  ------------------
15221|      0|    ada_log("parse_ipv6 in-place canonical match");
15222|    434|  } else {
15223|    434|    update_base_hostname(serialized);
15224|    434|  }
15225|    434|  ada_log("parse_ipv6 ", get_hostname());
15226|    434|  ADA_ASSERT_TRUE(validate());
15227|    434|  host_type = IPV6;
15228|    434|  return true;
15229|    455|}
_ZN3ada14url_aggregator17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15231|    652|bool url_aggregator::parse_opaque_host(std::string_view input) {
15232|    652|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
15233|    652|  ADA_ASSERT_TRUE(validate());
15234|    652|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
15235|    652|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (15235:7): [True: 33, False: 619]
  ------------------
15236|     33|    return is_valid = false;
15237|     33|  }
15238|       |
15239|       |  // Return the result of running UTF-8 percent-encode on input using the C0
15240|       |  // control percent-encode set.
15241|    619|  size_t idx = ada::unicode::percent_encode_index(
15242|    619|      input, character_sets::C0_CONTROL_PERCENT_ENCODE);
15243|    619|  if (idx == input.size()) {
  ------------------
  |  Branch (15243:7): [True: 586, False: 33]
  ------------------
15244|    586|    update_base_hostname(input);
15245|    586|  } else {
15246|       |    // We only create a temporary string if we need to.
15247|     33|    update_base_hostname(ada::unicode::percent_encode(
15248|     33|        input, character_sets::C0_CONTROL_PERCENT_ENCODE, idx));
15249|     33|  }
15250|    619|  ADA_ASSERT_TRUE(validate());
15251|    619|  return true;
15252|    652|}
simple_absolute.cc:_ZZN3ada4idna22utf32_length_from_utf8EPKcmENK3$_0clEa:
  274|   224k|  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|   224k|    return c > -65;
  278|   224k|  });
_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|}
simple_absolute.cc:_ZZN3ada4idna9ascii_mapEPcmENK3$_0clEh:
 5339|   368k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5340|   368k|    return 0x101010101010101ull * v;
 5341|   368k|  };
_ZN3ada4idna13ensure_tablesEv:
 5085|  32.2k|[[nodiscard]] inline bool ensure_tables() noexcept {
 5086|  32.2k|  uint8_t state = tables_init_state.load(std::memory_order_acquire);
 5087|  32.2k|  if (state == kTablesReady) {
  ------------------
  |  Branch (5087:7): [True: 32.2k, False: 1]
  ------------------
 5088|  32.2k|    return true;
 5089|  32.2k|  }
 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|    };
simple_absolute.cc:_ZN3ada4idnaL11idna_lookupEj:
 5277|   321k|static uint16_t idna_lookup(uint32_t cp) noexcept {
 5278|   321k|  if (cp < IDNA_LOW_RANGE_END) {
  ------------------
  |  Branch (5278:7): [True: 320k, False: 690]
  ------------------
 5279|   320k|    uint16_t ref = idna_stage1[cp >> IDNA_BLOCK_BITS];
 5280|   320k|    if (ref & IDNA_BOOL_FLAG) {
  ------------------
  |  Branch (5280:9): [True: 135k, False: 185k]
  ------------------
 5281|   135k|      uint32_t bit_idx =
 5282|   135k|          static_cast<uint32_t>(ref & ~IDNA_BOOL_FLAG) * IDNA_BLOCK_SIZE +
 5283|   135k|          (cp & IDNA_BLOCK_MASK);
 5284|   135k|      bool is_valid = (idna_bool_blocks[bit_idx >> 6] >> (bit_idx & 63u)) & 1u;
 5285|   135k|      return is_valid ? IDNA_VALID : IDNA_DISALLOWED;
  ------------------
  |  Branch (5285:14): [True: 135k, False: 36]
  ------------------
 5286|   135k|    }
 5287|   185k|    return idna_stage2[ref + (cp & IDNA_BLOCK_MASK)];
 5288|   320k|  }
 5289|       |
 5290|    690|  if (cp >= IDNA_HIGH_IGNORED_START && cp < IDNA_HIGH_IGNORED_END) {
  ------------------
  |  Branch (5290:7): [True: 640, False: 50]
  |  Branch (5290:40): [True: 408, False: 232]
  ------------------
 5291|    408|    return IDNA_IGNORED;
 5292|    408|  }
 5293|       |
 5294|    282|  return IDNA_DISALLOWED;
 5295|    690|}
simple_absolute.cc:_ZN3ada4idnaL21utf8_count_codepointsEPKh:
 5320|  50.4k|static size_t utf8_count_codepoints(const uint8_t* ptr) noexcept {
 5321|  50.4k|  size_t n = 0;
 5322|   267k|  while (*ptr != 0) {
  ------------------
  |  Branch (5322:10): [True: 217k, False: 50.4k]
  ------------------
 5323|   217k|    ++n;
 5324|   217k|    if (*ptr < 0x80u) {
  ------------------
  |  Branch (5324:9): [True: 79.7k, False: 137k]
  ------------------
 5325|  79.7k|      ++ptr;
 5326|   137k|    } else if (*ptr < 0xE0u) {
  ------------------
  |  Branch (5326:16): [True: 93.3k, False: 43.9k]
  ------------------
 5327|  93.3k|      ptr += 2;
 5328|  93.3k|    } else if (*ptr < 0xF0u) {
  ------------------
  |  Branch (5328:16): [True: 43.1k, False: 820]
  ------------------
 5329|  43.1k|      ptr += 3;
 5330|  43.1k|    } else {
 5331|    820|      ptr += 4;
 5332|    820|    }
 5333|   217k|  }
 5334|  50.4k|  return n;
 5335|  50.4k|}
simple_absolute.cc:_ZN3ada4idnaL9utf8_nextERPKh:
 5298|   216k|static char32_t utf8_next(const uint8_t*& ptr) noexcept {
 5299|   216k|  uint8_t b0 = *ptr++;
 5300|   216k|  if (b0 < 0x80u) return static_cast<char32_t>(b0);
  ------------------
  |  Branch (5300:7): [True: 79.6k, False: 137k]
  ------------------
 5301|   137k|  if (b0 < 0xE0u) {
  ------------------
  |  Branch (5301:7): [True: 93.3k, False: 43.7k]
  ------------------
 5302|  93.3k|    uint32_t cp = (b0 & 0x1Fu) << 6;
 5303|  93.3k|    cp |= (*ptr++ & 0x3Fu);
 5304|  93.3k|    return static_cast<char32_t>(cp);
 5305|  93.3k|  }
 5306|  43.7k|  if (b0 < 0xF0u) {
  ------------------
  |  Branch (5306:7): [True: 43.1k, False: 564]
  ------------------
 5307|  43.1k|    uint32_t cp = (b0 & 0x0Fu) << 12;
 5308|  43.1k|    cp |= ((*ptr++ & 0x3Fu) << 6);
 5309|  43.1k|    cp |= (*ptr++ & 0x3Fu);
 5310|  43.1k|    return static_cast<char32_t>(cp);
 5311|  43.1k|  }
 5312|    564|  uint32_t cp = (b0 & 0x07u) << 18;
 5313|    564|  cp |= ((*ptr++ & 0x3Fu) << 12);
 5314|    564|  cp |= ((*ptr++ & 0x3Fu) << 6);
 5315|    564|  cp |= (*ptr++ & 0x3Fu);
 5316|    564|  return static_cast<char32_t>(cp);
 5317|  43.7k|}
_ZN3ada4idna23decomposition_block_rowEh:
 5194|   705k|inline const uint16_t* decomposition_block_row(uint8_t bi) noexcept {
 5195|   705k|  if (bi >= kDecompBlockRows) {
  ------------------
  |  Branch (5195:7): [True: 0, False: 705k]
  ------------------
 5196|      0|    bi = 0;
 5197|      0|  }
 5198|   705k|  return decomposition_block_flat + static_cast<size_t>(bi) * kDecompBlockCols;
 5199|   705k|}
_ZN3ada4idna13ccc_block_rowEh:
 5201|  1.06M|inline const uint8_t* ccc_block_row(uint8_t bi) noexcept {
 5202|  1.06M|  if (bi >= kCccBlockRows) {
  ------------------
  |  Branch (5202:7): [True: 0, False: 1.06M]
  ------------------
 5203|      0|    bi = 0;
 5204|      0|  }
 5205|  1.06M|  return ccc_block_flat + static_cast<size_t>(bi) * kCccBlockCols;
 5206|  1.06M|}
_ZN3ada4idna16tables_are_readyEv:
 5080|  7.50k|[[nodiscard]] inline bool tables_are_ready() noexcept {
 5081|  7.50k|  return tables_init_state.load(std::memory_order_acquire) == kTablesReady;
 5082|  7.50k|}
simple_absolute.cc:_ZN3ada4idnaL24decomposition_is_not_nfcEDiDi:
 5707|   336k|static bool decomposition_is_not_nfc(char32_t previous, char32_t c) noexcept {
 5708|   336k|  if (c >= hangul_sbase && c < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5708:7): [True: 40.2k, False: 296k]
  |  Branch (5708:28): [True: 37.3k, False: 2.95k]
  ------------------
 5709|  37.3k|    return false;
 5710|  37.3k|  }
 5711|   298k|  if (c >= 0x110000) {
  ------------------
  |  Branch (5711:7): [True: 0, False: 298k]
  ------------------
 5712|      0|    return false;
 5713|      0|  }
 5714|   298k|  const uint16_t* const decomposition =
 5715|   298k|      decomposition_block_row(decomposition_index[c >> 8]) + (c % 256);
 5716|   298k|  const size_t length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5717|   298k|  if (length == 0 || (decomposition[0] & 1)) {
  ------------------
  |  Branch (5717:7): [True: 291k, False: 7.37k]
  |  Branch (5717:22): [True: 0, False: 7.37k]
  ------------------
 5718|   291k|    return false;  // no canonical decomposition (or compatibility-only)
 5719|   291k|  }
 5720|  7.37k|  if (length == 1) {
  ------------------
  |  Branch (5720:7): [True: 0, False: 7.37k]
  ------------------
 5721|      0|    return true;  // singleton
 5722|      0|  }
 5723|  7.37k|  const size_t base = decomposition[0] >> 2;
 5724|  7.37k|  if (base + length > decomposition_data_size) {
  ------------------
  |  Branch (5724:7): [True: 0, False: 7.37k]
  ------------------
 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|  7.37k|  std::u32string decomposed(decomposition_data + base,
 5730|  7.37k|                            decomposition_data + base + length);
 5731|  7.37k|  compose(decomposed);
 5732|  7.37k|  if (!(decomposed.size() == 1 && decomposed[0] == c)) {
  ------------------
  |  Branch (5732:9): [True: 7.37k, False: 0]
  |  Branch (5732:35): [True: 7.37k, False: 0]
  ------------------
 5733|      0|    return true;
 5734|      0|  }
 5735|  7.37k|  return previous < 0x110000 &&
  ------------------
  |  Branch (5735:10): [True: 5.46k, False: 1.91k]
  ------------------
 5736|  5.46k|         has_composition(
  ------------------
  |  Branch (5736:10): [True: 20, False: 5.44k]
  ------------------
 5737|  5.46k|             composition_block_row(composition_index[previous >> 8]) +
 5738|  5.46k|                 (previous % 256),
 5739|  5.46k|             decomposition_data[base]);
 5740|  7.37k|}
simple_absolute.cc:_ZN3ada4idnaL15has_compositionEPKtDi:
 5611|  72.5k|                            char32_t second) noexcept {
 5612|  72.5k|  int left = composition[0];
 5613|  72.5k|  int right = composition[1];
 5614|  72.5k|  if (left < 0 || right <= left ||
  ------------------
  |  Branch (5614:7): [True: 0, False: 72.5k]
  |  Branch (5614:19): [True: 4.37k, False: 68.2k]
  ------------------
 5615|  68.2k|      static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5615:7): [True: 0, False: 68.2k]
  ------------------
 5616|  4.37k|    return false;
 5617|  4.37k|  }
 5618|   175k|  while (left + 2 < right) {
  ------------------
  |  Branch (5618:10): [True: 107k, False: 68.2k]
  ------------------
 5619|   107k|    int middle = left + (((right - left) >> 1) & ~1);
 5620|   107k|    if (composition_data[static_cast<size_t>(middle)] <= second) {
  ------------------
  |  Branch (5620:9): [True: 4.22k, False: 103k]
  ------------------
 5621|  4.22k|      left = middle;
 5622|  4.22k|    }
 5623|   107k|    if (composition_data[static_cast<size_t>(middle)] >= second) {
  ------------------
  |  Branch (5623:9): [True: 103k, False: 3.58k]
  ------------------
 5624|   103k|      right = middle;
 5625|   103k|    }
 5626|   107k|  }
 5627|  68.2k|  return static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5627:10): [True: 68.2k, False: 0]
  ------------------
 5628|  68.2k|         composition_data[static_cast<size_t>(left)] == second;
  ------------------
  |  Branch (5628:10): [True: 996, False: 67.2k]
  ------------------
 5629|  72.5k|}
simple_absolute.cc:_ZN3ada4idnaL12trailing_cccEDi:
 5684|   326k|static uint8_t trailing_ccc(char32_t c) noexcept {
 5685|   326k|  const size_t length = canonical_decomp_length(c);
 5686|   326k|  if (length == 0) {
  ------------------
  |  Branch (5686:7): [True: 319k, False: 6.64k]
  ------------------
 5687|   319k|    return get_ccc(c);
 5688|   319k|  }
 5689|  6.64k|  const uint16_t* const decomposition =
 5690|  6.64k|      decomposition_block_row(decomposition_index[c >> 8]) + (c % 256);
 5691|  6.64k|  const size_t base = decomposition[0] >> 2;
 5692|  6.64k|  return get_ccc(decomposition_data[base + length - 1]);
 5693|   326k|}
simple_absolute.cc:_ZN3ada4idnaL23canonical_decomp_lengthEDi:
 5478|   326k|static size_t canonical_decomp_length(char32_t current_character) noexcept {
 5479|   326k|  if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5479:7): [True: 38.6k, False: 287k]
  ------------------
 5480|  38.6k|      current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5480:7): [True: 36.3k, False: 2.24k]
  ------------------
 5481|       |    // Hangul precomposed syllables are NFC.
 5482|  36.3k|    return 0;
 5483|  36.3k|  }
 5484|   289k|  if (current_character >= 0x110000) {
  ------------------
  |  Branch (5484:7): [True: 0, False: 289k]
  ------------------
 5485|      0|    return 0;
 5486|      0|  }
 5487|   289k|  const uint8_t di = decomposition_index[current_character >> 8];
 5488|   289k|  const uint16_t* const decomposition =
 5489|   289k|      decomposition_block_row(di) + (current_character % 256);
 5490|   289k|  size_t decomposition_length =
 5491|   289k|      (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5492|   289k|  if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5492:7): [True: 6.64k, False: 283k]
  |  Branch (5492:37): [True: 0, False: 6.64k]
  ------------------
 5493|      0|    decomposition_length = 0;  // compatibility-only: ignored for NFC
 5494|      0|  }
 5495|   289k|  return decomposition_length;
 5496|   289k|}
simple_absolute.cc:_ZN3ada4idnaL13would_composeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5632|  5.42k|static bool would_compose(std::u32string_view input) noexcept {
 5633|   307k|  for (size_t input_count = 0; input_count < input.size();) {
  ------------------
  |  Branch (5633:32): [True: 303k, False: 4.23k]
  ------------------
 5634|   303k|    const char32_t current = input[input_count];
 5635|   303k|    if (current >= hangul_lbase && current < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5635:9): [True: 51.4k, False: 251k]
  |  Branch (5635:36): [True: 1.72k, False: 49.7k]
  ------------------
 5636|  1.72k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5636:11): [True: 1.62k, False: 94]
  ------------------
 5637|  1.62k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5637:11): [True: 826, False: 802]
  ------------------
 5638|    826|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5638:11): [True: 160, False: 666]
  ------------------
 5639|    160|        return true;
 5640|    160|      }
 5641|  1.56k|      ++input_count;
 5642|  1.56k|      continue;
 5643|  1.72k|    }
 5644|   301k|    if (current >= hangul_sbase && current < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5644:9): [True: 37.0k, False: 264k]
  |  Branch (5644:36): [True: 35.8k, False: 1.19k]
  ------------------
 5645|  35.8k|      if ((current - hangul_sbase) % hangul_tcount == 0 &&
  ------------------
  |  Branch (5645:11): [True: 35.4k, False: 460]
  ------------------
 5646|  35.4k|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (5646:11): [True: 35.3k, False: 74]
  ------------------
 5647|  35.3k|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5647:11): [True: 17.9k, False: 17.4k]
  ------------------
 5648|  17.9k|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5648:11): [True: 48, False: 17.8k]
  ------------------
 5649|     48|        return true;
 5650|     48|      }
 5651|  35.8k|      ++input_count;
 5652|  35.8k|      continue;
 5653|  35.8k|    }
 5654|   265k|    if (current < 0x110000) {
  ------------------
  |  Branch (5654:9): [True: 265k, False: 0]
  ------------------
 5655|   265k|      const uint16_t* composition =
 5656|   265k|          composition_block_row(composition_index[current >> 8]) +
 5657|   265k|          (current % 256);
 5658|   265k|      int32_t previous_ccc = -1;
 5659|   265k|      size_t j = input_count;
 5660|   268k|      for (; j + 1 < input.size(); ++j) {
  ------------------
  |  Branch (5660:14): [True: 264k, False: 4.02k]
  ------------------
 5661|   264k|        uint8_t ccc = get_ccc(input[j + 1]);
 5662|   264k|        if (composition[1] != composition[0] && previous_ccc < ccc &&
  ------------------
  |  Branch (5662:13): [True: 68.4k, False: 195k]
  |  Branch (5662:49): [True: 67.1k, False: 1.38k]
  ------------------
 5663|  67.1k|            has_composition(composition, input[j + 1])) {
  ------------------
  |  Branch (5663:13): [True: 976, False: 66.1k]
  ------------------
 5664|    976|          return true;
 5665|    976|        }
 5666|   263k|        if (ccc == 0) {
  ------------------
  |  Branch (5666:13): [True: 260k, False: 2.63k]
  ------------------
 5667|   260k|          break;
 5668|   260k|        }
 5669|  2.63k|        previous_ccc = ccc;
 5670|  2.63k|      }
 5671|   264k|      input_count = j + 1;
 5672|   264k|      continue;
 5673|   265k|    }
 5674|      0|    ++input_count;
 5675|      0|  }
 5676|  4.23k|  return false;
 5677|  5.42k|}
_ZN3ada4idna21composition_block_rowEh:
 5208|   345k|inline const uint16_t* composition_block_row(uint8_t bi) noexcept {
 5209|   345k|  if (bi >= kCompBlockRows) {
  ------------------
  |  Branch (5209:7): [True: 0, False: 345k]
  ------------------
 5210|      0|    bi = 0;
 5211|      0|  }
 5212|   345k|  return composition_block_flat + static_cast<size_t>(bi) * kCompBlockCols;
 5213|   345k|}
simple_absolute.cc:_ZN3ada4idnaL19char_to_digit_valueEc:
 5913|  31.4k|static constexpr int32_t char_to_digit_value(char value) {
 5914|  31.4k|  if (value >= 'a' && value <= 'z') return value - 'a';
  ------------------
  |  Branch (5914:7): [True: 26.6k, False: 4.83k]
  |  Branch (5914:23): [True: 26.5k, False: 6]
  ------------------
 5915|  4.83k|  if (value >= '0' && value <= '9') return value - '0' + 26;
  ------------------
  |  Branch (5915:7): [True: 4.81k, False: 26]
  |  Branch (5915:23): [True: 4.80k, False: 10]
  ------------------
 5916|     36|  return -1;
 5917|  4.83k|}
simple_absolute.cc:_ZN3ada4idnaL5adaptEiib:
 5923|  70.6k|static constexpr int32_t adapt(int32_t d, int32_t n, bool firsttime) {
 5924|  70.6k|  if (firsttime) {
  ------------------
  |  Branch (5924:7): [True: 23.4k, False: 47.1k]
  ------------------
 5925|  23.4k|    d = d / damp;
 5926|  47.1k|  } else {
 5927|  47.1k|    d = d / 2;
 5928|  47.1k|  }
 5929|  70.6k|  d += d / n;
 5930|  70.6k|  int32_t k = 0;
 5931|  92.4k|  while (d > ((base - tmin) * tmax) / 2) {
  ------------------
  |  Branch (5931:10): [True: 21.7k, False: 70.6k]
  ------------------
 5932|  21.7k|    d /= base - tmin;
 5933|  21.7k|    k += base;
 5934|  21.7k|  }
 5935|  70.6k|  return k + (((base - tmin + 1) * d) / (d + skew));
 5936|  70.6k|}
simple_absolute.cc:_ZN3ada4idnaL13digit_to_charEi:
 5919|   166k|static constexpr char digit_to_char(int32_t digit) {
 5920|   166k|  return digit < 26 ? char(digit + 97) : char(digit + 22);
  ------------------
  |  Branch (5920:10): [True: 74.6k, False: 91.4k]
  ------------------
 5921|   166k|}
simple_absolute.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_0clIA2_jEEDaRKT_:
 6327|   207k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
simple_absolute.cc:_ZN3ada4idnaL17is_contextj_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6232|  23.3k|inline static bool is_contextj_valid(std::u32string_view label) noexcept {
 6233|  23.3k|  constexpr uint8_t virama_ccc = 9;
 6234|   199k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (6234:22): [True: 175k, False: 23.1k]
  ------------------
 6235|   175k|    if (label[i] != 0x200C && label[i] != 0x200D) {
  ------------------
  |  Branch (6235:9): [True: 175k, False: 654]
  |  Branch (6235:31): [True: 175k, False: 70]
  ------------------
 6236|   175k|      continue;
 6237|   175k|    }
 6238|       |    // If Canonical_Combining_Class(Before(cp)) .eq. Virama Then True;
 6239|    724|    if (i > 0 && get_ccc(label[i - 1]) == virama_ccc) {
  ------------------
  |  Branch (6239:9): [True: 712, False: 12]
  |  Branch (6239:18): [True: 80, False: 632]
  ------------------
 6240|     80|      continue;
 6241|     80|    }
 6242|    644|    if (label[i] == 0x200D) {
  ------------------
  |  Branch (6242:9): [True: 8, False: 636]
  ------------------
 6243|      8|      return false;
 6244|      8|    }
 6245|       |    // If RegExpMatch((Joining_Type:{L,D})(Joining_Type:T)*\u200C
 6246|       |    //    (Joining_Type:T)*(Joining_Type:{R,D})) Then True;
 6247|    636|    joining_type before = joining_type::U;
 6248|  1.13k|    for (size_t j = i; j > 0;) {
  ------------------
  |  Branch (6248:24): [True: 1.12k, False: 6]
  ------------------
 6249|  1.12k|      before = find_joining_type(label[--j]);
 6250|  1.12k|      if (before != joining_type::T) {
  ------------------
  |  Branch (6250:11): [True: 630, False: 494]
  ------------------
 6251|    630|        break;
 6252|    630|      }
 6253|  1.12k|    }
 6254|    636|    if (before != joining_type::L && before != joining_type::D) {
  ------------------
  |  Branch (6254:9): [True: 630, False: 6]
  |  Branch (6254:38): [True: 88, False: 542]
  ------------------
 6255|     88|      return false;
 6256|     88|    }
 6257|    548|    joining_type after = joining_type::U;
 6258|    904|    for (size_t j = i + 1; j < label.size(); j++) {
  ------------------
  |  Branch (6258:28): [True: 866, False: 38]
  ------------------
 6259|    866|      after = find_joining_type(label[j]);
 6260|    866|      if (after != joining_type::T) {
  ------------------
  |  Branch (6260:11): [True: 510, False: 356]
  ------------------
 6261|    510|        break;
 6262|    510|      }
 6263|    866|    }
 6264|    548|    if (after != joining_type::R && after != joining_type::D) {
  ------------------
  |  Branch (6264:9): [True: 356, False: 192]
  |  Branch (6264:37): [True: 102, False: 254]
  ------------------
 6265|    102|      return false;
 6266|    102|    }
 6267|    548|  }
 6268|  23.1k|  return true;
 6269|  23.3k|}
simple_absolute.cc:_ZN3ada4idnaL17find_joining_typeEj:
 6224|  1.99k|inline static joining_type find_joining_type(uint32_t code_point) noexcept {
 6225|  1.99k|  size_t i =
 6226|  1.99k|      find_range(joining_start, joining_final, joining_table_count, code_point);
 6227|  1.99k|  return i == joining_table_count ? joining_type::U
  ------------------
  |  Branch (6227:10): [True: 144, False: 1.84k]
  ------------------
 6228|  1.99k|                                  : static_cast<joining_type>(joining_value[i]);
 6229|  1.99k|}
simple_absolute.cc:_ZN3ada4idnaL10find_rangeEPKjS2_mj:
 6203|   214k|                                size_t count, uint32_t code_point) noexcept {
 6204|   214k|  size_t lo = 0, hi = count;
 6205|  2.44M|  while (lo < hi) {
  ------------------
  |  Branch (6205:10): [True: 2.22M, False: 214k]
  ------------------
 6206|  2.22M|    size_t mid = lo + (hi - lo) / 2;
 6207|  2.22M|    if (last[mid] < code_point) {
  ------------------
  |  Branch (6207:9): [True: 609k, False: 1.61M]
  ------------------
 6208|   609k|      lo = mid + 1;
 6209|  1.61M|    } else {
 6210|  1.61M|      hi = mid;
 6211|  1.61M|    }
 6212|  2.22M|  }
 6213|   214k|  return lo < count && code_point >= start[lo] ? lo : count;
  ------------------
  |  Branch (6213:10): [True: 214k, False: 0]
  |  Branch (6213:24): [True: 214k, False: 144]
  ------------------
 6214|   214k|}
simple_absolute.cc:_ZN3ada4idnaL20find_last_not_of_nsmENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6272|  23.1k|    const std::u32string_view label) noexcept {
 6273|  25.6k|  for (int i = static_cast<int>(label.size() - 1); i >= 0; i--)
  ------------------
  |  Branch (6273:52): [True: 25.6k, False: 0]
  ------------------
 6274|  25.6k|    if (find_direction(label[i]) != direction::NSM) return i;
  ------------------
  |  Branch (6274:9): [True: 23.1k, False: 2.48k]
  ------------------
 6275|       |
 6276|      0|  return std::u32string_view::npos;
 6277|  23.1k|}
simple_absolute.cc:_ZN3ada4idnaL12is_rtl_labelENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6281|  23.1k|inline static bool is_rtl_label(const std::u32string_view label) noexcept {
 6282|  23.1k|  const size_t mask =
 6283|  23.1k|      (1u << direction::R) | (1u << direction::AL) | (1u << direction::AN);
 6284|       |
 6285|  23.1k|  size_t directions = 0;
 6286|   195k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (6286:22): [True: 172k, False: 23.1k]
  ------------------
 6287|   172k|    directions |= 1u << find_direction(label[i]);
 6288|   172k|  }
 6289|  23.1k|  return (directions & mask) != 0;
 6290|  23.1k|}
simple_absolute.cc:_ZN3ada4idnaL14find_directionEj:
 6217|   212k|inline static direction find_direction(uint32_t code_point) noexcept {
 6218|   212k|  size_t i = find_range(dir_start, dir_final, dir_table_count, code_point);
 6219|   212k|  return i == dir_table_count ? direction::NONE
  ------------------
  |  Branch (6219:10): [True: 0, False: 212k]
  ------------------
 6220|   212k|                              : static_cast<direction>(dir_value[i]);
 6221|   212k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6552|   127k|bool constexpr is_ascii(std::string_view view) {
 6553|  1.92M|  for (uint8_t c : view) {
  ------------------
  |  Branch (6553:18): [True: 1.92M, False: 122k]
  ------------------
 6554|  1.92M|    if (c >= 0x80) {
  ------------------
  |  Branch (6554:9): [True: 5.02k, False: 1.92M]
  ------------------
 6555|  5.02k|      return false;
 6556|  5.02k|    }
 6557|  1.92M|  }
 6558|   122k|  return true;
 6559|   127k|}
simple_absolute.cc:_ZN3ada4idnaL19from_ascii_to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6589|   122k|static void from_ascii_to_ascii(std::string_view ut8_string, std::string& out) {
 6590|   122k|  out.assign(ut8_string);
 6591|   122k|  ascii_map(out.data(), out.size());
 6592|   122k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6543|  33.2k|bool constexpr is_ascii(std::u32string_view view) {
 6544|   112k|  for (uint32_t c : view) {
  ------------------
  |  Branch (6544:19): [True: 112k, False: 3.35k]
  ------------------
 6545|   112k|    if (c >= 0x80) {
  ------------------
  |  Branch (6545:9): [True: 29.9k, False: 82.6k]
  ------------------
 6546|  29.9k|      return false;
 6547|  29.9k|    }
 6548|   112k|  }
 6549|  3.35k|  return true;
 6550|  33.2k|}
simple_absolute.cc:_ZN3ada4idnaL13is_ace_prefixENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6605|  26.7k|static bool is_ace_prefix(std::u32string_view label) noexcept {
 6606|  26.7k|  return label.size() >= 4 && label[0] == U'x' && label[1] == U'n' &&
  ------------------
  |  Branch (6606:10): [True: 21.7k, False: 5.02k]
  |  Branch (6606:31): [True: 3.51k, False: 18.2k]
  |  Branch (6606:51): [True: 3.26k, False: 252]
  ------------------
 6607|  3.26k|         label[2] == U'-' && label[3] == U'-';
  ------------------
  |  Branch (6607:10): [True: 3.04k, False: 220]
  |  Branch (6607:30): [True: 2.82k, False: 214]
  ------------------
 6608|  26.7k|}
simple_absolute.cc:_ZN3ada4idnaL18append_ascii_labelERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS1_17basic_string_viewIDiNS3_IDiEEEE:
 6595|  5.29k|static void append_ascii_label(std::string& out, std::u32string_view label) {
 6596|  5.29k|  const size_t old = out.size();
 6597|  5.29k|  out.resize(old + label.size());
 6598|  5.29k|  char* dest = out.data() + old;
 6599|  70.8k|  for (char32_t c : label) {
  ------------------
  |  Branch (6599:19): [True: 70.8k, False: 5.29k]
  ------------------
 6600|  70.8k|    *dest++ = static_cast<char>(c);
 6601|  70.8k|  }
 6602|  5.29k|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7620|   294k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7621|   294k|    return character_sets::bit_at(character_set, c);
 7622|   294k|  });
_ZN3ada7unicode18is_ascii_hex_digitEc:
 7354|  92.0k|ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept {
 7355|  92.0k|  return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
  ------------------
  |  Branch (7355:11): [True: 88.1k, False: 3.86k]
  |  Branch (7355:23): [True: 44.2k, False: 43.9k]
  |  Branch (7355:37): [True: 43.9k, False: 3.87k]
  |  Branch (7355:49): [True: 42.0k, False: 1.86k]
  ------------------
 7356|  5.74k|         (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7356:11): [True: 1.80k, False: 3.93k]
  |  Branch (7356:23): [True: 1.64k, False: 156]
  ------------------
 7357|  92.0k|}
_ZN3ada7unicode21convert_hex_to_binaryEc:
 7444|  85.8k|unsigned constexpr convert_hex_to_binary(const char c) noexcept {
 7445|  85.8k|  return hex_to_binary_table[c - '0'];
 7446|  85.8k|}
simple_absolute.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
 7588|   993k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7589|   993k|    return character_sets::bit_at(character_set, c);
 7590|   993k|  });
_ZN3ada7unicode36contains_forbidden_domain_code_pointEPKcm:
 7281|   125k|    const char* input, size_t length) noexcept {
 7282|   125k|  size_t i = 0;
 7283|   125k|  uint8_t accumulator{};
 7284|   674k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7284:10): [True: 548k, False: 125k]
  ------------------
 7285|   548k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7286|   548k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 1])];
 7287|   548k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 2])];
 7288|   548k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 3])];
 7289|   548k|  }
 7290|   199k|  for (; i < length; i++) {
  ------------------
  |  Branch (7290:10): [True: 73.3k, False: 125k]
  ------------------
 7291|  73.3k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7292|  73.3k|  }
 7293|   125k|  return accumulator;
 7294|   125k|}
simple_absolute.cc:_ZN3ada7unicode12_GLOBAL__N_122percent_encode_to_wideEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7974|    863|                            const uint8_t character_set[], std::string& out) {
 7975|       |  // Worst case every byte becomes %XX. Avoids realloc while walking windows.
 7976|    863|  out.reserve(out.size() + static_cast<size_t>(end - p) * 3);
 7977|    863|#if ADA_UNICODE_SSSE3
 7978|    863|  const ssse3_percent_tables tables = load_ssse3_percent_tables(character_set);
 7979|    863|  percent_encode_to_ssse3(p, end, character_set, tables, out);
 7980|       |#elif ADA_NEON
 7981|       |  percent_encode_to_neon(p, end, character_set,
 7982|       |                         load_neon_percent_table(character_set), out);
 7983|       |#elif ADA_RVV
 7984|       |  percent_encode_to_rvv(p, end, character_set, out);
 7985|       |#endif
 7986|    863|}
simple_absolute.cc:_ZN3ada7unicode12_GLOBAL__N_125load_ssse3_percent_tablesEPKh:
 7801|    863|load_ssse3_percent_tables(const uint8_t character_set[]) noexcept {
 7802|    863|  ssse3_percent_tables t{};
 7803|    863|  t.cs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set));
 7804|    863|  t.cs_hi =
 7805|    863|      _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set + 16));
 7806|       |  // 1 << (0..7), duplicated so pshufb(index & 7) works for every lane.
 7807|    863|  t.pow2 =
 7808|    863|      _mm_setr_epi8(1, 2, 4, 8, 16, 32, 64, -128, 1, 2, 4, 8, 16, 32, 64, -128);
 7809|    863|  t.mask_0f = _mm_set1_epi8(0x0F);
 7810|    863|  t.mask_07 = _mm_set1_epi8(0x07);
 7811|    863|  t.zero = _mm_setzero_si128();
 7812|    863|  return t;
 7813|    863|}
simple_absolute.cc:_ZN3ada7unicode12_GLOBAL__N_123percent_encode_to_ssse3EPKcS3_PKhRKNS1_20ssse3_percent_tablesERNSt3__112basic_stringIcNS9_11char_traitsIcEENS9_9allocatorIcEEEE:
 7832|    863|    const ssse3_percent_tables& tables, std::string& out) {
 7833|       |  // Pair 16-byte windows so a fully clean 32-byte run is one append.
 7834|  3.47k|  while (p + 32 <= end) {
  ------------------
  |  Branch (7834:10): [True: 2.60k, False: 863]
  ------------------
 7835|  2.60k|    const __m128i word0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7836|  2.60k|    const __m128i word1 =
 7837|  2.60k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
 7838|  2.60k|    const int mask0 = ssse3_percent_mask(word0, tables);
 7839|  2.60k|    const int mask1 = ssse3_percent_mask(word1, tables);
 7840|  2.60k|    if ((mask0 | mask1) == 0) {
  ------------------
  |  Branch (7840:9): [True: 360, False: 2.24k]
  ------------------
 7841|    360|      out.append(p, 32);
 7842|  2.24k|    } else {
 7843|  2.24k|      if (mask0 == 0) {
  ------------------
  |  Branch (7843:11): [True: 70, False: 2.17k]
  ------------------
 7844|     70|        out.append(p, 16);
 7845|  2.17k|      } else {
 7846|  2.17k|        encode_mask_window(p, static_cast<uint32_t>(mask0), 16, out);
 7847|  2.17k|      }
 7848|  2.24k|      if (mask1 == 0) {
  ------------------
  |  Branch (7848:11): [True: 328, False: 1.91k]
  ------------------
 7849|    328|        out.append(p + 16, 16);
 7850|  1.91k|      } else {
 7851|  1.91k|        encode_mask_window(p + 16, static_cast<uint32_t>(mask1), 16, out);
 7852|  1.91k|      }
 7853|  2.24k|    }
 7854|  2.60k|    p += 32;
 7855|  2.60k|  }
 7856|    863|  if (p + 16 <= end) {
  ------------------
  |  Branch (7856:7): [True: 567, False: 296]
  ------------------
 7857|    567|    const __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7858|    567|    const int mask = ssse3_percent_mask(word, tables);
 7859|    567|    if (mask == 0) {
  ------------------
  |  Branch (7859:9): [True: 172, False: 395]
  ------------------
 7860|    172|      out.append(p, 16);
 7861|    395|    } else {
 7862|    395|      encode_mask_window(p, static_cast<uint32_t>(mask), 16, out);
 7863|    395|    }
 7864|    567|    p += 16;
 7865|    567|  }
 7866|    863|  percent_encode_to_scalar(p, end, character_set, out);
 7867|    863|}
simple_absolute.cc:_ZN3ada7unicode12_GLOBAL__N_118ssse3_percent_maskEDv2_xRKNS1_20ssse3_percent_tablesE:
 7816|  5.78k|    __m128i word, const ssse3_percent_tables& tables) noexcept {
 7817|  5.78k|  const __m128i idx = _mm_and_si128(_mm_srli_epi16(word, 3), tables.mask_0f);
 7818|  5.78k|  const __m128i lo = _mm_shuffle_epi8(tables.cs_lo, idx);
 7819|  5.78k|  const __m128i hi = _mm_shuffle_epi8(tables.cs_hi, idx);
 7820|       |  // Bytes 0x80-0xFF are signed-negative; select the high half of the bitmap.
 7821|  5.78k|  const __m128i high_byte = _mm_cmpgt_epi8(tables.zero, word);
 7822|  5.78k|  const __m128i cs_byte = _mm_or_si128(_mm_and_si128(hi, high_byte),
 7823|  5.78k|                                       _mm_andnot_si128(high_byte, lo));
 7824|  5.78k|  const __m128i bits =
 7825|  5.78k|      _mm_shuffle_epi8(tables.pow2, _mm_and_si128(word, tables.mask_07));
 7826|  5.78k|  const __m128i hits = _mm_and_si128(cs_byte, bits);
 7827|  5.78k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hits, tables.zero)) ^ 0xFFFF;
 7828|  5.78k|}
simple_absolute.cc:_ZN3ada7unicode12_GLOBAL__N_118encode_mask_windowEPKcjmRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE:
 7756|  4.49k|                                          size_t width, std::string& out) {
 7757|  4.49k|  uint64_t bits = mask;
 7758|  4.49k|  size_t off = 0;
 7759|  63.3k|  while (bits != 0) {
  ------------------
  |  Branch (7759:10): [True: 58.8k, False: 4.49k]
  ------------------
 7760|  58.8k|    const int zero_run = trailing_zeroes32(static_cast<uint32_t>(bits));
 7761|  58.8k|    if (zero_run != 0) {
  ------------------
  |  Branch (7761:9): [True: 2.75k, False: 56.0k]
  ------------------
 7762|  2.75k|      out.append(p + off, static_cast<size_t>(zero_run));
 7763|  2.75k|    }
 7764|  58.8k|    off += static_cast<size_t>(zero_run);
 7765|  58.8k|    out.append(character_sets::hex + uint8_t(p[off]) * 4, 3);
 7766|  58.8k|    ++off;
 7767|  58.8k|    bits >>= static_cast<unsigned>(zero_run + 1);
 7768|  58.8k|  }
 7769|  4.49k|  if (off < width) {
  ------------------
  |  Branch (7769:7): [True: 971, False: 3.52k]
  ------------------
 7770|    971|    out.append(p + off, width - off);
 7771|    971|  }
 7772|  4.49k|}
simple_absolute.cc:_ZN3ada7unicode12_GLOBAL__N_117trailing_zeroes32Ej:
 7744|  58.8k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
 7745|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 7746|       |  unsigned long ret;
 7747|       |  _BitScanForward(&ret, input_num);
 7748|       |  return static_cast<int>(ret);
 7749|       |#else
 7750|  58.8k|  return __builtin_ctz(input_num);
 7751|  58.8k|#endif
 7752|  58.8k|}
simple_absolute.cc:_ZN3ada7unicode12_GLOBAL__N_124percent_encode_to_scalarEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7777|    863|                                                std::string& out) {
 7778|  4.60k|  for (; p != end; ++p) {
  ------------------
  |  Branch (7778:10): [True: 3.74k, False: 863]
  ------------------
 7779|  3.74k|    if (character_sets::bit_at(character_set, *p)) {
  ------------------
  |  Branch (7779:9): [True: 2.10k, False: 1.63k]
  ------------------
 7780|  2.10k|      out.append(character_sets::hex + uint8_t(*p) * 4, 3);
 7781|  2.10k|    } else {
 7782|  1.63k|      out += *p;
 7783|  1.63k|    }
 7784|  3.74k|  }
 7785|    863|}
simple_absolute.cc:_ZN3ada11serializers12_GLOBAL__N_113write_hex_u16EPct:
 8047|  1.91k|ada_really_inline char* write_hex_u16(char* p, uint16_t v) noexcept {
 8048|  1.91k|  static constexpr char kHex[] = "0123456789abcdef";
 8049|  1.91k|  if (v >= 0x1000) {
  ------------------
  |  Branch (8049:7): [True: 174, False: 1.74k]
  ------------------
 8050|    174|    *p++ = kHex[(v >> 12) & 0xf];
 8051|    174|    *p++ = kHex[(v >> 8) & 0xf];
 8052|    174|    *p++ = kHex[(v >> 4) & 0xf];
 8053|    174|    *p++ = kHex[v & 0xf];
 8054|  1.74k|  } else if (v >= 0x100) {
  ------------------
  |  Branch (8054:14): [True: 284, False: 1.46k]
  ------------------
 8055|    284|    *p++ = kHex[(v >> 8) & 0xf];
 8056|    284|    *p++ = kHex[(v >> 4) & 0xf];
 8057|    284|    *p++ = kHex[v & 0xf];
 8058|  1.46k|  } else if (v >= 0x10) {
  ------------------
  |  Branch (8058:14): [True: 288, False: 1.17k]
  ------------------
 8059|    288|    *p++ = kHex[(v >> 4) & 0xf];
 8060|    288|    *p++ = kHex[v & 0xf];
 8061|  1.17k|  } else {
 8062|  1.17k|    *p++ = kHex[v & 0xf];
 8063|  1.17k|  }
 8064|  1.91k|  return p;
 8065|  1.91k|}
simple_absolute.cc:_ZN3ada11serializers12_GLOBAL__N_18write_u8EPch:
 8033|   163k|ada_really_inline char* write_u8(char* p, uint8_t v) noexcept {
 8034|   163k|  if (v < 10) {
  ------------------
  |  Branch (8034:7): [True: 66.6k, False: 97.2k]
  ------------------
 8035|  66.6k|    *p++ = static_cast<char>('0' + v);
 8036|  97.2k|  } else if (v < 100) {
  ------------------
  |  Branch (8036:14): [True: 384, False: 96.8k]
  ------------------
 8037|    384|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v) * 2], 2);
 8038|    384|    p += 2;
 8039|  96.8k|  } else {
 8040|  96.8k|    *p++ = static_cast<char>('0' + v / 100);
 8041|  96.8k|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v % 100) * 2], 2);
 8042|  96.8k|    p += 2;
 8043|  96.8k|  }
 8044|   163k|  return p;
 8045|   163k|}
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7130|   635k|    std::string_view user_input) noexcept {
 7131|       |  // first check for short strings in which case we do it naively.
 7132|   635k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (7132:7): [True: 124k, False: 510k]
  ------------------
 7133|   124k|    return std::ranges::any_of(user_input, is_tabs_or_newline);
 7134|   124k|  }
 7135|       |  // fast path for long strings (expected to be common)
 7136|   510k|  size_t i = 0;
 7137|   510k|  const __m128i mask1 = _mm_set1_epi8('\r');
 7138|   510k|  const __m128i mask2 = _mm_set1_epi8('\n');
 7139|   510k|  const __m128i mask3 = _mm_set1_epi8('\t');
 7140|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
 7141|   510k|  __m128i running{0};
 7142|  1.22M|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (7142:10): [True: 717k, False: 510k]
  ------------------
 7143|   717k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
 7144|   717k|    running = _mm_or_si128(
 7145|   717k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 7146|   717k|                                           _mm_cmpeq_epi8(word, mask2))),
 7147|   717k|        _mm_cmpeq_epi8(word, mask3));
 7148|   717k|  }
 7149|   510k|  if (i < user_input.size()) {
  ------------------
  |  Branch (7149:7): [True: 505k, False: 4.67k]
  ------------------
 7150|   505k|    __m128i word = _mm_loadu_si128(
 7151|   505k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
 7152|   505k|    running = _mm_or_si128(
 7153|   505k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 7154|   505k|                                           _mm_cmpeq_epi8(word, mask2))),
 7155|   505k|        _mm_cmpeq_epi8(word, mask3));
 7156|   505k|  }
 7157|   510k|  return _mm_movemask_epi8(running) != 0;
 7158|   635k|}
_ZN3ada7unicode18is_tabs_or_newlineEc:
 7016|  1.58M|constexpr bool is_tabs_or_newline(char c) noexcept {
 7017|  1.58M|  return c == '\r' || c == '\n' || c == '\t';
  ------------------
  |  Branch (7017:10): [True: 56, False: 1.58M]
  |  Branch (7017:23): [True: 62, False: 1.58M]
  |  Branch (7017:36): [True: 66, False: 1.58M]
  ------------------
 7018|  1.58M|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 8842|  1.01k|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
 8843|       |  // Fast path: inputs with no tab/newline (the common case) avoid the
 8844|       |  // per-character erase_if scan. has_tabs_or_newline uses SIMD for inputs
 8845|       |  // >= 16 bytes, making this check much cheaper than erase_if's scalar
 8846|       |  // predicate walk when there is nothing to remove.
 8847|  1.01k|  if (!ada::unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (8847:7): [True: 0, False: 1.01k]
  ------------------
 8848|      0|    return;
 8849|      0|  }
 8850|  1.01k|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
 8851|  1.01k|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
 7375|  79.8k|    const char c) noexcept {
 7376|  79.8k|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (7376:10): [True: 3.20k, False: 76.5k]
  |  Branch (7376:23): [True: 1.42k, False: 75.1k]
  |  Branch (7376:36): [True: 260, False: 74.9k]
  ------------------
 7377|  79.8k|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
 7370|  1.12M|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
 7371|  1.12M|  return (unsigned char)c <= ' ';
 7372|  1.12M|}
_ZN3ada7helpers19parse_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 9547|   239k|                                           std::string& path) {
 9548|   239k|  ada_log("parse_prepared_path ", input);
 9549|   239k|  uint8_t accumulator = checkers::path_signature(input);
 9550|       |  // Let us first detect a trivial case.
 9551|       |  // If it is special, we check that we have no dot, no %,  no \ and no
 9552|       |  // character needing percent encoding. Otherwise, we check that we have no %,
 9553|       |  // no dot, and no character needing percent encoding.
 9554|   239k|  constexpr uint8_t need_encoding = 1;
 9555|   239k|  constexpr uint8_t backslash_char = 2;
 9556|   239k|  constexpr uint8_t dot_char = 4;
 9557|   239k|  constexpr uint8_t percent_char = 8;
 9558|   239k|  bool special = type != ada::scheme::NOT_SPECIAL;
 9559|   239k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (9559:39): [True: 1.29k, False: 237k]
  ------------------
 9560|  1.29k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (9560:39): [True: 43, False: 1.25k]
  ------------------
 9561|   239k|  bool trivial_path =
 9562|   239k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (9562:7): [True: 208k, False: 30.4k]
  |  Branch (9562:8): [True: 238k, False: 810]
  ------------------
 9563|   239k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
 9564|    810|                  0)) &&
 9565|   208k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (9565:7): [True: 208k, False: 11]
  ------------------
 9566|   239k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (9566:7): [True: 13.0k, False: 226k]
  |  Branch (9566:34): [True: 12.9k, False: 24]
  ------------------
 9567|       |    // '4' means that we have at least one dot, but nothing that requires
 9568|       |    // percent encoding or decoding. The only part that is not trivial is
 9569|       |    // that we may have single dots and double dots path segments.
 9570|       |    // If we have such segments, then we either have a path that begins
 9571|       |    // with '.' (easy to check), or we have the sequence './'.
 9572|       |    // Note: input cannot be empty, it must at least contain one character ('.')
 9573|       |    // Note: we know that '\' is not present.
 9574|  12.9k|    if (input[0] != '.') {
  ------------------
  |  Branch (9574:9): [True: 11.7k, False: 1.28k]
  ------------------
 9575|  11.7k|      size_t slashdot = 0;
 9576|  11.7k|      bool dot_is_file = true;
 9577|  37.9k|      for (;;) {
 9578|  37.9k|        slashdot = input.find("/.", slashdot);
 9579|  37.9k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (9579:13): [True: 11.7k, False: 26.2k]
  ------------------
 9580|  11.7k|          break;
 9581|  26.2k|        } else {  // uncommon
 9582|       |          // only three cases matter: /./, /.. or a final /
 9583|  26.2k|          slashdot += 2;
 9584|  26.2k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (9584:28): [True: 74, False: 26.1k]
  |  Branch (9584:56): [True: 12.4k, False: 13.7k]
  ------------------
 9585|  13.7k|                           input[slashdot] == '/');
  ------------------
  |  Branch (9585:28): [True: 12.5k, False: 1.17k]
  ------------------
 9586|  26.2k|        }
 9587|  37.9k|      }
 9588|  11.7k|      trivial_path = dot_is_file;
 9589|  11.7k|    }
 9590|  12.9k|  }
 9591|   239k|  if (trivial_path) {
  ------------------
  |  Branch (9591:7): [True: 209k, False: 29.3k]
  ------------------
 9592|   209k|    ada_log("parse_path trivial");
 9593|   209k|    path += '/';
 9594|   209k|    path += input;
 9595|   209k|    return;
 9596|   209k|  }
 9597|       |  // We are going to need to look a bit at the path, but let us see if we can
 9598|       |  // ignore percent encoding *and* backslashes *and* percent characters.
 9599|       |  // Except for the trivial case, this is likely to capture 99% of paths out
 9600|       |  // there.
 9601|  29.3k|  bool fast_path =
 9602|  29.3k|      (special &&
  ------------------
  |  Branch (9602:8): [True: 28.9k, False: 396]
  ------------------
 9603|  28.9k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (9603:8): [True: 11.8k, False: 17.1k]
  ------------------
 9604|  11.8k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (9604:7): [True: 11.7k, False: 105]
  ------------------
 9605|  29.3k|  if (fast_path) {
  ------------------
  |  Branch (9605:7): [True: 11.7k, False: 17.6k]
  ------------------
 9606|  11.7k|    ada_log("parse_prepared_path fast");
 9607|       |    // Here we don't need to worry about \ or percent encoding.
 9608|       |    // We also do not have a file protocol. We might have dots, however,
 9609|       |    // but dots must as appear as '.', and they cannot be encoded because
 9610|       |    // the symbol '%' is not present.
 9611|  11.7k|    size_t previous_location = 0;  // We start at 0.
 9612|  72.3k|    do {
 9613|  72.3k|      size_t new_location = input.find('/', previous_location);
 9614|       |      // std::string_view path_view = input;
 9615|       |      //  We process the last segment separately:
 9616|  72.3k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (9616:11): [True: 11.7k, False: 60.6k]
  ------------------
 9617|  11.7k|        std::string_view path_view = input.substr(previous_location);
 9618|  11.7k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (9618:13): [True: 79, False: 11.6k]
  ------------------
 9619|       |          // e.g., if you receive ".." with an empty path, you go to "/".
 9620|     79|          if (path.empty()) {
  ------------------
  |  Branch (9620:15): [True: 18, False: 61]
  ------------------
 9621|     18|            path = '/';
 9622|     18|            return;
 9623|     18|          }
 9624|       |          // Fast case where we have nothing to do:
 9625|     61|          if (path.back() == '/') {
  ------------------
  |  Branch (9625:15): [True: 18, False: 43]
  ------------------
 9626|     18|            return;
 9627|     18|          }
 9628|       |          // If you have the path "/joe/myfriend",
 9629|       |          // then you delete 'myfriend'.
 9630|     43|          path.resize(path.rfind('/') + 1);
 9631|     43|          return;
 9632|     61|        }
 9633|  11.6k|        path += '/';
 9634|  11.6k|        if (path_view != ".") {
  ------------------
  |  Branch (9634:13): [True: 11.5k, False: 105]
  ------------------
 9635|  11.5k|          path.append(path_view);
 9636|  11.5k|        }
 9637|  11.6k|        return;
 9638|  60.6k|      } else {
 9639|       |        // This is a non-final segment.
 9640|  60.6k|        std::string_view path_view =
 9641|  60.6k|            input.substr(previous_location, new_location - previous_location);
 9642|  60.6k|        previous_location = new_location + 1;
 9643|  60.6k|        if (path_view == "..") {
  ------------------
  |  Branch (9643:13): [True: 12.9k, False: 47.7k]
  ------------------
 9644|  12.9k|          size_t last_delimiter = path.rfind('/');
 9645|  12.9k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (9645:15): [True: 11.0k, False: 1.81k]
  ------------------
 9646|  11.0k|            path.erase(last_delimiter);
 9647|  11.0k|          }
 9648|  47.7k|        } else if (path_view != ".") {
  ------------------
  |  Branch (9648:20): [True: 34.4k, False: 13.2k]
  ------------------
 9649|  34.4k|          path += '/';
 9650|  34.4k|          path.append(path_view);
 9651|  34.4k|        }
 9652|  60.6k|      }
 9653|  72.3k|    } while (true);
  ------------------
  |  Branch (9653:14): [True: 60.6k, Folded]
  ------------------
 9654|  17.6k|  } else {
 9655|  17.6k|    ada_log("parse_path slow");
 9656|       |    // we have reached the general case
 9657|  17.6k|    bool needs_percent_encoding = (accumulator & 1);
 9658|  64.7k|    do {
 9659|  64.7k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (9659:26): [True: 63.0k, False: 1.73k]
  |  Branch (9659:37): [True: 4.49k, False: 58.5k]
  ------------------
 9660|  64.7k|                            ? input.find_first_of("/\\")
 9661|  64.7k|                            : input.find('/');
 9662|  64.7k|      std::string_view path_view = input;
 9663|  64.7k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (9663:11): [True: 47.1k, False: 17.6k]
  ------------------
 9664|  47.1k|        path_view.remove_suffix(path_view.size() - location);
 9665|  47.1k|        input.remove_prefix(location + 1);
 9666|  47.1k|      }
 9667|       |      // Path encoding preserves dots and percent signs, so normalize first.
 9668|  64.7k|      if (unicode::is_double_dot_path_segment(path_view)) {
  ------------------
  |  Branch (9668:11): [True: 12.8k, False: 51.9k]
  ------------------
 9669|  12.8k|        helpers::shorten_path(path, type);
 9670|  12.8k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9670:13): [True: 9.80k, False: 3.06k]
  ------------------
 9671|  9.80k|          path += '/';
 9672|  9.80k|        }
 9673|  51.9k|      } else if (unicode::is_single_dot_path_segment(path_view) &&
  ------------------
  |  Branch (9673:18): [True: 2.25k, False: 49.6k]
  ------------------
 9674|  2.25k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (9674:18): [True: 406, False: 1.84k]
  ------------------
 9675|    406|        path += '/';
 9676|    406|      }
 9677|       |      // Otherwise, if path_view is not a single-dot path segment, then:
 9678|  51.5k|      else if (!unicode::is_single_dot_path_segment(path_view)) {
  ------------------
  |  Branch (9678:16): [True: 49.6k, False: 1.84k]
  ------------------
 9679|       |        // If url's scheme is "file", url's path is empty, and path_view is a
 9680|       |        // Windows drive letter, then replace the second code point in
 9681|       |        // path_view with U+003A (:).
 9682|  49.6k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (9682:13): [True: 1.78k, False: 47.8k]
  |  Branch (9682:48): [True: 501, False: 1.27k]
  ------------------
 9683|    501|            path_view.size() == 2 &&
  ------------------
  |  Branch (9683:13): [True: 91, False: 410]
  ------------------
 9684|     91|            checkers::is_windows_drive_letter(path_view)) {
  ------------------
  |  Branch (9684:13): [True: 45, False: 46]
  ------------------
 9685|     45|          path += '/';
 9686|     45|          path += path_view[0];
 9687|     45|          path += ':';
 9688|     45|          path_view.remove_prefix(2);
 9689|     45|          path.append(path_view);
 9690|  49.6k|        } else {
 9691|       |          // Append encoded bytes directly to the pathname.
 9692|  49.6k|          path += '/';
 9693|  49.6k|          if (!needs_percent_encoding ||
  ------------------
  |  Branch (9693:15): [True: 38.6k, False: 10.9k]
  ------------------
 9694|  10.9k|              !ada::unicode::percent_encode<true>(
  ------------------
  |  Branch (9694:15): [True: 7.45k, False: 3.50k]
  ------------------
 9695|  46.1k|                  path_view, character_sets::PATH_PERCENT_ENCODE, path)) {
 9696|  46.1k|            path.append(path_view);
 9697|  46.1k|          }
 9698|  49.6k|        }
 9699|  49.6k|      }
 9700|  64.7k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (9700:11): [True: 17.6k, False: 47.1k]
  ------------------
 9701|  17.6k|        return;
 9702|  17.6k|      }
 9703|  64.7k|    } while (true);
  ------------------
  |  Branch (9703:14): [True: 47.1k, Folded]
  ------------------
 9704|  17.6k|  }
 9705|  29.3k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   77|   478k|    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|   478k|  size_t i = 0;
   84|   478k|  uint8_t accumulator{};
   85|   640k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (85:10): [True: 162k, False: 478k]
  ------------------
   86|   162k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   87|   162k|                           path_signature_table[uint8_t(input[i + 1])] |
   88|   162k|                           path_signature_table[uint8_t(input[i + 2])] |
   89|   162k|                           path_signature_table[uint8_t(input[i + 3])] |
   90|   162k|                           path_signature_table[uint8_t(input[i + 4])] |
   91|   162k|                           path_signature_table[uint8_t(input[i + 5])] |
   92|   162k|                           path_signature_table[uint8_t(input[i + 6])] |
   93|   162k|                           path_signature_table[uint8_t(input[i + 7])]);
   94|   162k|  }
   95|   767k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (95:10): [True: 289k, False: 478k]
  ------------------
   96|   289k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
   97|   289k|  }
   98|   478k|  return accumulator;
   99|   478k|}
_ZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7383|   129k|    std::string_view input) noexcept {
 7384|       |  // This will catch most cases:
 7385|       |  // The length must be 2,4 or 6.
 7386|       |  // We divide by two and require
 7387|       |  // that the result be between 1 and 3 inclusively.
 7388|   129k|  uint64_t half_length = uint64_t(input.size()) / 2;
 7389|   129k|  if (half_length - 1 > 2) {
  ------------------
  |  Branch (7389:7): [True: 58.9k, False: 70.6k]
  ------------------
 7390|  58.9k|    return false;
 7391|  58.9k|  }
 7392|       |  // We have a string of length 2, 4 or 6.
 7393|       |  // We now check the first character:
 7394|  70.6k|  if ((input[0] != '.') && (input[0] != '%')) {
  ------------------
  |  Branch (7394:7): [True: 57.8k, False: 12.8k]
  |  Branch (7394:28): [True: 27.4k, False: 30.4k]
  ------------------
 7395|  27.4k|    return false;
 7396|  27.4k|  }
 7397|       |  // We are unlikely the get beyond this point.
 7398|  43.2k|  int hash_value = (input.size() + (unsigned)(input[0])) & 3;
 7399|  43.2k|  const std::string_view target = table_is_double_dot_path_segment[hash_value];
 7400|  43.2k|  if (target.size() != input.size()) {
  ------------------
  |  Branch (7400:7): [True: 7.84k, False: 35.4k]
  ------------------
 7401|  7.84k|    return false;
 7402|  7.84k|  }
 7403|       |  // We almost never get here.
 7404|       |  // Optimizing the rest is relatively unimportant.
 7405|  35.4k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7406|  35.4k|    uint16_t A, B;
 7407|  35.4k|    memcpy(&A, a.data(), sizeof(A));
 7408|  35.4k|    memcpy(&B, b.data(), sizeof(B));
 7409|  35.4k|    return A == B;
 7410|  35.4k|  };
 7411|  35.4k|  if (!prefix_equal_unsafe(input, target)) {
  ------------------
  |  Branch (7411:7): [True: 4.12k, False: 31.2k]
  ------------------
 7412|  4.12k|    return false;
 7413|  4.12k|  }
 7414|   116k|  for (size_t i = 2; i < input.size(); i++) {
  ------------------
  |  Branch (7414:22): [True: 90.9k, False: 25.7k]
  ------------------
 7415|  90.9k|    char c = input[i];
 7416|  90.9k|    if ((uint8_t((c | 0x20) - 0x61) <= 25 ? (c | 0x20) : c) != target[i]) {
  ------------------
  |  Branch (7416:9): [True: 5.53k, False: 85.4k]
  |  Branch (7416:10): [True: 43.9k, False: 47.0k]
  ------------------
 7417|  5.53k|      return false;
 7418|  5.53k|    }
 7419|  90.9k|  }
 7420|  25.7k|  return true;
 7421|       |  // The above code might be a bit better than the code below. Compilers
 7422|       |  // are not stupid and may use the fact that these strings have length 2,4 and
 7423|       |  // 6 and other tricks.
 7424|       |  // return input == ".." ||
 7425|       |  //  input == ".%2e" || input == ".%2E" ||
 7426|       |  //  input == "%2e." || input == "%2E." ||
 7427|       |  //  input == "%2e%2e" || input == "%2E%2E" || input == "%2E%2e" || input ==
 7428|       |  //  "%2e%2E";
 7429|  31.2k|}
_ZZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_S5_E_clES5_S5_:
 7405|  35.4k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7406|  35.4k|    uint16_t A, B;
 7407|  35.4k|    memcpy(&A, a.data(), sizeof(A));
 7408|  35.4k|    memcpy(&B, b.data(), sizeof(B));
 7409|  35.4k|    return A == B;
 7410|  35.4k|  };
_ZN3ada7unicode26is_single_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7432|   206k|    std::string_view input) noexcept {
 7433|   206k|  return input == "." || input == "%2e" || input == "%2E";
  ------------------
  |  Branch (7433:10): [True: 4.33k, False: 202k]
  |  Branch (7433:26): [True: 3.85k, False: 198k]
  |  Branch (7433:44): [True: 22, False: 198k]
  ------------------
 7434|   206k|}
_ZN3ada7unicode28is_forbidden_host_code_pointEc:
 7253|  16.2k|    const char c) noexcept {
 7254|  16.2k|  return is_forbidden_host_code_point_table[uint8_t(c)];
 7255|  16.2k|}
_ZN3ada6detail17parse_ipv4_numberERPKcS2_RmRb:
 9829|   100k|                              bool& is_pure_decimal) noexcept {
 9830|   100k|  is_pure_decimal = false;
 9831|   100k|  if (p >= end) [[unlikely]] {
  ------------------
  |  Branch (9831:7): [True: 0, False: 100k]
  ------------------
 9832|      0|    return false;
 9833|      0|  }
 9834|   100k|  if (end - p >= 2 && p[0] == '0' && ((p[1] | 0x20) == 'x')) {
  ------------------
  |  Branch (9834:7): [True: 78.8k, False: 21.1k]
  |  Branch (9834:23): [True: 39.2k, False: 39.6k]
  |  Branch (9834:38): [True: 38.9k, False: 278]
  ------------------
 9835|  38.9k|    p += 2;
 9836|  38.9k|    if (p == end || *p == '.') {
  ------------------
  |  Branch (9836:9): [True: 62, False: 38.8k]
  |  Branch (9836:21): [True: 32, False: 38.8k]
  ------------------
 9837|     94|      value = 0;
 9838|     94|      return true;
 9839|     94|    }
 9840|  38.8k|    uint64_t v = 0;
 9841|  38.8k|    int digits = 0;
 9842|   232k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9842:12): [True: 213k, False: 19.2k]
  |  Branch (9842:23): [True: 193k, False: 19.5k]
  ------------------
 9843|   193k|      const uint8_t nib = hex_nibble[static_cast<unsigned char>(*p)];
 9844|   193k|      if (nib == 0xff) [[unlikely]] {
  ------------------
  |  Branch (9844:11): [True: 8, False: 193k]
  ------------------
 9845|      8|        return false;
 9846|      8|      }
 9847|   193k|      if (v > (0xFFFFFFFFULL >> 4)) [[unlikely]] {
  ------------------
  |  Branch (9847:11): [True: 82, False: 193k]
  ------------------
 9848|     82|        return false;
 9849|     82|      }
 9850|   193k|      v = (v << 4) | nib;
 9851|   193k|      ++p;
 9852|   193k|      ++digits;
 9853|   193k|    }
 9854|  38.7k|    if (digits == 0) [[unlikely]] {
  ------------------
  |  Branch (9854:9): [True: 0, False: 38.7k]
  ------------------
 9855|      0|      return false;
 9856|      0|    }
 9857|  38.7k|    value = v;
 9858|  38.7k|    return true;
 9859|  38.7k|  }
 9860|  61.0k|  if (end - p >= 2 && p[0] == '0' && p[1] >= '0' && p[1] <= '9') {
  ------------------
  |  Branch (9860:7): [True: 39.9k, False: 21.1k]
  |  Branch (9860:23): [True: 278, False: 39.6k]
  |  Branch (9860:38): [True: 162, False: 116]
  |  Branch (9860:53): [True: 150, False: 12]
  ------------------
 9861|    150|    ++p;
 9862|    150|    uint64_t v = 0;
 9863|  1.08k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9863:12): [True: 1.03k, False: 46]
  |  Branch (9863:23): [True: 1.00k, False: 30]
  ------------------
 9864|  1.00k|      const char c = *p;
 9865|  1.00k|      if (c < '0' || c > '7') [[unlikely]] {
  ------------------
  |  Branch (9865:11): [True: 8, False: 1.00k]
  |  Branch (9865:22): [True: 20, False: 980]
  ------------------
 9866|     28|        return false;
 9867|     28|      }
 9868|    980|      if (v > (0xFFFFFFFFULL >> 3)) [[unlikely]] {
  ------------------
  |  Branch (9868:11): [True: 46, False: 934]
  ------------------
 9869|     46|        return false;
 9870|     46|      }
 9871|    934|      v = (v << 3) | static_cast<uint64_t>(c - '0');
 9872|    934|      ++p;
 9873|    934|    }
 9874|     76|    value = v;
 9875|     76|    return true;
 9876|    150|  }
 9877|  60.9k|  if (*p < '0' || *p > '9') [[unlikely]] {
  ------------------
  |  Branch (9877:7): [True: 58, False: 60.8k]
  |  Branch (9877:19): [True: 37.9k, False: 22.8k]
  ------------------
 9878|  38.0k|    return false;
 9879|  38.0k|  }
 9880|  22.8k|  is_pure_decimal = true;
 9881|  22.8k|  uint64_t v = static_cast<uint64_t>(*p - '0');
 9882|  22.8k|  ++p;
 9883|  29.4k|  while (p < end && *p != '.') {
  ------------------
  |  Branch (9883:10): [True: 7.78k, False: 21.6k]
  |  Branch (9883:21): [True: 6.74k, False: 1.04k]
  ------------------
 9884|  6.74k|    const char c = *p;
 9885|  6.74k|    if (c < '0' || c > '9') [[unlikely]] {
  ------------------
  |  Branch (9885:9): [True: 26, False: 6.71k]
  |  Branch (9885:20): [True: 48, False: 6.66k]
  ------------------
 9886|     74|      return false;
 9887|     74|    }
 9888|  6.66k|    if (v > 429496729ULL) [[unlikely]] {
  ------------------
  |  Branch (9888:9): [True: 86, False: 6.58k]
  ------------------
 9889|     86|      return false;
 9890|     86|    }
 9891|  6.58k|    v = v * 10u + static_cast<uint64_t>(c - '0');
 9892|  6.58k|    if (v > 0xFFFFFFFFULL) [[unlikely]] {
  ------------------
  |  Branch (9892:9): [True: 8, False: 6.57k]
  ------------------
 9893|      8|      return false;
 9894|      8|    }
 9895|  6.57k|    ++p;
 9896|  6.57k|  }
 9897|  22.7k|  value = v;
 9898|  22.7k|  return true;
 9899|  22.8k|}
_ZN3ada6detail15parse_hex_pieceERPKcS2_Rt:
 9903|  2.24k|                           uint16_t& value) noexcept {
 9904|  2.24k|  if (pointer == end) {
  ------------------
  |  Branch (9904:7): [True: 0, False: 2.24k]
  ------------------
 9905|      0|    return 0;
 9906|      0|  }
 9907|  2.24k|  const uint8_t n0 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9908|  2.24k|  if (n0 == 0xff) {
  ------------------
  |  Branch (9908:7): [True: 50, False: 2.19k]
  ------------------
 9909|     50|    return 0;
 9910|     50|  }
 9911|  2.19k|  uint32_t v = n0;
 9912|  2.19k|  ++pointer;
 9913|  2.19k|  int length = 1;
 9914|  2.19k|  if (pointer != end) {
  ------------------
  |  Branch (9914:7): [True: 1.93k, False: 260]
  ------------------
 9915|  1.93k|    const uint8_t n1 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9916|  1.93k|    if (n1 != 0xff) {
  ------------------
  |  Branch (9916:9): [True: 826, False: 1.10k]
  ------------------
 9917|    826|      v = (v << 4) | n1;
 9918|    826|      ++pointer;
 9919|    826|      ++length;
 9920|    826|      if (pointer != end) {
  ------------------
  |  Branch (9920:11): [True: 758, False: 68]
  ------------------
 9921|    758|        const uint8_t n2 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9922|    758|        if (n2 != 0xff) {
  ------------------
  |  Branch (9922:13): [True: 512, False: 246]
  ------------------
 9923|    512|          v = (v << 4) | n2;
 9924|    512|          ++pointer;
 9925|    512|          ++length;
 9926|    512|          if (pointer != end) {
  ------------------
  |  Branch (9926:15): [True: 424, False: 88]
  ------------------
 9927|    424|            const uint8_t n3 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9928|    424|            if (n3 != 0xff) {
  ------------------
  |  Branch (9928:17): [True: 186, False: 238]
  ------------------
 9929|    186|              v = (v << 4) | n3;
 9930|    186|              ++pointer;
 9931|    186|              ++length;
 9932|    186|            }
 9933|    424|          }
 9934|    512|        }
 9935|    758|      }
 9936|    826|    }
 9937|  1.93k|  }
 9938|  2.19k|  value = static_cast<uint16_t>(v);
 9939|  2.19k|  return length;
 9940|  2.24k|}
_ZN3ada3url10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10515|  24.3k|ada_really_inline void url::parse_path(std::string_view input) {
10516|  24.3k|  ada_log("parse_path ", input);
10517|  24.3k|  std::string tmp_buffer;
10518|  24.3k|  std::string_view internal_input;
10519|  24.3k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (10519:7): [True: 0, False: 24.3k]
  ------------------
10520|      0|    tmp_buffer = input;
10521|       |    // Optimization opportunity: Instead of copying and then pruning, we could
10522|       |    // just directly build the string from user_input.
10523|      0|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
10524|      0|    internal_input = tmp_buffer;
10525|  24.3k|  } else {
10526|  24.3k|    internal_input = input;
10527|  24.3k|  }
10528|       |
10529|       |  // If url is special, then:
10530|  24.3k|  if (is_special()) {
  ------------------
  |  Branch (10530:7): [True: 24.3k, False: 0]
  ------------------
10531|  24.3k|    if (internal_input.empty()) {
  ------------------
  |  Branch (10531:9): [True: 249, False: 24.1k]
  ------------------
10532|    249|      path = "/";
10533|  24.1k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (10533:16): [True: 24.1k, False: 0]
  |  Branch (10533:46): [True: 0, False: 0]
  ------------------
10534|  24.1k|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
10535|  24.1k|    } else {
10536|      0|      helpers::parse_prepared_path(internal_input, type, path);
10537|      0|    }
10538|  24.3k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (10538:14): [True: 0, False: 0]
  ------------------
10539|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (10539:9): [True: 0, False: 0]
  ------------------
10540|      0|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
10541|      0|    } else {
10542|      0|      helpers::parse_prepared_path(internal_input, type, path);
10543|      0|    }
10544|      0|  } else {
10545|      0|    if (!host.has_value()) {
  ------------------
  |  Branch (10545:9): [True: 0, False: 0]
  ------------------
10546|      0|      path = "/";
10547|      0|    }
10548|      0|  }
10549|  24.3k|}
_ZN3ada7unicode13is_alnum_plusEc:
 7347|  2.32M|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
 7348|  2.32M|  return is_alnum_plus_table[uint8_t(c)];
 7349|       |  // A table is almost surely much faster than the
 7350|       |  // following under most compilers: return
 7351|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
 7352|  2.32M|}
_ZN3ada7unicode14to_lower_asciiEPcm:
 7024|  51.6k|constexpr bool to_lower_ascii(char* input, size_t length) noexcept {
 7025|  51.6k|  uint64_t broadcast_80 = broadcast(0x80);
 7026|  51.6k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 7027|  51.6k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 7028|  51.6k|  uint64_t non_ascii = 0;
 7029|  51.6k|  size_t i = 0;
 7030|       |
 7031|  98.8k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (7031:10): [True: 47.1k, False: 51.6k]
  ------------------
 7032|  47.1k|    uint64_t word{};
 7033|  47.1k|    memcpy(&word, input + i, sizeof(word));
 7034|  47.1k|    non_ascii |= (word & broadcast_80);
 7035|  47.1k|    word ^=
 7036|  47.1k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 7037|  47.1k|    memcpy(input + i, &word, sizeof(word));
 7038|  47.1k|  }
 7039|  51.6k|  if (i < length) {
  ------------------
  |  Branch (7039:7): [True: 51.2k, False: 474]
  ------------------
 7040|  51.2k|    uint64_t word{};
 7041|  51.2k|    memcpy(&word, input + i, length - i);
 7042|  51.2k|    non_ascii |= (word & broadcast_80);
 7043|  51.2k|    word ^=
 7044|  51.2k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 7045|  51.2k|    memcpy(input + i, &word, length - i);
 7046|  51.2k|  }
 7047|  51.6k|  return non_ascii == 0;
 7048|  51.6k|}
_ZN3ada7unicode9broadcastEh:
 7020|   155k|constexpr uint64_t broadcast(uint8_t v) noexcept {
 7021|   155k|  return 0x101010101010101ull * v;
 7022|   155k|}
_ZZN3ada6parser25try_parse_simple_absoluteINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_ENKUlvE_clEv:
12524|  21.9k|    auto apply_query_and_hash = [&]() {
12525|  21.9k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12525:11): [True: 1.33k, False: 20.6k]
  ------------------
12526|  1.33k|        const size_t q_end =
12527|  1.33k|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12527:13): [True: 837, False: 502]
  ------------------
12528|  1.33k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12529|  1.33k|                                                q_end - query_start - 1),
12530|  1.33k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12531|  1.33k|      }
12532|  21.9k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12532:11): [True: 1.25k, False: 20.7k]
  ------------------
12533|  1.25k|        out.update_unencoded_base_hash(std::string_view(
12534|  1.25k|            input.data() + hash_start + 1, len - hash_start - 1));
12535|  1.25k|      }
12536|  21.9k|    };
_ZZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_ENKUlvE_clEv:
12524|  21.9k|    auto apply_query_and_hash = [&]() {
12525|  21.9k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12525:11): [True: 1.33k, False: 20.6k]
  ------------------
12526|  1.33k|        const size_t q_end =
12527|  1.33k|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12527:13): [True: 837, False: 502]
  ------------------
12528|  1.33k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12529|  1.33k|                                                q_end - query_start - 1),
12530|  1.33k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12531|  1.33k|      }
12532|  21.9k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12532:11): [True: 1.25k, False: 20.7k]
  ------------------
12533|  1.25k|        out.update_unencoded_base_hash(std::string_view(
12534|  1.25k|            input.data() + hash_start + 1, len - hash_start - 1));
12535|  1.25k|      }
12536|  21.9k|    };
_ZZN3ada6parser32finish_simple_absolute_with_portINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmbENKUlvE_clEv:
12138|  2.36k|    auto apply_query_and_hash = [&]() {
12139|  2.36k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12139:11): [True: 1.23k, False: 1.12k]
  ------------------
12140|  1.23k|        const size_t q_end =
12141|  1.23k|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12141:13): [True: 778, False: 460]
  ------------------
12142|  1.23k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12143|  1.23k|                                                q_end - query_start - 1),
12144|  1.23k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12145|  1.23k|      }
12146|  2.36k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12146:11): [True: 1.09k, False: 1.26k]
  ------------------
12147|  1.09k|        out.update_unencoded_base_hash(std::string_view(
12148|  1.09k|            input.data() + hash_start + 1, len - hash_start - 1));
12149|  1.09k|      }
12150|  2.36k|    };
_ZZN3ada6parser32finish_simple_absolute_with_portINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmbENKUlvE_clEv:
12138|  2.36k|    auto apply_query_and_hash = [&]() {
12139|  2.36k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12139:11): [True: 1.23k, False: 1.12k]
  ------------------
12140|  1.23k|        const size_t q_end =
12141|  1.23k|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12141:13): [True: 778, False: 460]
  ------------------
12142|  1.23k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12143|  1.23k|                                                q_end - query_start - 1),
12144|  1.23k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12145|  1.23k|      }
12146|  2.36k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12146:11): [True: 1.09k, False: 1.26k]
  ------------------
12147|  1.09k|        out.update_unencoded_base_hash(std::string_view(
12148|  1.09k|            input.data() + hash_start + 1, len - hash_start - 1));
12149|  1.09k|      }
12150|  2.36k|    };
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8782|   536k|    std::string_view& input) noexcept {
 8783|       |  // compiles down to 20--30 instructions including a class to memchr (C
 8784|       |  // function). this function should be quite fast.
 8785|   536k|  size_t location_of_first = input.find('#');
 8786|   536k|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (8786:7): [True: 514k, False: 21.7k]
  ------------------
 8787|   514k|    return std::nullopt;
 8788|   514k|  }
 8789|  21.7k|  std::string_view hash = input;
 8790|  21.7k|  hash.remove_prefix(location_of_first + 1);
 8791|  21.7k|  input.remove_suffix(input.size() - location_of_first);
 8792|  21.7k|  return hash;
 8793|   536k|}
_ZZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12847|   246k|  const auto enforce_max_input_length = [&]() {
12848|   246k|    if constexpr (store_values) {
12849|   246k|      if (url.is_valid) {
  ------------------
  |  Branch (12849:11): [True: 246k, False: 0]
  ------------------
12850|       |        if constexpr (result_type_is_ada_url_aggregator) {
12851|       |          if (url.buffer.size() > max_input_length) [[unlikely]] {
12852|       |            url.is_valid = false;
12853|       |          }
12854|   246k|        } else {
12855|   246k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12855:15): [True: 0, False: 246k]
  ------------------
12856|      0|            url.is_valid = false;
12857|      0|          }
12858|   246k|        }
12859|   246k|      }
12860|   246k|    }
12861|   246k|  };
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9740|   186k|find_authority_delimiter_special(std::string_view view) noexcept {
 9741|       |  // performance note: we might be able to gain further performance
 9742|       |  // with SIMD intrinsics.
 9743|  1.97M|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9743:33): [True: 1.95M, False: 19.8k]
  ------------------
 9744|  1.95M|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (9744:9): [True: 167k, False: 1.78M]
  ------------------
 9745|   167k|      return pos - view.begin();
 9746|   167k|    }
 9747|  1.95M|  }
 9748|  19.8k|  return size_t(view.size());
 9749|   186k|}
_ZN3ada7helpers24find_authority_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9761|    330|find_authority_delimiter(std::string_view view) noexcept {
 9762|       |  // performance note: we might be able to gain further performance
 9763|       |  // with SIMD instrinsics.
 9764|  4.03k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9764:33): [True: 4.00k, False: 34]
  ------------------
 9765|  4.00k|    if (authority_delimiter[(uint8_t)*pos]) {
  ------------------
  |  Branch (9765:9): [True: 296, False: 3.70k]
  ------------------
 9766|    296|      return pos - view.begin();
 9767|    296|    }
 9768|  4.00k|  }
 9769|     34|  return size_t(view.size());
 9770|    330|}
_ZN3ada7helpers12shorten_pathERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_6scheme4typeE:
 8795|  25.7k|ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) {
 8796|       |  // Let path be url's path.
 8797|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
 8798|       |  // Windows drive letter, then return.
 8799|  25.7k|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8799:7): [True: 318, False: 25.4k]
  ------------------
 8800|    318|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (8800:7): [True: 158, False: 160]
  |  Branch (8800:54): [True: 126, False: 32]
  ------------------
 8801|    126|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (8801:9): [True: 20, False: 106]
  ------------------
 8802|    126|            helpers::substring(path, 1))) {
 8803|     20|      return false;
 8804|     20|    }
 8805|    126|  }
 8806|       |
 8807|       |  // Remove path's last item, if any.
 8808|  25.7k|  size_t last_delimiter = path.rfind('/');
 8809|  25.7k|  if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (8809:7): [True: 22.4k, False: 3.23k]
  ------------------
 8810|  22.4k|    path.erase(last_delimiter);
 8811|  22.4k|    return true;
 8812|  22.4k|  }
 8813|       |
 8814|  3.23k|  return false;
 8815|  25.7k|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9456|   530k|    const bool is_special, std::string_view& view) noexcept {
 9457|       |  /**
 9458|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
 9459|       |   * compute a variable called insideBrackets but this variable is only used
 9460|       |   * once, to check whether a ':' character was found outside brackets. Exact
 9461|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
 9462|       |   * It is conceptually simpler and arguably more efficient to just return a
 9463|       |   * Boolean indicating whether ':' was found outside brackets.
 9464|       |   */
 9465|   530k|  const size_t view_size = view.size();
 9466|   530k|  size_t location = 0;
 9467|   530k|  bool found_colon = false;
 9468|       |  /**
 9469|       |   * Performance analysis:
 9470|       |   *
 9471|       |   * We are basically seeking the end of the hostname which can be indicated
 9472|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
 9473|       |   * (where '\\' is only applicable for special URLs). However, these must
 9474|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
 9475|       |   * '?' does not count.
 9476|       |   *
 9477|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
 9478|       |   * the set of delimiters, and that we handle it first.
 9479|       |   *
 9480|       |   * So the trick is to have a fast function that locates the next delimiter.
 9481|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
 9482|       |   * function would be provided by the C++ standard library, but it seems that
 9483|       |   * find_first_of is not very fast, so we are forced to roll our own.
 9484|       |   *
 9485|       |   * We do not break into two loops for speed, but for clarity.
 9486|       |   */
 9487|   530k|  if (is_special) {
  ------------------
  |  Branch (9487:7): [True: 528k, False: 1.53k]
  ------------------
 9488|       |    // We move to the next delimiter.
 9489|   528k|    location = find_next_host_delimiter_special(view, location);
 9490|       |    // Unless we find '[' then we are going only going to have to call
 9491|       |    // find_next_host_delimiter_special once.
 9492|   531k|    for (; location < view_size;
  ------------------
  |  Branch (9492:12): [True: 468k, False: 62.5k]
  ------------------
 9493|   528k|         location = find_next_host_delimiter_special(view, location)) {
 9494|   468k|      if (view[location] == '[') {
  ------------------
  |  Branch (9494:11): [True: 2.39k, False: 466k]
  ------------------
 9495|  2.39k|        location = view.find(']', location);
 9496|  2.39k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9496:13): [True: 166, False: 2.23k]
  ------------------
 9497|       |          // performance: view.find might get translated to a memchr, which
 9498|       |          // has no notion of std::string_view::npos, so the code does not
 9499|       |          // reflect the assembly.
 9500|    166|          location = view_size;
 9501|    166|          break;
 9502|    166|        }
 9503|   466k|      } else {
 9504|   466k|        found_colon = view[location] == ':';
 9505|   466k|        break;
 9506|   466k|      }
 9507|   468k|    }
 9508|   528k|  } else {
 9509|       |    // We move to the next delimiter.
 9510|  1.53k|    location = find_next_host_delimiter(view, location);
 9511|       |    // Unless we find '[' then we are going only going to have to call
 9512|       |    // find_next_host_delimiter_special once.
 9513|  1.67k|    for (; location < view_size;
  ------------------
  |  Branch (9513:12): [True: 1.44k, False: 234]
  ------------------
 9514|  1.53k|         location = find_next_host_delimiter(view, location)) {
 9515|  1.44k|      if (view[location] == '[') {
  ------------------
  |  Branch (9515:11): [True: 150, False: 1.29k]
  ------------------
 9516|    150|        location = view.find(']', location);
 9517|    150|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9517:13): [True: 12, False: 138]
  ------------------
 9518|       |          // performance: view.find might get translated to a memchr, which
 9519|       |          // has no notion of std::string_view::npos, so the code does not
 9520|       |          // reflect the assembly.
 9521|     12|          location = view_size;
 9522|     12|          break;
 9523|     12|        }
 9524|  1.29k|      } else {
 9525|  1.29k|        found_colon = view[location] == ':';
 9526|  1.29k|        break;
 9527|  1.29k|      }
 9528|  1.44k|    }
 9529|  1.53k|  }
 9530|       |  // performance: remove_suffix may translate into a single instruction.
 9531|   530k|  view.remove_suffix(view_size - location);
 9532|   530k|  return {location, found_colon};
 9533|   530k|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 9046|   531k|    std::string_view view, size_t location) noexcept {
 9047|       |  // first check for short strings in which case we do it naively.
 9048|   531k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (9048:7): [True: 412k, False: 118k]
  ------------------
 9049|  4.39M|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (9049:31): [True: 4.33M, False: 60.9k]
  ------------------
 9050|  4.33M|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (9050:11): [True: 1.36k, False: 4.32M]
  |  Branch (9050:29): [True: 348k, False: 3.98M]
  |  Branch (9050:47): [True: 198, False: 3.98M]
  ------------------
 9051|  3.98M|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (9051:11): [True: 484, False: 3.98M]
  |  Branch (9051:29): [True: 1.00k, False: 3.97M]
  ------------------
 9052|   351k|        return i;
 9053|   351k|      }
 9054|  4.33M|    }
 9055|  60.9k|    return size_t(view.size());
 9056|   412k|  }
 9057|       |  // fast path for long strings (expected to be common)
 9058|   118k|  size_t i = location;
 9059|   118k|  const __m128i mask1 = _mm_set1_epi8(':');
 9060|   118k|  const __m128i mask2 = _mm_set1_epi8('/');
 9061|   118k|  const __m128i mask3 = _mm_set1_epi8('\\');
 9062|   118k|  const __m128i mask4 = _mm_set1_epi8('?');
 9063|   118k|  const __m128i mask5 = _mm_set1_epi8('[');
 9064|       |
 9065|   233k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (9065:10): [True: 171k, False: 62.0k]
  ------------------
 9066|   171k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 9067|   171k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9068|   171k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9069|   171k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 9070|   171k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9071|   171k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9072|   171k|    __m128i m = _mm_or_si128(
 9073|   171k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 9074|   171k|    int mask = _mm_movemask_epi8(m);
 9075|   171k|    if (mask != 0) {
  ------------------
  |  Branch (9075:9): [True: 56.8k, False: 114k]
  ------------------
 9076|  56.8k|      return i + trailing_zeroes(mask);
 9077|  56.8k|    }
 9078|   171k|  }
 9079|  62.0k|  if (i < view.size()) {
  ------------------
  |  Branch (9079:7): [True: 61.8k, False: 262]
  ------------------
 9080|  61.8k|    __m128i word =
 9081|  61.8k|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 9082|  61.8k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9083|  61.8k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9084|  61.8k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 9085|  61.8k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9086|  61.8k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9087|  61.8k|    __m128i m = _mm_or_si128(
 9088|  61.8k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 9089|  61.8k|    int mask = _mm_movemask_epi8(m);
 9090|  61.8k|    if (mask != 0) {
  ------------------
  |  Branch (9090:9): [True: 60.4k, False: 1.41k]
  ------------------
 9091|  60.4k|      return view.length() - 16 + trailing_zeroes(mask);
 9092|  60.4k|    }
 9093|  61.8k|  }
 9094|  1.67k|  return size_t(view.length());
 9095|  62.0k|}
_ZN3ada7helpers15trailing_zeroesEj:
 8868|   118k|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
 8869|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 8870|       |  unsigned long ret;
 8871|       |  // Search the mask data from least significant bit (LSB)
 8872|       |  // to the most significant bit (MSB) for a set bit (1).
 8873|       |  _BitScanForward(&ret, input_num);
 8874|       |  return (int)ret;
 8875|       |#else   // ADA_REGULAR_VISUAL_STUDIO
 8876|   118k|  return __builtin_ctzl(input_num);
 8877|   118k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 8878|   118k|}
_ZN3ada7helpers24find_next_host_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 9322|  1.67k|                                                  size_t location) noexcept {
 9323|       |  // first check for short strings in which case we do it naively.
 9324|  1.67k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (9324:7): [True: 596, False: 1.07k]
  ------------------
 9325|  3.27k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (9325:31): [True: 3.09k, False: 184]
  ------------------
 9326|  3.09k|      if (view[i] == ':' || view[i] == '/' || view[i] == '?' ||
  ------------------
  |  Branch (9326:11): [True: 66, False: 3.02k]
  |  Branch (9326:29): [True: 216, False: 2.80k]
  |  Branch (9326:47): [True: 70, False: 2.73k]
  ------------------
 9327|  2.73k|          view[i] == '[') {
  ------------------
  |  Branch (9327:11): [True: 60, False: 2.67k]
  ------------------
 9328|    412|        return i;
 9329|    412|      }
 9330|  3.09k|    }
 9331|    184|    return size_t(view.size());
 9332|    596|  }
 9333|       |  // fast path for long strings (expected to be common)
 9334|  1.07k|  size_t i = location;
 9335|  1.07k|  const __m128i mask1 = _mm_set1_epi8(':');
 9336|  1.07k|  const __m128i mask2 = _mm_set1_epi8('/');
 9337|  1.07k|  const __m128i mask4 = _mm_set1_epi8('?');
 9338|  1.07k|  const __m128i mask5 = _mm_set1_epi8('[');
 9339|       |
 9340|  1.54k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (9340:10): [True: 1.37k, False: 172]
  ------------------
 9341|  1.37k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 9342|  1.37k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9343|  1.37k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9344|  1.37k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9345|  1.37k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9346|  1.37k|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 9347|  1.37k|    int mask = _mm_movemask_epi8(m);
 9348|  1.37k|    if (mask != 0) {
  ------------------
  |  Branch (9348:9): [True: 906, False: 466]
  ------------------
 9349|    906|      return i + trailing_zeroes(mask);
 9350|    906|    }
 9351|  1.37k|  }
 9352|    172|  if (i < view.size()) {
  ------------------
  |  Branch (9352:7): [True: 150, False: 22]
  ------------------
 9353|    150|    __m128i word =
 9354|    150|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 9355|    150|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9356|    150|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9357|    150|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9358|    150|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9359|    150|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 9360|    150|    int mask = _mm_movemask_epi8(m);
 9361|    150|    if (mask != 0) {
  ------------------
  |  Branch (9361:9): [True: 122, False: 28]
  ------------------
 9362|    122|      return view.length() - 16 + trailing_zeroes(mask);
 9363|    122|    }
 9364|    150|  }
 9365|     50|  return size_t(view.length());
 9366|    172|}
_ZN3ada3url10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10406|   266k|ada_really_inline bool url::parse_host(std::string_view input) {
10407|   266k|  ada_log("parse_host ", input, " [", input.size(), " bytes]");
10408|   266k|  if (input.empty()) {
  ------------------
  |  Branch (10408:7): [True: 5, False: 266k]
  ------------------
10409|      5|    return is_valid = false;
10410|      5|  }  // technically unnecessary.
10411|       |  // If input starts with U+005B ([), then:
10412|   266k|  if (input[0] == '[') {
  ------------------
  |  Branch (10412:7): [True: 664, False: 265k]
  ------------------
10413|       |    // If input does not end with U+005D (]), validation error, return failure.
10414|    664|    if (input.back() != ']') {
  ------------------
  |  Branch (10414:9): [True: 70, False: 594]
  ------------------
10415|     70|      return is_valid = false;
10416|     70|    }
10417|    594|    ada_log("parse_host ipv6");
10418|       |
10419|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
10420|       |    // trailing U+005D (]) removed.
10421|    594|    input.remove_prefix(1);
10422|    594|    input.remove_suffix(1);
10423|    594|    return parse_ipv6(input);
10424|    664|  }
10425|       |
10426|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
10427|       |  // input.
10428|   265k|  if (!is_special()) {
  ------------------
  |  Branch (10428:7): [True: 652, False: 264k]
  ------------------
10429|    652|    return parse_opaque_host(input);
10430|    652|  }
10431|       |
10432|       |  // Fast path: try to parse as pure decimal IPv4 first.
10433|   264k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
10434|   264k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (10434:7): [True: 107k, False: 156k]
  ------------------
10435|   107k|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (10435:9): [True: 107k, False: 0]
  |  Branch (10435:27): [True: 10, False: 107k]
  ------------------
10436|     10|      host = input.substr(0, input.size() - 1);
10437|   107k|    } else {
10438|   107k|      host = input;
10439|   107k|    }
10440|   107k|    host_type = IPV4;
10441|   107k|    is_valid = true;
10442|   107k|    ada_log("parse_host fast path decimal ipv4");
10443|   107k|    return true;
10444|   107k|  }
10445|       |  // Let domain be the result of running UTF-8 decode without BOM on the
10446|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
10447|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
10448|       |  // which case we do not need to call the expensive 'to_ascii' if a few
10449|       |  // conditions are met: no '%' and no 'xn-' subsequence.
10450|   156k|  const uint8_t forbidden_or_upper =
10451|   156k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
10452|   156k|                                                             input.size());
10453|   156k|  static constexpr std::string_view xn_dash{"xn-", 3};
10454|   156k|  if ((forbidden_or_upper & 1) == 0) {
  ------------------
  |  Branch (10454:7): [True: 134k, False: 22.0k]
  ------------------
10455|   134k|    host = std::string(input);
10456|   134k|    if ((forbidden_or_upper & 2) != 0) {
  ------------------
  |  Branch (10456:9): [True: 900, False: 133k]
  ------------------
10457|    900|      unicode::to_lower_ascii(host->data(), host->size());
10458|    900|    }
10459|   134k|    if (host->find('-') == std::string_view::npos ||
  ------------------
  |  Branch (10459:9): [True: 73.4k, False: 61.3k]
  ------------------
10460|  73.6k|        host->find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (10460:9): [True: 203, False: 61.1k]
  ------------------
10461|  73.6k|      if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10461:11): [True: 39.4k, False: 34.1k]
  ------------------
10462|  39.4k|        ada_log("parse_host fast path ipv4");
10463|  39.4k|        return parse_ipv4(*host);
10464|  39.4k|      }
10465|  34.1k|      ada_log("parse_host fast path ", *host);
10466|  34.1k|      is_valid = true;
10467|  34.1k|      return true;
10468|  73.6k|    }
10469|   134k|  } else if (const size_t first_percent = input.find('%');
10470|  22.0k|             first_percent != std::string_view::npos) {
  ------------------
  |  Branch (10470:14): [True: 19.5k, False: 2.46k]
  ------------------
10471|  19.5k|    std::string decoded = unicode::percent_decode(input, first_percent);
10472|  19.5k|    const uint8_t decoded_flags =
10473|  19.5k|        unicode::contains_forbidden_domain_code_point_or_upper(decoded.data(),
10474|  19.5k|                                                               decoded.size());
10475|  19.5k|    if ((decoded_flags & 1) == 0) {
  ------------------
  |  Branch (10475:9): [True: 19.2k, False: 295]
  ------------------
10476|  19.2k|      if ((decoded_flags & 2) != 0) {
  ------------------
  |  Branch (10476:11): [True: 123, False: 19.1k]
  ------------------
10477|    123|        unicode::to_lower_ascii(decoded.data(), decoded.size());
10478|    123|      }
10479|  19.2k|      if (decoded.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (10479:11): [True: 19.1k, False: 59]
  ------------------
10480|  19.2k|          decoded.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (10480:11): [True: 26, False: 33]
  ------------------
10481|  19.2k|        host = std::move(decoded);
10482|  19.2k|        if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10482:13): [True: 19.0k, False: 177]
  ------------------
10483|  19.0k|          return parse_ipv4(*host);
10484|  19.0k|        }
10485|    177|        is_valid = true;
10486|    177|        return true;
10487|  19.2k|      }
10488|  19.2k|    }
10489|  19.5k|  }
10490|  63.9k|  ada_log("parse_host calling to_ascii");
10491|  63.9k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
10492|  63.9k|  if (!is_valid || !host.has_value()) {
  ------------------
  |  Branch (10492:7): [True: 1.43k, False: 62.4k]
  |  Branch (10492:20): [True: 0, False: 62.4k]
  ------------------
10493|  1.43k|    ada_log("parse_host to_ascii returns false");
10494|  1.43k|    return is_valid = false;
10495|  1.43k|  }
10496|  62.4k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
10497|  62.4k|          " bytes]");
10498|       |
10499|  62.4k|  if (std::any_of(host->begin(), host->end(),
  ------------------
  |  Branch (10499:7): [True: 0, False: 62.4k]
  ------------------
10500|  62.4k|                  ada::unicode::is_forbidden_domain_code_point)) {
10501|      0|    host = std::nullopt;
10502|      0|    return is_valid = false;
10503|      0|  }
10504|       |
10505|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
10506|       |  // asciiDomain.
10507|  62.4k|  if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10507:7): [True: 121, False: 62.3k]
  ------------------
10508|    121|    ada_log("parse_host got ipv4 ", *host);
10509|    121|    return parse_ipv4(*host);
10510|    121|  }
10511|       |
10512|  62.3k|  return true;
10513|  62.4k|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
 7317|   352k|                                              size_t length) noexcept {
 7318|   352k|  size_t i = 0;
 7319|   352k|  uint8_t accumulator{};
 7320|  1.37M|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7320:10): [True: 1.02M, False: 352k]
  ------------------
 7321|  1.02M|    accumulator |=
 7322|  1.02M|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7323|  1.02M|    accumulator |=
 7324|  1.02M|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
 7325|  1.02M|    accumulator |=
 7326|  1.02M|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
 7327|  1.02M|    accumulator |=
 7328|  1.02M|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
 7329|  1.02M|  }
 7330|   953k|  for (; i < length; i++) {
  ------------------
  |  Branch (7330:10): [True: 600k, False: 352k]
  ------------------
 7331|   600k|    accumulator |=
 7332|   600k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7333|   600k|  }
 7334|   352k|  return accumulator;
 7335|   352k|}
_ZN3ada7unicode30is_forbidden_domain_code_pointEc:
 7276|  2.23M|    const char c) noexcept {
 7277|  2.23M|  return is_forbidden_domain_code_point_table[uint8_t(c)];
 7278|  2.23M|}
_ZZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12847|   246k|  const auto enforce_max_input_length = [&]() {
12848|   246k|    if constexpr (store_values) {
12849|   246k|      if (url.is_valid) {
  ------------------
  |  Branch (12849:11): [True: 246k, False: 0]
  ------------------
12850|   246k|        if constexpr (result_type_is_ada_url_aggregator) {
12851|   246k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12851:15): [True: 0, False: 246k]
  ------------------
12852|      0|            url.is_valid = false;
12853|      0|          }
12854|       |        } else {
12855|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
12856|       |            url.is_valid = false;
12857|       |          }
12858|       |        }
12859|   246k|      }
12860|   246k|    }
12861|   246k|  };
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_124port_decimal_digit_countEj:
11177|   122k|ada_really_inline uint32_t port_decimal_digit_count(uint32_t port) noexcept {
11178|   122k|  uint32_t digits = 1;
11179|   367k|  while (port >= 10) {
  ------------------
  |  Branch (11179:10): [True: 244k, False: 122k]
  ------------------
11180|   244k|    port /= 10;
11181|   244k|    ++digits;
11182|   244k|  }
11183|   122k|  return digits;
11184|   122k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_121append_canonical_portERNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEj:
11187|  11.6k|                                             uint32_t port) {
11188|  11.6k|  buffer += ':';
11189|  11.6k|  char port_buf[5];
11190|  11.6k|  auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, port);
11191|  11.6k|  (void)ec;
11192|  11.6k|  buffer.append(port_buf, static_cast<size_t>(ptr - port_buf));
11193|  11.6k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_116authority_has_atEPKhmm:
11335|  1.23M|                                        size_t n) noexcept {
11336|  1.23M|  const size_t rem = n - host_start;
11337|  1.23M|  if (rem == 0) {
  ------------------
  |  Branch (11337:7): [True: 0, False: 1.23M]
  ------------------
11338|      0|    return false;
11339|      0|  }
11340|       |#if ADA_NEON
11341|       |  if (rem >= 16) {
11342|       |    const uint8x16_t w = vld1q_u8(p + host_start);
11343|       |    const uint64_t at = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('@')));
11344|       |    if (at == 0) {
11345|       |      return false;
11346|       |    }
11347|       |    const uint64_t delim = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('/'))) |
11348|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('?'))) |
11349|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('#')));
11350|       |    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
11351|       |  }
11352|       |#elif ADA_SSE2
11353|  1.23M|  if (rem >= 16) {
  ------------------
  |  Branch (11353:7): [True: 673k, False: 564k]
  ------------------
11354|   673k|    const __m128i w =
11355|   673k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + host_start));
11356|   673k|    const uint16_t at = static_cast<uint16_t>(
11357|   673k|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('@'))));
11358|   673k|    if (at == 0) {
  ------------------
  |  Branch (11358:9): [True: 613k, False: 60.4k]
  ------------------
11359|   613k|      return false;
11360|   613k|    }
11361|  60.4k|    const uint16_t delim = static_cast<uint16_t>(
11362|  60.4k|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) |
11363|  60.4k|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('?'))) |
11364|  60.4k|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('#'))));
11365|  60.4k|    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
  ------------------
  |  Branch (11365:12): [True: 59.7k, False: 710]
  |  Branch (11365:26): [True: 154, False: 556]
  ------------------
11366|   673k|  }
11367|   564k|#endif
11368|   564k|  const size_t lim = host_start + rem;
11369|  5.96M|  for (size_t i = host_start; i < lim; ++i) {
  ------------------
  |  Branch (11369:31): [True: 5.90M, False: 60.9k]
  ------------------
11370|  5.90M|    const uint8_t c = p[i];
11371|  5.90M|    if (c == '@') {
  ------------------
  |  Branch (11371:9): [True: 19.3k, False: 5.88M]
  ------------------
11372|  19.3k|      return true;
11373|  19.3k|    }
11374|  5.88M|    if (c == '/' || c == '?' || c == '#') {
  ------------------
  |  Branch (11374:9): [True: 465k, False: 5.42M]
  |  Branch (11374:21): [True: 19.3k, False: 5.40M]
  |  Branch (11374:33): [True: 320, False: 5.40M]
  ------------------
11375|   484k|      return false;
11376|   484k|    }
11377|  5.88M|  }
11378|  60.9k|  return false;
11379|   564k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_:
11400|   875k|                                     bool& has_x) noexcept {
11401|   875k|  has_upper = false;
11402|   875k|  has_x = false;
11403|   875k|  size_t i = start;
11404|   875k|#if ADA_PARSER_SSSE3
11405|   875k|  if (parser_use_ssse3() && (len - start >= 16)) {
  ------------------
  |  Branch (11405:7): [True: 875k, False: 0]
  |  Branch (11405:29): [True: 561k, False: 313k]
  ------------------
11406|   561k|    const __m128i lo_tbl = nibble_load(k_host_nibbles.low);
11407|   561k|    const __m128i hi_tbl = nibble_load(k_host_nibbles.high);
11408|   561k|    const __m128i x_splat = _mm_set1_epi8('x');
11409|   561k|    auto visit = [&](size_t at) noexcept -> bool {
11410|   561k|      const __m128i w =
11411|   561k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11412|   561k|      const int up = sse2_uppercase(w);
11413|   561k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11414|   561k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11415|   561k|      if (mask == 0) {
11416|   561k|        if (up != 0) {
11417|   561k|          has_upper = true;
11418|   561k|        }
11419|   561k|        if (xs != 0) {
11420|   561k|          has_x = true;
11421|   561k|        }
11422|   561k|        return false;
11423|   561k|      }
11424|   561k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11425|   561k|      const int valid = (1 << hit) - 1;
11426|   561k|      if ((up & valid) != 0) {
11427|   561k|        has_upper = true;
11428|   561k|      }
11429|   561k|      if ((xs & valid) != 0) {
11430|   561k|        has_x = true;
11431|   561k|      }
11432|   561k|      end = at + static_cast<size_t>(hit);
11433|   561k|      return true;
11434|   561k|    };
11435|   580k|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11435:12): [True: 94.4k, False: 486k]
  ------------------
11436|  94.4k|      if (visit(i) || visit(i + 16)) {
  ------------------
  |  Branch (11436:11): [True: 74.2k, False: 20.2k]
  |  Branch (11436:23): [True: 1.50k, False: 18.7k]
  ------------------
11437|  75.7k|        return k_host_class[b[end]] == 1;
11438|  75.7k|      }
11439|  94.4k|    }
11440|   545k|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11440:12): [True: 485k, False: 59.9k]
  ------------------
11441|   485k|      if (visit(i)) {
  ------------------
  |  Branch (11441:11): [True: 426k, False: 59.0k]
  ------------------
11442|   426k|        return k_host_class[b[end]] == 1;
11443|   426k|      }
11444|   485k|    }
11445|       |    // Overlapping tail only after a full 16-byte step so the window cannot
11446|       |    // start before `start` (a prior '/' would otherwise look like a host stop).
11447|  59.9k|    if (i > start && i < len) {
  ------------------
  |  Branch (11447:9): [True: 59.9k, False: 0]
  |  Branch (11447:22): [True: 59.8k, False: 38]
  ------------------
11448|  59.8k|      if (visit(len - 16) && end >= i) {
  ------------------
  |  Branch (11448:11): [True: 59.7k, False: 132]
  |  Branch (11448:30): [True: 59.7k, False: 0]
  ------------------
11449|  59.7k|        return k_host_class[b[end]] == 1;
11450|  59.7k|      }
11451|    132|      end = len;
11452|    132|      return true;
11453|  59.8k|    }
11454|  59.9k|  }
11455|       |#elif ADA_SSE2
11456|       |  const __m128i x_splat = _mm_set1_epi8('x');
11457|       |  for (; i + 32 <= len; i += 32) {
11458|       |    for (size_t off = 0; off < 32; off += 16) {
11459|       |      const __m128i w =
11460|       |          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + off));
11461|       |      const int up = sse2_uppercase(w);
11462|       |      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11463|       |      const int mask = sse2_host_stop(w);
11464|       |      if (mask != 0) {
11465|       |        const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11466|       |        const int valid = (1 << hit) - 1;
11467|       |        if ((up & valid) != 0) {
11468|       |          has_upper = true;
11469|       |        }
11470|       |        if ((xs & valid) != 0) {
11471|       |          has_x = true;
11472|       |        }
11473|       |        end = i + off + static_cast<size_t>(hit);
11474|       |        return k_host_class[b[end]] == 1;
11475|       |      }
11476|       |      if (up != 0) {
11477|       |        has_upper = true;
11478|       |      }
11479|       |      if (xs != 0) {
11480|       |        has_x = true;
11481|       |      }
11482|       |    }
11483|       |  }
11484|       |  for (; i + 16 <= len; i += 16) {
11485|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11486|       |    const int up = sse2_uppercase(w);
11487|       |    const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11488|       |    const int mask = sse2_host_stop(w);
11489|       |    if (mask != 0) {
11490|       |      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11491|       |      const int valid = (1 << hit) - 1;
11492|       |      if ((up & valid) != 0) {
11493|       |        has_upper = true;
11494|       |      }
11495|       |      if ((xs & valid) != 0) {
11496|       |        has_x = true;
11497|       |      }
11498|       |      end = i + static_cast<size_t>(hit);
11499|       |      return k_host_class[b[end]] == 1;
11500|       |    }
11501|       |    if (up != 0) {
11502|       |      has_upper = true;
11503|       |    }
11504|       |    if (xs != 0) {
11505|       |      has_x = true;
11506|       |    }
11507|       |  }
11508|       |#elif ADA_NEON
11509|       |  if (len - start >= 16) {
11510|       |    const uint8x16_t lo_tbl = vld1q_u8(k_host_nibbles.low);
11511|       |    const uint8x16_t hi_tbl = vld1q_u8(k_host_nibbles.high);
11512|       |    const uint8x16_t x_splat = vdupq_n_u8('x');
11513|       |    auto visit = [&](size_t at) noexcept -> bool {
11514|       |      const uint8x16_t w = vld1q_u8(b + at);
11515|       |      const uint64_t up = neon_uppercase(w);
11516|       |      const uint64_t xs = neon_nibble_bits(vceqq_u8(w, x_splat));
11517|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11518|       |      if (bits == 0) {
11519|       |        if (up != 0) {
11520|       |          has_upper = true;
11521|       |        }
11522|       |        if (xs != 0) {
11523|       |          has_x = true;
11524|       |        }
11525|       |        return false;
11526|       |      }
11527|       |      const size_t hit = static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11528|       |      const uint64_t valid = (hit == 0) ? 0 : (uint64_t{1} << (hit * 4)) - 1;
11529|       |      if ((up & valid) != 0) {
11530|       |        has_upper = true;
11531|       |      }
11532|       |      if ((xs & valid) != 0) {
11533|       |        has_x = true;
11534|       |      }
11535|       |      end = at + hit;
11536|       |      return true;
11537|       |    };
11538|       |    for (; i + 32 <= len; i += 32) {
11539|       |      if (visit(i) || visit(i + 16)) {
11540|       |        return k_host_class[b[end]] == 1;
11541|       |      }
11542|       |    }
11543|       |    for (; i + 16 <= len; i += 16) {
11544|       |      if (visit(i)) {
11545|       |        return k_host_class[b[end]] == 1;
11546|       |      }
11547|       |    }
11548|       |    if (i > start && i < len) {
11549|       |      if (visit(len - 16) && end >= i) {
11550|       |        return k_host_class[b[end]] == 1;
11551|       |      }
11552|       |      end = len;
11553|       |      return true;
11554|       |    }
11555|       |  }
11556|       |#endif
11557|  3.38M|  for (; i < len; ++i) {
  ------------------
  |  Branch (11557:10): [True: 3.36M, False: 19.2k]
  ------------------
11558|  3.36M|    const uint8_t c = b[i];
11559|  3.36M|    const uint8_t cls = k_host_class[c];
11560|  3.36M|    if (cls == 1) {
  ------------------
  |  Branch (11560:9): [True: 291k, False: 3.06M]
  ------------------
11561|   291k|      end = i;
11562|   291k|      return true;
11563|   291k|    }
11564|  3.06M|    if (cls == 2) {
  ------------------
  |  Branch (11564:9): [True: 2.46k, False: 3.06M]
  ------------------
11565|  2.46k|      return false;
11566|  2.46k|    }
11567|  3.06M|    if (c >= 'A' && c <= 'Z') {
  ------------------
  |  Branch (11567:9): [True: 2.65M, False: 410k]
  |  Branch (11567:21): [True: 984, False: 2.65M]
  ------------------
11568|    984|      has_upper = true;
11569|  3.06M|    } else if (c == 'x') {
  ------------------
  |  Branch (11569:16): [True: 326k, False: 2.74M]
  ------------------
11570|   326k|      has_x = true;
11571|   326k|    }
11572|  3.06M|  }
11573|  19.2k|  end = len;
11574|  19.2k|  return true;
11575|   313k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_116parser_use_ssse3Ev:
11385|  1.83M|ada_really_inline bool parser_use_ssse3() noexcept {
11386|  1.83M|#ifdef ADA_PARSER_NEED_SSSE3_TARGET
11387|  1.83M|  static const bool supported = __builtin_cpu_supports("ssse3") != 0;
11388|  1.83M|  return supported;
11389|       |#else
11390|       |  return true;
11391|       |#endif
11392|  1.83M|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_111nibble_loadEPKh:
11216|  1.28M|ADA_PARSER_SIMD __m128i nibble_load(const uint8_t* t) noexcept {
11217|  1.28M|  return _mm_load_si128(reinterpret_cast<const __m128i*>(t));
11218|  1.28M|}
simple_absolute.cc:_ZZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_ENKUlmE_clEm:
11409|   659k|    auto visit = [&](size_t at) noexcept -> bool {
11410|   659k|      const __m128i w =
11411|   659k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11412|   659k|      const int up = sse2_uppercase(w);
11413|   659k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11414|   659k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11415|   659k|      if (mask == 0) {
  ------------------
  |  Branch (11415:11): [True: 98.1k, False: 561k]
  ------------------
11416|  98.1k|        if (up != 0) {
  ------------------
  |  Branch (11416:13): [True: 1.73k, False: 96.3k]
  ------------------
11417|  1.73k|          has_upper = true;
11418|  1.73k|        }
11419|  98.1k|        if (xs != 0) {
  ------------------
  |  Branch (11419:13): [True: 91.8k, False: 6.28k]
  ------------------
11420|  91.8k|          has_x = true;
11421|  91.8k|        }
11422|  98.1k|        return false;
11423|  98.1k|      }
11424|   561k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11425|   561k|      const int valid = (1 << hit) - 1;
11426|   561k|      if ((up & valid) != 0) {
  ------------------
  |  Branch (11426:11): [True: 20.1k, False: 541k]
  ------------------
11427|  20.1k|        has_upper = true;
11428|  20.1k|      }
11429|   561k|      if ((xs & valid) != 0) {
  ------------------
  |  Branch (11429:11): [True: 488k, False: 72.8k]
  ------------------
11430|   488k|        has_x = true;
11431|   488k|      }
11432|   561k|      end = at + static_cast<size_t>(hit);
11433|   561k|      return true;
11434|   659k|    };
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_114sse2_uppercaseEDv2_x:
11303|   659k|ada_really_inline int sse2_uppercase(__m128i w) noexcept {
11304|   659k|  return _mm_movemask_epi8(_mm_and_si128(
11305|   659k|      _mm_cmpgt_epi8(w, _mm_set1_epi8(static_cast<char>('A' - 1))),
11306|   659k|      _mm_cmplt_epi8(w, _mm_set1_epi8(static_cast<char>('Z' + 1)))));
11307|   659k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_117ssse3_nibble_maskEDv2_xS2_S2_:
11223|   782k|                                          __m128i hi_tbl) noexcept {
11224|   782k|  const __m128i nibble = _mm_set1_epi8(0x0F);
11225|   782k|  const __m128i lo = _mm_and_si128(w, nibble);
11226|   782k|  const __m128i hi = _mm_and_si128(_mm_srli_epi16(w, 4), nibble);
11227|   782k|  const __m128i hit =
11228|   782k|      _mm_and_si128(_mm_shuffle_epi8(lo_tbl, lo), _mm_shuffle_epi8(hi_tbl, hi));
11229|   782k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hit, _mm_setzero_si128())) ^ 0xFFFF;
11230|   782k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_117trailing_zeroes32Ej:
11195|   637k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
11196|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
11197|       |  unsigned long ret;
11198|       |  _BitScanForward(&ret, input_num);
11199|       |  return static_cast<int>(ret);
11200|       |#else
11201|   637k|  return __builtin_ctz(input_num);
11202|   637k|#endif
11203|   637k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_113scan_path_runEPKhRmmRb:
11635|   651k|                                   bool& maybe_dot_segment) noexcept {
11636|   651k|  const size_t run_start = i;
11637|   651k|#if ADA_PARSER_SSSE3
11638|   651k|  if (parser_use_ssse3() && (i + 16 <= len)) {
  ------------------
  |  Branch (11638:7): [True: 651k, False: 0]
  |  Branch (11638:29): [True: 72.9k, False: 579k]
  ------------------
11639|  72.9k|    const __m128i lo_tbl = nibble_load(k_path_nibbles.low);
11640|  72.9k|    const __m128i hi_tbl = nibble_load(k_path_nibbles.high);
11641|  80.4k|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11641:12): [True: 16.5k, False: 63.9k]
  ------------------
11642|  16.5k|      const __m128i w0 =
11643|  16.5k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11644|  16.5k|      const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);
11645|  16.5k|      if (m0 != 0) {
  ------------------
  |  Branch (11645:11): [True: 7.60k, False: 8.92k]
  ------------------
11646|  7.60k|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m0));
11647|  7.60k|        note_dots_in_window(b, i, run_start, w0, (1 << hit_bit) - 1,
11648|  7.60k|                            maybe_dot_segment);
11649|  7.60k|        i += static_cast<size_t>(hit_bit);
11650|  7.60k|        return;
11651|  7.60k|      }
11652|  8.92k|      note_dots_in_window(b, i, run_start, w0, 0xFFFF, maybe_dot_segment);
11653|  8.92k|      const __m128i w1 =
11654|  8.92k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));
11655|  8.92k|      const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);
11656|  8.92k|      if (m1 != 0) {
  ------------------
  |  Branch (11656:11): [True: 1.32k, False: 7.59k]
  ------------------
11657|  1.32k|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m1));
11658|  1.32k|        note_dots_in_window(b, i + 16, run_start, w1, (1 << hit_bit) - 1,
11659|  1.32k|                            maybe_dot_segment);
11660|  1.32k|        i += 16 + static_cast<size_t>(hit_bit);
11661|  1.32k|        return;
11662|  1.32k|      }
11663|  7.59k|      note_dots_in_window(b, i + 16, run_start, w1, 0xFFFF, maybe_dot_segment);
11664|  7.59k|    }
11665|  65.7k|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11665:12): [True: 63.1k, False: 2.64k]
  ------------------
11666|  63.1k|      const __m128i w =
11667|  63.1k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11668|  63.1k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11669|  63.1k|      if (mask != 0) {
  ------------------
  |  Branch (11669:11): [True: 61.3k, False: 1.78k]
  ------------------
11670|  61.3k|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11671|  61.3k|        note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11672|  61.3k|                            maybe_dot_segment);
11673|  61.3k|        i += static_cast<size_t>(hit_bit);
11674|  61.3k|        return;
11675|  61.3k|      }
11676|  1.78k|      note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11677|  1.78k|    }
11678|  2.64k|    if (i > run_start && i < len) {
  ------------------
  |  Branch (11678:9): [True: 2.64k, False: 0]
  |  Branch (11678:26): [True: 2.08k, False: 556]
  ------------------
11679|  2.08k|      const __m128i w =
11680|  2.08k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16));
11681|  2.08k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11682|  2.08k|      if (mask != 0) {
  ------------------
  |  Branch (11682:11): [True: 1.18k, False: 898]
  ------------------
11683|  1.18k|        const size_t hit =
11684|  1.18k|            len - 16 +
11685|  1.18k|            static_cast<size_t>(trailing_zeroes32(static_cast<uint32_t>(mask)));
11686|  1.18k|        if (hit >= i) {
  ------------------
  |  Branch (11686:13): [True: 1.18k, False: 0]
  ------------------
11687|  5.24k|          for (size_t j = i; j < hit; ++j) {
  ------------------
  |  Branch (11687:30): [True: 4.05k, False: 1.18k]
  ------------------
11688|  4.05k|            if (b[j] == '.') {
  ------------------
  |  Branch (11688:17): [True: 966, False: 3.09k]
  ------------------
11689|    966|              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11690|    966|            }
11691|  4.05k|          }
11692|  1.18k|          i = hit;
11693|  1.18k|          return;
11694|  1.18k|        }
11695|  1.18k|      }
11696|  7.59k|      for (size_t j = i; j < len; ++j) {
  ------------------
  |  Branch (11696:26): [True: 6.69k, False: 898]
  ------------------
11697|  6.69k|        if (b[j] == '.') {
  ------------------
  |  Branch (11697:13): [True: 1.75k, False: 4.94k]
  ------------------
11698|  1.75k|          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11699|  1.75k|        }
11700|  6.69k|      }
11701|    898|      i = len;
11702|    898|      return;
11703|  2.08k|    }
11704|  2.64k|  }
11705|       |#elif ADA_SSE2
11706|       |  for (; i + 16 <= len; i += 16) {
11707|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11708|       |    const int mask = sse2_path_stop(w);
11709|       |    if (mask != 0) {
11710|       |      const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11711|       |      note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11712|       |                          maybe_dot_segment);
11713|       |      i += static_cast<size_t>(hit_bit);
11714|       |      return;
11715|       |    }
11716|       |    note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11717|       |  }
11718|       |#elif ADA_NEON
11719|       |  if (i + 16 <= len) {
11720|       |    const uint8x16_t lo_tbl = vld1q_u8(k_path_nibbles.low);
11721|       |    const uint8x16_t hi_tbl = vld1q_u8(k_path_nibbles.high);
11722|       |    for (; i + 32 <= len; i += 32) {
11723|       |      const uint8x16_t w0 = vld1q_u8(b + i);
11724|       |      const uint64_t bits0 = neon_table_stop(w0, lo_tbl, hi_tbl);
11725|       |      if (bits0 != 0) {
11726|       |        const size_t hit_off =
11727|       |            static_cast<size_t>(trailing_zeroes64(bits0)) >> 2;
11728|       |        const uint64_t valid =
11729|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11730|       |        note_dots_in_window_neon(b, i, run_start, w0, valid, maybe_dot_segment);
11731|       |        i += hit_off;
11732|       |        return;
11733|       |      }
11734|       |      note_dots_in_window_neon(b, i, run_start, w0, ~uint64_t{0},
11735|       |                               maybe_dot_segment);
11736|       |      const uint8x16_t w1 = vld1q_u8(b + i + 16);
11737|       |      const uint64_t bits1 = neon_table_stop(w1, lo_tbl, hi_tbl);
11738|       |      if (bits1 != 0) {
11739|       |        const size_t hit_off =
11740|       |            static_cast<size_t>(trailing_zeroes64(bits1)) >> 2;
11741|       |        const uint64_t valid =
11742|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11743|       |        note_dots_in_window_neon(b, i + 16, run_start, w1, valid,
11744|       |                                 maybe_dot_segment);
11745|       |        i += 16 + hit_off;
11746|       |        return;
11747|       |      }
11748|       |      note_dots_in_window_neon(b, i + 16, run_start, w1, ~uint64_t{0},
11749|       |                               maybe_dot_segment);
11750|       |    }
11751|       |    for (; i + 16 <= len; i += 16) {
11752|       |      const uint8x16_t w = vld1q_u8(b + i);
11753|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11754|       |      if (bits != 0) {
11755|       |        const size_t hit_off =
11756|       |            static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11757|       |        const uint64_t valid =
11758|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11759|       |        note_dots_in_window_neon(b, i, run_start, w, valid, maybe_dot_segment);
11760|       |        i += hit_off;
11761|       |        return;
11762|       |      }
11763|       |      note_dots_in_window_neon(b, i, run_start, w, ~uint64_t{0},
11764|       |                               maybe_dot_segment);
11765|       |    }
11766|       |    if (i > run_start && i < len) {
11767|       |      const uint8x16_t w = vld1q_u8(b + len - 16);
11768|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11769|       |      if (bits != 0) {
11770|       |        const size_t hit =
11771|       |            len - 16 + (static_cast<size_t>(trailing_zeroes64(bits)) >> 2);
11772|       |        if (hit >= i) {
11773|       |          for (size_t j = i; j < hit; ++j) {
11774|       |            if (b[j] == '.') {
11775|       |              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11776|       |            }
11777|       |          }
11778|       |          i = hit;
11779|       |          return;
11780|       |        }
11781|       |      }
11782|       |      for (size_t j = i; j < len; ++j) {
11783|       |        if (b[j] == '.') {
11784|       |          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11785|       |        }
11786|       |      }
11787|       |      i = len;
11788|       |      return;
11789|       |    }
11790|       |  }
11791|       |#endif
11792|  2.16M|  for (; i < len; ++i) {
  ------------------
  |  Branch (11792:10): [True: 1.74M, False: 420k]
  ------------------
11793|  1.74M|    const uint8_t c = b[i];
11794|  1.74M|    const uint8_t cls = k_path[c];
11795|  1.74M|    if (cls != 0) {
  ------------------
  |  Branch (11795:9): [True: 158k, False: 1.58M]
  ------------------
11796|   158k|      return;
11797|   158k|    }
11798|  1.58M|    if (c == '.') {
  ------------------
  |  Branch (11798:9): [True: 172k, False: 1.41M]
  ------------------
11799|   172k|      note_possible_dot_segment(b, i, run_start, maybe_dot_segment);
11800|   172k|    }
11801|  1.58M|  }
11802|   579k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_119note_dots_in_windowEPKhmmDv2_xiRb:
11592|  88.5k|                                           bool& maybe_dot_segment) noexcept {
11593|  88.5k|  if (maybe_dot_segment || valid_mask == 0) {
  ------------------
  |  Branch (11593:7): [True: 5.68k, False: 82.8k]
  |  Branch (11593:28): [True: 5.12k, False: 77.7k]
  ------------------
11594|  10.8k|    return;
11595|  10.8k|  }
11596|  77.7k|  const int dots =
11597|  77.7k|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('.'))) & valid_mask;
11598|  77.7k|  if (dots == 0) {
  ------------------
  |  Branch (11598:7): [True: 67.4k, False: 10.3k]
  ------------------
11599|  67.4k|    return;
11600|  67.4k|  }
11601|  10.3k|  const int slashes =
11602|  10.3k|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) & valid_mask;
11603|  10.3k|  if (((dots & 1) != 0 && (i == run_start || b[i - 1] == '/')) ||
  ------------------
  |  Branch (11603:8): [True: 4.14k, False: 6.17k]
  |  Branch (11603:28): [True: 1.35k, False: 2.78k]
  |  Branch (11603:46): [True: 300, False: 2.48k]
  ------------------
11604|  8.66k|      (dots & (slashes << 1)) != 0) {
  ------------------
  |  Branch (11604:7): [True: 1.46k, False: 7.20k]
  ------------------
11605|  3.11k|    maybe_dot_segment = true;
11606|  3.11k|  }
11607|  10.3k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_125note_possible_dot_segmentEPKhmmRb:
11579|   175k|    bool& maybe_dot_segment) noexcept {
11580|   175k|  if (pos == run_start || b[pos - 1] == '/') {
  ------------------
  |  Branch (11580:7): [True: 486, False: 174k]
  |  Branch (11580:27): [True: 39.7k, False: 135k]
  ------------------
11581|  40.2k|    maybe_dot_segment = true;
11582|  40.2k|  }
11583|   175k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_114scan_query_runEPKhRmm:
11945|   181k|                                    size_t len) noexcept {
11946|   181k|  ADA_SCAN_STOP_RUN(k_query_nibbles, k_query, sse2_query_stop);
  ------------------
  |  |11808|   181k|  do {                                                                       \
  |  |11809|   181k|    const size_t scan_start = i;                                             \
  |  |11810|   181k|    if (parser_use_ssse3() && (i + 16 <= len)) {                             \
  |  |  ------------------
  |  |  |  Branch (11810:9): [True: 181k, False: 0]
  |  |  |  Branch (11810:31): [True: 5.28k, False: 176k]
  |  |  ------------------
  |  |11811|  5.28k|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11812|  5.28k|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11813|  12.9k|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11813:14): [True: 8.40k, False: 4.58k]
  |  |  ------------------
  |  |11814|  8.40k|        const __m128i w0 =                                                   \
  |  |11815|  8.40k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11816|  8.40k|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11817|  8.40k|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11817:13): [True: 470, False: 7.93k]
  |  |  ------------------
  |  |11818|    470|          i += static_cast<size_t>(                                          \
  |  |11819|    470|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11820|    470|          return;                                                            \
  |  |11821|    470|        }                                                                    \
  |  |11822|  8.40k|        const __m128i w1 =                                                   \
  |  |11823|  7.93k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11824|  7.93k|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11825|  7.93k|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11825:13): [True: 224, False: 7.71k]
  |  |  ------------------
  |  |11826|    224|          i += 16 + static_cast<size_t>(                                     \
  |  |11827|    224|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11828|    224|          return;                                                            \
  |  |11829|    224|        }                                                                    \
  |  |11830|  7.93k|      }                                                                      \
  |  |11831|  5.28k|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11831:14): [True: 1.79k, False: 3.28k]
  |  |  ------------------
  |  |11832|  1.79k|        const __m128i w =                                                    \
  |  |11833|  1.79k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11834|  1.79k|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11835|  1.79k|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11835:13): [True: 1.30k, False: 486]
  |  |  ------------------
  |  |11836|  1.30k|          i += static_cast<size_t>(                                          \
  |  |11837|  1.30k|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11838|  1.30k|          return;                                                            \
  |  |11839|  1.30k|        }                                                                    \
  |  |11840|  1.79k|      }                                                                      \
  |  |11841|  4.58k|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11841:11): [True: 3.28k, False: 0]
  |  |  |  Branch (11841:29): [True: 3.18k, False: 98]
  |  |  ------------------
  |  |11842|  3.18k|        const __m128i w =                                                    \
  |  |11843|  3.18k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11844|  3.18k|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11845|  3.18k|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11845:13): [True: 2.21k, False: 964]
  |  |  ------------------
  |  |11846|  2.21k|          const size_t hit = len - 16 +                                      \
  |  |11847|  2.21k|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11848|  2.21k|                                 static_cast<uint32_t>(mask)));              \
  |  |11849|  2.21k|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11849:15): [True: 2.21k, False: 0]
  |  |  ------------------
  |  |11850|  2.21k|            i = hit;                                                         \
  |  |11851|  2.21k|            return;                                                          \
  |  |11852|  2.21k|          }                                                                  \
  |  |11853|  2.21k|        }                                                                    \
  |  |11854|  3.18k|        i = len;                                                             \
  |  |11855|    964|        return;                                                              \
  |  |11856|  3.18k|      }                                                                      \
  |  |11857|  3.28k|    }                                                                        \
  |  |11858|  1.22M|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11858:12): [True: 1.10M, False: 115k]
  |  |  ------------------
  |  |11859|  1.10M|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11859:11): [True: 60.6k, False: 1.04M]
  |  |  ------------------
  |  |11860|  60.6k|        return;                                                              \
  |  |11861|  60.6k|      }                                                                      \
  |  |11862|  1.10M|    }                                                                        \
  |  |11863|   176k|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11863:12): [Folded, False: 115k]
  |  |  ------------------
  ------------------
11947|   181k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_113scan_hash_runEPKhRmm:
11950|   124k|                                   size_t len) noexcept {
11951|   124k|  ADA_SCAN_STOP_RUN(k_hash_nibbles, k_hash, sse2_hash_stop);
  ------------------
  |  |11808|   124k|  do {                                                                       \
  |  |11809|   124k|    const size_t scan_start = i;                                             \
  |  |11810|   124k|    if (parser_use_ssse3() && (i + 16 <= len)) {                             \
  |  |  ------------------
  |  |  |  Branch (11810:9): [True: 124k, False: 0]
  |  |  |  Branch (11810:31): [True: 1.39k, False: 123k]
  |  |  ------------------
  |  |11811|  1.39k|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11812|  1.39k|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11813|  5.84k|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11813:14): [True: 4.64k, False: 1.20k]
  |  |  ------------------
  |  |11814|  4.64k|        const __m128i w0 =                                                   \
  |  |11815|  4.64k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11816|  4.64k|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11817|  4.64k|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11817:13): [True: 146, False: 4.49k]
  |  |  ------------------
  |  |11818|    146|          i += static_cast<size_t>(                                          \
  |  |11819|    146|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11820|    146|          return;                                                            \
  |  |11821|    146|        }                                                                    \
  |  |11822|  4.64k|        const __m128i w1 =                                                   \
  |  |11823|  4.49k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11824|  4.49k|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11825|  4.49k|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11825:13): [True: 42, False: 4.45k]
  |  |  ------------------
  |  |11826|     42|          i += 16 + static_cast<size_t>(                                     \
  |  |11827|     42|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11828|     42|          return;                                                            \
  |  |11829|     42|        }                                                                    \
  |  |11830|  4.49k|      }                                                                      \
  |  |11831|  1.90k|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11831:14): [True: 750, False: 1.15k]
  |  |  ------------------
  |  |11832|    750|        const __m128i w =                                                    \
  |  |11833|    750|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11834|    750|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11835|    750|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11835:13): [True: 52, False: 698]
  |  |  ------------------
  |  |11836|     52|          i += static_cast<size_t>(                                          \
  |  |11837|     52|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11838|     52|          return;                                                            \
  |  |11839|     52|        }                                                                    \
  |  |11840|    750|      }                                                                      \
  |  |11841|  1.20k|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11841:11): [True: 1.15k, False: 0]
  |  |  |  Branch (11841:29): [True: 1.02k, False: 126]
  |  |  ------------------
  |  |11842|  1.02k|        const __m128i w =                                                    \
  |  |11843|  1.02k|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11844|  1.02k|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11845|  1.02k|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11845:13): [True: 64, False: 960]
  |  |  ------------------
  |  |11846|     64|          const size_t hit = len - 16 +                                      \
  |  |11847|     64|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11848|     64|                                 static_cast<uint32_t>(mask)));              \
  |  |11849|     64|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11849:15): [True: 64, False: 0]
  |  |  ------------------
  |  |11850|     64|            i = hit;                                                         \
  |  |11851|     64|            return;                                                          \
  |  |11852|     64|          }                                                                  \
  |  |11853|     64|        }                                                                    \
  |  |11854|  1.02k|        i = len;                                                             \
  |  |11855|    960|        return;                                                              \
  |  |11856|  1.02k|      }                                                                      \
  |  |11857|  1.15k|    }                                                                        \
  |  |11858|   602k|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11858:12): [True: 479k, False: 123k]
  |  |  ------------------
  |  |11859|   479k|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11859:11): [True: 144, False: 479k]
  |  |  ------------------
  |  |11860|    144|        return;                                                              \
  |  |11861|    144|      }                                                                      \
  |  |11862|   479k|    }                                                                        \
  |  |11863|   123k|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11863:12): [Folded, False: 123k]
  |  |  ------------------
  ------------------
11952|   124k|}
simple_absolute.cc:_ZN3ada6parser12_GLOBAL__N_120path_has_dot_segmentENSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
11956|  21.6k|bool path_has_dot_segment(std::string_view path) noexcept {
11957|  21.6k|  if (path.empty()) {
  ------------------
  |  Branch (11957:7): [True: 0, False: 21.6k]
  ------------------
11958|      0|    return false;
11959|      0|  }
11960|       |  // "." / ".." at the start, with or without a leading '/'.
11961|  21.6k|  const size_t i = (path[0] == '/') ? 1 : 0;
  ------------------
  |  Branch (11961:20): [True: 21.6k, False: 0]
  ------------------
11962|  21.6k|  if (i < path.size() && path[i] == '.') {
  ------------------
  |  Branch (11962:7): [True: 21.6k, False: 0]
  |  Branch (11962:26): [True: 1.08k, False: 20.5k]
  ------------------
11963|  1.08k|    const size_t after = i + 1;
11964|  1.08k|    if (after == path.size() || path[after] == '/' ||
  ------------------
  |  Branch (11964:9): [True: 38, False: 1.04k]
  |  Branch (11964:33): [True: 202, False: 844]
  ------------------
11965|    844|        (after < path.size() && path[after] == '.' &&
  ------------------
  |  Branch (11965:10): [True: 844, False: 0]
  |  Branch (11965:33): [True: 496, False: 348]
  ------------------
11966|    496|         (after + 1 == path.size() || path[after + 1] == '/'))) {
  ------------------
  |  Branch (11966:11): [True: 14, False: 482]
  |  Branch (11966:39): [True: 208, False: 274]
  ------------------
11967|    462|      return true;
11968|    462|    }
11969|  1.08k|  }
11970|  21.1k|  static constexpr std::string_view slash_dot{"/.", 2};
11971|  21.1k|  size_t p = i;
11972|  23.8k|  while ((p = path.find(slash_dot, p)) != std::string_view::npos) {
  ------------------
  |  Branch (11972:10): [True: 22.6k, False: 1.25k]
  ------------------
11973|  22.6k|    const size_t after = p + 2;
11974|  22.6k|    if (after == path.size() || path[after] == '/' ||
  ------------------
  |  Branch (11974:9): [True: 88, False: 22.5k]
  |  Branch (11974:33): [True: 19.5k, False: 2.95k]
  ------------------
11975|  2.95k|        (after < path.size() && path[after] == '.' &&
  ------------------
  |  Branch (11975:10): [True: 2.95k, False: 0]
  |  Branch (11975:33): [True: 1.06k, False: 1.89k]
  ------------------
11976|  19.9k|         (after + 1 == path.size() || path[after + 1] == '/'))) {
  ------------------
  |  Branch (11976:11): [True: 60, False: 1.00k]
  |  Branch (11976:39): [True: 202, False: 798]
  ------------------
11977|  19.9k|      return true;
11978|  19.9k|    }
11979|  2.69k|    p = after;
11980|  2.69k|  }
11981|  1.25k|  return false;
11982|  21.1k|}
simple_absolute.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
13942|   267k|    ada::url_components& components, uint32_t new_difference) {
13943|   267k|  components.username_end += new_difference;
13944|   267k|  components.host_start += new_difference;
13945|   267k|  components.host_end += new_difference;
13946|   267k|  components.pathname_start += new_difference;
13947|   267k|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13947:7): [True: 0, False: 267k]
  ------------------
13948|      0|    components.search_start += new_difference;
13949|      0|  }
13950|   267k|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13950:7): [True: 0, False: 267k]
  ------------------
13951|      0|    components.hash_start += new_difference;
13952|      0|  }
13953|   267k|}
_ZN3ada14url_aggregator10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14312|  24.3k|ada_really_inline void url_aggregator::parse_path(std::string_view input) {
14313|  24.3k|  ada_log("url_aggregator::parse_path ", input);
14314|  24.3k|  ADA_ASSERT_TRUE(validate());
14315|  24.3k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14316|  24.3k|  std::string tmp_buffer;
14317|  24.3k|  std::string_view internal_input;
14318|  24.3k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (14318:7): [True: 0, False: 24.3k]
  ------------------
14319|      0|    tmp_buffer = input;
14320|       |    // Optimization opportunity: Instead of copying and then pruning, we could
14321|       |    // just directly build the string from user_input.
14322|      0|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
14323|      0|    internal_input = tmp_buffer;
14324|  24.3k|  } else {
14325|  24.3k|    internal_input = input;
14326|  24.3k|  }
14327|       |
14328|       |  // If url is special, then:
14329|  24.3k|  if (is_special()) {
  ------------------
  |  Branch (14329:7): [True: 24.3k, False: 0]
  ------------------
14330|  24.3k|    if (internal_input.empty()) {
  ------------------
  |  Branch (14330:9): [True: 249, False: 24.1k]
  ------------------
14331|    249|      update_base_pathname("/");
14332|  24.1k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (14332:16): [True: 24.1k, False: 0]
  |  Branch (14332:46): [True: 0, False: 0]
  ------------------
14333|  24.1k|      consume_prepared_path(internal_input.substr(1));
14334|  24.1k|    } else {
14335|      0|      consume_prepared_path(internal_input);
14336|      0|    }
14337|  24.3k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (14337:14): [True: 0, False: 0]
  ------------------
14338|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (14338:9): [True: 0, False: 0]
  ------------------
14339|      0|      consume_prepared_path(internal_input.substr(1));
14340|      0|    } else {
14341|      0|      consume_prepared_path(internal_input);
14342|      0|    }
14343|      0|  } else {
14344|       |    // Non-special URLs with an empty host can have their paths erased
14345|       |    // Path-only URLs cannot have their paths erased
14346|      0|    if (components.host_start == components.host_end && !has_authority()) {
  ------------------
  |  Branch (14346:9): [True: 0, False: 0]
  |  Branch (14346:57): [True: 0, False: 0]
  ------------------
14347|      0|      update_base_pathname("/");
14348|      0|    }
14349|      0|  }
14350|  24.3k|  ADA_ASSERT_TRUE(validate());
14351|  24.3k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14445|   266k|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
14446|   266k|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
14447|   266k|          " bytes]");
14448|   266k|  ADA_ASSERT_TRUE(validate());
14449|   266k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14450|   266k|  if (input.empty()) {
  ------------------
  |  Branch (14450:7): [True: 5, False: 266k]
  ------------------
14451|      5|    return is_valid = false;
14452|      5|  }  // technically unnecessary.
14453|       |  // If input starts with U+005B ([), then:
14454|   266k|  if (input[0] == '[') {
  ------------------
  |  Branch (14454:7): [True: 664, False: 265k]
  ------------------
14455|       |    // If input does not end with U+005D (]), validation error, return failure.
14456|    664|    if (input.back() != ']') {
  ------------------
  |  Branch (14456:9): [True: 70, False: 594]
  ------------------
14457|     70|      return is_valid = false;
14458|     70|    }
14459|    594|    ada_log("parse_host ipv6");
14460|       |
14461|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
14462|       |    // trailing U+005D (]) removed.
14463|    594|    input.remove_prefix(1);
14464|    594|    input.remove_suffix(1);
14465|    594|    return parse_ipv6(input);
14466|    664|  }
14467|       |
14468|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
14469|       |  // input.
14470|   265k|  if (!is_special()) {
  ------------------
  |  Branch (14470:7): [True: 652, False: 264k]
  ------------------
14471|    652|    return parse_opaque_host(input);
14472|    652|  }
14473|       |  // Let domain be the result of running UTF-8 decode without BOM on the
14474|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
14475|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
14476|       |  // which case we do not need to call the expensive 'to_ascii' if a few
14477|       |  // conditions are met: no '%' and no 'xn-' subsequence.
14478|       |
14479|       |  // Often, the input does not contain any forbidden code points, and no upper
14480|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
14481|       |  // optimize for such a common case.
14482|       |
14483|       |  // Fast path: try to parse as pure decimal IPv4 first.
14484|   264k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
14485|   264k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (14485:7): [True: 107k, False: 156k]
  ------------------
14486|   107k|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (14486:9): [True: 107k, False: 0]
  |  Branch (14486:27): [True: 10, False: 107k]
  ------------------
14487|     10|      update_base_hostname(input.substr(0, input.size() - 1));
14488|   107k|    } else {
14489|   107k|      update_base_hostname(input);
14490|   107k|    }
14491|   107k|    host_type = IPV4;
14492|   107k|    is_valid = true;
14493|   107k|    ada_log("parse_host fast path decimal ipv4");
14494|   107k|    ADA_ASSERT_TRUE(validate());
14495|   107k|    return true;
14496|   107k|  }
14497|   156k|  uint8_t is_forbidden_or_upper =
14498|   156k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
14499|   156k|                                                             input.size());
14500|   156k|  static constexpr std::string_view xn_dash{"xn-", 3};
14501|   156k|  if ((is_forbidden_or_upper & 1) == 0) {
  ------------------
  |  Branch (14501:7): [True: 134k, False: 22.0k]
  ------------------
14502|   134k|    if ((is_forbidden_or_upper & 2) != 0) {
  ------------------
  |  Branch (14502:9): [True: 900, False: 133k]
  ------------------
14503|    900|      std::string lowered(input);
14504|    900|      unicode::to_lower_ascii(lowered.data(), lowered.size());
14505|    900|      if (lowered.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14505:11): [True: 616, False: 284]
  ------------------
14506|    665|          lowered.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14506:11): [True: 49, False: 235]
  ------------------
14507|       |        // Check IPv4 before mutating so failures leave the buffer untouched.
14508|       |        // This also avoids a double buffer rewrite for hex/octal IPv4.
14509|    665|        if (checkers::is_ipv4(lowered)) {
  ------------------
  |  Branch (14509:13): [True: 142, False: 523]
  ------------------
14510|    142|          ada_log("parse_host fast path ipv4");
14511|    142|          return parse_ipv4(lowered, false);
14512|    142|        }
14513|    523|        update_base_hostname(lowered);
14514|    523|        ada_log("parse_host fast path ", get_hostname());
14515|    523|        is_valid = true;
14516|    523|        return true;
14517|    665|      }
14518|   133k|    } else if (input.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14518:16): [True: 72.8k, False: 61.0k]
  ------------------
14519|  73.0k|               input.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14519:16): [True: 154, False: 60.8k]
  ------------------
14520|       |      // Check IPv4 before mutating so failures leave the buffer untouched.
14521|  73.0k|      if (checkers::is_ipv4(input)) {
  ------------------
  |  Branch (14521:11): [True: 39.3k, False: 33.6k]
  ------------------
14522|  39.3k|        ada_log("parse_host fast path ipv4");
14523|  39.3k|        return parse_ipv4(input, false);
14524|  39.3k|      }
14525|  33.6k|      update_base_hostname(input);
14526|  33.6k|      ada_log("parse_host fast path ", get_hostname());
14527|  33.6k|      is_valid = true;
14528|  33.6k|      return true;
14529|  73.0k|    }
14530|   134k|  } else if (const size_t first_percent = input.find('%');
14531|  22.0k|             first_percent != std::string_view::npos) {
  ------------------
  |  Branch (14531:14): [True: 19.5k, False: 2.46k]
  ------------------
14532|  19.5k|    std::string decoded = unicode::percent_decode(input, first_percent);
14533|  19.5k|    const uint8_t decoded_flags =
14534|  19.5k|        unicode::contains_forbidden_domain_code_point_or_upper(decoded.data(),
14535|  19.5k|                                                               decoded.size());
14536|  19.5k|    if ((decoded_flags & 1) == 0) {
  ------------------
  |  Branch (14536:9): [True: 19.2k, False: 295]
  ------------------
14537|  19.2k|      if ((decoded_flags & 2) != 0) {
  ------------------
  |  Branch (14537:11): [True: 123, False: 19.1k]
  ------------------
14538|    123|        unicode::to_lower_ascii(decoded.data(), decoded.size());
14539|    123|      }
14540|  19.2k|      if (decoded.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14540:11): [True: 19.1k, False: 59]
  ------------------
14541|  19.2k|          decoded.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14541:11): [True: 26, False: 33]
  ------------------
14542|       |        // Check IPv4 before mutating so failures leave the buffer untouched.
14543|  19.2k|        if (checkers::is_ipv4(decoded)) {
  ------------------
  |  Branch (14543:13): [True: 19.0k, False: 177]
  ------------------
14544|  19.0k|          return parse_ipv4(decoded, false);
14545|  19.0k|        }
14546|    177|        update_base_hostname(decoded);
14547|    177|        is_valid = true;
14548|    177|        return true;
14549|  19.2k|      }
14550|  19.2k|    }
14551|  19.5k|  }
14552|       |  // We have encountered at least one forbidden code point or the input contains
14553|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
14554|       |  // conversion.
14555|       |
14556|  63.9k|  ada_log("parse_host calling to_ascii");
14557|  63.9k|  std::optional<std::string> host = std::string(get_hostname());
14558|  63.9k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
14559|  63.9k|  if (!is_valid) {
  ------------------
  |  Branch (14559:7): [True: 1.43k, False: 62.4k]
  ------------------
14560|  1.43k|    ada_log("parse_host to_ascii returns false");
14561|  1.43k|    return is_valid = false;
14562|  1.43k|  }
14563|  62.4k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
14564|  62.4k|          " bytes]");
14565|       |
14566|  62.4k|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (14566:7): [True: 0, False: 62.4k]
  ------------------
14567|  62.4k|                          ada::unicode::is_forbidden_domain_code_point)) {
14568|      0|    return is_valid = false;
14569|      0|  }
14570|       |
14571|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
14572|       |  // asciiDomain.
14573|  62.4k|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (14573:7): [True: 121, False: 62.3k]
  ------------------
14574|    121|    ada_log("parse_host got ipv4 ", *host);
14575|    121|    return parse_ipv4(host.value(), false);
14576|    121|  }
14577|       |
14578|  62.3k|  update_base_hostname(host.value());
14579|  62.3k|  ADA_ASSERT_TRUE(validate());
14580|  62.3k|  return true;
14581|  62.4k|}
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8854|  23.2k|                                                       size_t pos) {
 8855|  23.2k|  ADA_ASSERT_TRUE(pos <= input.size());
 8856|       |  // The following is safer but unneeded if we have the above line:
 8857|       |  // return pos > input.size() ? std::string_view() : input.substr(pos);
 8858|  23.2k|  return input.substr(pos);
 8859|  23.2k|}
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15427|   239k|inline void url_aggregator::consume_prepared_path(std::string_view input) {
15428|   239k|  ada_log("url_aggregator::consume_prepared_path ", input);
15429|       |  /***
15430|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
15431|       |   * unfortunate. This particular function is nearly identical, except that it
15432|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
15433|       |   * very common) merely appends to the buffer. This is the same trivial path as
15434|       |   * with helpers::parse_prepared_path, except that we have the additional check
15435|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
15436|       |   * modify it, and then insert it back into the buffer.
15437|       |   */
15438|   239k|  uint8_t accumulator = checkers::path_signature(input);
15439|       |  // Let us first detect a trivial case.
15440|       |  // If it is special, we check that we have no dot, no %,  no \ and no
15441|       |  // character needing percent encoding. Otherwise, we check that we have no %,
15442|       |  // no dot, and no character needing percent encoding.
15443|   239k|  constexpr uint8_t need_encoding = 1;
15444|   239k|  constexpr uint8_t backslash_char = 2;
15445|   239k|  constexpr uint8_t dot_char = 4;
15446|   239k|  constexpr uint8_t percent_char = 8;
15447|   239k|  bool special = type != ada::scheme::NOT_SPECIAL;
15448|   239k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (15448:39): [True: 1.29k, False: 237k]
  ------------------
15449|  1.29k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (15449:39): [True: 43, False: 1.25k]
  ------------------
15450|   239k|  bool trivial_path =
15451|   239k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (15451:7): [True: 208k, False: 30.4k]
  |  Branch (15451:8): [True: 238k, False: 810]
  ------------------
15452|   239k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
15453|    810|                  0)) &&
15454|   208k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (15454:7): [True: 208k, False: 11]
  ------------------
15455|   239k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (15455:7): [True: 13.0k, False: 226k]
  |  Branch (15455:34): [True: 12.9k, False: 24]
  ------------------
15456|       |    // '4' means that we have at least one dot, but nothing that requires
15457|       |    // percent encoding or decoding. The only part that is not trivial is
15458|       |    // that we may have single dots and double dots path segments.
15459|       |    // If we have such segments, then we either have a path that begins
15460|       |    // with '.' (easy to check), or we have the sequence './'.
15461|       |    // Note: input cannot be empty, it must at least contain one character ('.')
15462|       |    // Note: we know that '\' is not present.
15463|  12.9k|    if (input[0] != '.') {
  ------------------
  |  Branch (15463:9): [True: 11.7k, False: 1.28k]
  ------------------
15464|  11.7k|      size_t slashdot = 0;
15465|  11.7k|      bool dot_is_file = true;
15466|  37.9k|      for (;;) {
15467|  37.9k|        slashdot = input.find("/.", slashdot);
15468|  37.9k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (15468:13): [True: 11.7k, False: 26.2k]
  ------------------
15469|  11.7k|          break;
15470|  26.2k|        } else {  // uncommon
15471|       |          // only three cases matter: /./, /.. or a final /
15472|  26.2k|          slashdot += 2;
15473|  26.2k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (15473:28): [True: 74, False: 26.1k]
  |  Branch (15473:56): [True: 12.4k, False: 13.7k]
  ------------------
15474|  13.7k|                           input[slashdot] == '/');
  ------------------
  |  Branch (15474:28): [True: 12.5k, False: 1.17k]
  ------------------
15475|  26.2k|        }
15476|  37.9k|      }
15477|  11.7k|      trivial_path = dot_is_file;
15478|  11.7k|    }
15479|  12.9k|  }
15480|   239k|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (15480:7): [True: 209k, False: 29.3k]
  |  Branch (15480:23): [True: 209k, False: 0]
  ------------------
15481|   209k|    ada_log("parse_path trivial");
15482|   209k|    buffer += '/';
15483|   209k|    buffer += input;
15484|   209k|    return;
15485|   209k|  }
15486|  29.3k|  std::string path = std::string(get_pathname());
15487|       |  // We are going to need to look a bit at the path, but let us see if we can
15488|       |  // ignore percent encoding *and* backslashes *and* percent characters.
15489|       |  // Except for the trivial case, this is likely to capture 99% of paths out
15490|       |  // there.
15491|  29.3k|  bool fast_path =
15492|  29.3k|      (special &&
  ------------------
  |  Branch (15492:8): [True: 28.9k, False: 396]
  ------------------
15493|  28.9k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (15493:8): [True: 11.8k, False: 17.1k]
  ------------------
15494|  11.8k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (15494:7): [True: 11.7k, False: 105]
  ------------------
15495|  29.3k|  if (fast_path) {
  ------------------
  |  Branch (15495:7): [True: 11.7k, False: 17.6k]
  ------------------
15496|  11.7k|    ada_log("parse_prepared_path fast");
15497|       |    // Here we don't need to worry about \ or percent encoding.
15498|       |    // We also do not have a file protocol. We might have dots, however,
15499|       |    // but dots must as appear as '.', and they cannot be encoded because
15500|       |    // the symbol '%' is not present.
15501|  11.7k|    size_t previous_location = 0;  // We start at 0.
15502|  72.3k|    do {
15503|  72.3k|      size_t new_location = input.find('/', previous_location);
15504|       |      // std::string_view path_view = input;
15505|       |      //  We process the last segment separately:
15506|  72.3k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (15506:11): [True: 11.7k, False: 60.6k]
  ------------------
15507|  11.7k|        std::string_view path_view = input.substr(previous_location);
15508|  11.7k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (15508:13): [True: 79, False: 11.6k]
  ------------------
15509|       |          // e.g., if you receive ".." with an empty path, you go to "/".
15510|     79|          if (path.empty()) {
  ------------------
  |  Branch (15510:15): [True: 18, False: 61]
  ------------------
15511|     18|            path = '/';
15512|     18|            update_base_pathname(path);
15513|     18|            return;
15514|     18|          }
15515|       |          // Fast case where we have nothing to do:
15516|     61|          if (path.back() == '/') {
  ------------------
  |  Branch (15516:15): [True: 18, False: 43]
  ------------------
15517|     18|            update_base_pathname(path);
15518|     18|            return;
15519|     18|          }
15520|       |          // If you have the path "/joe/myfriend",
15521|       |          // then you delete 'myfriend'.
15522|     43|          path.resize(path.rfind('/') + 1);
15523|     43|          update_base_pathname(path);
15524|     43|          return;
15525|     61|        }
15526|  11.6k|        path += '/';
15527|  11.6k|        if (path_view != ".") {
  ------------------
  |  Branch (15527:13): [True: 11.5k, False: 105]
  ------------------
15528|  11.5k|          path.append(path_view);
15529|  11.5k|        }
15530|  11.6k|        update_base_pathname(path);
15531|  11.6k|        return;
15532|  60.6k|      } else {
15533|       |        // This is a non-final segment.
15534|  60.6k|        std::string_view path_view =
15535|  60.6k|            input.substr(previous_location, new_location - previous_location);
15536|  60.6k|        previous_location = new_location + 1;
15537|  60.6k|        if (path_view == "..") {
  ------------------
  |  Branch (15537:13): [True: 12.9k, False: 47.7k]
  ------------------
15538|  12.9k|          size_t last_delimiter = path.rfind('/');
15539|  12.9k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (15539:15): [True: 11.0k, False: 1.81k]
  ------------------
15540|  11.0k|            path.erase(last_delimiter);
15541|  11.0k|          }
15542|  47.7k|        } else if (path_view != ".") {
  ------------------
  |  Branch (15542:20): [True: 34.4k, False: 13.2k]
  ------------------
15543|  34.4k|          path += '/';
15544|  34.4k|          path.append(path_view);
15545|  34.4k|        }
15546|  60.6k|      }
15547|  72.3k|    } while (true);
  ------------------
  |  Branch (15547:14): [True: 60.6k, Folded]
  ------------------
15548|  17.6k|  } else {
15549|  17.6k|    ada_log("parse_path slow");
15550|       |    // we have reached the general case
15551|  17.6k|    bool needs_percent_encoding = (accumulator & 1);
15552|  64.7k|    do {
15553|  64.7k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (15553:26): [True: 63.0k, False: 1.73k]
  |  Branch (15553:37): [True: 4.49k, False: 58.5k]
  ------------------
15554|  64.7k|                            ? input.find_first_of("/\\")
15555|  64.7k|                            : input.find('/');
15556|  64.7k|      std::string_view path_view = input;
15557|  64.7k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (15557:11): [True: 47.1k, False: 17.6k]
  ------------------
15558|  47.1k|        path_view.remove_suffix(path_view.size() - location);
15559|  47.1k|        input.remove_prefix(location + 1);
15560|  47.1k|      }
15561|       |      // Path encoding preserves dots and percent signs, so normalize first.
15562|  64.7k|      if (unicode::is_double_dot_path_segment(path_view)) {
  ------------------
  |  Branch (15562:11): [True: 12.8k, False: 51.9k]
  ------------------
15563|  12.8k|        helpers::shorten_path(path, type);
15564|  12.8k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (15564:13): [True: 9.80k, False: 3.06k]
  ------------------
15565|  9.80k|          path += '/';
15566|  9.80k|        }
15567|  51.9k|      } else if (unicode::is_single_dot_path_segment(path_view) &&
  ------------------
  |  Branch (15567:18): [True: 2.25k, False: 49.6k]
  ------------------
15568|  2.25k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (15568:18): [True: 406, False: 1.84k]
  ------------------
15569|    406|        path += '/';
15570|    406|      }
15571|       |      // Otherwise, if path_view is not a single-dot path segment, then:
15572|  51.5k|      else if (!unicode::is_single_dot_path_segment(path_view)) {
  ------------------
  |  Branch (15572:16): [True: 49.6k, False: 1.84k]
  ------------------
15573|       |        // If url's scheme is "file", url's path is empty, and path_view is a
15574|       |        // Windows drive letter, then replace the second code point in
15575|       |        // path_view with U+003A (:).
15576|  49.6k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (15576:13): [True: 1.78k, False: 47.8k]
  |  Branch (15576:48): [True: 501, False: 1.27k]
  ------------------
15577|    501|            path_view.size() == 2 &&
  ------------------
  |  Branch (15577:13): [True: 91, False: 410]
  ------------------
15578|     91|            checkers::is_windows_drive_letter(path_view)) {
  ------------------
  |  Branch (15578:13): [True: 45, False: 46]
  ------------------
15579|     45|          path += '/';
15580|     45|          path += path_view[0];
15581|     45|          path += ':';
15582|     45|          path_view.remove_prefix(2);
15583|     45|          path.append(path_view);
15584|  49.6k|        } else {
15585|       |          // Append encoded bytes directly to the pathname.
15586|  49.6k|          path += '/';
15587|  49.6k|          if (!needs_percent_encoding ||
  ------------------
  |  Branch (15587:15): [True: 38.6k, False: 10.9k]
  ------------------
15588|  10.9k|              !ada::unicode::percent_encode<true>(
  ------------------
  |  Branch (15588:15): [True: 7.45k, False: 3.50k]
  ------------------
15589|  46.1k|                  path_view, character_sets::PATH_PERCENT_ENCODE, path)) {
15590|  46.1k|            path.append(path_view);
15591|  46.1k|          }
15592|  49.6k|        }
15593|  49.6k|      }
15594|  64.7k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (15594:11): [True: 17.6k, False: 47.1k]
  ------------------
15595|  17.6k|        update_base_pathname(path);
15596|  17.6k|        return;
15597|  17.6k|      }
15598|  64.7k|    } while (true);
  ------------------
  |  Branch (15598:14): [True: 47.1k, Folded]
  ------------------
15599|  17.6k|  }
15600|  29.3k|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   14|   310k|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|   310k|  if (!last_label_may_be_a_number(view)) {
  ------------------
  |  Branch (16:7): [True: 191k, False: 119k]
  ------------------
   17|   191k|    return false;
   18|   191k|  }
   19|       |  // If the address ends with a dot, we need to prune it (special case).
   20|   119k|  if (view.ends_with('.')) {
  ------------------
  |  Branch (20:7): [True: 56.9k, False: 62.5k]
  ------------------
   21|  56.9k|    view.remove_suffix(1);
   22|  56.9k|  }
   23|       |  // From the last character, find the last dot.
   24|   119k|  size_t last_dot = view.rfind('.');
   25|   119k|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (25:7): [True: 96.9k, False: 22.5k]
  ------------------
   26|       |    // We have at least one dot.
   27|  96.9k|    view.remove_prefix(last_dot + 1);
   28|  96.9k|  }
   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|   119k|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (32:7): [True: 60.1k, False: 59.3k]
  ------------------
   33|  60.1k|    return true;
   34|  60.1k|  }
   35|       |  // It could be hex (0x), but not if there is a single character.
   36|  59.3k|  if (view.size() == 1) {
  ------------------
  |  Branch (36:7): [True: 0, False: 59.3k]
  ------------------
   37|      0|    return false;
   38|      0|  }
   39|       |  // It must start with 0x.
   40|  59.3k|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (40:7): [True: 1.39k, False: 57.9k]
  ------------------
   41|  1.39k|    return false;
   42|  1.39k|  }
   43|       |  // We must allow "0x".
   44|  57.9k|  if (view.size() == 2) {
  ------------------
  |  Branch (44:7): [True: 18.9k, False: 38.9k]
  ------------------
   45|  18.9k|    return true;
   46|  18.9k|  }
   47|       |  // We have 0x followed by some characters, we need to check that they are
   48|       |  // hexadecimals.
   49|  38.9k|  view.remove_prefix(2);
   50|  38.9k|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   51|  57.9k|}
_ZN3ada7unicode16is_lowercase_hexEc:
 7436|   328k|ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept {
 7437|   328k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7437:11): [True: 328k, False: 0]
  |  Branch (7437:23): [True: 7.19k, False: 321k]
  |  Branch (7437:37): [True: 321k, False: 0]
  |  Branch (7437:49): [True: 320k, False: 752]
  ------------------
 7438|   328k|}
_ZN3ada3url12parse_schemeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
10308|   267k|ada_really_inline bool url::parse_scheme(const std::string_view input) {
10309|   267k|  auto parsed_type = ada::scheme::get_scheme_type(input);
10310|   267k|  bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
10311|       |  /**
10312|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
10313|       |   *http, https), in which case, we can go really fast.
10314|       |   **/
10315|   267k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (10315:7): [True: 263k, False: 4.69k]
  ------------------
10316|       |    if constexpr (has_state_override) {
10317|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
10318|       |      // then return.
10319|       |      if (is_special() != is_input_special) {
10320|       |        return false;
10321|       |      }
10322|       |
10323|       |      // If url includes credentials or has a non-null port, and buffer is
10324|       |      // "file", then return.
10325|       |      if ((has_credentials() || port.has_value()) &&
10326|       |          parsed_type == ada::scheme::type::FILE) {
10327|       |        return false;
10328|       |      }
10329|       |
10330|       |      // If url's scheme is "file" and its host is an empty host, then return.
10331|       |      // An empty host is the empty string.
10332|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
10333|       |          host->empty()) {
10334|       |        return false;
10335|       |      }
10336|       |    }
10337|       |
10338|   263k|    type = parsed_type;
10339|       |
10340|       |    if constexpr (has_state_override) {
10341|       |      // This is uncommon.
10342|       |      uint16_t urls_scheme_port = get_special_port();
10343|       |
10344|       |      if (urls_scheme_port) {
10345|       |        // If url's port is url's scheme's default port, then set url's port to
10346|       |        // null.
10347|       |        if (port.has_value() && *port == urls_scheme_port) {
10348|       |          port = std::nullopt;
10349|       |        }
10350|       |      }
10351|       |    }
10352|   263k|  } else {  // slow path
10353|  4.69k|    std::string _buffer(input);
10354|       |    // Next function is only valid if the input is ASCII and returns false
10355|       |    // otherwise, but it seems that we always have ascii content so we do not
10356|       |    // need to check the return value.
10357|       |    // bool is_ascii =
10358|  4.69k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
10359|       |
10360|       |    if constexpr (has_state_override) {
10361|       |      // The state-override validation errors below ("return" in the WHATWG URL
10362|       |      // parser) leave the URL unchanged. The setter contract is
10363|       |      // "true on success, false if the scheme is invalid" -- the fast path
10364|       |      // above already returns false here, so the slow path must agree.
10365|       |
10366|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
10367|       |      // then return. If url's scheme is not a special scheme and buffer is a
10368|       |      // special scheme, then return.
10369|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
10370|       |        return false;
10371|       |      }
10372|       |
10373|       |      // If url includes credentials or has a non-null port, and buffer is
10374|       |      // "file", then return.
10375|       |      if ((has_credentials() || port.has_value()) && _buffer == "file") {
10376|       |        return false;
10377|       |      }
10378|       |
10379|       |      // If url's scheme is "file" and its host is an empty host, then return.
10380|       |      // An empty host is the empty string.
10381|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
10382|       |          host->empty()) {
10383|       |        return false;
10384|       |      }
10385|       |    }
10386|       |
10387|  4.69k|    set_scheme(std::move(_buffer));
10388|       |
10389|       |    if constexpr (has_state_override) {
10390|       |      // This is uncommon.
10391|       |      uint16_t urls_scheme_port = get_special_port();
10392|       |
10393|       |      if (urls_scheme_port) {
10394|       |        // If url's port is url's scheme's default port, then set url's port to
10395|       |        // null.
10396|       |        if (port.has_value() && *port == urls_scheme_port) {
10397|       |          port = std::nullopt;
10398|       |        }
10399|       |      }
10400|       |    }
10401|  4.69k|  }
10402|       |
10403|   267k|  return true;
10404|   267k|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
13960|   267k|    const std::string_view input_with_colon) {
13961|   267k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
13962|   267k|  ADA_ASSERT_TRUE(validate());
13963|   267k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
13964|   267k|  std::string_view input{input_with_colon};
13965|   267k|  input.remove_suffix(1);
13966|   267k|  auto parsed_type = ada::scheme::get_scheme_type(input);
13967|   267k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
13968|       |  /**
13969|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
13970|       |   *http, https), in which case, we can go really fast.
13971|       |   **/
13972|   267k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (13972:7): [True: 263k, False: 4.69k]
  ------------------
13973|       |    if constexpr (has_state_override) {
13974|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
13975|       |      // then return.
13976|       |      if (is_special() != is_input_special) {
13977|       |        return false;
13978|       |      }
13979|       |
13980|       |      // If url includes credentials or has a non-null port, and buffer is
13981|       |      // "file", then return.
13982|       |      if ((has_credentials() || components.port != url_components::omitted) &&
13983|       |          parsed_type == ada::scheme::type::FILE) {
13984|       |        return false;
13985|       |      }
13986|       |
13987|       |      // If url's scheme is "file" and its host is an empty host, then return.
13988|       |      // An empty host is the empty string.
13989|       |      if (type == ada::scheme::type::FILE &&
13990|       |          components.host_start == components.host_end) {
13991|       |        return false;
13992|       |      }
13993|       |    }
13994|       |
13995|   263k|    type = parsed_type;
13996|   263k|    set_scheme_from_view_with_colon(input_with_colon);
13997|       |
13998|       |    if constexpr (has_state_override) {
13999|       |      // This is uncommon.
14000|       |      uint16_t urls_scheme_port = get_special_port();
14001|       |
14002|       |      if (urls_scheme_port) {
14003|       |        // If url's port is url's scheme's default port, then set url's port to
14004|       |        // null.
14005|       |        if (components.port == urls_scheme_port) {
14006|       |          clear_port();
14007|       |        }
14008|       |      }
14009|       |    }
14010|   263k|  } else {  // slow path
14011|  4.69k|    std::string _buffer(input);
14012|       |    // Next function is only valid if the input is ASCII and returns false
14013|       |    // otherwise, but it seems that we always have ascii content so we do not
14014|       |    // need to check the return value.
14015|  4.69k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
14016|       |
14017|       |    if constexpr (has_state_override) {
14018|       |      // The state-override validation errors below ("return" in the WHATWG URL
14019|       |      // parser) leave the URL unchanged. The setter contract is
14020|       |      // "true on success, false if the scheme is invalid" -- the fast path
14021|       |      // above already returns false here, so the slow path must agree.
14022|       |
14023|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
14024|       |      // then return. If url's scheme is not a special scheme and buffer is a
14025|       |      // special scheme, then return.
14026|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
14027|       |        return false;
14028|       |      }
14029|       |
14030|       |      // If url includes credentials or has a non-null port, and buffer is
14031|       |      // "file", then return.
14032|       |      if ((has_credentials() || components.port != url_components::omitted) &&
14033|       |          _buffer == "file") {
14034|       |        return false;
14035|       |      }
14036|       |
14037|       |      // If url's scheme is "file" and its host is an empty host, then return.
14038|       |      // An empty host is the empty string.
14039|       |      if (type == ada::scheme::type::FILE &&
14040|       |          components.host_start == components.host_end) {
14041|       |        return false;
14042|       |      }
14043|       |    }
14044|       |
14045|  4.69k|    set_scheme(_buffer);
14046|       |
14047|       |    if constexpr (has_state_override) {
14048|       |      // This is uncommon.
14049|       |      uint16_t urls_scheme_port = get_special_port();
14050|       |
14051|       |      if (urls_scheme_port) {
14052|       |        // If url's port is url's scheme's default port, then set url's port to
14053|       |        // null.
14054|       |        if (components.port == urls_scheme_port) {
14055|       |          clear_port();
14056|       |        }
14057|       |      }
14058|       |    }
14059|  4.69k|  }
14060|   267k|  ADA_ASSERT_TRUE(validate());
14061|   267k|  return true;
14062|   267k|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14086|   263k|    std::string_view new_scheme_with_colon) {
14087|   263k|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
14088|   263k|          new_scheme_with_colon);
14089|   263k|  ADA_ASSERT_TRUE(validate());
14090|   263k|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
14091|   263k|                  new_scheme_with_colon.back() == ':');
14092|       |  // next line could overflow but unsigned arithmetic has well-defined
14093|       |  // overflows.
14094|   263k|  uint32_t new_difference =
14095|   263k|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
14096|       |
14097|   263k|  if (buffer.empty()) {
  ------------------
  |  Branch (14097:7): [True: 263k, False: 0]
  ------------------
14098|   263k|    buffer.append(new_scheme_with_colon);
14099|   263k|  } else {
14100|      0|    buffer.erase(0, components.protocol_end);
14101|      0|    buffer.insert(0, new_scheme_with_colon);
14102|      0|  }
14103|   263k|  components.protocol_end += new_difference;
14104|       |
14105|   263k|  apply_shifted_non_scheme_offsets(components, new_difference);
14106|   263k|  ADA_ASSERT_TRUE(validate());
14107|   263k|}
_ZN3ada14url_aggregator10set_schemeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14109|  4.69k|inline void url_aggregator::set_scheme(std::string_view new_scheme) {
14110|  4.69k|  ada_log("url_aggregator::set_scheme ", new_scheme);
14111|  4.69k|  ADA_ASSERT_TRUE(validate());
14112|  4.69k|  ADA_ASSERT_TRUE(new_scheme.empty() || new_scheme.back() != ':');
14113|       |  // next line could overflow but unsigned arithmetic has well-defined
14114|       |  // overflows.
14115|  4.69k|  uint32_t new_difference =
14116|  4.69k|      uint32_t(new_scheme.size()) - components.protocol_end + 1;
14117|       |
14118|  4.69k|  type = ada::scheme::get_scheme_type(new_scheme);
14119|  4.69k|  if (buffer.empty()) {
  ------------------
  |  Branch (14119:7): [True: 4.69k, False: 0]
  ------------------
14120|  4.69k|    buffer.append(helpers::concat(new_scheme, ":"));
14121|  4.69k|  } else {
14122|      0|    buffer.erase(0, components.protocol_end);
14123|      0|    buffer.insert(0, helpers::concat(new_scheme, ":"));
14124|      0|  }
14125|  4.69k|  components.protocol_end = uint32_t(new_scheme.size() + 1);
14126|       |
14127|  4.69k|  apply_shifted_non_scheme_offsets(components, new_difference);
14128|  4.69k|  ADA_ASSERT_TRUE(validate());
14129|  4.69k|}

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5869|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1086|  1.43M|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1087|  1.43M|  return !!(a[i >> 3] & (1 << (i & 7)));
 1088|  1.43M|}
_ZN2tl10unexpectedIN3ada6errorsEEC2EOS2_:
 2216|  86.9k|  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:
 3702|  21.7k|      : impl_base(unexpect, std::move(e.value())),
 3703|  21.7k|        ctor_base(detail::default_constructor_tag{}) {}
_ZNR2tl10unexpectedIN3ada6errorsEE5valueEv:
 2230|  43.4k|  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2688|  21.7k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada3urlENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3306|   623k|  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_:
 3788|   601k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3661|   601k|      : impl_base(in_place, std::forward<Args>(args)...),
 3662|   601k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2676|   601k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada3urlC2EOS0_:
 5108|   601k|  url(url&& u) noexcept = default;
_ZN3ada8url_baseD2Ev:
 1712|  2.44M|  virtual ~url_base() = default;
_ZN3ada3urlD2Ev:
 5111|  1.22M|  ~url() override = default;
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3702|  21.7k|      : impl_base(unexpect, std::move(e.value())),
 3703|  21.7k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2688|  21.7k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3306|   623k|  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_:
 3788|   601k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3661|   601k|      : impl_base(in_place, std::forward<Args>(args)...),
 3662|   601k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2676|   601k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorC2EOS0_:
 7922|   601k|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZN3ada14url_aggregatorD2Ev:
 7925|  1.22M|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6923|   545k|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6924|   545k|  if (scheme.empty()) {
  ------------------
  |  Branch (6924:7): [True: 0, False: 545k]
  ------------------
 6925|      0|    return ada::scheme::NOT_SPECIAL;
 6926|      0|  }
 6927|   545k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6928|   545k|  const std::string_view target = details::is_special_list[hash_value];
 6929|   545k|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6929:7): [True: 540k, False: 4.95k]
  ------------------
 6930|   540k|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6930:7): [True: 532k, False: 8.07k]
  ------------------
 6931|   540k|          details::scheme_keys[hash_value]) {
 6932|   532k|    return ada::scheme::type(hash_value);
 6933|   532k|  } else {
 6934|  13.0k|    return ada::scheme::NOT_SPECIAL;
 6935|  13.0k|  }
 6936|   545k|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6864|   540k|inline uint64_t branchless_load5(const char* p, size_t n) {
 6865|   540k|  uint64_t input = (uint8_t)p[0];
 6866|   540k|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6867|   540k|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6868|   540k|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6869|   540k|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6870|   540k|  return input;
 6871|   540k|}
_ZN3ada14url_aggregatorC2Ev:
 7920|   623k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 5017|   623k|  url_components() = default;
_ZN3ada14url_aggregatoraSEOS0_:
 7923|   200k|  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1460|   646k|try_parse_ipv4_fast(std::string_view input) noexcept {
 1461|   646k|  const size_t len = input.size();
 1462|       |  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
 1463|   646k|  if (len < 7 || len > 16) [[unlikely]] {
  ------------------
  |  Branch (1463:7): [True: 108k, False: 538k]
  |  Branch (1463:18): [True: 6.50k, False: 531k]
  ------------------
 1464|   114k|    return ipv4_fast_fail;
 1465|   114k|  }
 1466|   531k|  const char* data = input.data();
 1467|       |
 1468|       |#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
 1469|       |  return detail::try_parse_ipv4_avx512(data, len);
 1470|       |#else
 1471|   531k|  return detail::parse_ipv4_decimal_scalar(data, data + len);
 1472|   646k|#endif
 1473|   646k|}
_ZN3ada8checkers6detail25parse_ipv4_decimal_scalarEPKcS3_:
 1301|   531k|parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
 1302|   531k|  uint32_t ipv4 = 0;
 1303|  1.54M|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1303:19): [True: 1.29M, False: 254k]
  ------------------
 1304|  1.29M|    if (p == pend) [[unlikely]] {
  ------------------
  |  Branch (1304:9): [True: 20, False: 1.29M]
  ------------------
 1305|     20|      return ipv4_fast_fail;
 1306|     20|    }
 1307|  1.29M|    uint32_t val;
 1308|  1.29M|    char c = *p;
 1309|  1.29M|    if (c >= '0' && c <= '9') [[likely]] {
  ------------------
  |  Branch (1309:9): [True: 1.29M, False: 866]
  |  Branch (1309:21): [True: 1.09M, False: 199k]
  ------------------
 1310|  1.09M|      val = static_cast<uint32_t>(c - '0');
 1311|  1.09M|      ++p;
 1312|  1.09M|    } else {
 1313|   199k|      return ipv4_fast_fail;
 1314|   199k|    }
 1315|  1.09M|    if (p < pend) {
  ------------------
  |  Branch (1315:9): [True: 992k, False: 101k]
  ------------------
 1316|   992k|      c = *p;
 1317|   992k|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1317:11): [True: 730k, False: 262k]
  |  Branch (1317:23): [True: 692k, False: 38.4k]
  ------------------
 1318|   692k|        if (val == 0) [[unlikely]] {
  ------------------
  |  Branch (1318:13): [True: 206, False: 691k]
  ------------------
 1319|    206|          return ipv4_fast_fail;
 1320|    206|        }
 1321|   691k|        val = val * 10u + static_cast<uint32_t>(c - '0');
 1322|   691k|        ++p;
 1323|   691k|        if (p < pend) {
  ------------------
  |  Branch (1323:13): [True: 578k, False: 113k]
  ------------------
 1324|   578k|          c = *p;
 1325|   578k|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1325:15): [True: 312k, False: 265k]
  |  Branch (1325:27): [True: 312k, False: 104]
  ------------------
 1326|   312k|            val = val * 10u + static_cast<uint32_t>(c - '0');
 1327|   312k|            ++p;
 1328|   312k|            if (val > 255u) [[unlikely]] {
  ------------------
  |  Branch (1328:17): [True: 1.15k, False: 311k]
  ------------------
 1329|  1.15k|              return ipv4_fast_fail;
 1330|  1.15k|            }
 1331|   312k|          }
 1332|   578k|        }
 1333|   691k|      }
 1334|   992k|    }
 1335|  1.09M|    ipv4 = (ipv4 << 8) | val;
 1336|  1.09M|    if (i < 3) {
  ------------------
  |  Branch (1336:9): [True: 839k, False: 254k]
  ------------------
 1337|   839k|      if (p == pend || *p != '.') [[unlikely]] {
  ------------------
  |  Branch (1337:11): [True: 100, False: 839k]
  |  Branch (1337:24): [True: 76.4k, False: 762k]
  ------------------
 1338|  76.5k|        return ipv4_fast_fail;
 1339|  76.5k|      }
 1340|   762k|      ++p;
 1341|   762k|    }
 1342|  1.09M|  }
 1343|   254k|  if (p != pend) {
  ------------------
  |  Branch (1343:7): [True: 280, False: 253k]
  ------------------
 1344|    280|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1344:9): [True: 138, False: 142]
  |  Branch (1344:26): [True: 20, False: 118]
  ------------------
 1345|     20|      return ipv4;
 1346|     20|    }
 1347|    260|    return ipv4_fast_fail;
 1348|    280|  }
 1349|   253k|  return ipv4;
 1350|   254k|}
_ZNK3ada3url15has_credentialsEv:
 7451|  1.28M|[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
 7452|  1.28M|  return !username.empty() || !password.empty();
  ------------------
  |  Branch (7452:10): [True: 204k, False: 1.08M]
  |  Branch (7452:31): [True: 820, False: 1.08M]
  ------------------
 7453|  1.28M|}
_ZNK3ada8url_base10is_specialEv:
 7411|  5.77M|    const noexcept {
 7412|  5.77M|  return type != ada::scheme::NOT_SPECIAL;
 7413|  5.77M|}
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEEcvbEv:
 4101|   623k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEptEv:
 4070|  1.00M|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 4071|  1.00M|    TL_ASSERT(has_value());
  ------------------
  |  | 2123|  1.00M|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4071:5): [True: 1.00M, False: 0]
  ------------------
 4072|  1.00M|    return valptr();
 4073|  1.00M|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE6valptrEv:
 3366|  1.00M|  T* valptr() { return std::addressof(this->m_val); }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EED2Ev:
 2698|   623k|  ~expected_storage_base() {
 2699|   623k|    if (m_has_val) {
  ------------------
  |  Branch (2699:9): [True: 601k, False: 21.7k]
  ------------------
 2700|   601k|      m_val.~T();
 2701|   601k|    }
 2702|   623k|  }
_ZNK3ada3url13get_href_sizeEv:
 7709|  1.07M|[[nodiscard]] inline size_t url::get_href_size() const noexcept {
 7710|  1.07M|  size_t size = 0;
 7711|  1.07M|  if (is_special()) {
  ------------------
  |  Branch (7711:7): [True: 1.06M, False: 7.59k]
  ------------------
 7712|  1.06M|    size += ada::scheme::details::is_special_list[type].size() + 1;
 7713|  1.06M|  } else {
 7714|  7.59k|    size += non_special_scheme.size() + 1;
 7715|  7.59k|  }
 7716|  1.07M|  if (host.has_value()) {
  ------------------
  |  Branch (7716:7): [True: 1.06M, False: 4.53k]
  ------------------
 7717|  1.06M|    size += host->size();
 7718|  1.06M|    size += 2;
 7719|  1.06M|    if (has_credentials()) {
  ------------------
  |  Branch (7719:9): [True: 133k, False: 932k]
  ------------------
 7720|   133k|      size += username.size();
 7721|   133k|      if (!password.empty()) {
  ------------------
  |  Branch (7721:11): [True: 128k, False: 4.87k]
  ------------------
 7722|   128k|        size += 1 + password.size();
 7723|   128k|      }
 7724|   133k|      size += 1;
 7725|   133k|    }
 7726|  1.06M|    if (port.has_value()) {
  ------------------
  |  Branch (7726:9): [True: 211k, False: 854k]
  ------------------
 7727|   211k|      size += 1;
 7728|   211k|      uint16_t p = *port;
 7729|   211k|      size += (p >= 10000)  ? 5
  ------------------
  |  Branch (7729:15): [True: 6.03k, False: 205k]
  ------------------
 7730|   211k|              : (p >= 1000) ? 4
  ------------------
  |  Branch (7730:17): [True: 99.6k, False: 105k]
  ------------------
 7731|   205k|              : (p >= 100)  ? 3
  ------------------
  |  Branch (7731:17): [True: 426, False: 105k]
  ------------------
 7732|   105k|              : (p >= 10)   ? 2
  ------------------
  |  Branch (7732:17): [True: 94.7k, False: 10.6k]
  ------------------
 7733|   105k|                            : 1;
 7734|   211k|    }
 7735|  1.06M|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7735:14): [True: 1.52k, False: 3.01k]
  |  Branch (7735:34): [True: 117, False: 1.40k]
  ------------------
 7736|    117|    size += 2;
 7737|    117|  }
 7738|  1.07M|  size += path.size();
 7739|  1.07M|  if (query.has_value()) {
  ------------------
  |  Branch (7739:7): [True: 126k, False: 943k]
  ------------------
 7740|   126k|    size += 1 + query->size();
 7741|   126k|  }
 7742|  1.07M|  if (hash.has_value()) {
  ------------------
  |  Branch (7742:7): [True: 97.2k, False: 972k]
  ------------------
 7743|  97.2k|    size += 1 + hash->size();
 7744|  97.2k|  }
 7745|  1.07M|  return size;
 7746|  1.07M|}
_ZN3ada8checkers8is_alphaEc:
 1281|   540k|constexpr bool is_alpha(char x) noexcept {
 1282|   540k|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1282:10): [True: 538k, False: 1.75k]
  |  Branch (1282:34): [True: 538k, False: 34]
  ------------------
 1283|   540k|}
_ZN3ada8checkers8to_lowerEc:
 1279|  1.07M|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZN3ada3urlaSEOS0_:
 5109|   200k|  url& operator=(url&& u) noexcept = default;
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 4083|  1.00M|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 4084|  1.00M|    TL_ASSERT(has_value());
  ------------------
  |  | 2123|  1.00M|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4084:5): [True: 1.00M, False: 0]
  ------------------
 4085|  1.00M|    return val();
 4086|  1.00M|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3375|  1.00M|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3376|  1.00M|    return this->m_val;
 3377|  1.00M|  }
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEE9has_valueEv:
 4100|  2.42M|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN3ada8checkers26last_label_may_be_a_numberENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1254|  1.17M|constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
 1255|  1.17M|  if (view.empty()) {
  ------------------
  |  Branch (1255:7): [True: 0, False: 1.17M]
  ------------------
 1256|      0|    return false;
 1257|      0|  }
 1258|  1.17M|  const char* start = view.data();
 1259|  1.17M|  const char* end = start + view.size();
 1260|  1.17M|  if (end[-1] == '.') {
  ------------------
  |  Branch (1260:7): [True: 59.7k, False: 1.12M]
  ------------------
 1261|  59.7k|    --end;
 1262|  59.7k|    if (end == start) {
  ------------------
  |  Branch (1262:9): [True: 1.75k, False: 58.0k]
  ------------------
 1263|  1.75k|      return false;
 1264|  1.75k|    }
 1265|  59.7k|  }
 1266|  1.17M|  if (!is_ipv4_number_char(end[-1])) {
  ------------------
  |  Branch (1266:7): [True: 886k, False: 291k]
  ------------------
 1267|   886k|    return false;
 1268|   886k|  }
 1269|   291k|  const char* label = end;
 1270|  1.31M|  while (label != start && is_ipv4_number_char(label[-1])) {
  ------------------
  |  Branch (1270:10): [True: 1.28M, False: 28.0k]
  |  Branch (1270:28): [True: 1.01M, False: 263k]
  ------------------
 1271|  1.01M|    --label;
 1272|  1.01M|  }
 1273|   291k|  if (label != start && label[-1] != '.') {
  ------------------
  |  Branch (1273:7): [True: 263k, False: 28.0k]
  |  Branch (1273:25): [True: 126k, False: 137k]
  ------------------
 1274|   126k|    return false;
 1275|   126k|  }
 1276|   165k|  return label != end && is_digit(*label);
  ------------------
  |  Branch (1276:10): [True: 165k, False: 0]
  |  Branch (1276:26): [True: 157k, False: 8.01k]
  ------------------
 1277|   291k|}
_ZN3ada8checkers19is_ipv4_number_charEc:
 1248|  2.46M|constexpr bool is_ipv4_number_char(char x) noexcept {
 1249|  2.46M|  const unsigned char c = static_cast<unsigned char>(x);
 1250|  2.46M|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
  ------------------
  |  Branch (1250:11): [True: 2.20M, False: 259k]
  |  Branch (1250:23): [True: 310k, False: 1.89M]
  |  Branch (1250:37): [True: 1.89M, False: 259k]
  |  Branch (1250:49): [True: 835k, False: 1.05M]
  ------------------
 1251|  1.31M|         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
  ------------------
  |  Branch (1251:11): [True: 1.05M, False: 259k]
  |  Branch (1251:23): [True: 0, False: 1.05M]
  |  Branch (1251:36): [True: 165k, False: 1.15M]
  |  Branch (1251:48): [True: 0, False: 1.15M]
  ------------------
 1252|  2.46M|}
_ZN3ada8checkers8is_digitEc:
 1246|   403k|constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
_ZN3ada6scheme16get_special_portENS0_4typeE:
 6920|   127k|constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
 6921|   127k|  return details::special_ports[int(type)];
 6922|   127k|}
_ZN3ada14url_aggregator20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8653|  60.8k|inline void url_aggregator::update_base_pathname(const std::string_view input) {
 8654|  60.8k|  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
 8655|  60.8k|          " bytes] \n", to_diagram());
 8656|  60.8k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8657|  60.8k|  ADA_ASSERT_TRUE(validate());
 8658|       |
 8659|  60.8k|  const bool begins_with_dashdash = input.starts_with("//");
 8660|  60.8k|  if (!begins_with_dashdash && has_dash_dot()) {
  ------------------
  |  Branch (8660:7): [True: 60.1k, False: 763]
  |  Branch (8660:32): [True: 0, False: 60.1k]
  ------------------
 8661|       |    // We must delete the ./
 8662|      0|    delete_dash_dot();
 8663|      0|  }
 8664|       |
 8665|  60.8k|  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
  ------------------
  |  Branch (8665:7): [True: 763, False: 60.1k]
  |  Branch (8665:31): [True: 763, False: 0]
  |  Branch (8665:51): [True: 27, False: 736]
  ------------------
 8666|     27|      !has_dash_dot()) {
  ------------------
  |  Branch (8666:7): [True: 27, False: 0]
  ------------------
 8667|       |    // If url's host is null, url does not have an opaque path, url's path's
 8668|       |    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
 8669|       |    // output.
 8670|     27|    buffer.insert(components.pathname_start, "/.");
 8671|     27|    components.pathname_start += 2;
 8672|     27|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8672:9): [True: 0, False: 27]
  ------------------
 8673|      0|      components.search_start += 2;
 8674|      0|    }
 8675|     27|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8675:9): [True: 0, False: 27]
  ------------------
 8676|      0|      components.hash_start += 2;
 8677|      0|    }
 8678|     27|  }
 8679|       |
 8680|  60.8k|  uint32_t difference = replace_and_resize(
 8681|  60.8k|      components.pathname_start,
 8682|  60.8k|      components.pathname_start + get_pathname_length(), input);
 8683|  60.8k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8683:7): [True: 0, False: 60.8k]
  ------------------
 8684|      0|    components.search_start += difference;
 8685|      0|  }
 8686|  60.8k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8686:7): [True: 0, False: 60.8k]
  ------------------
 8687|      0|    components.hash_start += difference;
 8688|      0|  }
 8689|  60.8k|  ADA_ASSERT_TRUE(validate());
 8690|  60.8k|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8495|   307k|    uint32_t start, uint32_t end, std::string_view input) {
 8496|   307k|  uint32_t current_length = end - start;
 8497|   307k|  uint32_t input_size = uint32_t(input.size());
 8498|   307k|  uint32_t new_difference = input_size - current_length;
 8499|       |
 8500|   307k|  if (current_length == 0) {
  ------------------
  |  Branch (8500:7): [True: 276k, False: 30.8k]
  ------------------
 8501|   276k|    buffer.insert(start, input);
 8502|   276k|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8502:14): [True: 501, False: 30.3k]
  ------------------
 8503|    501|    if (input_size != 0) {
  ------------------
  |  Branch (8503:9): [True: 501, False: 0]
  ------------------
 8504|    501|      std::memmove(buffer.data() + start, input.data(), input_size);
 8505|    501|    }
 8506|  30.3k|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8506:14): [True: 3, False: 30.3k]
  ------------------
 8507|      3|    buffer.erase(start, current_length - input_size);
 8508|      3|    buffer.replace(start, input_size, input);
 8509|  30.3k|  } else {
 8510|  30.3k|    buffer.replace(start, current_length, input.substr(0, current_length));
 8511|  30.3k|    buffer.insert(start + current_length, input.substr(current_length));
 8512|  30.3k|  }
 8513|       |
 8514|   307k|  return new_difference;
 8515|   307k|}
_ZNK3ada14url_aggregator19get_pathname_lengthEv:
 8546|  60.8k|url_aggregator::get_pathname_length() const noexcept {
 8547|  60.8k|  ada_log("url_aggregator::get_pathname_length");
 8548|  60.8k|  uint32_t ending_index = uint32_t(buffer.size());
 8549|  60.8k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8549:7): [True: 0, False: 60.8k]
  ------------------
 8550|      0|    ending_index = components.search_start;
 8551|  60.8k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8551:14): [True: 0, False: 60.8k]
  ------------------
 8552|      0|    ending_index = components.hash_start;
 8553|      0|  }
 8554|  60.8k|  return ending_index - components.pathname_start;
 8555|  60.8k|}
_ZNK3ada14url_aggregator12get_pathnameEv:
 9488|   229k|    ada_lifetime_bound {
 9489|   229k|  ada_log("url_aggregator::get_pathname pathname_start = ",
 9490|   229k|          components.pathname_start, " buffer.size() = ", buffer.size(),
 9491|   229k|          " components.search_start = ", components.search_start,
 9492|   229k|          " components.hash_start = ", components.hash_start);
 9493|   229k|  auto ending_index = uint32_t(buffer.size());
 9494|   229k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9494:7): [True: 34.1k, False: 195k]
  ------------------
 9495|  34.1k|    ending_index = components.search_start;
 9496|   195k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9496:14): [True: 11.0k, False: 184k]
  ------------------
 9497|  11.0k|    ending_index = components.hash_start;
 9498|  11.0k|  }
 9499|   229k|  return helpers::substring(buffer, components.pathname_start, ending_index);
 9500|   229k|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8471|  12.2k|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8472|  12.2k|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8473|  12.2k|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8474|  12.2k|          " bytes] components.hash_start = ", components.hash_start);
 8475|  12.2k|  ADA_ASSERT_TRUE(validate());
 8476|  12.2k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8477|  12.2k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8477:7): [True: 0, False: 12.2k]
  ------------------
 8478|      0|    buffer.resize(components.hash_start);
 8479|      0|  }
 8480|  12.2k|  components.hash_start = uint32_t(buffer.size());
 8481|  12.2k|  buffer += "#";
 8482|  12.2k|  bool encoding_required = unicode::percent_encode<true>(
 8483|  12.2k|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8484|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8485|       |  // encoding was not deemed required.
 8486|  12.2k|  if (!encoding_required) {
  ------------------
  |  Branch (8486:7): [True: 11.9k, False: 287]
  ------------------
 8487|  11.9k|    buffer.append(input);
 8488|  11.9k|  }
 8489|  12.2k|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8490|  12.2k|          buffer, "' [", buffer.size(), " bytes]");
 8491|  12.2k|  ADA_ASSERT_TRUE(validate());
 8492|  12.2k|}
_ZN3ada3urlC2Ev:
 5106|   623k|  url() = default;
_ZN3ada3url26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7559|  12.2k|inline void url::update_unencoded_base_hash(std::string_view input) {
 7560|       |  // We do the percent encoding
 7561|  12.2k|  hash = unicode::percent_encode(input,
 7562|  12.2k|                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
 7563|  12.2k|}
_ZN3ada3url18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7566|  12.3k|                                    const uint8_t query_percent_encode_set[]) {
 7567|  12.3k|  query = ada::unicode::percent_encode(input, query_percent_encode_set);
 7568|  12.3k|}
_ZN3ada3url20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7557|     84|inline void url::update_base_hostname(std::string_view input) { host = input; }
_ZN3ada3url20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7574|  31.2k|inline void url::update_base_pathname(const std::string_view input) {
 7575|  31.2k|  path = input;
 7576|  31.2k|}
_ZN3ada3url10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 7749|  2.20k|                                         bool check_trailing_content) noexcept {
 7750|  2.20k|  ada_log("parse_port('", view, "') ", view.size());
 7751|  2.20k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (7751:7): [True: 2.01k, False: 193]
  |  Branch (7751:24): [True: 6, False: 2.00k]
  ------------------
 7752|      6|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 7753|      6|    is_valid = false;
 7754|      6|    return 0;
 7755|      6|  }
 7756|  2.20k|  uint16_t parsed_port{};
 7757|  2.20k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 7758|  2.20k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (7758:7): [True: 27, False: 2.17k]
  ------------------
 7759|     27|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 7760|     27|    is_valid = false;
 7761|     27|    return 0;
 7762|     27|  }
 7763|  2.17k|  ada_log("parse_port: ", parsed_port);
 7764|  2.17k|  const auto consumed = size_t(r.ptr - view.data());
 7765|  2.17k|  ada_log("parse_port: consumed ", consumed);
 7766|  2.17k|  if (check_trailing_content) {
  ------------------
  |  Branch (7766:7): [True: 2.17k, False: 0]
  ------------------
 7767|  2.17k|    is_valid &=
 7768|  2.17k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (7768:10): [True: 402, False: 1.77k]
  |  Branch (7768:37): [True: 1.41k, False: 353]
  ------------------
 7769|    353|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (7769:10): [True: 112, False: 241]
  |  Branch (7769:36): [True: 232, False: 9]
  |  Branch (7769:52): [True: 51, False: 181]
  ------------------
 7770|  2.17k|  }
 7771|  2.17k|  ada_log("parse_port: is_valid = ", is_valid);
 7772|  2.17k|  if (is_valid) {
  ------------------
  |  Branch (7772:7): [True: 1.98k, False: 190]
  ------------------
 7773|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 7774|  1.98k|    auto default_port = scheme_default_port();
 7775|  1.98k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (7775:27): [True: 175, False: 1.80k]
  |  Branch (7775:48): [True: 94, False: 81]
  ------------------
 7776|  1.89k|                         (default_port != parsed_port);
  ------------------
  |  Branch (7776:26): [True: 1.88k, False: 3]
  ------------------
 7777|  1.98k|    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
  ------------------
  |  Branch (7777:13): [True: 1.64k, False: 340]
  |  Branch (7777:36): [True: 1.64k, False: 3]
  ------------------
 7778|  1.98k|                                                  : std::nullopt;
 7779|  1.98k|  }
 7780|  2.17k|  return consumed;
 7781|  2.20k|}
_ZNK3ada8url_base19scheme_default_portEv:
 7420|  3.96k|url_base::scheme_default_port() const noexcept {
 7421|  3.96k|  return scheme::get_special_port(type);
 7422|  3.96k|}
_ZNK3ada3url12get_pathnameEv:
 7477|   200k|[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
 7478|   200k|  return path;
 7479|   200k|}
_ZN3ada8checkers23is_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1285|  5.35k|constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
 1286|  5.35k|  return input.size() >= 2 &&
  ------------------
  |  Branch (1286:10): [True: 4.01k, False: 1.33k]
  ------------------
 1287|  4.01k|         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
  ------------------
  |  Branch (1287:11): [True: 2.50k, False: 1.51k]
  |  Branch (1287:34): [True: 234, False: 2.27k]
  |  Branch (1287:55): [True: 28, False: 2.24k]
  ------------------
 1288|    262|         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
  ------------------
  |  Branch (1288:11): [True: 106, False: 156]
  |  Branch (1288:35): [True: 70, False: 86]
  |  Branch (1288:54): [True: 2, False: 84]
  ------------------
 1289|     84|                                  input[2] == '?' || input[2] == '#'));
  ------------------
  |  Branch (1289:35): [True: 0, False: 84]
  |  Branch (1289:54): [True: 0, False: 84]
  ------------------
 1290|  5.35k|}
_ZN3ada8checkers34is_normalized_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1293|    126|    std::string_view input) noexcept {
 1294|    126|  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
  ------------------
  |  Branch (1294:10): [True: 38, False: 88]
  |  Branch (1294:32): [True: 24, False: 14]
  |  Branch (1294:54): [True: 20, False: 4]
  ------------------
 1295|    126|}
_ZN3ada3url20set_protocol_as_fileEv:
 7602|  1.41k|constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
_ZN3ada7helpers14leading_zeroesEj:
 2001|   289k|inline int leading_zeroes(uint32_t input_num) noexcept {
 2002|       |#if ADA_REGULAR_VISUAL_STUDIO
 2003|       |  unsigned long leading_zero(0);
 2004|       |  unsigned long in(input_num);
 2005|       |  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
 2006|       |#else
 2007|   289k|  return __builtin_clz(input_num);
 2008|   289k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 2009|   289k|}
_ZN3ada14url_aggregator7reserveEj:
 9161|   268k|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 9162|   268k|  buffer.reserve(capacity);
 9163|   268k|}
_ZN3ada14url_aggregator20append_base_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8879|  38.9k|inline void url_aggregator::append_base_password(const std::string_view input) {
 8880|  38.9k|  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
 8881|  38.9k|          "\n", to_diagram());
 8882|  38.9k|  ADA_ASSERT_TRUE(validate());
 8883|  38.9k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8884|       |#if ADA_DEVELOPMENT_CHECKS
 8885|       |  // computing the expected password.
 8886|       |  std::string password_expected = std::string(get_password());
 8887|       |  password_expected.append(input);
 8888|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8889|  38.9k|  add_authority_slashes_if_needed();
 8890|       |
 8891|       |  // If input is empty, do nothing.
 8892|  38.9k|  if (input.empty()) {
  ------------------
  |  Branch (8892:7): [True: 3.96k, False: 35.0k]
  ------------------
 8893|  3.96k|    return;
 8894|  3.96k|  }
 8895|       |
 8896|  35.0k|  uint32_t difference = uint32_t(input.size());
 8897|  35.0k|  if (has_password()) {
  ------------------
  |  Branch (8897:7): [True: 5.23k, False: 29.7k]
  ------------------
 8898|  5.23k|    buffer.insert(components.host_start, input);
 8899|  29.7k|  } else {
 8900|  29.7k|    difference++;  // Increment for ":"
 8901|  29.7k|    buffer.insert(components.username_end, ":");
 8902|  29.7k|    buffer.insert(components.username_end + 1, input);
 8903|  29.7k|  }
 8904|  35.0k|  components.host_start += difference;
 8905|       |
 8906|       |  // The following line is required to add "@" to hostname. When updating
 8907|       |  // password if hostname does not start with "@", it is "append_base_password"s
 8908|       |  // responsibility to set it.
 8909|  35.0k|  if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8909:7): [True: 191, False: 34.8k]
  ------------------
 8910|    191|    buffer.insert(components.host_start, "@");
 8911|    191|    difference++;
 8912|    191|  }
 8913|       |
 8914|  35.0k|  components.host_end += difference;
 8915|  35.0k|  components.pathname_start += difference;
 8916|  35.0k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8916:7): [True: 0, False: 35.0k]
  ------------------
 8917|      0|    components.search_start += difference;
 8918|      0|  }
 8919|  35.0k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8919:7): [True: 0, False: 35.0k]
  ------------------
 8920|      0|    components.hash_start += difference;
 8921|      0|  }
 8922|       |#if ADA_DEVELOPMENT_CHECKS
 8923|       |  std::string password_after(get_password());
 8924|       |  ADA_ASSERT_EQUAL(
 8925|       |      password_expected, password_after,
 8926|       |      "append_base_password problem after inserting " + std::string(input));
 8927|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8928|  35.0k|  ADA_ASSERT_TRUE(validate());
 8929|  35.0k|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 9135|   341k|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 9136|   341k|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 9137|   341k|  ADA_ASSERT_TRUE(validate());
 9138|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 9139|       |  // to insert
 9140|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 9141|   341k|  if (has_authority()) {
  ------------------
  |  Branch (9141:7): [True: 95.8k, False: 245k]
  ------------------
 9142|  95.8k|    return;
 9143|  95.8k|  }
 9144|       |  // Performance: the common case is components.protocol_end == buffer.size()
 9145|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 9146|       |  // the insert could be fused with another insert.
 9147|   245k|  buffer.insert(components.protocol_end, "//");
 9148|   245k|  components.username_end += 2;
 9149|   245k|  components.host_start += 2;
 9150|   245k|  components.host_end += 2;
 9151|   245k|  components.pathname_start += 2;
 9152|   245k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9152:7): [True: 0, False: 245k]
  ------------------
 9153|      0|    components.search_start += 2;
 9154|      0|  }
 9155|   245k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9155:7): [True: 0, False: 245k]
  ------------------
 9156|      0|    components.hash_start += 2;
 9157|      0|  }
 9158|   245k|  ADA_ASSERT_TRUE(validate());
 9159|   245k|}
_ZN3ada14url_aggregator20append_base_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8763|  55.9k|inline void url_aggregator::append_base_username(const std::string_view input) {
 8764|  55.9k|  ada_log("url_aggregator::append_base_username ", input);
 8765|  55.9k|  ADA_ASSERT_TRUE(validate());
 8766|  55.9k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8767|       |#if ADA_DEVELOPMENT_CHECKS
 8768|       |  // computing the expected password.
 8769|       |  std::string username_expected(get_username());
 8770|       |  username_expected.append(input);
 8771|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8772|  55.9k|  add_authority_slashes_if_needed();
 8773|       |
 8774|       |  // If input is empty, do nothing.
 8775|  55.9k|  if (input.empty()) {
  ------------------
  |  Branch (8775:7): [True: 16.4k, False: 39.5k]
  ------------------
 8776|  16.4k|    return;
 8777|  16.4k|  }
 8778|       |
 8779|  39.5k|  uint32_t difference = uint32_t(input.size());
 8780|  39.5k|  buffer.insert(components.username_end, input);
 8781|  39.5k|  components.username_end += difference;
 8782|  39.5k|  components.host_start += difference;
 8783|       |
 8784|  39.5k|  if (buffer[components.host_start] != '@' &&
  ------------------
  |  Branch (8784:7): [True: 30.8k, False: 8.60k]
  ------------------
 8785|  30.8k|      components.host_start != components.host_end) {
  ------------------
  |  Branch (8785:7): [True: 30.8k, False: 0]
  ------------------
 8786|  30.8k|    buffer.insert(components.host_start, "@");
 8787|  30.8k|    difference++;
 8788|  30.8k|  }
 8789|       |
 8790|  39.5k|  components.host_end += difference;
 8791|  39.5k|  components.pathname_start += difference;
 8792|  39.5k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8792:7): [True: 0, False: 39.5k]
  ------------------
 8793|      0|    components.search_start += difference;
 8794|      0|  }
 8795|  39.5k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8795:7): [True: 0, False: 39.5k]
  ------------------
 8796|      0|    components.hash_start += difference;
 8797|      0|  }
 8798|       |#if ADA_DEVELOPMENT_CHECKS
 8799|       |  std::string username_after(get_username());
 8800|       |  ADA_ASSERT_EQUAL(
 8801|       |      username_expected, username_after,
 8802|       |      "append_base_username problem after inserting " + std::string(input));
 8803|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8804|  39.5k|  ADA_ASSERT_TRUE(validate());
 8805|  39.5k|}
_ZNK3ada14url_aggregator8get_hrefEv:
 9239|  1.20M|    const noexcept ada_lifetime_bound {
 9240|  1.20M|  ada_log("url_aggregator::get_href");
 9241|  1.20M|  return buffer;
 9242|  1.20M|}
_ZN3ada14url_aggregator16update_base_portEj:
 8931|  1.64k|inline void url_aggregator::update_base_port(uint32_t input) {
 8932|  1.64k|  ada_log("url_aggregator::update_base_port");
 8933|  1.64k|  ADA_ASSERT_TRUE(validate());
 8934|  1.64k|  if (input == url_components::omitted) {
  ------------------
  |  Branch (8934:7): [True: 0, False: 1.64k]
  ------------------
 8935|      0|    clear_port();
 8936|      0|    return;
 8937|      0|  }
 8938|       |  // calling std::to_string(input.value()) is unfortunate given that the port
 8939|       |  // value is probably already available as a string.
 8940|  1.64k|  std::string value = helpers::concat(":", std::to_string(input));
 8941|  1.64k|  uint32_t difference = uint32_t(value.size());
 8942|       |
 8943|  1.64k|  if (components.port != url_components::omitted) {
  ------------------
  |  Branch (8943:7): [True: 0, False: 1.64k]
  ------------------
 8944|      0|    difference -= components.pathname_start - components.host_end;
 8945|      0|    buffer.erase(components.host_end,
 8946|      0|                 components.pathname_start - components.host_end);
 8947|      0|  }
 8948|       |
 8949|  1.64k|  buffer.insert(components.host_end, value);
 8950|  1.64k|  components.pathname_start += difference;
 8951|  1.64k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8951:7): [True: 0, False: 1.64k]
  ------------------
 8952|      0|    components.search_start += difference;
 8953|      0|  }
 8954|  1.64k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8954:7): [True: 0, False: 1.64k]
  ------------------
 8955|      0|    components.hash_start += difference;
 8956|      0|  }
 8957|  1.64k|  components.port = input;
 8958|  1.64k|  ADA_ASSERT_TRUE(validate());
 8959|  1.64k|}
_ZN3ada7helpers6concatIJPKcNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEESA_DpT_:
 1991|  1.64k|std::string concat(Args... args) {
 1992|  1.64k|  std::string answer;
 1993|  1.64k|  inner_concat(answer, args...);
 1994|  1.64k|  return answer;
 1995|  1.64k|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEEvRSA_T_DpT0_:
 1980|  1.64k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1981|  1.64k|  buffer.append(t);
 1982|  1.64k|  return inner_concat(buffer, args...);
 1983|  1.64k|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJEEEvRS8_T_:
 1972|  1.64k|inline void inner_concat(std::string& buffer, T t) {
 1973|  1.64k|  buffer.append(t);
 1974|  1.64k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8602|  12.3k|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8603|  12.3k|  ada_log("url_aggregator::update_base_search ", input,
 8604|  12.3k|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8605|  12.3k|  ADA_ASSERT_TRUE(validate());
 8606|  12.3k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8607|       |
 8608|  12.3k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8608:7): [True: 12.3k, False: 0]
  ------------------
 8609|  12.3k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8609:9): [True: 12.3k, False: 0]
  ------------------
 8610|  12.3k|      components.search_start = uint32_t(buffer.size());
 8611|  12.3k|      buffer += "?";
 8612|  12.3k|    } else {
 8613|      0|      buffer.resize(components.search_start + 1);
 8614|      0|    }
 8615|       |
 8616|  12.3k|    bool encoding_required =
 8617|  12.3k|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8618|       |    // When encoding_required is false, then buffer is left unchanged, and
 8619|       |    // percent encoding was not deemed required.
 8620|  12.3k|    if (!encoding_required) {
  ------------------
  |  Branch (8620:9): [True: 12.0k, False: 346]
  ------------------
 8621|  12.0k|      buffer.append(input);
 8622|  12.0k|    }
 8623|  12.3k|  } else {
 8624|      0|    const size_t idx =
 8625|      0|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8626|      0|    std::string encoded;
 8627|      0|    std::string_view replacement = input;
 8628|      0|    if (idx != input.size()) {
  ------------------
  |  Branch (8628:9): [True: 0, False: 0]
  ------------------
 8629|      0|      encoded =
 8630|      0|          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
 8631|      0|      replacement = encoded;
 8632|      0|    }
 8633|       |
 8634|      0|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8634:9): [True: 0, False: 0]
  ------------------
 8635|      0|      const uint32_t difference = replace_and_resize(
 8636|      0|          components.search_start + 1, components.hash_start, replacement);
 8637|      0|      components.hash_start += difference;
 8638|      0|    } else {
 8639|      0|      components.search_start = components.hash_start;
 8640|      0|      buffer.insert(components.search_start, replacement.size() + 1, '?');
 8641|      0|      if (!replacement.empty()) {
  ------------------
  |  Branch (8641:11): [True: 0, False: 0]
  ------------------
 8642|      0|        std::memmove(buffer.data() + components.search_start + 1,
 8643|      0|                     replacement.data(), replacement.size());
 8644|      0|      }
 8645|      0|      components.hash_start +=
 8646|      0|          uint32_t(replacement.size() + 1);  // Do not forget `?`
 8647|      0|    }
 8648|      0|  }
 8649|       |
 8650|  12.3k|  ADA_ASSERT_TRUE(validate());
 8651|  12.3k|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8517|   246k|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8518|   246k|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8519|   246k|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8520|   246k|  ADA_ASSERT_TRUE(validate());
 8521|   246k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8522|       |
 8523|       |  // This next line is required for when parsing a URL like `foo://`
 8524|   246k|  add_authority_slashes_if_needed();
 8525|       |
 8526|   246k|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8527|   246k|  uint32_t new_difference =
 8528|   246k|      replace_and_resize(components.host_start, components.host_end, input);
 8529|       |
 8530|   246k|  if (has_credentials) {
  ------------------
  |  Branch (8530:7): [True: 30.8k, False: 216k]
  ------------------
 8531|  30.8k|    buffer.insert(components.host_start, "@");
 8532|  30.8k|    new_difference++;
 8533|  30.8k|  }
 8534|   246k|  components.host_end += new_difference;
 8535|   246k|  components.pathname_start += new_difference;
 8536|   246k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8536:7): [True: 0, False: 246k]
  ------------------
 8537|      0|    components.search_start += new_difference;
 8538|      0|  }
 8539|   246k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8539:7): [True: 0, False: 246k]
  ------------------
 8540|      0|    components.hash_start += new_difference;
 8541|      0|  }
 8542|   246k|  ADA_ASSERT_TRUE(validate());
 8543|   246k|}
_ZN3ada14url_aggregator10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 9249|  2.20k|url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
 9250|  2.20k|  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
 9251|  2.20k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (9251:7): [True: 2.01k, False: 193]
  |  Branch (9251:24): [True: 6, False: 2.00k]
  ------------------
 9252|      6|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 9253|      6|    is_valid = false;
 9254|      6|    return 0;
 9255|      6|  }
 9256|  2.20k|  uint16_t parsed_port{};
 9257|  2.20k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 9258|  2.20k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (9258:7): [True: 27, False: 2.17k]
  ------------------
 9259|     27|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 9260|     27|    is_valid = false;
 9261|     27|    return 0;
 9262|     27|  }
 9263|  2.17k|  ada_log("parse_port: ", parsed_port);
 9264|  2.17k|  const size_t consumed = size_t(r.ptr - view.data());
 9265|  2.17k|  ada_log("parse_port: consumed ", consumed);
 9266|  2.17k|  if (check_trailing_content) {
  ------------------
  |  Branch (9266:7): [True: 2.17k, False: 0]
  ------------------
 9267|  2.17k|    is_valid &=
 9268|  2.17k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (9268:10): [True: 402, False: 1.77k]
  |  Branch (9268:37): [True: 1.41k, False: 353]
  ------------------
 9269|    353|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (9269:10): [True: 112, False: 241]
  |  Branch (9269:36): [True: 232, False: 9]
  |  Branch (9269:52): [True: 51, False: 181]
  ------------------
 9270|  2.17k|  }
 9271|  2.17k|  ada_log("parse_port: is_valid = ", is_valid);
 9272|  2.17k|  if (is_valid) {
  ------------------
  |  Branch (9272:7): [True: 1.98k, False: 190]
  ------------------
 9273|  1.98k|    ada_log("parse_port", r.ec == std::errc());
 9274|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 9275|  1.98k|    auto default_port = scheme_default_port();
 9276|  1.98k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (9276:27): [True: 175, False: 1.80k]
  |  Branch (9276:48): [True: 94, False: 81]
  ------------------
 9277|  1.89k|                         (default_port != parsed_port);
  ------------------
  |  Branch (9277:26): [True: 1.88k, False: 3]
  ------------------
 9278|  1.98k|    if (r.ec == std::errc() && is_port_valid) {
  ------------------
  |  Branch (9278:9): [True: 1.64k, False: 340]
  |  Branch (9278:32): [True: 1.64k, False: 3]
  ------------------
 9279|  1.64k|      update_base_port(parsed_port);
 9280|  1.64k|    } else {
 9281|    343|      clear_port();
 9282|    343|    }
 9283|  1.98k|  }
 9284|  2.17k|  return consumed;
 9285|  2.20k|}
_ZN3ada14url_aggregator20set_protocol_as_fileEv:
 9287|  1.41k|constexpr void url_aggregator::set_protocol_as_file() {
 9288|  1.41k|  ada_log("url_aggregator::set_protocol_as_file ");
 9289|  1.41k|  ADA_ASSERT_TRUE(validate());
 9290|  1.41k|  type = ada::scheme::type::FILE;
 9291|       |  // next line could overflow but unsigned arithmetic has well-defined
 9292|       |  // overflows.
 9293|  1.41k|  uint32_t new_difference = 5 - components.protocol_end;
 9294|       |
 9295|  1.41k|  if (buffer.empty()) {
  ------------------
  |  Branch (9295:7): [True: 0, False: 1.41k]
  ------------------
 9296|      0|    buffer.append("file:");
 9297|  1.41k|  } else {
 9298|  1.41k|    buffer.erase(0, components.protocol_end);
 9299|  1.41k|    buffer.insert(0, "file:");
 9300|  1.41k|  }
 9301|  1.41k|  components.protocol_end = 5;
 9302|       |
 9303|       |  // Update the rest of the components.
 9304|  1.41k|  components.username_end += new_difference;
 9305|  1.41k|  components.host_start += new_difference;
 9306|  1.41k|  components.host_end += new_difference;
 9307|  1.41k|  components.pathname_start += new_difference;
 9308|  1.41k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9308:7): [True: 0, False: 1.41k]
  ------------------
 9309|      0|    components.search_start += new_difference;
 9310|      0|  }
 9311|  1.41k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9311:7): [True: 0, False: 1.41k]
  ------------------
 9312|      0|    components.hash_start += new_difference;
 9313|      0|  }
 9314|  1.41k|  ADA_ASSERT_TRUE(validate());
 9315|  1.41k|}
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8369|    619|                                              const uint8_t character_set[]) {
 8370|    619|  const char* data = input.data();
 8371|    619|  const size_t size = input.size();
 8372|       |
 8373|       |  // Process 8 bytes at a time using unrolled loop
 8374|    619|  size_t i = 0;
 8375|  1.29k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (8375:10): [True: 706, False: 592]
  ------------------
 8376|    706|    unsigned char chunk[8];
 8377|    706|    std::memcpy(&chunk, data + i,
 8378|    706|                8);  // entices compiler to unconditionally process 8 characters
 8379|       |
 8380|       |    // Check 8 characters at once
 8381|  6.22k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (8381:24): [True: 5.54k, False: 679]
  ------------------
 8382|  5.54k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (8382:11): [True: 27, False: 5.52k]
  ------------------
 8383|     27|        return i + j;
 8384|     27|      }
 8385|  5.54k|    }
 8386|    706|  }
 8387|       |
 8388|       |  // Handle remaining bytes
 8389|  2.41k|  for (; i < size; i++) {
  ------------------
  |  Branch (8389:10): [True: 1.82k, False: 586]
  ------------------
 8390|  1.82k|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (8390:9): [True: 6, False: 1.82k]
  ------------------
 8391|      6|      return i;
 8392|      6|    }
 8393|  1.82k|  }
 8394|       |
 8395|    586|  return size;
 8396|    592|}
_ZN3ada14url_aggregator10clear_portEv:
 8961|    343|inline void url_aggregator::clear_port() {
 8962|    343|  ada_log("url_aggregator::clear_port");
 8963|    343|  ADA_ASSERT_TRUE(validate());
 8964|    343|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (8964:7): [True: 343, False: 0]
  ------------------
 8965|    343|    return;
 8966|    343|  }
 8967|      0|  uint32_t length = components.pathname_start - components.host_end;
 8968|      0|  buffer.erase(components.host_end, length);
 8969|      0|  components.pathname_start -= length;
 8970|      0|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8970:7): [True: 0, False: 0]
  ------------------
 8971|      0|    components.search_start -= length;
 8972|      0|  }
 8973|      0|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8973:7): [True: 0, False: 0]
  ------------------
 8974|      0|    components.hash_start -= length;
 8975|      0|  }
 8976|      0|  components.port = url_components::omitted;
 8977|      0|  ADA_ASSERT_TRUE(validate());
 8978|      0|}
_ZNK3ada14url_aggregator13has_authorityEv:
 9126|   342k|    const noexcept {
 9127|   342k|  ada_log("url_aggregator::has_authority");
 9128|       |  // Performance: instead of doing this potentially expensive check, we could
 9129|       |  // have a boolean in the struct.
 9130|   342k|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (9130:10): [True: 96.5k, False: 246k]
  ------------------
 9131|  96.5k|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (9131:10): [True: 96.5k, False: 0]
  ------------------
 9132|  96.5k|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (9132:10): [True: 96.5k, False: 0]
  ------------------
 9133|   342k|}
_ZNK3ada14url_aggregator12has_dash_dotEv:
 9206|  60.1k|[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
 9207|       |  // If url's host is null, url does not have an opaque path, url's path's size
 9208|       |  // is greater than 1, and url's path[0] is the empty string, then append
 9209|       |  // U+002F (/) followed by U+002E (.) to output.
 9210|  60.1k|  ada_log("url_aggregator::has_dash_dot");
 9211|       |#if ADA_DEVELOPMENT_CHECKS
 9212|       |  // If pathname_start and host_end are exactly two characters apart, then we
 9213|       |  // either have a one-digit port such as http://test.com:5?param=1 or else we
 9214|       |  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
 9215|       |  if (components.pathname_start == components.host_end + 2) {
 9216|       |    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
 9217|       |                     buffer[components.host_end + 1] == '.') ||
 9218|       |                    (buffer[components.host_end] == ':' &&
 9219|       |                     checkers::is_digit(buffer[components.host_end + 1])));
 9220|       |  }
 9221|       |  if (components.pathname_start == components.host_end + 2 &&
 9222|       |      buffer[components.host_end] == '/' &&
 9223|       |      buffer[components.host_end + 1] == '.') {
 9224|       |    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
 9225|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
 9226|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
 9227|       |  }
 9228|       |#endif
 9229|       |  // Performance: it should be uncommon for components.pathname_start ==
 9230|       |  // components.host_end + 2 to be true. So we put this check first in the
 9231|       |  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
 9232|       |  // more expensive, but should be uncommon.
 9233|  60.1k|  return components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (9233:10): [True: 1.32k, False: 58.8k]
  ------------------
 9234|  1.32k|         !has_opaque_path && buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9234:10): [True: 1.32k, False: 0]
  |  Branch (9234:30): [True: 0, False: 1.32k]
  ------------------
 9235|      0|         buffer[components.host_end + 1] == '.';
  ------------------
  |  Branch (9235:10): [True: 0, False: 0]
  ------------------
 9236|  60.1k|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 4101|   400k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 4070|  1.00M|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 4071|  1.00M|    TL_ASSERT(has_value());
  ------------------
  |  | 2123|  1.00M|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4071:5): [True: 1.00M, False: 0]
  ------------------
 4072|  1.00M|    return valptr();
 4073|  1.00M|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3366|  1.00M|  T* valptr() { return std::addressof(this->m_val); }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 4083|  1.00M|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 4084|  1.00M|    TL_ASSERT(has_value());
  ------------------
  |  | 2123|  1.00M|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4084:5): [True: 1.00M, False: 0]
  ------------------
 4085|  1.00M|    return val();
 4086|  1.00M|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3375|  1.00M|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3376|  1.00M|    return this->m_val;
 3377|  1.00M|  }
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 4100|  2.42M|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2698|   623k|  ~expected_storage_base() {
 2699|   623k|    if (m_has_val) {
  ------------------
  |  Branch (2699:9): [True: 601k, False: 21.7k]
  ------------------
 2700|   601k|      m_val.~T();
 2701|   601k|    }
 2702|   623k|  }
_ZNK3ada14url_aggregator22has_non_empty_usernameEv:
 9165|   200k|constexpr bool url_aggregator::has_non_empty_username() const noexcept {
 9166|   200k|  ada_log("url_aggregator::has_non_empty_username");
 9167|   200k|  return components.protocol_end + 2 < components.username_end;
 9168|   200k|}
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1913|   970k|                                                       size_t pos2) {
 1914|       |#if ADA_DEVELOPMENT_CHECKS
 1915|       |  if (pos2 < pos1) {
 1916|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1917|       |              << std::endl;
 1918|       |    abort();
 1919|       |  }
 1920|       |#endif
 1921|   970k|  return input.substr(pos1, pos2 - pos1);
 1922|   970k|}
_ZNK3ada14url_aggregator22has_non_empty_passwordEv:
 9170|   200k|constexpr bool url_aggregator::has_non_empty_password() const noexcept {
 9171|   200k|  ada_log("url_aggregator::has_non_empty_password");
 9172|   200k|  return components.host_start > components.username_end;
 9173|   200k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8558|   209k|    const noexcept {
 8559|   209k|  return buffer.size() == components.pathname_start;
 8560|   209k|}
_ZNK3ada14url_aggregator12has_passwordEv:
 9175|  35.0k|constexpr bool url_aggregator::has_password() const noexcept {
 9176|  35.0k|  ada_log("url_aggregator::has_password");
 9177|       |  // This function does not care about the length of the password
 9178|  35.0k|  return components.host_start > components.username_end &&
  ------------------
  |  Branch (9178:10): [True: 5.23k, False: 29.7k]
  ------------------
 9179|  5.23k|         buffer[components.username_end] == ':';
  ------------------
  |  Branch (9179:10): [True: 5.23k, False: 0]
  ------------------
 9180|  35.0k|}
_ZNK3ada14url_aggregator13get_href_sizeEv:
 9244|   400k|[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
 9245|   400k|  return buffer.size();
 9246|   400k|}
_ZNK3ada3url8get_hrefEv:
 7622|  1.40M|[[nodiscard]] ada_really_inline std::string url::get_href() const {
 7623|  1.40M|  if (is_special() && host.has_value() && username.empty() &&
  ------------------
  |  Branch (7623:7): [True: 1.39M, False: 4.08k]
  |  Branch (7623:23): [True: 1.39M, False: 0]
  |  Branch (7623:43): [True: 1.32M, False: 71.4k]
  ------------------
 7624|  1.32M|      password.empty() && !port.has_value()) [[likely]] {
  ------------------
  |  Branch (7624:7): [True: 1.32M, False: 287]
  |  Branch (7624:27): [True: 1.18M, False: 146k]
  ------------------
 7625|  1.18M|    const std::string_view scheme = ada::scheme::details::is_special_list[type];
 7626|  1.18M|    const size_t host_size = host->size();
 7627|  1.18M|    const size_t path_size = path.size();
 7628|  1.18M|    const size_t query_size = query.has_value() ? query->size() : 0;
  ------------------
  |  Branch (7628:31): [True: 224k, False: 956k]
  ------------------
 7629|  1.18M|    const size_t hash_size = hash.has_value() ? hash->size() : 0;
  ------------------
  |  Branch (7629:30): [True: 159k, False: 1.02M]
  ------------------
 7630|  1.18M|    const size_t total = scheme.size() + 3 + host_size + path_size +
 7631|  1.18M|                         (query.has_value() ? query_size + 1 : 0) +
  ------------------
  |  Branch (7631:27): [True: 224k, False: 956k]
  ------------------
 7632|  1.18M|                         (hash.has_value() ? hash_size + 1 : 0);
  ------------------
  |  Branch (7632:27): [True: 159k, False: 1.02M]
  ------------------
 7633|  1.18M|    std::string output(total, '\0');
 7634|  1.18M|    char* p = output.data();
 7635|  1.18M|    std::memcpy(p, scheme.data(), scheme.size());
 7636|  1.18M|    p += scheme.size();
 7637|  1.18M|    p[0] = ':';
 7638|  1.18M|    p[1] = '/';
 7639|  1.18M|    p[2] = '/';
 7640|  1.18M|    p += 3;
 7641|       |    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7642|  1.18M|    std::memcpy(p, host->data(), host_size);
 7643|  1.18M|    p += host_size;
 7644|  1.18M|    std::memcpy(p, path.data(), path_size);
 7645|  1.18M|    p += path_size;
 7646|  1.18M|    if (query.has_value()) {
  ------------------
  |  Branch (7646:9): [True: 224k, False: 956k]
  ------------------
 7647|   224k|      *p++ = '?';
 7648|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7649|   224k|      std::memcpy(p, query->data(), query_size);
 7650|   224k|      p += query_size;
 7651|   224k|    }
 7652|  1.18M|    if (hash.has_value()) {
  ------------------
  |  Branch (7652:9): [True: 159k, False: 1.02M]
  ------------------
 7653|   159k|      *p++ = '#';
 7654|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7655|   159k|      std::memcpy(p, hash->data(), hash_size);
 7656|   159k|    }
 7657|  1.18M|    return output;
 7658|  1.18M|  }
 7659|       |
 7660|   222k|  std::string output;
 7661|   222k|  output.reserve(get_href_size());
 7662|       |
 7663|   222k|  if (is_special()) {
  ------------------
  |  Branch (7663:7): [True: 218k, False: 4.08k]
  ------------------
 7664|   218k|    output.append(ada::scheme::details::is_special_list[type]);
 7665|   218k|    output += ':';
 7666|   218k|  } else {
 7667|  4.08k|    output.append(non_special_scheme);
 7668|  4.08k|    output += ':';
 7669|  4.08k|  }
 7670|       |
 7671|   222k|  if (host.has_value()) {
  ------------------
  |  Branch (7671:7): [True: 219k, False: 2.44k]
  ------------------
 7672|   219k|    output += '/';
 7673|   219k|    output += '/';
 7674|   219k|    if (has_credentials()) {
  ------------------
  |  Branch (7674:9): [True: 71.8k, False: 147k]
  ------------------
 7675|  71.8k|      output.append(username);
 7676|  71.8k|      if (!password.empty()) {
  ------------------
  |  Branch (7676:11): [True: 69.2k, False: 2.62k]
  ------------------
 7677|  69.2k|        output += ':';
 7678|  69.2k|        output.append(password);
 7679|  69.2k|      }
 7680|  71.8k|      output += '@';
 7681|  71.8k|    }
 7682|   219k|    output.append(*host);
 7683|   219k|    if (port.has_value()) {
  ------------------
  |  Branch (7683:9): [True: 146k, False: 72.8k]
  ------------------
 7684|   146k|      output += ':';
 7685|   146k|      char port_buf[5];
 7686|   146k|      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
 7687|   146k|      (void)ec;
 7688|   146k|      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
 7689|   146k|    }
 7690|   219k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7690:14): [True: 819, False: 1.62k]
  |  Branch (7690:34): [True: 63, False: 756]
  ------------------
 7691|       |    // If url's host is null, url does not have an opaque path, url's path's
 7692|       |    // size is greater than 1, and url's path[0] is the empty string, then
 7693|       |    // append U+002F (/) followed by U+002E (.) to output.
 7694|     63|    output += '/';
 7695|     63|    output += '.';
 7696|     63|  }
 7697|   222k|  output.append(path);
 7698|   222k|  if (query.has_value()) {
  ------------------
  |  Branch (7698:7): [True: 14.0k, False: 208k]
  ------------------
 7699|  14.0k|    output += '?';
 7700|  14.0k|    output.append(*query);
 7701|  14.0k|  }
 7702|   222k|  if (hash.has_value()) {
  ------------------
  |  Branch (7702:7): [True: 13.4k, False: 208k]
  ------------------
 7703|  13.4k|    output += '#';
 7704|  13.4k|    output.append(*hash);
 7705|  13.4k|  }
 7706|   222k|  return output;
 7707|  1.40M|}
_ZNK3ada14url_aggregator8validateEv:
 9317|   200k|[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
 9318|   200k|  if (!is_valid) {
  ------------------
  |  Branch (9318:7): [True: 0, False: 200k]
  ------------------
 9319|      0|    return true;
 9320|      0|  }
 9321|   200k|  if (!components.check_offset_consistency()) {
  ------------------
  |  Branch (9321:7): [True: 0, False: 200k]
  ------------------
 9322|      0|    ada_log("url_aggregator::validate inconsistent components \n",
 9323|      0|            to_diagram());
 9324|      0|    return false;
 9325|      0|  }
 9326|       |  // We have a credible components struct, but let us investivate more
 9327|       |  // carefully:
 9328|       |  /**
 9329|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 9330|       |   *       |     |    |          | ^^^^|       |   |
 9331|       |   *       |     |    |          | |   |       |   `----- hash_start
 9332|       |   *       |     |    |          | |   |       `--------- search_start
 9333|       |   *       |     |    |          | |   `----------------- pathname_start
 9334|       |   *       |     |    |          | `--------------------- port
 9335|       |   *       |     |    |          `----------------------- host_end
 9336|       |   *       |     |    `---------------------------------- host_start
 9337|       |   *       |     `--------------------------------------- username_end
 9338|       |   *       `--------------------------------------------- protocol_end
 9339|       |   */
 9340|   200k|  if (components.protocol_end == url_components::omitted) {
  ------------------
  |  Branch (9340:7): [True: 0, False: 200k]
  ------------------
 9341|      0|    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
 9342|      0|    return false;
 9343|      0|  }
 9344|   200k|  if (components.username_end == url_components::omitted) {
  ------------------
  |  Branch (9344:7): [True: 0, False: 200k]
  ------------------
 9345|      0|    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
 9346|      0|    return false;
 9347|      0|  }
 9348|   200k|  if (components.host_start == url_components::omitted) {
  ------------------
  |  Branch (9348:7): [True: 0, False: 200k]
  ------------------
 9349|      0|    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
 9350|      0|    return false;
 9351|      0|  }
 9352|   200k|  if (components.host_end == url_components::omitted) {
  ------------------
  |  Branch (9352:7): [True: 0, False: 200k]
  ------------------
 9353|      0|    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
 9354|      0|    return false;
 9355|      0|  }
 9356|   200k|  if (components.pathname_start == url_components::omitted) {
  ------------------
  |  Branch (9356:7): [True: 0, False: 200k]
  ------------------
 9357|      0|    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
 9358|      0|    return false;
 9359|      0|  }
 9360|       |
 9361|   200k|  if (components.protocol_end > buffer.size()) {
  ------------------
  |  Branch (9361:7): [True: 0, False: 200k]
  ------------------
 9362|      0|    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
 9363|      0|    return false;
 9364|      0|  }
 9365|   200k|  if (components.username_end > buffer.size()) {
  ------------------
  |  Branch (9365:7): [True: 0, False: 200k]
  ------------------
 9366|      0|    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
 9367|      0|    return false;
 9368|      0|  }
 9369|   200k|  if (components.host_start > buffer.size()) {
  ------------------
  |  Branch (9369:7): [True: 0, False: 200k]
  ------------------
 9370|      0|    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
 9371|      0|    return false;
 9372|      0|  }
 9373|   200k|  if (components.host_end > buffer.size()) {
  ------------------
  |  Branch (9373:7): [True: 0, False: 200k]
  ------------------
 9374|      0|    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
 9375|      0|    return false;
 9376|      0|  }
 9377|   200k|  if (components.pathname_start > buffer.size()) {
  ------------------
  |  Branch (9377:7): [True: 0, False: 200k]
  ------------------
 9378|      0|    ada_log("url_aggregator::validate pathname_start overflow \n",
 9379|      0|            to_diagram());
 9380|      0|    return false;
 9381|      0|  }
 9382|       |
 9383|   200k|  if (components.protocol_end > 0) {
  ------------------
  |  Branch (9383:7): [True: 200k, False: 0]
  ------------------
 9384|   200k|    if (buffer[components.protocol_end - 1] != ':') {
  ------------------
  |  Branch (9384:9): [True: 0, False: 200k]
  ------------------
 9385|      0|      ada_log(
 9386|      0|          "url_aggregator::validate missing : at the end of the protocol \n",
 9387|      0|          to_diagram());
 9388|      0|      return false;
 9389|      0|    }
 9390|   200k|  }
 9391|       |
 9392|   200k|  if (components.username_end != buffer.size() &&
  ------------------
  |  Branch (9392:7): [True: 200k, False: 25]
  ------------------
 9393|   200k|      components.username_end > components.protocol_end + 2) {
  ------------------
  |  Branch (9393:7): [True: 10.2k, False: 190k]
  ------------------
 9394|  10.2k|    if (buffer[components.username_end] != ':' &&
  ------------------
  |  Branch (9394:9): [True: 375, False: 9.85k]
  ------------------
 9395|    375|        buffer[components.username_end] != '@') {
  ------------------
  |  Branch (9395:9): [True: 0, False: 375]
  ------------------
 9396|      0|      ada_log(
 9397|      0|          "url_aggregator::validate missing : or @ at the end of the username "
 9398|      0|          "\n",
 9399|      0|          to_diagram());
 9400|      0|      return false;
 9401|      0|    }
 9402|  10.2k|  }
 9403|       |
 9404|   200k|  if (components.host_start != buffer.size()) {
  ------------------
  |  Branch (9404:7): [True: 200k, False: 25]
  ------------------
 9405|   200k|    if (components.host_start > components.username_end) {
  ------------------
  |  Branch (9405:9): [True: 9.89k, False: 190k]
  ------------------
 9406|  9.89k|      if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (9406:11): [True: 0, False: 9.89k]
  ------------------
 9407|      0|        ada_log(
 9408|      0|            "url_aggregator::validate missing @ at the end of the password \n",
 9409|      0|            to_diagram());
 9410|      0|        return false;
 9411|      0|      }
 9412|   190k|    } else if (components.host_start == components.username_end &&
  ------------------
  |  Branch (9412:16): [True: 190k, False: 0]
  ------------------
 9413|   190k|               components.host_end > components.host_start) {
  ------------------
  |  Branch (9413:16): [True: 190k, False: 481]
  ------------------
 9414|   190k|      if (components.host_start == components.protocol_end + 2) {
  ------------------
  |  Branch (9414:11): [True: 189k, False: 375]
  ------------------
 9415|   189k|        if (buffer[components.protocol_end] != '/' ||
  ------------------
  |  Branch (9415:13): [True: 0, False: 189k]
  ------------------
 9416|   189k|            buffer[components.protocol_end + 1] != '/') {
  ------------------
  |  Branch (9416:13): [True: 0, False: 189k]
  ------------------
 9417|      0|          ada_log(
 9418|      0|              "url_aggregator::validate missing // between protocol and host "
 9419|      0|              "\n",
 9420|      0|              to_diagram());
 9421|      0|          return false;
 9422|      0|        }
 9423|   189k|      } else {
 9424|    375|        if (components.host_start > components.protocol_end &&
  ------------------
  |  Branch (9424:13): [True: 375, False: 0]
  ------------------
 9425|    375|            buffer[components.host_start] != '@') {
  ------------------
  |  Branch (9425:13): [True: 0, False: 375]
  ------------------
 9426|      0|          ada_log(
 9427|      0|              "url_aggregator::validate missing @ at the end of the username "
 9428|      0|              "\n",
 9429|      0|              to_diagram());
 9430|      0|          return false;
 9431|      0|        }
 9432|    375|      }
 9433|   190k|    } else {
 9434|    481|      if (components.host_end != components.host_start) {
  ------------------
  |  Branch (9434:11): [True: 0, False: 481]
  ------------------
 9435|      0|        ada_log("url_aggregator::validate expected omitted host \n",
 9436|      0|                to_diagram());
 9437|      0|        return false;
 9438|      0|      }
 9439|    481|    }
 9440|   200k|  }
 9441|   200k|  if (components.host_end != buffer.size() &&
  ------------------
  |  Branch (9441:7): [True: 200k, False: 37]
  ------------------
 9442|   200k|      components.pathname_start > components.host_end) {
  ------------------
  |  Branch (9442:7): [True: 21.0k, False: 179k]
  ------------------
 9443|  21.0k|    if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (9443:9): [True: 991, False: 20.0k]
  ------------------
 9444|    991|        buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9444:9): [True: 9, False: 982]
  ------------------
 9445|      9|        buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (9445:9): [True: 9, False: 0]
  ------------------
 9446|      9|      if (components.pathname_start + 1 >= buffer.size() ||
  ------------------
  |  Branch (9446:11): [True: 0, False: 9]
  ------------------
 9447|      9|          buffer[components.pathname_start] != '/' ||
  ------------------
  |  Branch (9447:11): [True: 0, False: 9]
  ------------------
 9448|      9|          buffer[components.pathname_start + 1] != '/') {
  ------------------
  |  Branch (9448:11): [True: 0, False: 9]
  ------------------
 9449|      0|        ada_log(
 9450|      0|            "url_aggregator::validate expected the path to begin with // \n",
 9451|      0|            to_diagram());
 9452|      0|        return false;
 9453|      0|      }
 9454|  20.9k|    } else if (buffer[components.host_end] != ':') {
  ------------------
  |  Branch (9454:16): [True: 0, False: 20.9k]
  ------------------
 9455|      0|      ada_log("url_aggregator::validate missing : at the port \n",
 9456|      0|              to_diagram());
 9457|      0|      return false;
 9458|      0|    }
 9459|  21.0k|  }
 9460|   200k|  if (components.pathname_start != buffer.size() &&
  ------------------
  |  Branch (9460:7): [True: 200k, False: 41]
  ------------------
 9461|   200k|      components.pathname_start < components.search_start &&
  ------------------
  |  Branch (9461:7): [True: 200k, False: 54]
  ------------------
 9462|   200k|      components.pathname_start < components.hash_start && !has_opaque_path) {
  ------------------
  |  Branch (9462:7): [True: 200k, False: 46]
  |  Branch (9462:60): [True: 200k, False: 173]
  ------------------
 9463|   200k|    if (buffer[components.pathname_start] != '/') {
  ------------------
  |  Branch (9463:9): [True: 0, False: 200k]
  ------------------
 9464|      0|      ada_log("url_aggregator::validate missing / at the path \n",
 9465|      0|              to_diagram());
 9466|      0|      return false;
 9467|      0|    }
 9468|   200k|  }
 9469|   200k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9469:7): [True: 34.1k, False: 166k]
  ------------------
 9470|  34.1k|    if (buffer[components.search_start] != '?') {
  ------------------
  |  Branch (9470:9): [True: 0, False: 34.1k]
  ------------------
 9471|      0|      ada_log("url_aggregator::validate missing ? at the search \n",
 9472|      0|              to_diagram());
 9473|      0|      return false;
 9474|      0|    }
 9475|  34.1k|  }
 9476|   200k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9476:7): [True: 24.6k, False: 175k]
  ------------------
 9477|  24.6k|    if (buffer[components.hash_start] != '#') {
  ------------------
  |  Branch (9477:9): [True: 0, False: 24.6k]
  ------------------
 9478|      0|      ada_log("url_aggregator::validate missing # at the hash \n",
 9479|      0|              to_diagram());
 9480|      0|      return false;
 9481|      0|    }
 9482|  24.6k|  }
 9483|       |
 9484|   200k|  return true;
 9485|   200k|}
_ZNK3ada14url_components24check_offset_consistencyEv:
 7801|   200k|    const noexcept {
 7802|       |  /**
 7803|       |   * https://user:pass@example.com:1234/foo/bar?baz#quux
 7804|       |   *       |     |    |          | ^^^^|       |   |
 7805|       |   *       |     |    |          | |   |       |   `----- hash_start
 7806|       |   *       |     |    |          | |   |       `--------- search_start
 7807|       |   *       |     |    |          | |   `----------------- pathname_start
 7808|       |   *       |     |    |          | `--------------------- port
 7809|       |   *       |     |    |          `----------------------- host_end
 7810|       |   *       |     |    `---------------------------------- host_start
 7811|       |   *       |     `--------------------------------------- username_end
 7812|       |   *       `--------------------------------------------- protocol_end
 7813|       |   */
 7814|       |  // These conditions can be made more strict.
 7815|   200k|  if (protocol_end == url_components::omitted) {
  ------------------
  |  Branch (7815:7): [True: 0, False: 200k]
  ------------------
 7816|      0|    return false;
 7817|      0|  }
 7818|   200k|  uint32_t index = protocol_end;
 7819|       |
 7820|   200k|  if (username_end == url_components::omitted) {
  ------------------
  |  Branch (7820:7): [True: 0, False: 200k]
  ------------------
 7821|      0|    return false;
 7822|      0|  }
 7823|   200k|  if (username_end < index) {
  ------------------
  |  Branch (7823:7): [True: 0, False: 200k]
  ------------------
 7824|      0|    return false;
 7825|      0|  }
 7826|   200k|  index = username_end;
 7827|       |
 7828|   200k|  if (host_start == url_components::omitted) {
  ------------------
  |  Branch (7828:7): [True: 0, False: 200k]
  ------------------
 7829|      0|    return false;
 7830|      0|  }
 7831|   200k|  if (host_start < index) {
  ------------------
  |  Branch (7831:7): [True: 0, False: 200k]
  ------------------
 7832|      0|    return false;
 7833|      0|  }
 7834|   200k|  index = host_start;
 7835|       |
 7836|   200k|  if (port != url_components::omitted) {
  ------------------
  |  Branch (7836:7): [True: 20.9k, False: 179k]
  ------------------
 7837|  20.9k|    if (port > 0xffff) {
  ------------------
  |  Branch (7837:9): [True: 0, False: 20.9k]
  ------------------
 7838|      0|      return false;
 7839|      0|    }
 7840|  20.9k|    uint32_t port_length = helpers::fast_digit_count(port) + 1;
 7841|  20.9k|    if (index + port_length < index) {
  ------------------
  |  Branch (7841:9): [True: 0, False: 20.9k]
  ------------------
 7842|      0|      return false;
 7843|      0|    }
 7844|  20.9k|    index += port_length;
 7845|  20.9k|  }
 7846|       |
 7847|   200k|  if (pathname_start == url_components::omitted) {
  ------------------
  |  Branch (7847:7): [True: 0, False: 200k]
  ------------------
 7848|      0|    return false;
 7849|      0|  }
 7850|   200k|  if (pathname_start < index) {
  ------------------
  |  Branch (7850:7): [True: 0, False: 200k]
  ------------------
 7851|      0|    return false;
 7852|      0|  }
 7853|   200k|  index = pathname_start;
 7854|       |
 7855|   200k|  if (search_start != url_components::omitted) {
  ------------------
  |  Branch (7855:7): [True: 34.1k, False: 166k]
  ------------------
 7856|  34.1k|    if (search_start < index) {
  ------------------
  |  Branch (7856:9): [True: 0, False: 34.1k]
  ------------------
 7857|      0|      return false;
 7858|      0|    }
 7859|  34.1k|    index = search_start;
 7860|  34.1k|  }
 7861|       |
 7862|   200k|  if (hash_start != url_components::omitted) {
  ------------------
  |  Branch (7862:7): [True: 24.6k, False: 175k]
  ------------------
 7863|  24.6k|    if (hash_start < index) {
  ------------------
  |  Branch (7863:9): [True: 0, False: 24.6k]
  ------------------
 7864|      0|      return false;
 7865|      0|    }
 7866|  24.6k|  }
 7867|       |
 7868|   200k|  return true;
 7869|   200k|}
_ZN3ada7helpers16fast_digit_countEj:
 2017|  20.9k|inline int fast_digit_count(uint32_t x) noexcept {
 2018|  20.9k|  auto int_log2 = [](uint32_t z) -> int {
 2019|  20.9k|    return 31 - ada::helpers::leading_zeroes(z | 1);
 2020|  20.9k|  };
 2021|       |  // Compiles to very few instructions. Note that the
 2022|       |  // table is static and thus effectively a constant.
 2023|       |  // We leave it inside the function because it is meaningless
 2024|       |  // outside of it (this comes at no performance cost).
 2025|  20.9k|  const static uint64_t table[] = {
 2026|  20.9k|      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
 2027|  20.9k|      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
 2028|  20.9k|      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
 2029|  20.9k|      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
 2030|  20.9k|      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
 2031|  20.9k|      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
 2032|  20.9k|      42949672960, 42949672960};
 2033|  20.9k|  return int((x + table[int_log2(x)]) >> 32);
 2034|  20.9k|}
_ZZN3ada7helpers16fast_digit_countEjENKUljE_clEj:
 2018|  20.9k|  auto int_log2 = [](uint32_t z) -> int {
 2019|  20.9k|    return 31 - ada::helpers::leading_zeroes(z | 1);
 2020|  20.9k|  };
_ZN3ada7helpers6concatIJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKcEEENS2_12basic_stringIcS5_NS2_9allocatorIcEEEEDpT_:
 1991|   204k|std::string concat(Args... args) {
 1992|   204k|  std::string answer;
 1993|   204k|  inner_concat(answer, args...);
 1994|   204k|  return answer;
 1995|   204k|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJPKcEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_DpT0_:
 1980|   204k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1981|   204k|  buffer.append(t);
 1982|   204k|  return inner_concat(buffer, args...);
 1983|   204k|}
_ZN3ada7helpers12inner_concatIPKcJEEEvRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEET_:
 1972|   205k|inline void inner_concat(std::string& buffer, T t) {
 1973|   205k|  buffer.append(t);
 1974|   205k|}
_ZN3ada7helpers6concatIJNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEPKcEEES8_DpT_:
 1991|    584|std::string concat(Args... args) {
 1992|    584|  std::string answer;
 1993|    584|  inner_concat(answer, args...);
 1994|    584|  return answer;
 1995|    584|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJPKcEEEvRS8_T_DpT0_:
 1980|    584|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1981|    584|  buffer.append(t);
 1982|    584|  return inner_concat(buffer, args...);
 1983|    584|}
_ZN3ada3url10set_schemeEONSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 7604|  4.69k|inline void url::set_scheme(std::string&& new_scheme) noexcept {
 7605|  4.69k|  type = ada::scheme::get_scheme_type(new_scheme);
 7606|       |  // We only move the 'scheme' if it is non-special.
 7607|  4.69k|  if (!is_special()) {
  ------------------
  |  Branch (7607:7): [True: 1.81k, False: 2.88k]
  ------------------
 7608|  1.81k|    non_special_scheme = std::move(new_scheme);
 7609|  1.81k|  }
 7610|  4.69k|}

LLVMFuzzerTestOneInput:
  212|  9.44k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  213|  9.44k|  FuzzedDataProvider fdp(data, size);
  214|       |
  215|  9.44k|  fuzz_one_input(make_candidate(fdp));
  216|       |
  217|  9.44k|  if (fdp.remaining_bytes() > 0) {
  ------------------
  |  Branch (217:7): [True: 4.95k, False: 4.49k]
  ------------------
  218|  4.95k|    std::string raw = fdp.ConsumeRemainingBytesAsString();
  219|  4.95k|    if (raw.size() > 512) {
  ------------------
  |  Branch (219:9): [True: 21, False: 4.92k]
  ------------------
  220|     21|      raw.resize(512);
  221|     21|    }
  222|  4.95k|    if (!raw.empty() && (static_cast<unsigned char>(raw[0]) & 1u)) {
  ------------------
  |  Branch (222:9): [True: 4.95k, False: 0]
  |  Branch (222:25): [True: 2.26k, False: 2.69k]
  ------------------
  223|  2.26k|      raw = std::string("https://") + raw;
  224|  2.69k|    } else if (!raw.empty()) {
  ------------------
  |  Branch (224:16): [True: 2.69k, False: 0]
  ------------------
  225|  2.69k|      raw = std::string("http://") + raw;
  226|  2.69k|    }
  227|  4.95k|    fuzz_one_input(raw);
  228|  4.95k|  }
  229|       |
  230|  9.44k|  static constexpr const char* kAnchors[] = {
  231|  9.44k|      "https://example.com",
  232|  9.44k|      "https://example.com/",
  233|  9.44k|      "https://example.com?q=1",
  234|  9.44k|      "https://example.com#frag",
  235|  9.44k|      "https://example.com/?q=1#frag",
  236|  9.44k|      "http://www.example.com/path/file.js",
  237|  9.44k|      "http://WWW.Example.COM/file.js",
  238|  9.44k|      "https://www.google.com/imghp?hl=en&tab=wi",
  239|  9.44k|      "http://192.168.0.1/x",
  240|  9.44k|      "http://0x7f.1/",
  241|  9.44k|      "https://user:pass@example.com/x",
  242|  9.44k|      "https://example.com:8080/x",
  243|  9.44k|      "https://example.com/a/./b/../c",
  244|  9.44k|      "https://example.com/foo/%2e%2e",
  245|  9.44k|      "https://xn--nxasmq6b.com/",
  246|  9.44k|      "https://xn--a/",
  247|  9.44k|      "http://foo.0xffffffff/",
  248|  9.44k|      "http://example.0x/",
  249|  9.44k|      "https://example.com:0080/x",
  250|  9.44k|      "http://@19%2E68.1.10.",
  251|  9.44k|      "http://19%2E68.1.10.",
  252|  9.44k|      "http://0xffffffff.",
  253|  9.44k|  };
  254|   207k|  for (const char* seed : kAnchors) {
  ------------------
  |  Branch (254:25): [True: 207k, False: 9.44k]
  ------------------
  255|   207k|    fuzz_one_input(seed);
  256|   207k|  }
  257|       |
  258|  9.44k|  return 0;
  259|  9.44k|}
simple_absolute.cc:_ZL14fuzz_one_inputNSt3__117basic_string_viewIcNS_11char_traitsIcEEEE:
  171|   222k|static void fuzz_one_input(std::string_view input) {
  172|   222k|  auto url = ada::parse<ada::url>(input);
  173|   222k|  auto agg = ada::parse<ada::url_aggregator>(input);
  174|       |
  175|   222k|  if (url.has_value() != agg.has_value()) {
  ------------------
  |  Branch (175:7): [True: 0, False: 222k]
  ------------------
  176|      0|    printf(
  177|      0|        "parse agreement failure\n"
  178|      0|        "  input: %.*s\n"
  179|      0|        "  url: %d aggregator: %d\n",
  180|      0|        static_cast<int>(input.size()), input.data(), url.has_value(),
  181|      0|        agg.has_value());
  182|      0|    abort();
  183|      0|  }
  184|       |
  185|   222k|  if (!url) {
  ------------------
  |  Branch (185:7): [True: 21.7k, False: 200k]
  ------------------
  186|  21.7k|    return;
  187|  21.7k|  }
  188|       |
  189|   200k|  check_components_agree(*url, *agg, input);
  190|   200k|  check_href_size(*url, input);
  191|   200k|  check_href_size(*agg, input);
  192|   200k|  check_reparse_idempotent(*url, input);
  193|   200k|  check_reparse_idempotent(*agg, input);
  194|       |
  195|   200k|  const std::string href = url->get_href();
  196|   200k|  url->set_href(href);
  197|   200k|  agg->set_href(href);
  198|   200k|  if (url->get_href() != std::string(agg->get_href())) {
  ------------------
  |  Branch (198:7): [True: 0, False: 200k]
  ------------------
  199|      0|    printf(
  200|      0|        "set_href agreement failure\n"
  201|      0|        "  href: %s\n",
  202|      0|        href.c_str());
  203|      0|    abort();
  204|      0|  }
  205|   200k|  check_href_size(*url, href);
  206|   200k|  check_href_size(*agg, href);
  207|       |
  208|   200k|  volatile bool valid = agg->validate();
  209|   200k|  (void)valid;
  210|   200k|}
simple_absolute.cc:_ZL22check_components_agreeRKN3ada3urlERKNS_14url_aggregatorENSt3__117basic_string_viewIcNS6_11char_traitsIcEEEE:
  148|   200k|                                   std::string_view input) {
  149|   200k|  if (u.get_protocol() != a.get_protocol() ||
  ------------------
  |  Branch (149:7): [True: 0, False: 200k]
  |  Branch (149:7): [True: 0, False: 200k]
  ------------------
  150|   200k|      u.get_href() != std::string(a.get_href()) ||
  ------------------
  |  Branch (150:7): [True: 0, False: 200k]
  ------------------
  151|   200k|      std::string(u.get_hostname()) != std::string(a.get_hostname()) ||
  ------------------
  |  Branch (151:7): [True: 0, False: 200k]
  ------------------
  152|   200k|      std::string(u.get_pathname()) != std::string(a.get_pathname()) ||
  ------------------
  |  Branch (152:7): [True: 0, False: 200k]
  ------------------
  153|   200k|      std::string(u.get_search()) != std::string(a.get_search()) ||
  ------------------
  |  Branch (153:7): [True: 0, False: 200k]
  ------------------
  154|   200k|      std::string(u.get_hash()) != std::string(a.get_hash()) ||
  ------------------
  |  Branch (154:7): [True: 0, False: 200k]
  ------------------
  155|   200k|      std::string(u.get_port()) != std::string(a.get_port()) ||
  ------------------
  |  Branch (155:7): [True: 0, False: 200k]
  ------------------
  156|   200k|      u.get_username() != std::string(a.get_username()) ||
  ------------------
  |  Branch (156:7): [True: 0, False: 200k]
  ------------------
  157|   200k|      u.get_password() != std::string(a.get_password()) ||
  ------------------
  |  Branch (157:7): [True: 0, False: 200k]
  ------------------
  158|   200k|      std::string(u.get_host()) != std::string(a.get_host())) {
  ------------------
  |  Branch (158:7): [True: 0, False: 200k]
  ------------------
  159|      0|    printf(
  160|      0|        "url vs aggregator component mismatch\n"
  161|      0|        "  input: %.*s\n"
  162|      0|        "  url href: %s\n"
  163|      0|        "  agg href: %s\n",
  164|      0|        static_cast<int>(input.size()), input.data(), u.get_href().c_str(),
  165|      0|        std::string(a.get_href()).c_str());
  166|       |    fflush(stdout);
  167|      0|    abort();
  168|      0|  }
  169|   200k|}
simple_absolute.cc:_ZL15check_href_sizeRKN3ada3urlENSt3__117basic_string_viewIcNS3_11char_traitsIcEEEE:
   94|   400k|static void check_href_size(const ada::url& u, std::string_view input) {
   95|   400k|  if (u.get_href_size() != u.get_href().size()) {
  ------------------
  |  Branch (95:7): [True: 0, False: 400k]
  ------------------
   96|      0|    printf(
   97|      0|        "get_href_size mismatch (url)\n"
   98|      0|        "  input: %.*s\n"
   99|      0|        "  size:  %zu href.size: %zu\n"
  100|      0|        "  href:  %s\n",
  101|      0|        static_cast<int>(input.size()), input.data(), u.get_href_size(),
  102|      0|        u.get_href().size(), u.get_href().c_str());
  103|      0|    abort();
  104|      0|  }
  105|   400k|}
simple_absolute.cc:_ZL15check_href_sizeRKN3ada14url_aggregatorENSt3__117basic_string_viewIcNS3_11char_traitsIcEEEE:
  108|   400k|                            std::string_view input) {
  109|   400k|  if (u.get_href_size() != u.get_href().size()) {
  ------------------
  |  Branch (109:7): [True: 0, False: 400k]
  ------------------
  110|      0|    printf(
  111|      0|        "get_href_size mismatch (aggregator)\n"
  112|      0|        "  input: %.*s\n"
  113|      0|        "  size:  %zu href.size: %zu\n"
  114|      0|        "  href:  %s\n",
  115|      0|        static_cast<int>(input.size()), input.data(), u.get_href_size(),
  116|      0|        u.get_href().size(), std::string(u.get_href()).c_str());
  117|      0|    abort();
  118|      0|  }
  119|   400k|}
simple_absolute.cc:_ZL24check_reparse_idempotentIN3ada3urlEEvRKT_NSt3__117basic_string_viewIcNS5_11char_traitsIcEEEE:
  123|   200k|                                     std::string_view input) {
  124|   200k|  const std::string href = std::string(parsed.get_href());
  125|   200k|  auto again = ada::parse<Result>(href);
  126|   200k|  if (!again) {
  ------------------
  |  Branch (126:7): [True: 0, False: 200k]
  ------------------
  127|      0|    printf(
  128|      0|        "re-parse of href failed\n"
  129|      0|        "  input: %.*s\n"
  130|      0|        "  href:  %s\n",
  131|      0|        static_cast<int>(input.size()), input.data(), href.c_str());
  132|      0|    abort();
  133|      0|  }
  134|   200k|  if (std::string(again->get_href()) != href) {
  ------------------
  |  Branch (134:7): [True: 0, False: 200k]
  ------------------
  135|      0|    printf(
  136|      0|        "href not idempotent\n"
  137|      0|        "  input: %.*s\n"
  138|      0|        "  href1: %s\n"
  139|      0|        "  href2: %s\n",
  140|      0|        static_cast<int>(input.size()), input.data(), href.c_str(),
  141|      0|        std::string(again->get_href()).c_str());
  142|      0|    abort();
  143|      0|  }
  144|   200k|}
simple_absolute.cc:_ZL24check_reparse_idempotentIN3ada14url_aggregatorEEvRKT_NSt3__117basic_string_viewIcNS5_11char_traitsIcEEEE:
  123|   200k|                                     std::string_view input) {
  124|   200k|  const std::string href = std::string(parsed.get_href());
  125|   200k|  auto again = ada::parse<Result>(href);
  126|   200k|  if (!again) {
  ------------------
  |  Branch (126:7): [True: 0, False: 200k]
  ------------------
  127|      0|    printf(
  128|      0|        "re-parse of href failed\n"
  129|      0|        "  input: %.*s\n"
  130|      0|        "  href:  %s\n",
  131|      0|        static_cast<int>(input.size()), input.data(), href.c_str());
  132|      0|    abort();
  133|      0|  }
  134|   200k|  if (std::string(again->get_href()) != href) {
  ------------------
  |  Branch (134:7): [True: 0, False: 200k]
  ------------------
  135|      0|    printf(
  136|      0|        "href not idempotent\n"
  137|      0|        "  input: %.*s\n"
  138|      0|        "  href1: %s\n"
  139|      0|        "  href2: %s\n",
  140|      0|        static_cast<int>(input.size()), input.data(), href.c_str(),
  141|      0|        std::string(again->get_href()).c_str());
  142|      0|    abort();
  143|      0|  }
  144|   200k|}
simple_absolute.cc:_ZL14make_candidateR18FuzzedDataProvider:
   74|  9.44k|static std::string make_candidate(FuzzedDataProvider& fdp) {
   75|  9.44k|  std::string out;
   76|  9.44k|  out += pick(fdp, kSchemes);
   77|  9.44k|  out += pick(fdp, kHosts);
   78|  9.44k|  out += pick(fdp, kPaths);
   79|  9.44k|  out += pick(fdp, kQueries);
   80|  9.44k|  out += pick(fdp, kFrags);
   81|       |
   82|  9.44k|  if (fdp.ConsumeBool() && !out.empty()) {
  ------------------
  |  Branch (82:7): [True: 3.49k, False: 5.95k]
  |  Branch (82:28): [True: 3.49k, False: 0]
  ------------------
   83|  3.49k|    std::string mid = fdp.ConsumeRandomLengthString(24);
   84|  3.49k|    size_t pos = fdp.ConsumeIntegralInRange<size_t>(0, out.size());
   85|  3.49k|    out.insert(pos, mid);
   86|  3.49k|  }
   87|  9.44k|  if (fdp.ConsumeBool() && !out.empty()) {
  ------------------
  |  Branch (87:7): [True: 2.18k, False: 7.26k]
  |  Branch (87:28): [True: 2.18k, False: 0]
  ------------------
   88|  2.18k|    size_t i = fdp.ConsumeIntegralInRange<size_t>(0, out.size() - 1);
   89|  2.18k|    out[i] = static_cast<char>(fdp.ConsumeIntegral<uint8_t>());
   90|  2.18k|  }
   91|  9.44k|  return out;
   92|  9.44k|}
simple_absolute.cc:_ZL4pickILm8EEPKcR18FuzzedDataProviderRAT__KS1_:
   70|  9.44k|static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
   71|  9.44k|  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
   72|  9.44k|}
simple_absolute.cc:_ZL4pickILm20EEPKcR18FuzzedDataProviderRAT__KS1_:
   70|  9.44k|static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
   71|  9.44k|  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
   72|  9.44k|}
simple_absolute.cc:_ZL4pickILm16EEPKcR18FuzzedDataProviderRAT__KS1_:
   70|  9.44k|static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
   71|  9.44k|  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
   72|  9.44k|}
simple_absolute.cc:_ZL4pickILm6EEPKcR18FuzzedDataProviderRAT__KS1_:
   70|  9.44k|static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
   71|  9.44k|  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
   72|  9.44k|}
simple_absolute.cc:_ZL4pickILm5EEPKcR18FuzzedDataProviderRAT__KS1_:
   70|  9.44k|static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
   71|  9.44k|  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
   72|  9.44k|}

