_ZN3ada4idna13utf8_to_utf32EPKcmPDi:
  172|  12.9k|size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
  173|  12.9k|  const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
  174|  12.9k|  size_t pos = 0;
  175|  12.9k|  const char32_t* start{utf32_output};
  176|   191k|  while (pos < len) {
  ------------------
  |  Branch (176:10): [True: 179k, False: 12.0k]
  ------------------
  177|       |    // try to convert the next block of 16 ASCII bytes
  178|   179k|    if (pos + 16 <= len) {  // if it is safe to read 16 more
  ------------------
  |  Branch (178:9): [True: 109k, False: 69.0k]
  ------------------
  179|       |                            // bytes, check that they are ascii
  180|   109k|      uint64_t v1;
  181|   109k|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  182|   109k|      uint64_t v2;
  183|   109k|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  184|   109k|      uint64_t v{v1 | v2};
  185|   109k|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (185:11): [True: 6.19k, False: 103k]
  ------------------
  186|  6.19k|        size_t final_pos = pos + 16;
  187|   105k|        while (pos < final_pos) {
  ------------------
  |  Branch (187:16): [True: 99.1k, False: 6.19k]
  ------------------
  188|  99.1k|          *utf32_output++ = char32_t(buf[pos]);
  189|  99.1k|          pos++;
  190|  99.1k|        }
  191|  6.19k|        continue;
  192|  6.19k|      }
  193|   109k|    }
  194|   172k|    uint8_t leading_byte = data[pos];  // leading byte
  195|   172k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (195:9): [True: 95.7k, False: 77.0k]
  ------------------
  196|       |      // converting one ASCII byte !!!
  197|  95.7k|      *utf32_output++ = char32_t(leading_byte);
  198|  95.7k|      pos++;
  199|  95.7k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (199:16): [True: 19.1k, False: 57.9k]
  ------------------
  200|       |      // We have a two-byte UTF-8
  201|  19.1k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (201:11): [True: 153, False: 18.9k]
  ------------------
  202|    153|        return 0;
  203|    153|      }  // minimal bound checking
  204|  18.9k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (204:11): [True: 129, False: 18.8k]
  ------------------
  205|    129|        return 0;
  206|    129|      }
  207|       |      // range check
  208|  18.8k|      uint32_t code_point =
  209|  18.8k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  210|  18.8k|      if (code_point < 0x80 || 0x7ff < code_point) {
  ------------------
  |  Branch (210:11): [True: 12, False: 18.8k]
  |  Branch (210:32): [True: 0, False: 18.8k]
  ------------------
  211|     12|        return 0;
  212|     12|      }
  213|  18.8k|      *utf32_output++ = char32_t(code_point);
  214|  18.8k|      pos += 2;
  215|  57.9k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (215:16): [True: 55.5k, False: 2.40k]
  ------------------
  216|       |      // We have a three-byte UTF-8
  217|  55.5k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (217:11): [True: 54, False: 55.5k]
  ------------------
  218|     54|        return 0;
  219|     54|      }  // minimal bound checking
  220|       |
  221|  55.5k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (221:11): [True: 48, False: 55.4k]
  ------------------
  222|     48|        return 0;
  223|     48|      }
  224|  55.4k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (224:11): [True: 21, False: 55.4k]
  ------------------
  225|     21|        return 0;
  226|     21|      }
  227|       |      // range check
  228|  55.4k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  229|  55.4k|                            (data[pos + 1] & 0b00111111) << 6 |
  230|  55.4k|                            (data[pos + 2] & 0b00111111);
  231|  55.4k|      if (code_point < 0x800 || 0xffff < code_point ||
  ------------------
  |  Branch (231:11): [True: 24, False: 55.4k]
  |  Branch (231:33): [True: 0, False: 55.4k]
  ------------------
  232|  55.4k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (232:12): [True: 8.18k, False: 47.2k]
  |  Branch (232:35): [True: 9, False: 8.17k]
  ------------------
  233|     33|        return 0;
  234|     33|      }
  235|  55.3k|      *utf32_output++ = char32_t(code_point);
  236|  55.3k|      pos += 3;
  237|  55.3k|    } else if ((leading_byte & 0b11111000) == 0b11110000) {  // 0b11110000
  ------------------
  |  Branch (237:16): [True: 2.07k, False: 336]
  ------------------
  238|       |      // we have a 4-byte UTF-8 word.
  239|  2.07k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (239:11): [True: 24, False: 2.04k]
  ------------------
  240|     24|        return 0;
  241|     24|      }  // minimal bound checking
  242|  2.04k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (242:11): [True: 27, False: 2.02k]
  ------------------
  243|     27|        return 0;
  244|     27|      }
  245|  2.02k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (245:11): [True: 15, False: 2.00k]
  ------------------
  246|     15|        return 0;
  247|     15|      }
  248|  2.00k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (248:11): [True: 15, False: 1.99k]
  ------------------
  249|     15|        return 0;
  250|     15|      }
  251|       |
  252|       |      // range check
  253|  1.99k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  254|  1.99k|                            (data[pos + 1] & 0b00111111) << 12 |
  255|  1.99k|                            (data[pos + 2] & 0b00111111) << 6 |
  256|  1.99k|                            (data[pos + 3] & 0b00111111);
  257|  1.99k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (257:11): [True: 9, False: 1.98k]
  |  Branch (257:35): [True: 9, False: 1.97k]
  ------------------
  258|     18|        return 0;
  259|     18|      }
  260|  1.97k|      *utf32_output++ = char32_t(code_point);
  261|  1.97k|      pos += 4;
  262|  1.97k|    } else {
  263|    336|      return 0;
  264|    336|    }
  265|   172k|  }
  266|  12.0k|  return utf32_output - start;
  267|  12.9k|}
_ZN3ada4idna22utf32_length_from_utf8EPKcm:
  282|  13.0k|size_t utf32_length_from_utf8(const char* buf, size_t len) {
  283|  13.0k|  const int8_t* p = reinterpret_cast<const int8_t*>(buf);
  284|  13.0k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  285|       |    // -65 is 0b10111111, anything larger in two-complement's
  286|       |    // should start a new code point.
  287|  13.0k|    return c > -65;
  288|  13.0k|  });
  289|  13.0k|}
_ZN3ada4idna9ascii_mapEPcm:
 5133|  7.17k|void ascii_map(char* input, size_t length) {
 5134|  7.17k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5135|  7.17k|    return 0x101010101010101ull * v;
 5136|  7.17k|  };
 5137|  7.17k|  uint64_t broadcast_80 = broadcast(0x80);
 5138|  7.17k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 5139|  7.17k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 5140|  7.17k|  size_t i = 0;
 5141|       |
 5142|  56.8k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (5142:10): [True: 49.6k, False: 7.17k]
  ------------------
 5143|  49.6k|    uint64_t word{};
 5144|  49.6k|    std::memcpy(&word, input + i, sizeof(word));
 5145|  49.6k|    word ^=
 5146|  49.6k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5147|  49.6k|    std::memcpy(input + i, &word, sizeof(word));
 5148|  49.6k|  }
 5149|  7.17k|  if (i < length) {
  ------------------
  |  Branch (5149:7): [True: 6.20k, False: 967]
  ------------------
 5150|  6.20k|    uint64_t word{};
 5151|  6.20k|    std::memcpy(&word, input + i, length - i);
 5152|  6.20k|    word ^=
 5153|  6.20k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5154|  6.20k|    std::memcpy(input + i, &word, length - i);
 5155|  6.20k|  }
 5156|  7.17k|}
_ZN3ada4idna3mapENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIDiS4_NS1_9allocatorIDiEEEE:
 5160|  16.6k|bool map(std::u32string_view input, std::u32string& out) {
 5161|       |  //  [Map](https://www.unicode.org/reports/tr46/#ProcessingStepMap).
 5162|  16.6k|  out.clear();
 5163|  16.6k|  if (!ensure_tables()) {
  ------------------
  |  Branch (5163:7): [True: 0, False: 16.6k]
  ------------------
 5164|      0|    return false;
 5165|      0|  }
 5166|       |
 5167|       |  // Pass 1: validate and compute exact output length.
 5168|  16.6k|  size_t out_size = 0;
 5169|   320k|  for (char32_t x : input) {
  ------------------
  |  Branch (5169:19): [True: 320k, False: 15.9k]
  ------------------
 5170|   320k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5171|   320k|    if (status == IDNA_DISALLOWED) {
  ------------------
  |  Branch (5171:9): [True: 678, False: 319k]
  ------------------
 5172|    678|      return false;
 5173|    678|    }
 5174|   319k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5174:9): [True: 215k, False: 104k]
  ------------------
 5175|   215k|      ++out_size;
 5176|   215k|      continue;
 5177|   215k|    }
 5178|   104k|    if (static_cast<size_t>(status) >= idna_utf8_mappings_size) {
  ------------------
  |  Branch (5178:9): [True: 0, False: 104k]
  ------------------
 5179|      0|      return false;
 5180|      0|    }
 5181|   104k|    out_size += utf8_count_codepoints(idna_utf8_mappings + status);
 5182|   104k|  }
 5183|       |
 5184|       |  // Pass 2: write into a single allocation.
 5185|  15.9k|  out.resize(out_size);
 5186|  15.9k|  size_t w = 0;
 5187|   318k|  for (char32_t x : input) {
  ------------------
  |  Branch (5187:19): [True: 318k, False: 15.9k]
  ------------------
 5188|   318k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5189|   318k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5189:9): [True: 214k, False: 103k]
  ------------------
 5190|   214k|      out[w++] = x;
 5191|   214k|      continue;
 5192|   214k|    }
 5193|       |    // IGNORED or mapped (status already validated in pass 1).
 5194|   103k|    const uint8_t* ptr = idna_utf8_mappings + status;
 5195|   580k|    while (*ptr != 0) {
  ------------------
  |  Branch (5195:12): [True: 476k, False: 103k]
  ------------------
 5196|   476k|      out[w++] = utf8_next(ptr);
 5197|   476k|    }
 5198|   103k|  }
 5199|  15.9k|  return true;
 5200|  16.6k|}
_ZN3ada4idna28compute_decomposition_lengthENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5293|  2.84k|    const std::u32string_view input) noexcept {
 5294|  2.84k|  bool decomposition_needed{false};
 5295|  2.84k|  size_t additional_elements{0};
 5296|  72.6k|  for (char32_t current_character : input) {
  ------------------
  |  Branch (5296:35): [True: 72.6k, False: 2.84k]
  ------------------
 5297|  72.6k|    size_t decomposition_length{0};
 5298|       |
 5299|  72.6k|    if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5299:9): [True: 9.78k, False: 62.8k]
  ------------------
 5300|  9.78k|        current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5300:9): [True: 5.92k, False: 3.85k]
  ------------------
 5301|       |      // Full NFD-style Hangul decomp for the decompose() step.
 5302|  5.92k|      decomposition_length = 2;
 5303|  5.92k|      if ((current_character - hangul_sbase) % hangul_tcount) {
  ------------------
  |  Branch (5303:11): [True: 3.61k, False: 2.31k]
  ------------------
 5304|  3.61k|        decomposition_length = 3;
 5305|  3.61k|      }
 5306|  66.6k|    } else if (current_character < 0x110000) {
  ------------------
  |  Branch (5306:16): [True: 66.6k, False: 0]
  ------------------
 5307|  66.6k|      const uint8_t di = decomposition_index[current_character >> 8];
 5308|  66.6k|      const uint16_t* const decomposition =
 5309|  66.6k|          decomposition_block_row(di) + (current_character % 256);
 5310|  66.6k|      decomposition_length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5311|  66.6k|      if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5311:11): [True: 3.94k, False: 62.7k]
  |  Branch (5311:41): [True: 0, False: 3.94k]
  ------------------
 5312|      0|        decomposition_length = 0;
 5313|      0|      }
 5314|  66.6k|    }
 5315|  72.6k|    if (decomposition_length != 0) {
  ------------------
  |  Branch (5315:9): [True: 9.87k, False: 62.7k]
  ------------------
 5316|  9.87k|      decomposition_needed = true;
 5317|  9.87k|      additional_elements += decomposition_length - 1;
 5318|  9.87k|    }
 5319|  72.6k|  }
 5320|  2.84k|  return {decomposition_needed, additional_elements};
 5321|  2.84k|}
_ZN3ada4idna9decomposeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEEm:
 5323|  1.44k|void decompose(std::u32string& input, size_t additional_elements) {
 5324|  1.44k|  input.resize(input.size() + additional_elements);
 5325|  1.44k|  for (size_t descending_idx = input.size(),
 5326|  1.44k|              input_count = descending_idx - additional_elements;
 5327|  47.2k|       input_count--;) {
  ------------------
  |  Branch (5327:8): [True: 45.8k, False: 1.44k]
  ------------------
 5328|  45.8k|    if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5328:9): [True: 7.78k, False: 38.0k]
  ------------------
 5329|  7.78k|        input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5329:9): [True: 5.92k, False: 1.85k]
  ------------------
 5330|       |      // Hangul decomposition.
 5331|  5.92k|      char32_t s_index = input[input_count] - hangul_sbase;
 5332|  5.92k|      if (s_index % hangul_tcount != 0) {
  ------------------
  |  Branch (5332:11): [True: 3.61k, False: 2.31k]
  ------------------
 5333|  3.61k|        input[--descending_idx] = hangul_tbase + s_index % hangul_tcount;
 5334|  3.61k|      }
 5335|  5.92k|      input[--descending_idx] =
 5336|  5.92k|          hangul_vbase + (s_index % hangul_ncount) / hangul_tcount;
 5337|  5.92k|      input[--descending_idx] = hangul_lbase + s_index / hangul_ncount;
 5338|  39.8k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5338:16): [True: 39.8k, False: 0]
  ------------------
 5339|  39.8k|      const uint16_t* decomposition =
 5340|  39.8k|          decomposition_block_row(
 5341|  39.8k|              decomposition_index[input[input_count] >> 8]) +
 5342|  39.8k|          (input[input_count] % 256);
 5343|  39.8k|      uint16_t decomposition_length =
 5344|  39.8k|          (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5345|  39.8k|      if (decomposition_length > 0 && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5345:11): [True: 3.94k, False: 35.9k]
  |  Branch (5345:39): [True: 0, False: 3.94k]
  ------------------
 5346|      0|        decomposition_length = 0;
 5347|      0|      }
 5348|  39.8k|      if (decomposition_length > 0) {
  ------------------
  |  Branch (5348:11): [True: 3.94k, False: 35.9k]
  ------------------
 5349|  3.94k|        const size_t base = static_cast<size_t>(decomposition[0] >> 2);
 5350|  3.94k|        if (base + decomposition_length > decomposition_data_size) {
  ------------------
  |  Branch (5350:13): [True: 0, False: 3.94k]
  ------------------
 5351|      0|          input[--descending_idx] = input[input_count];
 5352|  3.94k|        } else {
 5353|  13.3k|          while (decomposition_length-- > 0) {
  ------------------
  |  Branch (5353:18): [True: 9.45k, False: 3.94k]
  ------------------
 5354|  9.45k|            input[--descending_idx] =
 5355|  9.45k|                decomposition_data[base + decomposition_length];
 5356|  9.45k|          }
 5357|  3.94k|        }
 5358|  35.9k|      } else {
 5359|  35.9k|        input[--descending_idx] = input[input_count];
 5360|  35.9k|      }
 5361|  39.8k|    } else {
 5362|      0|      input[--descending_idx] = input[input_count];
 5363|      0|    }
 5364|  45.8k|  }
 5365|  1.44k|}
_ZN3ada4idna7get_cccEDi:
 5367|  1.11M|uint8_t get_ccc(char32_t c) noexcept {
 5368|  1.11M|  if (c >= 0x110000) {
  ------------------
  |  Branch (5368:7): [True: 0, False: 1.11M]
  ------------------
 5369|      0|    return 0;
 5370|      0|  }
 5371|  1.11M|  return ccc_block_row(ccc_index[c >> 8])[c % 256];
 5372|  1.11M|}
_ZN3ada4idna10sort_marksERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5374|  2.84k|void sort_marks(std::u32string& input) {
 5375|  87.6k|  for (size_t idx = 1; idx < input.size(); idx++) {
  ------------------
  |  Branch (5375:24): [True: 84.8k, False: 2.84k]
  ------------------
 5376|  84.8k|    uint8_t ccc = get_ccc(input[idx]);
 5377|  84.8k|    if (ccc == 0) {
  ------------------
  |  Branch (5377:9): [True: 69.8k, False: 14.9k]
  ------------------
 5378|  69.8k|      continue;
 5379|  69.8k|    }
 5380|  14.9k|    auto current_character = input[idx];
 5381|  14.9k|    size_t back_idx = idx;
 5382|  22.1k|    while (back_idx != 0 && get_ccc(input[back_idx - 1]) > ccc) {
  ------------------
  |  Branch (5382:12): [True: 21.7k, False: 441]
  |  Branch (5382:29): [True: 7.26k, False: 14.4k]
  ------------------
 5383|  7.26k|      input[back_idx] = input[back_idx - 1];
 5384|  7.26k|      back_idx--;
 5385|  7.26k|    }
 5386|  14.9k|    input[back_idx] = current_character;
 5387|  14.9k|  }
 5388|  2.84k|}
_ZN3ada4idna13decompose_nfcERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5390|  2.84k|void decompose_nfc(std::u32string& input) {
 5391|       |  /**
 5392|       |   * Decompose the domain_name string to Unicode Normalization Form C.
 5393|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepDecompose
 5394|       |   */
 5395|  2.84k|  auto [decomposition_needed, additional_elements] =
 5396|  2.84k|      compute_decomposition_length(input);
 5397|  2.84k|  if (decomposition_needed) {
  ------------------
  |  Branch (5397:7): [True: 1.44k, False: 1.39k]
  ------------------
 5398|  1.44k|    decompose(input, additional_elements);
 5399|  1.44k|  }
 5400|  2.84k|  sort_marks(input);
 5401|  2.84k|}
_ZN3ada4idna14is_already_nfcENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5468|  15.6k|bool is_already_nfc(std::u32string_view input) noexcept {
 5469|  15.6k|  if (input.empty()) {
  ------------------
  |  Branch (5469:7): [True: 0, False: 15.6k]
  ------------------
 5470|      0|    return true;
 5471|      0|  }
 5472|  15.6k|  if (!tables_are_ready() && !ensure_tables()) {
  ------------------
  |  Branch (5472:7): [True: 0, False: 15.6k]
  |  Branch (5472:30): [True: 0, False: 0]
  ------------------
 5473|      0|    return false;
 5474|      0|  }
 5475|       |  // 1) Singleton decompositions are never NFC (e.g. U+212B ANGSTROM SIGN).
 5476|       |  //    Multi-code-point decomps are primary composites that are NFC as-is.
 5477|   526k|  for (char32_t c : input) {
  ------------------
  |  Branch (5477:19): [True: 526k, False: 15.6k]
  ------------------
 5478|   526k|    if (canonical_decomp_length(c) == 1) {
  ------------------
  |  Branch (5478:9): [True: 0, False: 526k]
  ------------------
 5479|      0|      return false;
 5480|      0|    }
 5481|   526k|  }
 5482|       |  // 2) Combining marks already in canonical order.
 5483|  15.6k|  uint8_t prev_ccc = 0;
 5484|   509k|  for (char32_t c : input) {
  ------------------
  |  Branch (5484:19): [True: 509k, False: 14.4k]
  ------------------
 5485|   509k|    uint8_t ccc = get_ccc(c);
 5486|   509k|    if (ccc != 0 && prev_ccc > ccc) {
  ------------------
  |  Branch (5486:9): [True: 20.7k, False: 488k]
  |  Branch (5486:21): [True: 1.24k, False: 19.4k]
  ------------------
 5487|  1.24k|      return false;
 5488|  1.24k|    }
 5489|   508k|    prev_ccc = ccc;
 5490|   508k|  }
 5491|       |  // 3) No pairwise composition would apply (including Hangul L+V / LV+T).
 5492|  14.4k|  return !would_compose(input);
 5493|  15.6k|}
_ZN3ada4idna7composeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5495|  2.84k|void compose(std::u32string& input) {
 5496|       |  /**
 5497|       |   * Compose the domain_name string to Unicode Normalization Form C.
 5498|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepCompose
 5499|       |   */
 5500|  2.84k|  size_t input_count{0};
 5501|  2.84k|  size_t composition_count{0};
 5502|  65.4k|  for (; input_count < input.size(); input_count++, composition_count++) {
  ------------------
  |  Branch (5502:10): [True: 62.6k, False: 2.84k]
  ------------------
 5503|  62.6k|    input[composition_count] = input[input_count];
 5504|  62.6k|    if (input[input_count] >= hangul_lbase &&
  ------------------
  |  Branch (5504:9): [True: 17.3k, False: 45.2k]
  ------------------
 5505|  17.3k|        input[input_count] < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5505:9): [True: 9.07k, False: 8.31k]
  ------------------
 5506|  9.07k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5506:11): [True: 9.03k, False: 42]
  ------------------
 5507|  9.03k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5507:11): [True: 6.71k, False: 2.31k]
  ------------------
 5508|  6.71k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5508:11): [True: 6.12k, False: 594]
  ------------------
 5509|  6.12k|        input[composition_count] =
 5510|  6.12k|            hangul_sbase +
 5511|  6.12k|            ((input[input_count] - hangul_lbase) * hangul_vcount +
 5512|  6.12k|             input[input_count + 1] - hangul_vbase) *
 5513|  6.12k|                hangul_tcount;
 5514|  6.12k|        input_count++;
 5515|  6.12k|        if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5515:13): [True: 5.96k, False: 159]
  ------------------
 5516|  5.96k|            input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5516:13): [True: 4.14k, False: 1.81k]
  ------------------
 5517|  4.14k|            input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5517:13): [True: 3.61k, False: 528]
  ------------------
 5518|  3.61k|          input[composition_count] += input[++input_count] - hangul_tbase;
 5519|  3.61k|        }
 5520|  6.12k|      }
 5521|  53.5k|    } else if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5521:16): [True: 2.61k, False: 50.9k]
  ------------------
 5522|  2.61k|               input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5522:16): [True: 0, False: 2.61k]
  ------------------
 5523|      0|      if ((input[input_count] - hangul_sbase) % hangul_tcount &&
  ------------------
  |  Branch (5523:11): [True: 0, False: 0]
  ------------------
 5524|      0|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (5524:11): [True: 0, False: 0]
  ------------------
 5525|      0|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5525:11): [True: 0, False: 0]
  ------------------
 5526|      0|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5526:11): [True: 0, False: 0]
  ------------------
 5527|      0|        input[composition_count] += input[++input_count] - hangul_tbase;
 5528|      0|      }
 5529|  53.5k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5529:16): [True: 53.5k, False: 0]
  ------------------
 5530|  53.5k|      const uint16_t* composition =
 5531|  53.5k|          composition_block_row(composition_index[input[input_count] >> 8]) +
 5532|  53.5k|          (input[input_count] % 256);
 5533|  53.5k|      size_t initial_composition_count = composition_count;
 5534|  68.8k|      for (int32_t previous_ccc = -1; input_count + 1 < input.size();
  ------------------
  |  Branch (5534:39): [True: 66.2k, False: 2.60k]
  ------------------
 5535|  66.2k|           input_count++) {
 5536|  66.2k|        uint8_t ccc = get_ccc(input[input_count + 1]);
 5537|       |
 5538|  66.2k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (5538:13): [True: 23.9k, False: 42.2k]
  |  Branch (5538:49): [True: 20.8k, False: 3.11k]
  ------------------
 5539|  20.8k|          int left = composition[0];
 5540|  20.8k|          int right = composition[1];
 5541|  20.8k|          if (left < 0 || right < left ||
  ------------------
  |  Branch (5541:15): [True: 0, False: 20.8k]
  |  Branch (5541:27): [True: 0, False: 20.8k]
  ------------------
 5542|  20.8k|              static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5542:15): [True: 0, False: 20.8k]
  ------------------
 5543|      0|            break;
 5544|      0|          }
 5545|  54.9k|          while (left + 2 < right) {
  ------------------
  |  Branch (5545:18): [True: 34.0k, False: 20.8k]
  ------------------
 5546|  34.0k|            int middle = left + (((right - left) >> 1) & ~1);
 5547|  34.0k|            if (composition_data[static_cast<size_t>(middle)] <=
  ------------------
  |  Branch (5547:17): [True: 14.2k, False: 19.8k]
  ------------------
 5548|  34.0k|                input[input_count + 1]) {
 5549|  14.2k|              left = middle;
 5550|  14.2k|            }
 5551|  34.0k|            if (composition_data[static_cast<size_t>(middle)] >=
  ------------------
  |  Branch (5551:17): [True: 24.9k, False: 9.14k]
  ------------------
 5552|  34.0k|                input[input_count + 1]) {
 5553|  24.9k|              right = middle;
 5554|  24.9k|            }
 5555|  34.0k|          }
 5556|  20.8k|          if (static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5556:15): [True: 20.8k, False: 0]
  ------------------
 5557|  20.8k|              composition_data[static_cast<size_t>(left)] ==
  ------------------
  |  Branch (5557:15): [True: 6.97k, False: 13.8k]
  ------------------
 5558|  20.8k|                  input[input_count + 1]) {
 5559|  6.97k|            char32_t composed = composition_data[static_cast<size_t>(left + 1)];
 5560|  6.97k|            input[initial_composition_count] = composed;
 5561|  6.97k|            composition =
 5562|  6.97k|                composition_block_row(composition_index[composed >> 8]) +
 5563|  6.97k|                (composed % 256);
 5564|  6.97k|            continue;
 5565|  6.97k|          }
 5566|  20.8k|        }
 5567|       |
 5568|  59.2k|        if (ccc == 0) {
  ------------------
  |  Branch (5568:13): [True: 50.9k, False: 8.31k]
  ------------------
 5569|  50.9k|          break;
 5570|  50.9k|        }
 5571|  8.31k|        previous_ccc = ccc;
 5572|  8.31k|        input[++composition_count] = input[input_count + 1];
 5573|  8.31k|      }
 5574|  53.5k|    }
 5575|  62.6k|  }
 5576|       |
 5577|  2.84k|  if (composition_count < input_count) {
  ------------------
  |  Branch (5577:7): [True: 2.44k, False: 405]
  ------------------
 5578|  2.44k|    input.resize(composition_count);
 5579|  2.44k|  }
 5580|  2.84k|}
_ZN3ada4idna9normalizeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5582|  2.84k|bool normalize(std::u32string& input) {
 5583|       |  /**
 5584|       |   * Normalize the characters according to IDNA (Unicode Normalization Form C).
 5585|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepNormalize
 5586|       |   */
 5587|  2.84k|  if (!ensure_tables() || decomposition_index == nullptr ||
  ------------------
  |  Branch (5587:7): [True: 0, False: 2.84k]
  |  Branch (5587:27): [True: 0, False: 2.84k]
  ------------------
 5588|  2.84k|      composition_index == nullptr) {
  ------------------
  |  Branch (5588:7): [True: 0, False: 2.84k]
  ------------------
 5589|      0|    return false;
 5590|      0|  }
 5591|  2.84k|  if (is_already_nfc(input)) {
  ------------------
  |  Branch (5591:7): [True: 0, False: 2.84k]
  ------------------
 5592|      0|    return true;
 5593|      0|  }
 5594|  2.84k|  decompose_nfc(input);
 5595|  2.84k|  compose(input);
 5596|  2.84k|  return true;
 5597|  2.84k|}
_ZN3ada4idna17punycode_to_utf32ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIDiNS3_IDiEENS1_9allocatorIDiEEEE:
 5657|  5.24k|bool punycode_to_utf32(std::string_view input, std::u32string &out) {
 5658|  5.24k|  int32_t written_out{0};
 5659|  5.24k|  out.reserve(out.size() + input.size());
 5660|  5.24k|  uint32_t n = initial_n;
 5661|  5.24k|  int32_t i = 0;
 5662|  5.24k|  int32_t bias = initial_bias;
 5663|       |  // grab ascii content
 5664|  5.24k|  size_t end_of_ascii = input.find_last_of('-');
 5665|  5.24k|  if (end_of_ascii != std::string_view::npos) {
  ------------------
  |  Branch (5665:7): [True: 1.84k, False: 3.39k]
  ------------------
 5666|  10.6k|    for (uint8_t c : input.substr(0, end_of_ascii)) {
  ------------------
  |  Branch (5666:20): [True: 10.6k, False: 1.84k]
  ------------------
 5667|  10.6k|      if (c >= 0x80) {
  ------------------
  |  Branch (5667:11): [True: 0, False: 10.6k]
  ------------------
 5668|      0|        return false;
 5669|      0|      }
 5670|  10.6k|      out.push_back(c);
 5671|  10.6k|      written_out++;
 5672|  10.6k|    }
 5673|  1.84k|    input.remove_prefix(end_of_ascii + 1);
 5674|  1.84k|  }
 5675|  64.8k|  while (!input.empty()) {
  ------------------
  |  Branch (5675:10): [True: 59.9k, False: 4.86k]
  ------------------
 5676|  59.9k|    int32_t oldi = i;
 5677|  59.9k|    int32_t w = 1;
 5678|  83.2k|    for (int32_t k = base;; k += base) {
 5679|  83.2k|      if (input.empty()) {
  ------------------
  |  Branch (5679:11): [True: 201, False: 83.0k]
  ------------------
 5680|    201|        return false;
 5681|    201|      }
 5682|  83.0k|      uint8_t code_point = input.front();
 5683|  83.0k|      input.remove_prefix(1);
 5684|  83.0k|      int32_t digit = char_to_digit_value(code_point);
 5685|  83.0k|      if (digit < 0) {
  ------------------
  |  Branch (5685:11): [True: 72, False: 82.9k]
  ------------------
 5686|     72|        return false;
 5687|     72|      }
 5688|  82.9k|      if (digit > (0x7fffffff - i) / w) {
  ------------------
  |  Branch (5688:11): [True: 21, False: 82.9k]
  ------------------
 5689|     21|        return false;
 5690|     21|      }
 5691|  82.9k|      i = i + digit * w;
 5692|  82.9k|      int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (5692:19): [True: 15.1k, False: 67.8k]
  |  Branch (5692:38): [True: 62.8k, False: 4.99k]
  ------------------
 5693|  82.9k|      if (digit < t) {
  ------------------
  |  Branch (5693:11): [True: 59.6k, False: 23.2k]
  ------------------
 5694|  59.6k|        break;
 5695|  59.6k|      }
 5696|  23.2k|      if (w > 0x7fffffff / (base - t)) {
  ------------------
  |  Branch (5696:11): [True: 0, False: 23.2k]
  ------------------
 5697|      0|        return false;
 5698|      0|      }
 5699|  23.2k|      w = w * (base - t);
 5700|  23.2k|    }
 5701|  59.6k|    bias = adapt(i - oldi, written_out + 1, oldi == 0);
 5702|  59.6k|    if (i / (written_out + 1) > int32_t(0x7fffffff - n)) {
  ------------------
  |  Branch (5702:9): [True: 81, False: 59.5k]
  ------------------
 5703|     81|      return false;
 5704|     81|    }
 5705|  59.5k|    n = n + i / (written_out + 1);
 5706|  59.5k|    i = i % (written_out + 1);
 5707|  59.5k|    if (n < 0x80) {
  ------------------
  |  Branch (5707:9): [True: 0, False: 59.5k]
  ------------------
 5708|      0|      return false;
 5709|      0|    }
 5710|  59.5k|    out.insert(out.begin() + i, n);
 5711|  59.5k|    written_out++;
 5712|  59.5k|    ++i;
 5713|  59.5k|  }
 5714|       |  // See https://github.com/whatwg/url/issues/803
 5715|       |  // Reject labels whose decoded form begins with "xn--" (double-encoded ACE).
 5716|  4.86k|  if (out.size() >= 4 && out[0] == U'x' && out[1] == U'n' && out[2] == U'-' &&
  ------------------
  |  Branch (5716:7): [True: 2.73k, False: 2.13k]
  |  Branch (5716:26): [True: 609, False: 2.12k]
  |  Branch (5716:44): [True: 342, False: 267]
  |  Branch (5716:62): [True: 165, False: 177]
  ------------------
 5717|    165|      out[3] == U'-') {
  ------------------
  |  Branch (5717:7): [True: 15, False: 150]
  ------------------
 5718|     15|    return false;
 5719|     15|  }
 5720|  4.85k|  return true;
 5721|  4.86k|}
_ZN3ada4idna17utf32_to_punycodeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIcNS3_IcEENS1_9allocatorIcEEEE:
 5801|  38.0k|bool utf32_to_punycode(std::u32string_view input, std::string &out) {
 5802|  38.0k|  out.reserve(input.size() + out.size());
 5803|  38.0k|  uint32_t n = initial_n;
 5804|  38.0k|  int32_t d = 0;
 5805|  38.0k|  int32_t bias = initial_bias;
 5806|  38.0k|  size_t h = 0;
 5807|       |  // first push the ascii content
 5808|   235k|  for (uint32_t c : input) {
  ------------------
  |  Branch (5808:19): [True: 235k, False: 38.0k]
  ------------------
 5809|   235k|    if (c < 0x80) {
  ------------------
  |  Branch (5809:9): [True: 29.6k, False: 206k]
  ------------------
 5810|  29.6k|      ++h;
 5811|  29.6k|      out.push_back(char(c));
 5812|  29.6k|    }
 5813|   235k|    if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) {
  ------------------
  |  Branch (5813:9): [True: 0, False: 235k]
  |  Branch (5813:26): [True: 2.31k, False: 233k]
  |  Branch (5813:41): [True: 0, False: 2.31k]
  ------------------
 5814|      0|      return false;
 5815|      0|    }
 5816|   235k|  }
 5817|  38.0k|  size_t b = h;
 5818|  38.0k|  if (b > 0) {
  ------------------
  |  Branch (5818:7): [True: 6.06k, False: 32.0k]
  ------------------
 5819|  6.06k|    out.push_back('-');
 5820|  6.06k|  }
 5821|   168k|  while (h < input.size()) {
  ------------------
  |  Branch (5821:10): [True: 130k, False: 38.0k]
  ------------------
 5822|   130k|    uint32_t m = 0x10FFFF;
 5823|  1.69M|    for (auto code_point : input) {
  ------------------
  |  Branch (5823:26): [True: 1.69M, False: 130k]
  ------------------
 5824|  1.69M|      if (code_point >= n && code_point < m) m = code_point;
  ------------------
  |  Branch (5824:11): [True: 789k, False: 904k]
  |  Branch (5824:30): [True: 224k, False: 565k]
  ------------------
 5825|  1.69M|    }
 5826|       |
 5827|   130k|    if ((m - n) > (0x7fffffff - d) / (h + 1)) {
  ------------------
  |  Branch (5827:9): [True: 0, False: 130k]
  ------------------
 5828|      0|      return false;
 5829|      0|    }
 5830|   130k|    d = d + int32_t((m - n) * (h + 1));
 5831|   130k|    n = m;
 5832|  1.69M|    for (auto c : input) {
  ------------------
  |  Branch (5832:17): [True: 1.69M, False: 130k]
  ------------------
 5833|  1.69M|      if (c < n) {
  ------------------
  |  Branch (5833:11): [True: 904k, False: 789k]
  ------------------
 5834|   904k|        if (d == 0x7fffffff) {
  ------------------
  |  Branch (5834:13): [True: 0, False: 904k]
  ------------------
 5835|      0|          return false;
 5836|      0|        }
 5837|   904k|        ++d;
 5838|   904k|      }
 5839|  1.69M|      if (c == n) {
  ------------------
  |  Branch (5839:11): [True: 206k, False: 1.48M]
  ------------------
 5840|   206k|        int32_t q = d;
 5841|   506k|        for (int32_t k = base;; k += base) {
 5842|   506k|          int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (5842:23): [True: 135k, False: 370k]
  |  Branch (5842:42): [True: 283k, False: 86.9k]
  ------------------
 5843|       |
 5844|   506k|          if (q < t) {
  ------------------
  |  Branch (5844:15): [True: 206k, False: 300k]
  ------------------
 5845|   206k|            break;
 5846|   206k|          }
 5847|   300k|          out.push_back(digit_to_char(t + ((q - t) % (base - t))));
 5848|   300k|          q = (q - t) / (base - t);
 5849|   300k|        }
 5850|   206k|        out.push_back(digit_to_char(q));
 5851|   206k|        bias = adapt(d, int32_t(h + 1), h == b);
 5852|   206k|        d = 0;
 5853|   206k|        ++h;
 5854|   206k|      }
 5855|  1.69M|    }
 5856|   130k|    ++d;
 5857|   130k|    ++n;
 5858|   130k|  }
 5859|  38.0k|  return true;
 5860|  38.0k|}
_ZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5944|  43.6k|bool is_label_valid(const std::u32string_view label) {
 5945|  43.6k|  if (label.empty()) {
  ------------------
  |  Branch (5945:7): [True: 0, False: 43.6k]
  ------------------
 5946|      0|    return true;
 5947|      0|  }
 5948|  43.6k|  if (!ensure_tables() || combining_ranges == nullptr || dir_start == nullptr ||
  ------------------
  |  Branch (5948:7): [True: 0, False: 43.6k]
  |  Branch (5948:27): [True: 0, False: 43.6k]
  |  Branch (5948:58): [True: 0, False: 43.6k]
  ------------------
 5949|  43.6k|      dir_final == nullptr || dir_value == nullptr) {
  ------------------
  |  Branch (5949:7): [True: 0, False: 43.6k]
  |  Branch (5949:31): [True: 0, False: 43.6k]
  ------------------
 5950|      0|    return false;
 5951|      0|  }
 5952|       |
 5953|       |  ///////////////
 5954|       |  // We have a normalization step which ensures that we are in NFC.
 5955|       |  // If we receive punycode, we normalize and check that the normalized
 5956|       |  // version matches the original.
 5957|       |  // --------------------------------------
 5958|       |  // The label must be in Unicode Normalization Form NFC.
 5959|       |
 5960|       |  // Current URL standard indicatest that CheckHyphens is set to false.
 5961|       |  // ---------------------------------------
 5962|       |  // If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character
 5963|       |  // in both the third and fourth positions. If CheckHyphens, the label must
 5964|       |  // neither begin nor end with a U+002D HYPHEN-MINUS character.
 5965|       |
 5966|       |  // This is not necessary because we segment the
 5967|       |  // labels by '.'.
 5968|       |  // ---------------------------------------
 5969|       |  // The label must not contain a U+002E ( . ) FULL STOP.
 5970|       |  // if (label.find('.') != std::string_view::npos) return false;
 5971|       |
 5972|       |  // The label must not begin with a combining mark.
 5973|       |  // Range membership via lower_bound on range end.
 5974|  43.6k|  const std::span<const uint32_t[2]> comb_span{combining_ranges,
 5975|  43.6k|                                               combining_range_count};
 5976|  43.6k|  const auto comb_it = std::ranges::lower_bound(
 5977|  43.6k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
 5978|  43.6k|  if (comb_it != comb_span.end() && label.front() >= (*comb_it)[0]) {
  ------------------
  |  Branch (5978:7): [True: 43.6k, False: 0]
  |  Branch (5978:7): [True: 249, False: 43.3k]
  |  Branch (5978:37): [True: 249, False: 43.3k]
  ------------------
 5979|    249|    return false;
 5980|    249|  }
 5981|       |  // We verify this next step as part of the mapping:
 5982|       |  // ---------------------------------------------
 5983|       |  // Each code point in the label must only have certain status values
 5984|       |  // according to Section 5, IDNA Mapping Table:
 5985|       |  // - For Transitional Processing, each value must be valid.
 5986|       |  // - For Nontransitional Processing, each value must be either valid or
 5987|       |  // deviation.
 5988|       |
 5989|       |  // If CheckJoiners, the label must satisfy the ContextJ rules from Appendix
 5990|       |  // A, in The Unicode Code Points and Internationalized Domain Names for
 5991|       |  // Applications (IDNA) [IDNA2008].
 5992|  43.3k|  constexpr static uint32_t virama[] = {
 5993|  43.3k|      0x094D,  0x09CD,  0x0A4D,  0x0ACD,  0x0B4D,  0x0BCD,  0x0C4D,  0x0CCD,
 5994|  43.3k|      0x0D3B,  0x0D3C,  0x0D4D,  0x0DCA,  0x0E3A,  0x0EBA,  0x0F84,  0x1039,
 5995|  43.3k|      0x103A,  0x1714,  0x1734,  0x17D2,  0x1A60,  0x1B44,  0x1BAA,  0x1BAB,
 5996|  43.3k|      0x1BF2,  0x1BF3,  0x2D7F,  0xA806,  0xA82C,  0xA8C4,  0xA953,  0xA9C0,
 5997|  43.3k|      0xAAF6,  0xABED,  0x10A3F, 0x11046, 0x1107F, 0x110B9, 0x11133, 0x11134,
 5998|  43.3k|      0x111C0, 0x11235, 0x112EA, 0x1134D, 0x11442, 0x114C2, 0x115BF, 0x1163F,
 5999|  43.3k|      0x116B6, 0x1172B, 0x11839, 0x1193D, 0x1193E, 0x119E0, 0x11A34, 0x11A47,
 6000|  43.3k|      0x11A99, 0x11C3F, 0x11D44, 0x11D45, 0x11D97};
 6001|  43.3k|  constexpr static uint32_t R[] = {
 6002|  43.3k|      0x622, 0x623, 0x624, 0x625, 0x627, 0x629, 0x62f, 0x630, 0x631,
 6003|  43.3k|      0x632, 0x648, 0x671, 0x672, 0x673, 0x675, 0x676, 0x677, 0x688,
 6004|  43.3k|      0x689, 0x68a, 0x68b, 0x68c, 0x68d, 0x68e, 0x68f, 0x690, 0x691,
 6005|  43.3k|      0x692, 0x693, 0x694, 0x695, 0x696, 0x697, 0x698, 0x699, 0x6c0,
 6006|  43.3k|      0x6c3, 0x6c4, 0x6c5, 0x6c6, 0x6c7, 0x6c8, 0x6c9, 0x6ca, 0x6cb,
 6007|  43.3k|      0x6cd, 0x6cf, 0x6d2, 0x6d3, 0x6d5, 0x6ee, 0x6ef, 0x710, 0x715,
 6008|  43.3k|      0x716, 0x717, 0x718, 0x719, 0x71e, 0x728, 0x72a, 0x72c, 0x72f,
 6009|  43.3k|      0x74d, 0x759, 0x75a, 0x75b, 0x854, 0x8aa, 0x8ab, 0x8ac};
 6010|  43.3k|  constexpr static uint32_t L[] = {0xa872};
 6011|  43.3k|  constexpr static uint32_t D[] = {
 6012|  43.3k|      0x620,  0x626,  0x628,  0x62a,  0x62b,  0x62c,  0x62d,  0x62e,  0x633,
 6013|  43.3k|      0x634,  0x635,  0x636,  0x637,  0x638,  0x639,  0x63a,  0x63b,  0x63c,
 6014|  43.3k|      0x63d,  0x63e,  0x63f,  0x641,  0x642,  0x643,  0x644,  0x645,  0x646,
 6015|  43.3k|      0x647,  0x649,  0x64a,  0x66e,  0x66f,  0x678,  0x679,  0x67a,  0x67b,
 6016|  43.3k|      0x67c,  0x67d,  0x67e,  0x67f,  0x680,  0x681,  0x682,  0x683,  0x684,
 6017|  43.3k|      0x685,  0x686,  0x687,  0x69a,  0x69b,  0x69c,  0x69d,  0x69e,  0x69f,
 6018|  43.3k|      0x6a0,  0x6a1,  0x6a2,  0x6a3,  0x6a4,  0x6a5,  0x6a6,  0x6a7,  0x6a8,
 6019|  43.3k|      0x6a9,  0x6aa,  0x6ab,  0x6ac,  0x6ad,  0x6ae,  0x6af,  0x6b0,  0x6b1,
 6020|  43.3k|      0x6b2,  0x6b3,  0x6b4,  0x6b5,  0x6b6,  0x6b7,  0x6b8,  0x6b9,  0x6ba,
 6021|  43.3k|      0x6bb,  0x6bc,  0x6bd,  0x6be,  0x6bf,  0x6c1,  0x6c2,  0x6cc,  0x6ce,
 6022|  43.3k|      0x6d0,  0x6d1,  0x6fa,  0x6fb,  0x6fc,  0x6ff,  0x712,  0x713,  0x714,
 6023|  43.3k|      0x71a,  0x71b,  0x71c,  0x71d,  0x71f,  0x720,  0x721,  0x722,  0x723,
 6024|  43.3k|      0x724,  0x725,  0x726,  0x727,  0x729,  0x72b,  0x72d,  0x72e,  0x74e,
 6025|  43.3k|      0x74f,  0x750,  0x751,  0x752,  0x753,  0x754,  0x755,  0x756,  0x757,
 6026|  43.3k|      0x758,  0x75c,  0x75d,  0x75e,  0x75f,  0x760,  0x761,  0x762,  0x763,
 6027|  43.3k|      0x764,  0x765,  0x766,  0x850,  0x851,  0x852,  0x853,  0x855,  0x8a0,
 6028|  43.3k|      0x8a2,  0x8a3,  0x8a4,  0x8a5,  0x8a6,  0x8a7,  0x8a8,  0x8a9,  0x1807,
 6029|  43.3k|      0x1820, 0x1821, 0x1822, 0x1823, 0x1824, 0x1825, 0x1826, 0x1827, 0x1828,
 6030|  43.3k|      0x1829, 0x182a, 0x182b, 0x182c, 0x182d, 0x182e, 0x182f, 0x1830, 0x1831,
 6031|  43.3k|      0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837, 0x1838, 0x1839, 0x183a,
 6032|  43.3k|      0x183b, 0x183c, 0x183d, 0x183e, 0x183f, 0x1840, 0x1841, 0x1842, 0x1843,
 6033|  43.3k|      0x1844, 0x1845, 0x1846, 0x1847, 0x1848, 0x1849, 0x184a, 0x184b, 0x184c,
 6034|  43.3k|      0x184d, 0x184e, 0x184f, 0x1850, 0x1851, 0x1852, 0x1853, 0x1854, 0x1855,
 6035|  43.3k|      0x1856, 0x1857, 0x1858, 0x1859, 0x185a, 0x185b, 0x185c, 0x185d, 0x185e,
 6036|  43.3k|      0x185f, 0x1860, 0x1861, 0x1862, 0x1863, 0x1864, 0x1865, 0x1866, 0x1867,
 6037|  43.3k|      0x1868, 0x1869, 0x186a, 0x186b, 0x186c, 0x186d, 0x186e, 0x186f, 0x1870,
 6038|  43.3k|      0x1871, 0x1872, 0x1873, 0x1874, 0x1875, 0x1876, 0x1877, 0x1887, 0x1888,
 6039|  43.3k|      0x1889, 0x188a, 0x188b, 0x188c, 0x188d, 0x188e, 0x188f, 0x1890, 0x1891,
 6040|  43.3k|      0x1892, 0x1893, 0x1894, 0x1895, 0x1896, 0x1897, 0x1898, 0x1899, 0x189a,
 6041|  43.3k|      0x189b, 0x189c, 0x189d, 0x189e, 0x189f, 0x18a0, 0x18a1, 0x18a2, 0x18a3,
 6042|  43.3k|      0x18a4, 0x18a5, 0x18a6, 0x18a7, 0x18a8, 0x18aa, 0xa840, 0xa841, 0xa842,
 6043|  43.3k|      0xa843, 0xa844, 0xa845, 0xa846, 0xa847, 0xa848, 0xa849, 0xa84a, 0xa84b,
 6044|  43.3k|      0xa84c, 0xa84d, 0xa84e, 0xa84f, 0xa850, 0xa851, 0xa852, 0xa853, 0xa854,
 6045|  43.3k|      0xa855, 0xa856, 0xa857, 0xa858, 0xa859, 0xa85a, 0xa85b, 0xa85c, 0xa85d,
 6046|  43.3k|      0xa85e, 0xa85f, 0xa860, 0xa861, 0xa862, 0xa863, 0xa864, 0xa865, 0xa866,
 6047|  43.3k|      0xa867, 0xa868, 0xa869, 0xa86a, 0xa86b, 0xa86c, 0xa86d, 0xa86e, 0xa86f,
 6048|  43.3k|      0xa870, 0xa871};
 6049|       |
 6050|   299k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (6050:22): [True: 259k, False: 40.2k]
  ------------------
 6051|   259k|    uint32_t c = label[i];
 6052|   259k|    if (c == 0x200c) {
  ------------------
  |  Branch (6052:9): [True: 2.37k, False: 257k]
  ------------------
 6053|  2.37k|      if (i > 0) {
  ------------------
  |  Branch (6053:11): [True: 2.36k, False: 12]
  ------------------
 6054|  2.36k|        if (std::ranges::binary_search(virama, label[i - 1])) {
  ------------------
  |  Branch (6054:13): [True: 222, False: 2.14k]
  ------------------
 6055|    222|          return true;
 6056|    222|        }
 6057|  2.36k|      }
 6058|  2.15k|      if ((i == 0) || (i + 1 >= label.size())) {
  ------------------
  |  Branch (6058:11): [True: 12, False: 2.14k]
  |  Branch (6058:23): [True: 207, False: 1.93k]
  ------------------
 6059|    219|        return false;
 6060|    219|      }
 6061|       |      // we go backward looking for L or D
 6062|  1.93k|      auto is_l_or_d = [](uint32_t code) {
 6063|  1.93k|        return std::ranges::binary_search(L, code) ||
 6064|  1.93k|               std::ranges::binary_search(D, code);
 6065|  1.93k|      };
 6066|  1.93k|      auto is_r_or_d = [](uint32_t code) {
 6067|  1.93k|        return std::ranges::binary_search(R, code) ||
 6068|  1.93k|               std::ranges::binary_search(D, code);
 6069|  1.93k|      };
 6070|  1.93k|      std::u32string_view before = label.substr(0, i);
 6071|  1.93k|      std::u32string_view after = label.substr(i + 1);
 6072|  1.93k|      return (std::find_if(before.begin(), before.end(), is_l_or_d) !=
  ------------------
  |  Branch (6072:14): [True: 1.77k, False: 162]
  ------------------
 6073|  1.93k|              before.end()) &&
 6074|  1.77k|             (std::find_if(after.begin(), after.end(), is_r_or_d) !=
  ------------------
  |  Branch (6074:14): [True: 1.48k, False: 294]
  ------------------
 6075|  1.77k|              after.end());
 6076|   257k|    } else if (c == 0x200d) {
  ------------------
  |  Branch (6076:16): [True: 732, False: 256k]
  ------------------
 6077|    732|      if (i > 0) {
  ------------------
  |  Branch (6077:11): [True: 711, False: 21]
  ------------------
 6078|    711|        if (std::ranges::binary_search(virama, label[i - 1])) {
  ------------------
  |  Branch (6078:13): [True: 591, False: 120]
  ------------------
 6079|    591|          return true;
 6080|    591|        }
 6081|    711|      }
 6082|    141|      return false;
 6083|    732|    }
 6084|   259k|  }
 6085|       |
 6086|       |  // If CheckBidi, and if the domain name is a  Bidi domain name, then the label
 6087|       |  // must satisfy all six of the numbered conditions in [IDNA2008] RFC 5893,
 6088|       |  // Section 2.
 6089|       |
 6090|       |  // The following rule, consisting of six conditions, applies to labels
 6091|       |  // in Bidi domain names.  The requirements that this rule satisfies are
 6092|       |  // described in Section 3.  All of the conditions must be satisfied for
 6093|       |  // the rule to be satisfied.
 6094|       |  //
 6095|       |  //  1.  The first character must be a character with Bidi property L, R,
 6096|       |  //     or AL.  If it has the R or AL property, it is an RTL label; if it
 6097|       |  //     has the L property, it is an LTR label.
 6098|       |  //
 6099|       |  //  2.  In an RTL label, only characters with the Bidi properties R, AL,
 6100|       |  //      AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
 6101|       |  //
 6102|       |  //   3.  In an RTL label, the end of the label must be a character with
 6103|       |  //       Bidi property R, AL, EN, or AN, followed by zero or more
 6104|       |  //       characters with Bidi property NSM.
 6105|       |  //
 6106|       |  //   4.  In an RTL label, if an EN is present, no AN may be present, and
 6107|       |  //       vice versa.
 6108|       |  //
 6109|       |  //  5.  In an LTR label, only characters with the Bidi properties L, EN,
 6110|       |  //       ES, CS, ET, ON, BN, or NSM are allowed.
 6111|       |  //
 6112|       |  //   6.  In an LTR label, the end of the label must be a character with
 6113|       |  //       Bidi property L or EN, followed by zero or more characters with
 6114|       |  //       Bidi property NSM.
 6115|       |
 6116|  40.2k|  size_t last_non_nsm_char = find_last_not_of_nsm(label);
 6117|  40.2k|  if (last_non_nsm_char == std::u32string_view::npos) {
  ------------------
  |  Branch (6117:7): [True: 0, False: 40.2k]
  ------------------
 6118|      0|    return false;
 6119|      0|  }
 6120|       |
 6121|       |  // A "Bidi domain name" is a domain name that contains at least one RTL label.
 6122|       |  // The following rule, consisting of six conditions, applies to labels in Bidi
 6123|       |  // domain names.
 6124|  40.2k|  if (is_rtl_label(label)) {
  ------------------
  |  Branch (6124:7): [True: 3.79k, False: 36.4k]
  ------------------
 6125|       |    // The first character must be a character with Bidi property L, R,
 6126|       |    // or AL. If it has the R or AL property, it is an RTL label; if it
 6127|       |    // has the L property, it is an LTR label.
 6128|       |
 6129|  3.79k|    if (find_direction(label[0]) == direction::L) {
  ------------------
  |  Branch (6129:9): [True: 522, False: 3.27k]
  ------------------
 6130|       |      // Eval as LTR
 6131|       |
 6132|       |      // In an LTR label, only characters with the Bidi properties L, EN,
 6133|       |      // ES, CS, ET, ON, BN, or NSM are allowed.
 6134|  10.6k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6134:26): [True: 10.6k, False: 0]
  ------------------
 6135|  10.6k|        const direction d = find_direction(label[i]);
 6136|  10.6k|        if (!(d == direction::L || d == direction::EN || d == direction::ES ||
  ------------------
  |  Branch (6136:15): [True: 2.29k, False: 8.34k]
  |  Branch (6136:36): [True: 1.34k, False: 6.99k]
  |  Branch (6136:58): [True: 993, False: 6.00k]
  ------------------
 6137|  6.00k|              d == direction::CS || d == direction::ET || d == direction::ON ||
  ------------------
  |  Branch (6137:15): [True: 1.19k, False: 4.80k]
  |  Branch (6137:37): [True: 999, False: 3.80k]
  |  Branch (6137:59): [True: 1.10k, False: 2.70k]
  ------------------
 6138|  2.70k|              d == direction::BN || d == direction::NSM)) {
  ------------------
  |  Branch (6138:15): [True: 1.18k, False: 1.51k]
  |  Branch (6138:37): [True: 996, False: 522]
  ------------------
 6139|    522|          return false;
 6140|    522|        }
 6141|  10.6k|      }
 6142|       |
 6143|      0|      const direction last_dir = find_direction(label[last_non_nsm_char]);
 6144|      0|      if (!(last_dir == direction::L || last_dir == direction::EN)) {
  ------------------
  |  Branch (6144:13): [True: 0, False: 0]
  |  Branch (6144:41): [True: 0, False: 0]
  ------------------
 6145|      0|        return false;
 6146|      0|      }
 6147|       |
 6148|      0|      return true;
 6149|       |
 6150|  3.27k|    } else {
 6151|       |      // Eval as RTL
 6152|       |
 6153|       |      // The first character must be R or AL; a leading AN (or any other
 6154|       |      // direction) does not start a valid RTL label.
 6155|  3.27k|      const direction first_dir = find_direction(label[0]);
 6156|  3.27k|      if (first_dir != direction::R && first_dir != direction::AL) {
  ------------------
  |  Branch (6156:11): [True: 1.73k, False: 1.53k]
  |  Branch (6156:40): [True: 90, False: 1.64k]
  ------------------
 6157|     90|        return false;
 6158|     90|      }
 6159|       |
 6160|  3.18k|      bool has_an = false;
 6161|  3.18k|      bool has_en = false;
 6162|  20.3k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6162:26): [True: 17.6k, False: 2.69k]
  ------------------
 6163|  17.6k|        const direction d = find_direction(label[i]);
 6164|       |
 6165|       |        // In an RTL label, if an EN is present, no AN may be present, and vice
 6166|       |        // versa.
 6167|  17.6k|        if ((d == direction::EN && ((has_en = true) && has_an)) ||
  ------------------
  |  Branch (6167:14): [True: 2.18k, False: 15.4k]
  |  Branch (6167:37): [True: 2.18k, False: 0]
  |  Branch (6167:56): [True: 18, False: 2.16k]
  ------------------
 6168|  17.6k|            (d == direction::AN && ((has_an = true) && has_en))) {
  ------------------
  |  Branch (6168:14): [True: 1.46k, False: 16.1k]
  |  Branch (6168:37): [True: 1.46k, False: 0]
  |  Branch (6168:56): [True: 9, False: 1.45k]
  ------------------
 6169|     27|          return false;
 6170|     27|        }
 6171|       |
 6172|  17.6k|        if (!(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6172:15): [True: 2.47k, False: 15.1k]
  |  Branch (6172:36): [True: 3.29k, False: 11.8k]
  |  Branch (6172:58): [True: 1.45k, False: 10.4k]
  ------------------
 6173|  10.4k|              d == direction::EN || d == direction::ES || d == direction::CS ||
  ------------------
  |  Branch (6173:15): [True: 2.16k, False: 8.24k]
  |  Branch (6173:37): [True: 1.05k, False: 7.18k]
  |  Branch (6173:59): [True: 1.38k, False: 5.79k]
  ------------------
 6174|  5.79k|              d == direction::ET || d == direction::ON || d == direction::BN ||
  ------------------
  |  Branch (6174:15): [True: 1.09k, False: 4.70k]
  |  Branch (6174:37): [True: 1.38k, False: 3.32k]
  |  Branch (6174:59): [True: 1.98k, False: 1.33k]
  ------------------
 6175|  1.33k|              d == direction::NSM)) {
  ------------------
  |  Branch (6175:15): [True: 1.11k, False: 225]
  ------------------
 6176|    225|          return false;
 6177|    225|        }
 6178|       |
 6179|  17.4k|        if (i == last_non_nsm_char &&
  ------------------
  |  Branch (6179:13): [True: 2.92k, False: 14.4k]
  ------------------
 6180|  2.92k|            !(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6180:15): [True: 945, False: 1.98k]
  |  Branch (6180:36): [True: 717, False: 1.26k]
  |  Branch (6180:58): [True: 498, False: 768]
  ------------------
 6181|    768|              d == direction::EN)) {
  ------------------
  |  Branch (6181:15): [True: 531, False: 237]
  ------------------
 6182|    237|          return false;
 6183|    237|        }
 6184|  17.4k|      }
 6185|       |
 6186|  2.69k|      return true;
 6187|  3.18k|    }
 6188|  3.79k|  }
 6189|       |
 6190|  36.4k|  return true;
 6191|  40.2k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6348|  20.2k|[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out) {
 6349|  20.2k|  out.clear();
 6350|  20.2k|  if (ut8_string.size() > max_domain_input_bytes) {
  ------------------
  |  Branch (6350:7): [True: 0, False: 20.2k]
  ------------------
 6351|      0|    return false;
 6352|      0|  }
 6353|  20.2k|  if (is_ascii(ut8_string)) {
  ------------------
  |  Branch (6353:7): [True: 7.17k, False: 13.0k]
  ------------------
 6354|  7.17k|    from_ascii_to_ascii(ut8_string, out);
 6355|  7.17k|    return true;
 6356|  7.17k|  }
 6357|       |
 6358|       |#ifdef ADA_USE_SIMDUTF
 6359|       |  size_t utf32_length =
 6360|       |      simdutf::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 6361|       |  if (utf32_length == 0 && !ut8_string.empty()) {
 6362|       |    return false;
 6363|       |  }
 6364|       |  std::u32string working(utf32_length, U'\0');
 6365|       |  size_t actual_utf32_length = simdutf::convert_utf8_to_utf32(
 6366|       |      ut8_string.data(), ut8_string.size(), working.data());
 6367|       |#else
 6368|  13.0k|  size_t utf32_length =
 6369|  13.0k|      ada::idna::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 6370|  13.0k|  if (utf32_length == 0 && !ut8_string.empty()) {
  ------------------
  |  Branch (6370:7): [True: 171, False: 12.9k]
  |  Branch (6370:28): [True: 171, False: 0]
  ------------------
 6371|    171|    return false;
 6372|    171|  }
 6373|  12.9k|  std::u32string working(utf32_length, U'\0');
 6374|  12.9k|  size_t actual_utf32_length = ada::idna::utf8_to_utf32(
 6375|  12.9k|      ut8_string.data(), ut8_string.size(), working.data());
 6376|  12.9k|#endif
 6377|  12.9k|  if (actual_utf32_length == 0 || actual_utf32_length != utf32_length) {
  ------------------
  |  Branch (6377:7): [True: 885, False: 12.0k]
  |  Branch (6377:35): [True: 0, False: 12.0k]
  ------------------
 6378|    885|    return false;
 6379|    885|  }
 6380|  12.0k|  working.resize(actual_utf32_length);
 6381|       |
 6382|       |  // Map into a second buffer with exact sizing (no growth reallocs).
 6383|  12.0k|  std::u32string mapped;
 6384|  12.0k|  if (!ada::idna::map(working, mapped)) {
  ------------------
  |  Branch (6384:7): [True: 138, False: 11.9k]
  ------------------
 6385|    138|    return false;
 6386|    138|  }
 6387|       |  // Drop UTF-32 input; reuse `working` as scratch for ACE validation below.
 6388|  11.9k|  working.clear();
 6389|       |
 6390|       |  // Skip NFC when already normalized (ASCII is a fast subset of this check).
 6391|  11.9k|  if (!is_ascii(mapped) && !is_already_nfc(mapped)) {
  ------------------
  |  Branch (6391:7): [True: 9.04k, False: 2.85k]
  |  Branch (6391:28): [True: 1.80k, False: 7.23k]
  ------------------
 6392|  1.80k|    if (!normalize(mapped)) {
  ------------------
  |  Branch (6392:9): [True: 0, False: 1.80k]
  ------------------
 6393|      0|      return false;
 6394|      0|    }
 6395|  1.80k|  }
 6396|       |
 6397|       |  // Estimate ASCII output size (punycode may expand non-ASCII labels).
 6398|  11.9k|  out.reserve(mapped.size() + 8);
 6399|       |
 6400|       |  // Walk labels with a single pointer scan (no repeated string::find).
 6401|  11.9k|  const char32_t* p = mapped.data();
 6402|  11.9k|  const char32_t* const end = p + mapped.size();
 6403|  11.9k|  std::u32string post_map;
 6404|       |
 6405|  61.9k|  while (p < end) {
  ------------------
  |  Branch (6405:10): [True: 54.1k, False: 7.85k]
  ------------------
 6406|  54.1k|    const char32_t* label_begin = p;
 6407|   517k|    while (p < end && *p != U'.') {
  ------------------
  |  Branch (6407:12): [True: 506k, False: 11.3k]
  |  Branch (6407:23): [True: 463k, False: 42.7k]
  ------------------
 6408|   463k|      ++p;
 6409|   463k|    }
 6410|  54.1k|    const size_t label_size = static_cast<size_t>(p - label_begin);
 6411|  54.1k|    std::u32string_view label_view(label_begin, label_size);
 6412|  54.1k|    const bool is_last_label = (p == end);
 6413|  54.1k|    if (p < end) {
  ------------------
  |  Branch (6413:9): [True: 42.7k, False: 11.3k]
  ------------------
 6414|  42.7k|      ++p;  // skip dot
 6415|  42.7k|    }
 6416|       |
 6417|  54.1k|    if (label_size == 0) {
  ------------------
  |  Branch (6417:9): [True: 3.16k, False: 50.9k]
  ------------------
 6418|       |      // empty label
 6419|  50.9k|    } else if (is_ace_prefix(label_view)) {
  ------------------
  |  Branch (6419:16): [True: 5.28k, False: 45.6k]
  ------------------
 6420|   119k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (6420:23): [True: 119k, False: 5.24k]
  ------------------
 6421|   119k|        if (c >= 0x80) {
  ------------------
  |  Branch (6421:13): [True: 48, False: 119k]
  ------------------
 6422|     48|          out.clear();
 6423|     48|          return false;
 6424|     48|        }
 6425|   119k|      }
 6426|  5.24k|      append_ascii_label(out, label_view);
 6427|  5.24k|      std::string_view puny_segment_ascii(
 6428|  5.24k|          out.data() + (out.size() - label_size) + 4, label_size - 4);
 6429|  5.24k|      working.clear();
 6430|  5.24k|      if (!ada::idna::punycode_to_utf32(puny_segment_ascii, working)) {
  ------------------
  |  Branch (6430:11): [True: 390, False: 4.85k]
  ------------------
 6431|    390|        out.clear();
 6432|    390|        return false;
 6433|    390|      }
 6434|  4.85k|      if (is_ascii(working)) {
  ------------------
  |  Branch (6434:11): [True: 264, False: 4.58k]
  ------------------
 6435|    264|        out.clear();
 6436|    264|        return false;
 6437|    264|      }
 6438|  4.58k|      if (!ada::idna::map(working, post_map) || working != post_map) {
  ------------------
  |  Branch (6438:11): [True: 540, False: 4.04k]
  |  Branch (6438:49): [True: 255, False: 3.79k]
  ------------------
 6439|    795|        out.clear();
 6440|    795|        return false;
 6441|    795|      }
 6442|       |      // Mapping must be stable; NFC must not change the mapped form either.
 6443|  3.79k|      if (!is_ascii(post_map) && !is_already_nfc(post_map)) {
  ------------------
  |  Branch (6443:11): [True: 3.79k, False: 0]
  |  Branch (6443:34): [True: 1.03k, False: 2.75k]
  ------------------
 6444|  1.03k|        if (!normalize(post_map) || post_map != working) {
  ------------------
  |  Branch (6444:13): [True: 0, False: 1.03k]
  |  Branch (6444:37): [True: 384, False: 654]
  ------------------
 6445|    384|          out.clear();
 6446|    384|          return false;
 6447|    384|        }
 6448|  1.03k|      }
 6449|  3.40k|      if (post_map.empty() || !is_label_valid(post_map)) {
  ------------------
  |  Branch (6449:11): [True: 0, False: 3.40k]
  |  Branch (6449:31): [True: 42, False: 3.36k]
  ------------------
 6450|     42|        out.clear();
 6451|     42|        return false;
 6452|     42|      }
 6453|  45.6k|    } else if (is_ascii(label_view)) {
  ------------------
  |  Branch (6453:16): [True: 5.46k, False: 40.2k]
  ------------------
 6454|  5.46k|      append_ascii_label(out, label_view);
 6455|  40.2k|    } else {
 6456|  40.2k|      if (!is_label_valid(label_view)) {
  ------------------
  |  Branch (6456:11): [True: 2.12k, False: 38.0k]
  ------------------
 6457|  2.12k|        out.clear();
 6458|  2.12k|        return false;
 6459|  2.12k|      }
 6460|  38.0k|      out.append("xn--");
 6461|  38.0k|      if (!ada::idna::utf32_to_punycode(label_view, out)) {
  ------------------
  |  Branch (6461:11): [True: 0, False: 38.0k]
  ------------------
 6462|      0|        out.clear();
 6463|      0|        return false;
 6464|      0|      }
 6465|  38.0k|    }
 6466|  50.0k|    if (!is_last_label) {
  ------------------
  |  Branch (6466:9): [True: 42.6k, False: 7.42k]
  ------------------
 6467|  42.6k|      out.push_back('.');
 6468|  42.6k|    }
 6469|  50.0k|  }
 6470|  7.85k|  return true;
 6471|  11.9k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6473|  20.2k|std::string to_ascii(std::string_view ut8_string) {
 6474|  20.2k|  std::string out;
 6475|  20.2k|  if (!to_ascii(ut8_string, out)) {
  ------------------
  |  Branch (6475:7): [True: 5.24k, False: 15.0k]
  ------------------
 6476|  5.24k|    return {};
 6477|  5.24k|  }
 6478|  15.0k|  return out;
 6479|  20.2k|}
_ZN3ada7unicode14percent_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 7169|    882|std::string percent_decode(const std::string_view input, size_t first_percent) {
 7170|       |  // next line is for safety only, we expect users to avoid calling
 7171|       |  // percent_decode when first_percent is outside the range.
 7172|    882|  if (first_percent == std::string_view::npos) {
  ------------------
  |  Branch (7172:7): [True: 0, False: 882]
  ------------------
 7173|      0|    return std::string(input);
 7174|      0|  }
 7175|       |
 7176|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7177|    882|  const char* const src = input.data();
 7178|    882|  const char* const end = src + input.size();
 7179|       |
 7180|       |  // Decoding never grows the string, so a single pre-sized buffer written via
 7181|       |  // bulk memcpy of the plain runs (then shrunk to the final length) avoids the
 7182|       |  // byte-at-a-time appends of the naive version.
 7183|    882|  std::string out(input.size(), '\0');
 7184|    882|  char* d = out.data();
 7185|    882|  char* const d0 = d;
 7186|       |
 7187|    882|  std::memcpy(d, src, first_percent);
 7188|    882|  d += first_percent;
 7189|       |
 7190|    882|  const char* p = src + first_percent;
 7191|  13.6k|  while (p < end) {
  ------------------
  |  Branch (7191:10): [True: 12.8k, False: 882]
  ------------------
 7192|  12.8k|    if (*p == '%') {
  ------------------
  |  Branch (7192:9): [True: 8.56k, False: 4.23k]
  ------------------
 7193|       |      // Decode runs of valid %XX tightly (common for nested/encoded URLs).
 7194|  10.5k|      while (p + 2 < end && *p == '%') {
  ------------------
  |  Branch (7194:14): [True: 9.92k, False: 645]
  |  Branch (7194:29): [True: 9.42k, False: 501]
  ------------------
 7195|  9.42k|        if (!is_ascii_hex_digit(p[1]) || !is_ascii_hex_digit(p[2])) {
  ------------------
  |  Branch (7195:13): [True: 4.27k, False: 5.14k]
  |  Branch (7195:42): [True: 3.13k, False: 2.00k]
  ------------------
 7196|  7.41k|          break;
 7197|  7.41k|        }
 7198|  2.00k|        *d++ = static_cast<char>(convert_hex_to_binary(p[1]) * 16 +
 7199|  2.00k|                                 convert_hex_to_binary(p[2]));
 7200|  2.00k|        p += 3;
 7201|  2.00k|      }
 7202|  8.56k|      if (p < end && *p == '%') {
  ------------------
  |  Branch (7202:11): [True: 8.37k, False: 186]
  |  Branch (7202:22): [True: 7.81k, False: 558]
  ------------------
 7203|       |        // Not a valid escape (too few chars left or bad hex): copy '%'
 7204|       |        // literally and keep scanning after it.
 7205|  7.81k|        *d++ = *p++;
 7206|  7.81k|      }
 7207|  8.56k|    } else {
 7208|  4.23k|      const char* q = static_cast<const char*>(
 7209|  4.23k|          std::memchr(p, '%', static_cast<size_t>(end - p)));
 7210|  4.23k|      const char* run_end = q ? q : end;
  ------------------
  |  Branch (7210:29): [True: 3.79k, False: 444]
  ------------------
 7211|  4.23k|      const size_t n = static_cast<size_t>(run_end - p);
 7212|  4.23k|      std::memcpy(d, p, n);
 7213|  4.23k|      d += n;
 7214|  4.23k|      p = run_end;
 7215|  4.23k|    }
 7216|  12.8k|  }
 7217|       |
 7218|    882|  out.resize(static_cast<size_t>(d - d0));
 7219|    882|  return out;
 7220|    882|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7305|  21.2k|                           const uint8_t character_set[]) {
 7306|  21.2k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7307|  21.2k|    return character_sets::bit_at(character_set, c);
 7308|  21.2k|  });
 7309|       |  // Optimization: Don't iterate if percent encode is not required
 7310|  21.2k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7310:7): [True: 18.2k, False: 3.03k]
  ------------------
 7311|  18.2k|    return std::string(input);
 7312|  18.2k|  }
 7313|       |
 7314|  3.03k|  std::string result;
 7315|  3.03k|  result.reserve(input.length());  // in the worst case, percent encoding might
 7316|       |                                   // produce 3 characters.
 7317|  3.03k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
 7318|       |
 7319|  49.7k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7319:10): [True: 46.7k, False: 3.03k]
  ------------------
 7320|  46.7k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7320:9): [True: 42.8k, False: 3.88k]
  ------------------
 7321|  42.8k|      result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7322|  42.8k|    } else {
 7323|  3.88k|      result += *pointer;
 7324|  3.88k|    }
 7325|  46.7k|  }
 7326|       |
 7327|  3.03k|  return result;
 7328|  21.2k|}
_ZN3ada7unicode8to_asciiERNSt3__18optionalINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEEENS1_17basic_string_viewIcS5_EEm:
 7366|  20.2k|              size_t first_percent) {
 7367|  20.2k|  std::string percent_decoded_buffer;
 7368|  20.2k|  std::string_view input = plain;
 7369|  20.2k|  if (first_percent != std::string_view::npos) {
  ------------------
  |  Branch (7369:7): [True: 882, False: 19.3k]
  ------------------
 7370|    882|    percent_decoded_buffer = unicode::percent_decode(plain, first_percent);
 7371|    882|    input = percent_decoded_buffer;
 7372|    882|  }
 7373|       |  // input is a non-empty UTF-8 string, must be percent decoded
 7374|  20.2k|  std::string idna_ascii = ada::idna::to_ascii(input);
 7375|  20.2k|  if (idna_ascii.empty() || contains_forbidden_domain_code_point(
  ------------------
  |  Branch (7375:7): [True: 5.31k, False: 14.9k]
  |  Branch (7375:29): [True: 1.13k, False: 13.8k]
  ------------------
 7376|  14.9k|                                idna_ascii.data(), idna_ascii.size())) {
 7377|  6.44k|    return false;
 7378|  6.44k|  }
 7379|  13.8k|  out = std::move(idna_ascii);
 7380|  13.8k|  return true;
 7381|  20.2k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhm:
 7384|    314|                           const uint8_t character_set[], size_t index) {
 7385|    314|  std::string out;
 7386|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7387|    314|  out.append(input.data(), index);
 7388|    314|  auto pointer = input.begin() + index;
 7389|  6.65k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7389:10): [True: 6.33k, False: 314]
  ------------------
 7390|  6.33k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7390:9): [True: 5.92k, False: 418]
  ------------------
 7391|  5.92k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7392|  5.92k|    } else {
 7393|    418|      out += *pointer;
 7394|    418|    }
 7395|  6.33k|  }
 7396|    314|  return out;
 7397|    314|}
_ZN3ada11serializers36find_longest_sequence_of_ipv6_piecesERKNSt3__15arrayItLm8EEERmS6_:
 7461|  1.00k|    size_t& compress_length) noexcept {
 7462|  1.00k|  compress = 0;
 7463|  1.00k|  compress_length = 0;
 7464|  1.00k|  size_t i = 0;
 7465|  4.02k|  while (i < 8) {
  ------------------
  |  Branch (7465:10): [True: 3.02k, False: 1.00k]
  ------------------
 7466|  3.02k|    if (address[i] != 0) {
  ------------------
  |  Branch (7466:9): [True: 1.87k, False: 1.15k]
  ------------------
 7467|  1.87k|      ++i;
 7468|  1.87k|      continue;
 7469|  1.87k|    }
 7470|  1.15k|    size_t next = i + 1;
 7471|  6.16k|    while (next < 8 && address[next] == 0) {
  ------------------
  |  Branch (7471:12): [True: 5.58k, False: 582]
  |  Branch (7471:24): [True: 5.01k, False: 570]
  ------------------
 7472|  5.01k|      ++next;
 7473|  5.01k|    }
 7474|  1.15k|    const size_t count = next - i;
 7475|  1.15k|    if (count > compress_length) {
  ------------------
  |  Branch (7475:9): [True: 1.02k, False: 123]
  ------------------
 7476|  1.02k|      compress_length = count;
 7477|  1.02k|      compress = i;
 7478|  1.02k|    }
 7479|  1.15k|    i = next;
 7480|  1.15k|  }
 7481|  1.00k|}
_ZN3ada11serializers4ipv6ERKNSt3__15arrayItLm8EEE:
 7483|  1.00k|std::string ipv6(const std::array<uint16_t, 8>& address) {
 7484|  1.00k|  size_t compress = 0;
 7485|  1.00k|  size_t compress_length = 0;
 7486|  1.00k|  find_longest_sequence_of_ipv6_pieces(address, compress, compress_length);
 7487|       |
 7488|       |  // Skip compression when the longest zero run is a single piece.
 7489|  1.00k|  if (compress_length <= 1) {
  ------------------
  |  Branch (7489:7): [True: 69, False: 936]
  ------------------
 7490|     69|    compress = compress_length = 8;
 7491|     69|  }
 7492|       |
 7493|       |  // Max: '[' + 8*4 hex + 7 ':' + ']' = 41
 7494|  1.00k|  std::string output(4 * 8 + 7 + 2, '\0');
 7495|  1.00k|  char* point = output.data();
 7496|  1.00k|  *point++ = '[';
 7497|  1.00k|  size_t piece_index = 0;
 7498|  2.64k|  while (true) {
  ------------------
  |  Branch (7498:10): [True: 2.64k, Folded]
  ------------------
 7499|  2.64k|    if (piece_index == compress) {
  ------------------
  |  Branch (7499:9): [True: 936, False: 1.70k]
  ------------------
 7500|    936|      *point++ = ':';
 7501|       |      // If we skip a value initially, we need to write '::'.
 7502|    936|      if (piece_index == 0) {
  ------------------
  |  Branch (7502:11): [True: 564, False: 372]
  ------------------
 7503|    564|        *point++ = ':';
 7504|    564|      }
 7505|    936|      piece_index += compress_length;
 7506|    936|      if (piece_index == 8) {
  ------------------
  |  Branch (7506:11): [True: 531, False: 405]
  ------------------
 7507|    531|        break;
 7508|    531|      }
 7509|    936|    }
 7510|  2.10k|    point = write_hex_u16(point, address[piece_index]);
 7511|  2.10k|    ++piece_index;
 7512|  2.10k|    if (piece_index == 8) {
  ------------------
  |  Branch (7512:9): [True: 474, False: 1.63k]
  ------------------
 7513|    474|      break;
 7514|    474|    }
 7515|  1.63k|    *point++ = ':';
 7516|  1.63k|  }
 7517|  1.00k|  *point++ = ']';
 7518|  1.00k|  output.resize(static_cast<size_t>(point - output.data()));
 7519|  1.00k|  return output;
 7520|  1.00k|}
_ZN3ada11serializers4ipv4Em:
 7522|  3.21k|std::string ipv4(const uint64_t address) {
 7523|  3.21k|  std::string output(15, '\0');
 7524|  3.21k|  char* point = output.data();
 7525|  3.21k|  point = write_u8(point, static_cast<uint8_t>(address >> 24));
 7526|  3.21k|  *point++ = '.';
 7527|  3.21k|  point = write_u8(point, static_cast<uint8_t>(address >> 16));
 7528|  3.21k|  *point++ = '.';
 7529|  3.21k|  point = write_u8(point, static_cast<uint8_t>(address >> 8));
 7530|  3.21k|  *point++ = '.';
 7531|  3.21k|  point = write_u8(point, static_cast<uint8_t>(address));
 7532|  3.21k|  output.resize(static_cast<size_t>(point - output.data()));
 7533|  3.21k|  return output;
 7534|  3.21k|}
_ZN3ada5parseINS_3urlEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 7884|  35.5k|    std::string_view input, const result_type* base_url) {
 7885|  35.5k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 7886|  35.5k|  if (!u.is_valid) {
  ------------------
  |  Branch (7886:7): [True: 19.3k, False: 16.1k]
  ------------------
 7887|  19.3k|    return tl::unexpected(errors::type_error);
 7888|  19.3k|  }
 7889|  16.1k|  return u;
 7890|  35.5k|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 7884|  35.5k|    std::string_view input, const result_type* base_url) {
 7885|  35.5k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 7886|  35.5k|  if (!u.is_valid) {
  ------------------
  |  Branch (7886:7): [True: 19.3k, False: 16.1k]
  ------------------
 7887|  19.3k|    return tl::unexpected(errors::type_error);
 7888|  19.3k|  }
 7889|  16.1k|  return u;
 7890|  35.5k|}
_ZN3ada20get_max_input_lengthEv:
 7556|   117k|uint32_t get_max_input_length() {
 7557|   117k|  return max_input_length_.load(std::memory_order_relaxed);
 7558|   117k|}
_ZN3ada9can_parseENSt3__117basic_string_viewIcNS0_11char_traitsIcEEEEPKS4_:
 7932|  32.3k|bool can_parse(std::string_view input, const std::string_view* base_input) {
 7933|       |  // Must match parse().has_value(), including post-normalization max length.
 7934|       |  // Percent-encoding expands a byte by at most 3x, but IDNA expands more: a
 7935|       |  // 3-byte UTF-8 label such as U+337F becomes the 17-byte "xn--6oqv20b1zgzxr",
 7936|       |  // so a dotted host sustains 4.5x. When the input (plus base, if any) fits in
 7937|       |  // max_length/5, the normalized href cannot exceed max_length, so
 7938|       |  // validation-only parsing (store_values=false) is safe.
 7939|       |
 7940|       |  // Hot path first: absolute special URLs, no base. Avoid loading max_length
 7941|       |  // until we need it (common absolute-fast true/false cases).
 7942|  32.3k|  if (base_input == nullptr) {
  ------------------
  |  Branch (7942:7): [True: 23.2k, False: 9.02k]
  ------------------
 7943|  23.2k|    auto r = try_can_parse_clean_http(input);
 7944|  23.2k|    if (!r.has_value()) {
  ------------------
  |  Branch (7944:9): [True: 22.9k, False: 382]
  ------------------
 7945|  22.9k|      r = try_can_parse_absolute_fast(input);
 7946|  22.9k|    }
 7947|  23.2k|    if (r.has_value()) {
  ------------------
  |  Branch (7947:9): [True: 11.6k, False: 11.6k]
  ------------------
 7948|  11.6k|      if (!*r) {
  ------------------
  |  Branch (7948:11): [True: 10.1k, False: 1.51k]
  ------------------
 7949|  10.1k|        return false;
 7950|  10.1k|      }
 7951|       |      // size <= max/5 => normalized href cannot exceed max (4.5x expansion).
 7952|       |      // Check this first: default max is ~4GB so almost all URLs return true.
 7953|  1.51k|      const uint32_t max_length = ada::get_max_input_length();
 7954|  1.51k|      if (input.size() <= static_cast<size_t>(max_length) / 5) {
  ------------------
  |  Branch (7954:11): [True: 1.51k, False: 0]
  ------------------
 7955|  1.51k|        return true;
 7956|  1.51k|      }
 7957|      0|      if (input.size() > max_length) {
  ------------------
  |  Branch (7957:11): [True: 0, False: 0]
  ------------------
 7958|      0|        return false;
 7959|      0|      }
 7960|      0|      return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 7961|      0|                                                                    nullptr)
 7962|      0|          .is_valid;
 7963|      0|    }
 7964|  23.2k|  }
 7965|       |
 7966|  20.6k|  const uint32_t max_length = ada::get_max_input_length();
 7967|  20.6k|  if (input.size() > max_length) {
  ------------------
  |  Branch (7967:7): [True: 0, False: 20.6k]
  ------------------
 7968|      0|    return false;
 7969|      0|  }
 7970|  20.6k|  if (base_input != nullptr && base_input->size() > max_length) {
  ------------------
  |  Branch (7970:7): [True: 9.02k, False: 11.6k]
  |  Branch (7970:32): [True: 0, False: 9.02k]
  ------------------
 7971|      0|    return false;
 7972|      0|  }
 7973|       |
 7974|       |  // Relative resolution combines base + input; bound the sum so 4.5x expansion
 7975|       |  // of either side cannot push the final href past max_length.
 7976|  20.6k|  const size_t combined =
 7977|  20.6k|      input.size() + (base_input == nullptr ? 0 : base_input->size());
  ------------------
  |  Branch (7977:23): [True: 11.6k, False: 9.02k]
  ------------------
 7978|  20.6k|  const bool size_safe = combined <= static_cast<size_t>(max_length) / 5;
 7979|       |
 7980|  20.6k|  if (size_safe) {
  ------------------
  |  Branch (7980:7): [True: 20.6k, False: 0]
  ------------------
 7981|       |    // Validation-only: no buffer build, host still fully checked.
 7982|  20.6k|    ada::url_aggregator base_agg;
 7983|  20.6k|    ada::url_aggregator* base_ptr = nullptr;
 7984|  20.6k|    if (base_input != nullptr) {
  ------------------
  |  Branch (7984:9): [True: 9.02k, False: 11.6k]
  ------------------
 7985|  9.02k|      base_agg = ada::parser::parse_url_impl<ada::url_aggregator, false>(
 7986|  9.02k|          *base_input, nullptr);
 7987|  9.02k|      if (!base_agg.is_valid) {
  ------------------
  |  Branch (7987:11): [True: 5.77k, False: 3.24k]
  ------------------
 7988|  5.77k|        return false;
 7989|  5.77k|      }
 7990|  3.24k|      base_ptr = &base_agg;
 7991|  3.24k|    }
 7992|  14.8k|    return ada::parser::parse_url_impl<ada::url_aggregator, false>(input,
 7993|  14.8k|                                                                   base_ptr)
 7994|  14.8k|        .is_valid;
 7995|  20.6k|  }
 7996|       |
 7997|       |  // Near the limit: full parse so post-normalization length matches parse().
 7998|      0|  if (base_input == nullptr) {
  ------------------
  |  Branch (7998:7): [True: 0, False: 0]
  ------------------
 7999|      0|    return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8000|      0|                                                                  nullptr)
 8001|      0|        .is_valid;
 8002|      0|  }
 8003|      0|  ada::url_aggregator base_agg =
 8004|      0|      ada::parser::parse_url_impl<ada::url_aggregator, true>(*base_input,
 8005|      0|                                                             nullptr);
 8006|      0|  if (!base_agg.is_valid) {
  ------------------
  |  Branch (8006:7): [True: 0, False: 0]
  ------------------
 8007|      0|    return false;
 8008|      0|  }
 8009|      0|  return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8010|      0|                                                                &base_agg)
 8011|      0|      .is_valid;
 8012|      0|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8859|  93.9k|void trim_c0_whitespace(std::string_view& input) noexcept {
 8860|  96.1k|  while (!input.empty() &&
  ------------------
  |  Branch (8860:10): [True: 59.5k, False: 36.5k]
  ------------------
 8861|  59.5k|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (8861:10): [True: 2.18k, False: 57.4k]
  ------------------
 8862|  2.18k|    input.remove_prefix(1);
 8863|  2.18k|  }
 8864|  96.6k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (8864:10): [True: 60.1k, False: 36.5k]
  |  Branch (8864:28): [True: 2.72k, False: 57.4k]
  ------------------
 8865|  2.72k|    input.remove_suffix(1);
 8866|  2.72k|  }
 8867|  93.9k|}
_ZN3ada3url17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9314|    411|bool url::parse_opaque_host(std::string_view input) {
 9315|    411|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
 9316|    411|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (9316:7): [True: 9, False: 402]
  ------------------
 9317|      9|    return is_valid = false;
 9318|      9|  }
 9319|       |
 9320|       |  // Return the result of running UTF-8 percent-encode on input using the C0
 9321|       |  // control percent-encode set.
 9322|    402|  host = ada::unicode::percent_encode(
 9323|    402|      input, ada::character_sets::C0_CONTROL_PERCENT_ENCODE);
 9324|    402|  return true;
 9325|    411|}
_ZN3ada3url10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9327|  1.31k|bool url::parse_ipv4(std::string_view input) {
 9328|  1.31k|  ada_log("parse_ipv4 ", input, " [", input.size(), " bytes]");
 9329|  1.31k|  if (input.empty()) {
  ------------------
  |  Branch (9329:7): [True: 0, False: 1.31k]
  ------------------
 9330|      0|    return is_valid = false;
 9331|      0|  }
 9332|  1.31k|  std::string_view original_input = input;
 9333|  1.31k|  if (original_input.back() == '.') {
  ------------------
  |  Branch (9333:7): [True: 37, False: 1.27k]
  ------------------
 9334|     37|    original_input.remove_suffix(1);
 9335|     37|  }
 9336|  1.31k|  if (input.back() == '.') {
  ------------------
  |  Branch (9336:7): [True: 37, False: 1.27k]
  ------------------
 9337|     37|    input.remove_suffix(1);
 9338|     37|    if (input.empty()) {
  ------------------
  |  Branch (9338:9): [True: 0, False: 37]
  ------------------
 9339|      0|      return is_valid = false;
 9340|      0|    }
 9341|     37|  }
 9342|       |
 9343|  1.31k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
 9344|  1.31k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (9344:7): [True: 12, False: 1.30k]
  ------------------
 9345|     12|    host = original_input;
 9346|     12|    host_type = IPV4;
 9347|     12|    return true;
 9348|     12|  }
 9349|       |
 9350|  1.30k|  const char* p = input.data();
 9351|  1.30k|  const char* end = p + input.size();
 9352|  1.30k|  uint64_t ipv4 = 0;
 9353|  1.30k|  int digit_count = 0;
 9354|  1.30k|  int pure_decimal_count = 0;
 9355|       |
 9356|  1.77k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (9356:10): [True: 1.77k, False: 7]
  |  Branch (9356:29): [True: 1.77k, False: 0]
  ------------------
 9357|  1.77k|    uint64_t segment = 0;
 9358|  1.77k|    bool pure = false;
 9359|  1.77k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (9359:9): [True: 166, False: 1.60k]
  ------------------
 9360|    166|      return is_valid = false;
 9361|    166|    }
 9362|  1.60k|    if (pure) {
  ------------------
  |  Branch (9362:9): [True: 1.15k, False: 453]
  ------------------
 9363|  1.15k|      ++pure_decimal_count;
 9364|  1.15k|    }
 9365|  1.60k|    if (p >= end) {
  ------------------
  |  Branch (9365:9): [True: 1.09k, False: 508]
  ------------------
 9366|  1.09k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
 9367|  1.09k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (9367:11): [True: 27, False: 1.07k]
  ------------------
 9368|     27|        return is_valid = false;
 9369|     27|      }
 9370|  1.07k|      ipv4 = (ipv4 << shift) | segment;
 9371|  1.07k|      host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (9371:14): [True: 0, False: 1.07k]
  ------------------
 9372|  1.07k|                                       : ada::serializers::ipv4(ipv4);
 9373|  1.07k|      host_type = IPV4;
 9374|  1.07k|      return true;
 9375|  1.09k|    }
 9376|    508|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (9376:9): [True: 29, False: 479]
  |  Branch (9376:26): [True: 0, False: 479]
  ------------------
 9377|     29|      return is_valid = false;
 9378|     29|    }
 9379|    479|    ipv4 = (ipv4 << 8) | segment;
 9380|    479|    ++p;
 9381|    479|  }
 9382|      7|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (9382:7): [True: 0, False: 7]
  |  Branch (9382:27): [True: 7, False: 0]
  ------------------
 9383|      7|    return is_valid = false;
 9384|      7|  }
 9385|      0|  host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (9385:10): [True: 0, False: 0]
  ------------------
 9386|      0|                                   : ada::serializers::ipv4(ipv4);
 9387|      0|  host_type = IPV4;
 9388|      0|  return true;
 9389|      7|}
_ZN3ada3url10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9391|    568|bool url::parse_ipv6(std::string_view input) {
 9392|    568|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
 9393|    568|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (9393:7): [True: 21, False: 547]
  |  Branch (9393:24): [True: 16, False: 531]
  ------------------
 9394|     37|    return is_valid = false;
 9395|     37|  }
 9396|       |#if defined(ADA_AVX512)
 9397|       |  // One masked load classifies colon/dot shape; rejects impossible inputs
 9398|       |  // before the piece parser (free on AVX-512BW+VL builds).
 9399|       |  if (!detail::ipv6_structure_plausible(input.data(), input.size()))
 9400|       |      [[unlikely]] {
 9401|       |    return is_valid = false;
 9402|       |  }
 9403|       |#endif
 9404|       |
 9405|    531|  std::array<uint16_t, 8> address{};
 9406|    531|  const char* pointer = input.data();
 9407|    531|  const char* const end = pointer + input.size();
 9408|    531|  int piece_index = 0;
 9409|    531|  int compress = -1;
 9410|       |
 9411|    531|  if (*pointer == ':') {
  ------------------
  |  Branch (9411:7): [True: 202, False: 329]
  ------------------
 9412|    202|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (9412:9): [True: 3, False: 199]
  |  Branch (9412:30): [True: 10, False: 189]
  ------------------
 9413|     13|      return is_valid = false;
 9414|     13|    }
 9415|    189|    pointer += 2;
 9416|    189|    compress = ++piece_index;
 9417|    189|  }
 9418|       |
 9419|  1.42k|  while (pointer != end) {
  ------------------
  |  Branch (9419:10): [True: 1.06k, False: 352]
  ------------------
 9420|  1.06k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (9420:9): [True: 3, False: 1.06k]
  ------------------
 9421|      3|      return is_valid = false;
 9422|      3|    }
 9423|  1.06k|    if (*pointer == ':') {
  ------------------
  |  Branch (9423:9): [True: 144, False: 921]
  ------------------
 9424|    144|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (9424:11): [True: 3, False: 141]
  ------------------
 9425|      3|        return is_valid = false;
 9426|      3|      }
 9427|    141|      ++pointer;
 9428|    141|      compress = ++piece_index;
 9429|    141|      continue;
 9430|    144|    }
 9431|       |
 9432|    921|    uint16_t value = 0;
 9433|    921|    const int length = detail::parse_hex_piece(pointer, end, value);
 9434|       |
 9435|    921|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (9435:9): [True: 743, False: 178]
  |  Branch (9435:27): [True: 113, False: 630]
  ------------------
 9436|    113|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (9436:11): [True: 3, False: 110]
  ------------------
 9437|      3|        return is_valid = false;
 9438|      3|      }
 9439|    110|      pointer -= length;
 9440|    110|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (9440:11): [True: 3, False: 107]
  ------------------
 9441|      3|        return is_valid = false;
 9442|      3|      }
 9443|       |
 9444|    107|      int numbers_seen = 0;
 9445|    293|      while (pointer != end) {
  ------------------
  |  Branch (9445:14): [True: 263, False: 30]
  ------------------
 9446|    263|        int ipv4_piece = -1;
 9447|    263|        if (numbers_seen > 0) {
  ------------------
  |  Branch (9447:13): [True: 156, False: 107]
  ------------------
 9448|    156|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (9448:15): [True: 135, False: 21]
  |  Branch (9448:34): [True: 130, False: 5]
  ------------------
 9449|    130|            ++pointer;
 9450|    130|          } else {
 9451|     26|            return is_valid = false;
 9452|     26|          }
 9453|    156|        }
 9454|    237|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (9454:13): [True: 24, False: 213]
  |  Branch (9454:31): [True: 7, False: 206]
  |  Branch (9454:49): [True: 8, False: 198]
  ------------------
 9455|     39|          return is_valid = false;
 9456|     39|        }
 9457|    198|        ipv4_piece = *pointer - '0';
 9458|    198|        ++pointer;
 9459|    198|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (9459:13): [True: 180, False: 18]
  |  Branch (9459:31): [True: 78, False: 102]
  |  Branch (9459:50): [True: 73, False: 5]
  ------------------
 9460|     73|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (9460:15): [True: 4, False: 69]
  ------------------
 9461|      4|            return is_valid = false;
 9462|      4|          }
 9463|     69|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
 9464|     69|          ++pointer;
 9465|     69|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (9465:15): [True: 63, False: 6]
  |  Branch (9465:33): [True: 36, False: 27]
  |  Branch (9465:52): [True: 32, False: 4]
  ------------------
 9466|     32|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
 9467|     32|            ++pointer;
 9468|     32|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (9468:17): [True: 8, False: 24]
  ------------------
 9469|      8|              return is_valid = false;
 9470|      8|            }
 9471|     32|          }
 9472|     69|        }
 9473|    186|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
 9474|    186|            address[static_cast<size_t>(piece_index)] * 0x100 +
 9475|    186|            static_cast<uint16_t>(ipv4_piece));
 9476|    186|        ++numbers_seen;
 9477|    186|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (9477:13): [True: 55, False: 131]
  |  Branch (9477:34): [True: 17, False: 114]
  ------------------
 9478|     72|          ++piece_index;
 9479|     72|        }
 9480|    186|      }
 9481|     30|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (9481:11): [True: 18, False: 12]
  ------------------
 9482|     18|        return is_valid = false;
 9483|     18|      }
 9484|     12|      break;
 9485|     30|    }
 9486|       |
 9487|    808|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (9487:9): [True: 40, False: 768]
  ------------------
 9488|     40|      return is_valid = false;
 9489|     40|    }
 9490|       |
 9491|    768|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (9491:9): [True: 590, False: 178]
  |  Branch (9491:27): [True: 586, False: 4]
  ------------------
 9492|    586|      ++pointer;
 9493|    586|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (9493:11): [True: 3, False: 583]
  ------------------
 9494|      3|        return is_valid = false;
 9495|      3|      }
 9496|    586|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (9496:16): [True: 4, False: 178]
  ------------------
 9497|      4|      return is_valid = false;
 9498|      4|    }
 9499|       |
 9500|    761|    address[static_cast<size_t>(piece_index)] = value;
 9501|    761|    ++piece_index;
 9502|    761|  }
 9503|       |
 9504|    364|  if (compress != -1) {
  ------------------
  |  Branch (9504:7): [True: 321, False: 43]
  ------------------
 9505|    321|    const int right = piece_index - compress;
 9506|    321|    if (right > 0) {
  ------------------
  |  Branch (9506:9): [True: 147, False: 174]
  ------------------
 9507|    147|      const size_t dest = static_cast<size_t>(8 - right);
 9508|    147|      const size_t src = static_cast<size_t>(compress);
 9509|    147|      if (dest != src) {
  ------------------
  |  Branch (9509:11): [True: 138, False: 9]
  ------------------
 9510|    388|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (9510:53): [True: 250, False: 138]
  ------------------
 9511|    250|          address[dest + i] = address[src + i];
 9512|    250|          address[src + i] = 0;
 9513|    250|        }
 9514|    138|      }
 9515|    147|    }
 9516|    321|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (9516:14): [True: 29, False: 14]
  ------------------
 9517|     29|    return is_valid = false;
 9518|     29|  }
 9519|       |
 9520|    335|  host = ada::serializers::ipv6(address);
 9521|    335|  ada_log("parse_ipv6 ", *host);
 9522|    335|  host_type = IPV6;
 9523|    335|  return true;
 9524|    364|}
_ZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
10503|  35.5k|                           const result_type* base_url) {
10504|       |  // We can specialize the implementation per type.
10505|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
10506|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
10507|       |  // something else } is free (at runtime). This means that ada::url_aggregator
10508|       |  // and ada::url **do not have to support the exact same API**.
10509|  35.5k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
10510|  35.5k|  constexpr bool result_type_is_ada_url_aggregator =
10511|  35.5k|      std::is_same_v<url_aggregator, result_type>;
10512|  35.5k|  static_assert(result_type_is_ada_url ||
10513|  35.5k|                result_type_is_ada_url_aggregator);  // We don't support
10514|       |                                                     // anything else for now.
10515|       |
10516|  35.5k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
10517|  35.5k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
10518|  35.5k|          ")");
10519|       |
10520|  35.5k|  state state = state::SCHEME_START;
10521|  35.5k|  result_type url{};
10522|       |
10523|  35.5k|  const uint32_t max_input_length = ada::get_max_input_length();
10524|       |
10525|       |  // We refuse to parse URL strings that exceed the maximum input length.
10526|       |  // By default, this is 4GB but can be configured via
10527|       |  // ada::set_max_input_length().
10528|  35.5k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (10528:7): [True: 0, False: 35.5k]
  ------------------
10529|      0|    url.is_valid = false;
10530|      0|  }
10531|       |  // Going forward, user_input.size() is in [0,
10532|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
10533|       |  // base, or the optional_url was invalid, we must return.
10534|  35.5k|  if (base_url != nullptr) {
  ------------------
  |  Branch (10534:7): [True: 3.24k, False: 32.3k]
  ------------------
10535|  3.24k|    url.is_valid &= base_url->is_valid;
10536|  3.24k|  }
10537|  35.5k|  if (!url.is_valid) {
  ------------------
  |  Branch (10537:7): [True: 0, False: 35.5k]
  ------------------
10538|      0|    return url;
10539|      0|  }
10540|       |
10541|       |  // Simple absolute http(s) fast path (before tabs/newline scan).
10542|       |  // Skip digit-led hosts (IPv4) with a cheap peek.
10543|  35.5k|  if constexpr (store_values) {
10544|  35.5k|    if (base_url == nullptr) {
  ------------------
  |  Branch (10544:9): [True: 32.3k, False: 3.24k]
  ------------------
10545|  32.3k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
10546|  32.3k|      const size_t n = user_input.size();
10547|  32.3k|      const bool digit_led_host = (n >= 8 && p[4] == ':' && p[5] == '/' &&
  ------------------
  |  Branch (10547:36): [True: 11.6k, False: 20.6k]
  |  Branch (10547:46): [True: 2.90k, False: 8.77k]
  |  Branch (10547:61): [True: 2.13k, False: 774]
  ------------------
10548|  2.13k|                                   p[6] == '/' && p[7] >= '0' && p[7] <= '9') ||
  ------------------
  |  Branch (10548:36): [True: 2.10k, False: 27]
  |  Branch (10548:51): [True: 1.47k, False: 627]
  |  Branch (10548:66): [True: 522, False: 956]
  ------------------
10549|  31.7k|                                  (n >= 9 && p[5] == ':' && p[6] == '/' &&
  ------------------
  |  Branch (10549:36): [True: 10.1k, False: 21.6k]
  |  Branch (10549:46): [True: 262, False: 9.84k]
  |  Branch (10549:61): [True: 61, False: 201]
  ------------------
10550|     61|                                   p[7] == '/' && p[8] >= '0' && p[8] <= '9');
  ------------------
  |  Branch (10550:36): [True: 47, False: 14]
  |  Branch (10550:51): [True: 26, False: 21]
  |  Branch (10550:66): [True: 4, False: 22]
  ------------------
10551|  32.3k|      if (!digit_led_host && try_parse_simple_absolute(user_input, url)) {
  ------------------
  |  Branch (10551:11): [True: 31.7k, False: 526]
  |  Branch (10551:30): [True: 528, False: 31.2k]
  ------------------
10552|       |        if constexpr (result_type_is_ada_url_aggregator) {
10553|       |          if (url.buffer.size() > max_input_length) [[unlikely]] {
10554|       |            url.is_valid = false;
10555|       |          }
10556|    528|        } else {
10557|    528|          if (url.get_href_size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (10557:15): [True: 0, False: 528]
  ------------------
10558|      0|            url.is_valid = false;
10559|      0|          }
10560|    528|        }
10561|    528|        return url;
10562|    528|      }
10563|  32.3k|    }
10564|  35.5k|  }
10565|       |
10566|  35.0k|  std::string tmp_buffer;
10567|  35.5k|  std::string_view url_data;
10568|  35.5k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (10568:7): [True: 220, False: 35.3k]
  ------------------
10569|    220|    tmp_buffer = user_input;
10570|       |    // Optimization opportunity: Instead of copying and then pruning, we could
10571|       |    // just directly build the string from user_input.
10572|    220|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
10573|    220|    url_data = tmp_buffer;
10574|  35.3k|  } else [[likely]] {
10575|  35.3k|    url_data = user_input;
10576|  35.3k|  }
10577|       |
10578|       |  // Leading and trailing control characters are uncommon and easy to deal with
10579|       |  // (no performance concern).
10580|  35.5k|  helpers::trim_c0_whitespace(url_data);
10581|       |
10582|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
10583|       |    // Most of the time, we just need user_input.size().
10584|       |    // In some instances, we may need a bit more.
10585|       |    ///////////////////////////
10586|       |    // This is *very* important. This line should *not* be removed
10587|       |    // hastily. There are principled reasons why reserve is important
10588|       |    // for performance. If you have a benchmark with small inputs,
10589|       |    // it may not matter, but in other instances, it could.
10590|       |    ////
10591|       |    // This rounds up to the next power of two.
10592|       |    // We know that user_input.size() is in [0,
10593|       |    // std::numeric_limits<uint32_t>::max).
10594|       |    uint32_t reserve_capacity =
10595|       |        (0xFFFFFFFF >>
10596|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
10597|       |        1;
10598|       |    url.reserve(reserve_capacity);
10599|       |  }
10600|       |
10601|       |  // Optimization opportunity. Most websites do not have fragment.
10602|  35.5k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
10603|       |  // We add it last so that an implementation like ada::url_aggregator
10604|       |  // can append it last to its internal buffer, thus improving performance.
10605|       |
10606|       |  // Here url_data no longer has its fragment.
10607|       |  // We are going to access the data from url_data (it is immutable).
10608|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
10609|       |  // The input_position variable should range from 0 to input_size.
10610|       |  // It is illegal to access url_data at input_size.
10611|  35.5k|  size_t input_position = 0;
10612|  35.5k|  const size_t input_size = url_data.size();
10613|       |  // Keep running the following state machine by switching on state.
10614|       |  // If after a run pointer points to the EOF code point, go to the next step.
10615|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
10616|       |  // We never decrement input_position.
10617|   145k|  while (input_position <= input_size) {
  ------------------
  |  Branch (10617:10): [True: 134k, False: 10.5k]
  ------------------
10618|   134k|    ada_log("In parsing at ", input_position, " out of ", input_size,
10619|   134k|            " in state ", ada::to_string(state));
10620|   134k|    switch (state) {
10621|  35.0k|      case state::SCHEME_START: {
  ------------------
  |  Branch (10621:7): [True: 35.0k, False: 99.5k]
  ------------------
10622|  35.0k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
10623|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
10624|       |        // state to scheme state.
10625|  35.0k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10625:13): [True: 19.5k, False: 15.4k]
  ------------------
10626|  19.5k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (10626:13): [True: 18.6k, False: 938]
  ------------------
10627|  18.6k|          state = state::SCHEME;
10628|  18.6k|          input_position++;
10629|  18.6k|        } else {
10630|       |          // Otherwise, if state override is not given, set state to no scheme
10631|       |          // state and decrease pointer by 1.
10632|  16.4k|          state = state::NO_SCHEME;
10633|  16.4k|        }
10634|  35.0k|        break;
10635|      0|      }
10636|  18.6k|      case state::SCHEME: {
  ------------------
  |  Branch (10636:7): [True: 18.6k, False: 115k]
  ------------------
10637|  18.6k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
10638|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
10639|       |        // append c, lowercased, to buffer.
10640|  45.6k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (10640:16): [True: 45.4k, False: 262]
  ------------------
10641|  45.4k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (10641:16): [True: 27.0k, False: 18.3k]
  ------------------
10642|  27.0k|          input_position++;
10643|  27.0k|        }
10644|       |        // Otherwise, if c is U+003A (:), then:
10645|  18.6k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10645:13): [True: 18.3k, False: 262]
  ------------------
10646|  18.3k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (10646:13): [True: 18.0k, False: 254]
  ------------------
10647|  18.0k|          ada_log("SCHEME the scheme should be ",
10648|  18.0k|                  url_data.substr(0, input_position));
10649|  18.0k|          if constexpr (result_type_is_ada_url) {
10650|  18.0k|            if (!url.parse_scheme(url_data.substr(0, input_position))) {
  ------------------
  |  Branch (10650:17): [True: 0, False: 18.0k]
  ------------------
10651|      0|              return url;
10652|      0|            }
10653|       |          } else {
10654|       |            // we pass the colon along instead of painfully adding it back.
10655|       |            if (!url.parse_scheme_with_colon(
10656|       |                    url_data.substr(0, input_position + 1))) {
10657|       |              return url;
10658|       |            }
10659|       |          }
10660|  18.0k|          ada_log("SCHEME the scheme is ", url.get_protocol());
10661|       |
10662|       |          // If url's scheme is "file", then:
10663|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
10664|  18.0k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (10664:15): [True: 2.13k, False: 15.9k]
  ------------------
10665|       |            // Set state to file state.
10666|  2.13k|            state = state::FILE;
10667|  2.13k|          }
10668|       |          // Otherwise, if url is special, base is non-null, and base's scheme
10669|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
10670|       |          // != nullptr is false.
10671|  15.9k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (10671:20): [True: 11.2k, False: 4.67k]
  |  Branch (10671:40): [True: 1.65k, False: 9.61k]
  ------------------
10672|  1.65k|                   base_url->type == url.type) {
  ------------------
  |  Branch (10672:20): [True: 124, False: 1.53k]
  ------------------
10673|       |            // Set state to special relative or authority state.
10674|    124|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
10675|    124|          }
10676|       |          // Otherwise, if url is special, set state to special authority
10677|       |          // slashes state.
10678|  15.8k|          else if (url.is_special()) {
  ------------------
  |  Branch (10678:20): [True: 11.1k, False: 4.67k]
  ------------------
10679|  11.1k|            state = state::SPECIAL_AUTHORITY_SLASHES;
10680|  11.1k|          }
10681|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
10682|       |          // path or authority state and increase pointer by 1.
10683|  4.67k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (10683:20): [True: 2.69k, False: 1.97k]
  ------------------
10684|  2.69k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (10684:20): [True: 2.02k, False: 673]
  ------------------
10685|  2.02k|            state = state::PATH_OR_AUTHORITY;
10686|  2.02k|            input_position++;
10687|  2.02k|          }
10688|       |          // Otherwise, set url's path to the empty string and set state to
10689|       |          // opaque path state.
10690|  2.65k|          else {
10691|  2.65k|            state = state::OPAQUE_PATH;
10692|  2.65k|          }
10693|  18.0k|        }
10694|       |        // Otherwise, if state override is not given, set buffer to the empty
10695|       |        // string, state to no scheme state, and start over (from the first code
10696|       |        // point in input).
10697|    516|        else {
10698|    516|          state = state::NO_SCHEME;
10699|    516|          input_position = 0;
10700|    516|          break;
10701|    516|        }
10702|  18.0k|        input_position++;
10703|  18.0k|        break;
10704|  18.6k|      }
10705|  16.9k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (10705:7): [True: 16.9k, False: 117k]
  ------------------
10706|  16.9k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
10707|       |        // The fragment was pruned from url_data before the state machine ran,
10708|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
10709|       |        // nothing else remains in front of it.
10710|  16.9k|        const bool c_is_hash =
10711|  16.9k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (10711:13): [True: 203, False: 16.7k]
  |  Branch (10711:37): [True: 180, False: 23]
  ------------------
10712|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
10713|       |        // validation error, return failure.
10714|  16.9k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (10714:13): [True: 15.9k, False: 942]
  |  Branch (10714:37): [True: 138, False: 804]
  |  Branch (10714:66): [True: 53, False: 85]
  ------------------
10715|  16.0k|          ada_log("NO_SCHEME validation error");
10716|  16.0k|          url.is_valid = false;
10717|  16.0k|          return url;
10718|  16.0k|        }
10719|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
10720|       |        // set url's scheme to base's scheme, url's path to base's path, url's
10721|       |        // query to base's query, and set state to fragment state.
10722|    889|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (10722:18): [True: 85, False: 804]
  |  Branch (10722:47): [True: 85, False: 0]
  ------------------
10723|     85|          ada_log("NO_SCHEME opaque base with fragment");
10724|     85|          url.copy_scheme(*base_url);
10725|     85|          url.has_opaque_path = base_url->has_opaque_path;
10726|       |
10727|     85|          if constexpr (result_type_is_ada_url) {
10728|     85|            url.path = base_url->path;
10729|     85|            url.query = base_url->query;
10730|       |          } else {
10731|       |            url.update_base_pathname(base_url->get_pathname());
10732|       |            if (base_url->has_search()) {
10733|       |              // get_search() returns "" for an empty query string (URL ends
10734|       |              // with '?'). update_base_search("") would incorrectly clear the
10735|       |              // query, so pass "?" to preserve the empty query distinction.
10736|       |              auto s = base_url->get_search();
10737|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
10738|       |            }
10739|       |          }
10740|     85|          url.update_unencoded_base_hash(*fragment);
10741|     85|          return url;
10742|     85|        }
10743|       |        // Otherwise, if base's scheme is not "file", set state to relative
10744|       |        // state and decrease pointer by 1.
10745|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
10746|    804|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (10746:18): [True: 531, False: 273]
  ------------------
10747|    531|          ada_log("NO_SCHEME non-file relative path");
10748|    531|          state = state::RELATIVE_SCHEME;
10749|    531|        }
10750|       |        // Otherwise, set state to file state and decrease pointer by 1.
10751|    273|        else {
10752|    273|          ada_log("NO_SCHEME file base type");
10753|    273|          state = state::FILE;
10754|    273|        }
10755|    804|        break;
10756|  16.9k|      }
10757|  11.8k|      case state::AUTHORITY: {
  ------------------
  |  Branch (10757:7): [True: 11.8k, False: 122k]
  ------------------
10758|  11.8k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
10759|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
10760|       |        // about AUTHORITY. Of course, we could have @ and still not have to
10761|       |        // worry about AUTHORITY.
10762|       |        // TODO: Instead of just collecting a bool, collect the location of the
10763|       |        // '@' and do something useful with it.
10764|       |        // TODO: We could do various processing early on, using a single pass
10765|       |        // over the string to collect information about it, e.g., telling us
10766|       |        // whether there is a @ and if so, where (or how many).
10767|       |
10768|       |        // Check if url data contains an @.
10769|  11.8k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (10769:13): [True: 10.8k, False: 977]
  ------------------
10770|  10.8k|          state = state::HOST;
10771|  10.8k|          break;
10772|  10.8k|        }
10773|    977|        bool at_sign_seen{false};
10774|    977|        bool password_token_seen{false};
10775|       |        /**
10776|       |         * We expect something of the sort...
10777|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
10778|       |         * --------^
10779|       |         */
10780|  7.71k|        do {
10781|  7.71k|          std::string_view view = url_data.substr(input_position);
10782|       |          // The delimiters are @, /, ? \\.
10783|  7.71k|          size_t location =
10784|  7.71k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (10784:15): [True: 7.16k, False: 552]
  ------------------
10785|  7.71k|                               : helpers::find_authority_delimiter(view);
10786|  7.71k|          std::string_view authority_view = view.substr(0, location);
10787|  7.71k|          size_t end_of_authority = input_position + authority_view.size();
10788|       |          // If c is U+0040 (@), then:
10789|  7.71k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (10789:15): [True: 7.03k, False: 681]
  ------------------
10790|  7.03k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (10790:15): [True: 6.73k, False: 296]
  ------------------
10791|       |            // If atSignSeen is true, then prepend "%40" to buffer.
10792|  6.73k|            if (at_sign_seen) {
  ------------------
  |  Branch (10792:17): [True: 5.84k, False: 896]
  ------------------
10793|  5.84k|              if (password_token_seen) {
  ------------------
  |  Branch (10793:19): [True: 2.26k, False: 3.57k]
  ------------------
10794|  2.26k|                if constexpr (result_type_is_ada_url) {
10795|  2.26k|                  url.password += "%40";
10796|       |                } else {
10797|       |                  url.append_base_password("%40");
10798|       |                }
10799|  3.57k|              } else {
10800|  3.57k|                if constexpr (result_type_is_ada_url) {
10801|  3.57k|                  url.username += "%40";
10802|       |                } else {
10803|       |                  url.append_base_username("%40");
10804|       |                }
10805|  3.57k|              }
10806|  5.84k|            }
10807|       |
10808|  6.73k|            at_sign_seen = true;
10809|       |
10810|  6.73k|            if (!password_token_seen) {
  ------------------
  |  Branch (10810:17): [True: 4.47k, False: 2.26k]
  ------------------
10811|  4.47k|              size_t password_token_location = authority_view.find(':');
10812|  4.47k|              password_token_seen =
10813|  4.47k|                  password_token_location != std::string_view::npos;
10814|       |
10815|  4.47k|              if constexpr (store_values) {
10816|  4.47k|                if (!password_token_seen) {
  ------------------
  |  Branch (10816:21): [True: 4.09k, False: 373]
  ------------------
10817|  4.09k|                  if constexpr (result_type_is_ada_url) {
10818|  4.09k|                    url.username += unicode::percent_encode(
10819|  4.09k|                        authority_view,
10820|  4.09k|                        character_sets::USERINFO_PERCENT_ENCODE);
10821|       |                  } else {
10822|       |                    url.append_base_username(unicode::percent_encode(
10823|       |                        authority_view,
10824|       |                        character_sets::USERINFO_PERCENT_ENCODE));
10825|       |                  }
10826|  4.09k|                } else {
10827|    373|                  if constexpr (result_type_is_ada_url) {
10828|    373|                    url.username += unicode::percent_encode(
10829|    373|                        authority_view.substr(0, password_token_location),
10830|    373|                        character_sets::USERINFO_PERCENT_ENCODE);
10831|    373|                    url.password += unicode::percent_encode(
10832|    373|                        authority_view.substr(password_token_location + 1),
10833|    373|                        character_sets::USERINFO_PERCENT_ENCODE);
10834|       |                  } else {
10835|       |                    url.append_base_username(unicode::percent_encode(
10836|       |                        authority_view.substr(0, password_token_location),
10837|       |                        character_sets::USERINFO_PERCENT_ENCODE));
10838|       |                    url.append_base_password(unicode::percent_encode(
10839|       |                        authority_view.substr(password_token_location + 1),
10840|       |                        character_sets::USERINFO_PERCENT_ENCODE));
10841|       |                  }
10842|    373|                }
10843|  4.47k|              }
10844|  4.47k|            } else if constexpr (store_values) {
10845|  2.26k|              if constexpr (result_type_is_ada_url) {
10846|  2.26k|                url.password += unicode::percent_encode(
10847|  2.26k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
10848|       |              } else {
10849|       |                url.append_base_password(unicode::percent_encode(
10850|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
10851|       |              }
10852|  2.26k|            }
10853|  6.73k|          }
10854|       |          // Otherwise, if one of the following is true:
10855|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
10856|       |          // - url is special and c is U+005C (\)
10857|    977|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (10857:20): [True: 681, False: 296]
  ------------------
10858|    296|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (10858:20): [True: 269, False: 27]
  ------------------
10859|     27|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (10859:20): [True: 20, False: 7]
  ------------------
10860|    977|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (10860:21): [True: 7, False: 0]
  |  Branch (10860:41): [True: 7, False: 0]
  ------------------
10861|       |            // If atSignSeen is true and authority_view is the empty string,
10862|       |            // validation error, return failure.
10863|    977|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (10863:17): [True: 896, False: 81]
  |  Branch (10863:33): [True: 345, False: 551]
  ------------------
10864|    345|              url.is_valid = false;
10865|    345|              return url;
10866|    345|            }
10867|    632|            state = state::HOST;
10868|    632|            break;
10869|    977|          }
10870|  6.73k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (10870:15): [True: 0, False: 6.73k]
  ------------------
10871|      0|            if constexpr (store_values) {
10872|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (10872:19): [True: 0, False: 0]
  ------------------
10873|      0|                url.update_unencoded_base_hash(*fragment);
10874|      0|              }
10875|      0|            }
10876|      0|            return url;
10877|      0|          }
10878|  6.73k|          input_position = end_of_authority + 1;
10879|  6.73k|        } while (true);
  ------------------
  |  Branch (10879:18): [True: 6.73k, Folded]
  ------------------
10880|       |
10881|    632|        break;
10882|    977|      }
10883|    632|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (10883:7): [True: 124, False: 134k]
  ------------------
10884|    124|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
10885|    124|                helpers::substring(url_data, input_position));
10886|       |
10887|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
10888|       |        // then set state to special authority ignore slashes state and increase
10889|       |        // pointer by 1.
10890|    124|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (10890:13): [True: 50, False: 74]
  ------------------
10891|     50|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
10892|     50|          input_position += 2;
10893|     74|        } else {
10894|       |          // Otherwise, validation error, set state to relative state and
10895|       |          // decrease pointer by 1.
10896|     74|          state = state::RELATIVE_SCHEME;
10897|     74|        }
10898|       |
10899|    124|        break;
10900|    977|      }
10901|  2.02k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (10901:7): [True: 2.02k, False: 132k]
  ------------------
10902|  2.02k|        ada_log("PATH_OR_AUTHORITY ",
10903|  2.02k|                helpers::substring(url_data, input_position));
10904|       |
10905|       |        // If c is U+002F (/), then set state to authority state.
10906|  2.02k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10906:13): [True: 1.89k, False: 131]
  ------------------
10907|  1.89k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (10907:13): [True: 639, False: 1.25k]
  ------------------
10908|    639|          state = state::AUTHORITY;
10909|    639|          input_position++;
10910|  1.38k|        } else {
10911|       |          // Otherwise, set state to path state, and decrease pointer by 1.
10912|  1.38k|          state = state::PATH;
10913|  1.38k|        }
10914|       |
10915|  2.02k|        break;
10916|    977|      }
10917|    605|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (10917:7): [True: 605, False: 133k]
  ------------------
10918|    605|        ada_log("RELATIVE_SCHEME ",
10919|    605|                helpers::substring(url_data, input_position));
10920|       |
10921|       |        // Set url's scheme to base's scheme.
10922|    605|        url.copy_scheme(*base_url);
10923|       |
10924|       |        // If c is U+002F (/), then set state to relative slash state.
10925|    605|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10925:13): [True: 439, False: 166]
  ------------------
10926|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
10927|    439|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (10927:13): [True: 70, False: 369]
  ------------------
10928|     70|          ada_log(
10929|     70|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
10930|     70|              "slash state");
10931|     70|          state = state::RELATIVE_SLASH;
10932|    535|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (10932:20): [True: 372, False: 163]
  |  Branch (10932:40): [True: 257, False: 115]
  ------------------
10933|    257|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (10933:20): [True: 5, False: 252]
  ------------------
10934|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
10935|       |          // set state to relative slash state.
10936|      5|          ada_log(
10937|      5|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
10938|      5|              "error, set state to relative slash state");
10939|      5|          state = state::RELATIVE_SLASH;
10940|    530|        } else {
10941|    530|          ada_log("RELATIVE_SCHEME otherwise");
10942|       |          // Set url's username to base's username, url's password to base's
10943|       |          // password, url's host to base's host, url's port to base's port,
10944|       |          // url's path to a clone of base's path, and url's query to base's
10945|       |          // query.
10946|    530|          if constexpr (result_type_is_ada_url) {
10947|    530|            url.username = base_url->username;
10948|    530|            url.password = base_url->password;
10949|    530|            url.host = base_url->host;
10950|    530|            url.port = base_url->port;
10951|       |            // cloning the base path includes cloning the has_opaque_path flag
10952|    530|            url.has_opaque_path = base_url->has_opaque_path;
10953|    530|            url.path = base_url->path;
10954|    530|            url.query = base_url->query;
10955|       |          } else {
10956|       |            url.update_base_authority(base_url->get_href(),
10957|       |                                      base_url->get_components());
10958|       |            url.update_host_to_base_host(base_url->get_hostname());
10959|       |            url.update_base_port(base_url->retrieve_base_port());
10960|       |            // cloning the base path includes cloning the has_opaque_path flag
10961|       |            url.has_opaque_path = base_url->has_opaque_path;
10962|       |            url.update_base_pathname(base_url->get_pathname());
10963|       |            if (base_url->has_search()) {
10964|       |              // get_search() returns "" for an empty query string (URL ends
10965|       |              // with '?'). update_base_search("") would incorrectly clear the
10966|       |              // query, so pass "?" to preserve the empty query distinction.
10967|       |              auto s = base_url->get_search();
10968|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
10969|       |            }
10970|       |          }
10971|       |
10972|    530|          url.has_opaque_path = base_url->has_opaque_path;
10973|       |
10974|       |          // If c is U+003F (?), then set url's query to the empty string, and
10975|       |          // state to query state.
10976|    530|          if ((input_position != input_size) &&
  ------------------
  |  Branch (10976:15): [True: 364, False: 166]
  ------------------
10977|    364|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (10977:15): [True: 29, False: 335]
  ------------------
10978|     29|            state = state::QUERY;
10979|     29|          }
10980|       |          // Otherwise, if c is not the EOF code point:
10981|    501|          else if (input_position != input_size) {
  ------------------
  |  Branch (10981:20): [True: 335, False: 166]
  ------------------
10982|       |            // Set url's query to null.
10983|    335|            url.clear_search();
10984|    335|            if constexpr (result_type_is_ada_url) {
10985|       |              // Shorten url's path.
10986|    335|              helpers::shorten_path(url.path, url.type);
10987|       |            } else {
10988|       |              std::string_view path = url.get_pathname();
10989|       |              if (helpers::shorten_path(path, url.type)) {
10990|       |                url.update_base_pathname(std::move(std::string(path)));
10991|       |              }
10992|       |            }
10993|       |            // Set state to path state and decrease pointer by 1.
10994|    335|            state = state::PATH;
10995|    335|            break;
10996|    335|          }
10997|    530|        }
10998|    270|        input_position++;
10999|    270|        break;
11000|    605|      }
11001|     75|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (11001:7): [True: 75, False: 134k]
  ------------------
11002|     75|        ada_log("RELATIVE_SLASH ",
11003|     75|                helpers::substring(url_data, input_position));
11004|       |
11005|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
11006|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
11007|     75|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (11007:13): [True: 49, False: 26]
  |  Branch (11007:33): [True: 24, False: 25]
  ------------------
11008|     24|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11008:14): [True: 3, False: 21]
  ------------------
11009|     21|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11009:14): [True: 1, False: 20]
  ------------------
11010|       |          // Set state to special authority ignore slashes state.
11011|      4|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
11012|      4|        }
11013|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
11014|     71|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (11014:18): [True: 38, False: 33]
  ------------------
11015|     38|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (11015:18): [True: 7, False: 31]
  ------------------
11016|      7|          state = state::AUTHORITY;
11017|      7|        }
11018|       |        // Otherwise, set
11019|       |        // - url's username to base's username,
11020|       |        // - url's password to base's password,
11021|       |        // - url's host to base's host,
11022|       |        // - url's port to base's port,
11023|       |        // - state to path state, and then, decrease pointer by 1.
11024|     64|        else {
11025|     64|          if constexpr (result_type_is_ada_url) {
11026|     64|            url.username = base_url->username;
11027|     64|            url.password = base_url->password;
11028|     64|            url.host = base_url->host;
11029|     64|            url.port = base_url->port;
11030|       |          } else {
11031|       |            url.update_base_authority(base_url->get_href(),
11032|       |                                      base_url->get_components());
11033|       |            url.update_host_to_base_host(base_url->get_hostname());
11034|       |            url.update_base_port(base_url->retrieve_base_port());
11035|       |          }
11036|     64|          state = state::PATH;
11037|     64|          break;
11038|     64|        }
11039|       |
11040|     11|        input_position++;
11041|     11|        break;
11042|     75|      }
11043|  11.1k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (11043:7): [True: 11.1k, False: 123k]
  ------------------
11044|  11.1k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
11045|  11.1k|                helpers::substring(url_data, input_position));
11046|       |
11047|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
11048|       |        // then set state to special authority ignore slashes state and increase
11049|       |        // pointer by 1.
11050|  11.1k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (11050:13): [True: 3.41k, False: 7.73k]
  ------------------
11051|  3.41k|          input_position += 2;
11052|  3.41k|        }
11053|       |
11054|  11.1k|        [[fallthrough]];
11055|  11.1k|      }
11056|  11.2k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (11056:7): [True: 54, False: 134k]
  ------------------
11057|  11.2k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
11058|  11.2k|                helpers::substring(url_data, input_position));
11059|       |
11060|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
11061|       |        // authority state and decrease pointer by 1.
11062|  12.1k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (11062:16): [True: 12.1k, False: 68]
  ------------------
11063|  12.1k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (11063:17): [True: 588, False: 11.5k]
  ------------------
11064|  11.5k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (11064:17): [True: 398, False: 11.1k]
  ------------------
11065|    986|          input_position++;
11066|    986|        }
11067|  11.2k|        state = state::AUTHORITY;
11068|       |
11069|  11.2k|        break;
11070|  11.1k|      }
11071|    776|      case state::QUERY: {
  ------------------
  |  Branch (11071:7): [True: 776, False: 133k]
  ------------------
11072|    776|        ada_log("QUERY ", helpers::substring(url_data, input_position));
11073|    776|        if constexpr (store_values) {
11074|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
11075|       |          // if url is special; otherwise the query percent-encode set.
11076|    776|          const uint8_t* query_percent_encode_set =
11077|    776|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (11077:15): [True: 376, False: 400]
  ------------------
11078|    776|                               : character_sets::QUERY_PERCENT_ENCODE;
11079|       |
11080|       |          // Percent-encode after encoding, with encoding, buffer, and
11081|       |          // queryPercentEncodeSet, and append the result to url's query.
11082|    776|          url.update_base_search(url_data.substr(input_position),
11083|    776|                                 query_percent_encode_set);
11084|    776|          ada_log("QUERY update_base_search completed ");
11085|    776|          if (fragment.has_value()) {
  ------------------
  |  Branch (11085:15): [True: 124, False: 652]
  ------------------
11086|    124|            url.update_unencoded_base_hash(*fragment);
11087|    124|          }
11088|    776|        }
11089|    776|        return url;
11090|  11.1k|      }
11091|  11.5k|      case state::HOST: {
  ------------------
  |  Branch (11091:7): [True: 11.5k, False: 123k]
  ------------------
11092|  11.5k|        ada_log("HOST ", helpers::substring(url_data, input_position));
11093|       |
11094|  11.5k|        std::string_view host_view = url_data.substr(input_position);
11095|  11.5k|        auto [location, found_colon] =
11096|  11.5k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
11097|  11.5k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (11097:26): [True: 11.5k, False: 0]
  ------------------
11098|  11.5k|                             ? input_position + location
11099|  11.5k|                             : input_size;
11100|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
11101|       |        // Note: the 'found_colon' value is true if and only if a colon was
11102|       |        // encountered while not inside brackets.
11103|  11.5k|        if (found_colon) {
  ------------------
  |  Branch (11103:13): [True: 1.45k, False: 10.0k]
  ------------------
11104|       |          // If buffer is the empty string, validation error, return failure.
11105|       |          // Let host be the result of host parsing buffer with url is not
11106|       |          // special.
11107|  1.45k|          ada_log("HOST parsing ", host_view);
11108|  1.45k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (11108:15): [True: 123, False: 1.32k]
  ------------------
11109|    123|            return url;
11110|    123|          }
11111|  1.32k|          ada_log("HOST parsing results in ", url.get_hostname());
11112|       |          // Set url's host to host, buffer to the empty string, and state to
11113|       |          // port state.
11114|  1.32k|          state = state::PORT;
11115|  1.32k|          input_position++;
11116|  1.32k|        }
11117|       |        // Otherwise, if one of the following is true:
11118|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
11119|       |        // - url is special and c is U+005C (\)
11120|       |        // The get_host_delimiter_location function either brings us to
11121|       |        // the colon outside of the bracket, or to one of those characters.
11122|  10.0k|        else {
11123|       |          // If url is special and host_view is the empty string, validation
11124|       |          // error, return failure.
11125|  10.0k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (11125:15): [True: 147, False: 9.90k]
  |  Branch (11125:36): [True: 73, False: 74]
  ------------------
11126|     73|            url.is_valid = false;
11127|     73|            return url;
11128|     73|          }
11129|  9.98k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
11130|       |          // Let host be the result of host parsing host_view with url is not
11131|       |          // special.
11132|  9.98k|          if (host_view.empty()) {
  ------------------
  |  Branch (11132:15): [True: 74, False: 9.90k]
  ------------------
11133|     74|            url.update_base_hostname("");
11134|  9.90k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (11134:22): [True: 2.58k, False: 7.32k]
  ------------------
11135|  2.58k|            return url;
11136|  2.58k|          }
11137|  7.39k|          ada_log("HOST parsing results in ", url.get_hostname(),
11138|  7.39k|                  " href=", url.get_href());
11139|       |
11140|       |          // Set url's host to host, and state to path start state.
11141|  7.39k|          state = state::PATH_START;
11142|  7.39k|        }
11143|       |
11144|  8.72k|        break;
11145|  11.5k|      }
11146|  8.72k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (11146:7): [True: 2.65k, False: 131k]
  ------------------
11147|  2.65k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
11148|       |        // Opaque path, query, and fragment are structurally always valid:
11149|       |        // the parser would just percent-encode whatever is there. When we
11150|       |        // are not storing values (can_parse), we can return immediately.
11151|       |        // We must set has_opaque_path = true before returning so that when
11152|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
11153|       |        // correctly rejects relative inputs against an opaque-path base
11154|       |        // (e.g. can_parse("", &"W:") must return false).
11155|       |        if constexpr (!store_values) {
11156|       |          url.has_opaque_path = true;
11157|       |          return url;
11158|       |        }
11159|  2.65k|        std::string_view view = url_data.substr(input_position);
11160|       |        // If c is U+003F (?), then set url's query to the empty string and
11161|       |        // state to query state.
11162|  2.65k|        size_t location = view.find('?');
11163|  2.65k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (11163:13): [True: 231, False: 2.41k]
  ------------------
11164|    231|          view.remove_suffix(view.size() - location);
11165|    231|          state = state::QUERY;
11166|    231|          input_position += location + 1;
11167|  2.41k|        } else {
11168|  2.41k|          input_position = input_size + 1;
11169|  2.41k|        }
11170|  2.65k|        url.has_opaque_path = true;
11171|       |
11172|       |        // This is a really unlikely scenario in real world. We should not seek
11173|       |        // to optimize it.
11174|  2.65k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (11174:13): [True: 55, False: 2.59k]
  ------------------
11175|     55|          std::string modified_view =
11176|     55|              std::string(view.substr(0, view.size() - 1)) + "%20";
11177|     55|          url.update_base_pathname(unicode::percent_encode(
11178|     55|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
11179|  2.59k|        } else {
11180|  2.59k|          url.update_base_pathname(unicode::percent_encode(
11181|  2.59k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
11182|  2.59k|        }
11183|  2.65k|        break;
11184|  11.5k|      }
11185|  1.32k|      case state::PORT: {
  ------------------
  |  Branch (11185:7): [True: 1.32k, False: 133k]
  ------------------
11186|  1.32k|        ada_log("PORT ", helpers::substring(url_data, input_position));
11187|  1.32k|        std::string_view port_view = url_data.substr(input_position);
11188|  1.32k|        input_position += url.parse_port(port_view, true);
11189|  1.32k|        if (!url.is_valid) {
  ------------------
  |  Branch (11189:13): [True: 105, False: 1.22k]
  ------------------
11190|    105|          return url;
11191|    105|        }
11192|  1.22k|        state = state::PATH_START;
11193|  1.22k|        [[fallthrough]];
11194|  1.22k|      }
11195|  10.0k|      case state::PATH_START: {
  ------------------
  |  Branch (11195:7): [True: 8.81k, False: 125k]
  ------------------
11196|  10.0k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
11197|       |        // Path, query, and fragment are structurally always valid: the
11198|       |        // parser would just percent-encode whatever is there. When we are
11199|       |        // not storing values (can_parse), we can return immediately since
11200|       |        // no subsequent state can invalidate the URL.
11201|       |        if constexpr (!store_values) {
11202|       |          return url;
11203|       |        }
11204|       |
11205|       |        // If url is special, then:
11206|  10.0k|        if (url.is_special()) {
  ------------------
  |  Branch (11206:13): [True: 9.52k, False: 515]
  ------------------
11207|       |          // Set state to path state.
11208|  9.52k|          state = state::PATH;
11209|       |
11210|       |          // Optimization: Avoiding going into PATH state improves the
11211|       |          // performance of urls ending with /.
11212|  9.52k|          if (input_position == input_size) {
  ------------------
  |  Branch (11212:15): [True: 4.74k, False: 4.77k]
  ------------------
11213|  4.74k|            if constexpr (store_values) {
11214|  4.74k|              url.update_base_pathname("/");
11215|  4.74k|              if (fragment.has_value()) {
  ------------------
  |  Branch (11215:19): [True: 88, False: 4.65k]
  ------------------
11216|     88|                url.update_unencoded_base_hash(*fragment);
11217|     88|              }
11218|  4.74k|            }
11219|  4.74k|            return url;
11220|  4.74k|          }
11221|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
11222|       |          // by 1. We know that (input_position == input_size) is impossible
11223|       |          // here, because of the previous if-check.
11224|  4.77k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (11224:15): [True: 215, False: 4.55k]
  ------------------
11225|    215|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (11225:15): [True: 201, False: 14]
  ------------------
11226|    201|            break;
11227|    201|          }
11228|  4.77k|        }
11229|       |        // Otherwise, if state override is not given and c is U+003F (?),
11230|       |        // set url's query to the empty string and state to query state.
11231|    515|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (11231:18): [True: 151, False: 364]
  ------------------
11232|    151|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (11232:18): [True: 57, False: 94]
  ------------------
11233|     57|          state = state::QUERY;
11234|     57|        }
11235|       |        // Otherwise, if c is not the EOF code point:
11236|    458|        else if (input_position != input_size) {
  ------------------
  |  Branch (11236:18): [True: 94, False: 364]
  ------------------
11237|       |          // Set state to path state.
11238|     94|          state = state::PATH;
11239|       |
11240|       |          // If c is not U+002F (/), then decrease pointer by 1.
11241|     94|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (11241:15): [True: 0, False: 94]
  ------------------
11242|      0|            break;
11243|      0|          }
11244|     94|        }
11245|       |
11246|  5.08k|        input_position++;
11247|  5.08k|        break;
11248|  10.0k|      }
11249|  7.47k|      case state::PATH: {
  ------------------
  |  Branch (11249:7): [True: 7.47k, False: 127k]
  ------------------
11250|  7.47k|        ada_log("PATH ", helpers::substring(url_data, input_position));
11251|       |        // Path, query, and fragment are structurally always valid: the
11252|       |        // parser would just percent-encode whatever is there. When we are
11253|       |        // not storing values (can_parse), we can return immediately since
11254|       |        // no subsequent state can invalidate the URL.
11255|       |        if constexpr (!store_values) {
11256|       |          return url;
11257|       |        }
11258|  7.47k|        std::string_view view = url_data.substr(input_position);
11259|       |
11260|       |        // Most time, we do not need percent encoding.
11261|       |        // Furthermore, we can immediately locate the '?'.
11262|  7.47k|        size_t locofquestionmark = view.find('?');
11263|  7.47k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (11263:13): [True: 455, False: 7.01k]
  ------------------
11264|    455|          state = state::QUERY;
11265|    455|          view.remove_suffix(view.size() - locofquestionmark);
11266|    455|          input_position += locofquestionmark + 1;
11267|  7.01k|        } else {
11268|  7.01k|          input_position = input_size + 1;
11269|  7.01k|        }
11270|  7.47k|        if constexpr (store_values) {
11271|  7.47k|          if constexpr (result_type_is_ada_url) {
11272|  7.47k|            helpers::parse_prepared_path(view, url.type, url.path);
11273|       |          } else {
11274|       |            url.consume_prepared_path(view);
11275|       |            ADA_ASSERT_TRUE(url.validate());
11276|       |          }
11277|  7.47k|        }
11278|  7.47k|        break;
11279|  10.0k|      }
11280|  1.65k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (11280:7): [True: 1.65k, False: 132k]
  ------------------
11281|  1.65k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
11282|       |
11283|       |        // If c is U+002F (/) or U+005C (\), then:
11284|  1.65k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (11284:13): [True: 1.58k, False: 74]
  ------------------
11285|  1.58k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11285:14): [True: 1.52k, False: 59]
  ------------------
11286|  1.53k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11286:14): [True: 6, False: 53]
  ------------------
11287|  1.53k|          ada_log("FILE_SLASH c is U+002F or U+005C");
11288|       |          // Set state to file host state.
11289|  1.53k|          state = state::FILE_HOST;
11290|  1.53k|          input_position++;
11291|  1.53k|        } else {
11292|    127|          ada_log("FILE_SLASH otherwise");
11293|       |          // If base is non-null and base's scheme is "file", then:
11294|       |          // Note: it is unsafe to do base_url->scheme unless you know that
11295|       |          // base_url_has_value() is true.
11296|    127|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (11296:15): [True: 97, False: 30]
  |  Branch (11296:38): [True: 95, False: 2]
  ------------------
11297|       |            // Set url's host to base's host.
11298|     95|            if constexpr (result_type_is_ada_url) {
11299|     95|              url.host = base_url->host;
11300|       |            } else {
11301|       |              url.update_host_to_base_host(base_url->get_host());
11302|       |            }
11303|       |            // If the code point substring from pointer to the end of input does
11304|       |            // not start with a Windows drive letter and base's path[0] is a
11305|       |            // normalized Windows drive letter, then append base's path[0] to
11306|       |            // url's path.
11307|     95|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (11307:17): [True: 95, False: 0]
  ------------------
11308|     95|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (11308:19): [True: 90, False: 5]
  ------------------
11309|     95|                      url_data.substr(input_position))) {
11310|     90|                std::string_view first_base_url_path =
11311|     90|                    base_url->get_pathname().substr(1);
11312|     90|                size_t loc = first_base_url_path.find('/');
11313|     90|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (11313:21): [True: 17, False: 73]
  ------------------
11314|     17|                  helpers::resize(first_base_url_path, loc);
11315|     17|                }
11316|     90|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (11316:21): [True: 2, False: 88]
  ------------------
11317|     90|                        first_base_url_path)) {
11318|      2|                  if constexpr (result_type_is_ada_url) {
11319|      2|                    url.path += '/';
11320|      2|                    url.path += first_base_url_path;
11321|       |                  } else {
11322|       |                    url.append_base_pathname(
11323|       |                        helpers::concat("/", first_base_url_path));
11324|       |                  }
11325|      2|                }
11326|     90|              }
11327|     95|            }
11328|     95|          }
11329|       |
11330|       |          // Set state to path state, and decrease pointer by 1.
11331|    127|          state = state::PATH;
11332|    127|        }
11333|       |
11334|  1.65k|        break;
11335|  10.0k|      }
11336|  1.53k|      case state::FILE_HOST: {
  ------------------
  |  Branch (11336:7): [True: 1.53k, False: 133k]
  ------------------
11337|  1.53k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
11338|  1.53k|        std::string_view view = url_data.substr(input_position);
11339|       |
11340|  1.53k|        size_t location = view.find_first_of("/\\?");
11341|  1.53k|        std::string_view file_host_buffer = view.substr(
11342|  1.53k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (11342:16): [True: 732, False: 800]
  ------------------
11343|       |
11344|  1.53k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (11344:13): [True: 12, False: 1.52k]
  ------------------
11345|     12|          state = state::PATH;
11346|  1.52k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (11346:20): [True: 302, False: 1.21k]
  ------------------
11347|       |          // Set url's host to the empty string.
11348|    302|          if constexpr (result_type_is_ada_url) {
11349|    302|            url.host = "";
11350|       |          } else {
11351|       |            url.update_base_hostname("");
11352|       |          }
11353|       |          // Set state to path start state.
11354|    302|          state = state::PATH_START;
11355|  1.21k|        } else {
11356|  1.21k|          size_t consumed_bytes = file_host_buffer.size();
11357|  1.21k|          input_position += consumed_bytes;
11358|       |          // Let host be the result of host parsing buffer with url is not
11359|       |          // special.
11360|  1.21k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (11360:15): [True: 106, False: 1.11k]
  ------------------
11361|    106|            return url;
11362|    106|          }
11363|       |
11364|  1.11k|          if constexpr (result_type_is_ada_url) {
11365|       |            // If host is "localhost", then set host to the empty string.
11366|  1.11k|            if (url.host.has_value() && url.host.value() == "localhost") {
  ------------------
  |  Branch (11366:17): [True: 1.11k, False: 0]
  |  Branch (11366:41): [True: 6, False: 1.10k]
  ------------------
11367|      6|              url.host = "";
11368|      6|            }
11369|       |          } else {
11370|       |            if (url.get_hostname() == "localhost") {
11371|       |              url.update_base_hostname("");
11372|       |            }
11373|       |          }
11374|       |
11375|       |          // Set buffer to the empty string and state to path start state.
11376|  1.11k|          state = state::PATH_START;
11377|  1.11k|        }
11378|       |
11379|  1.42k|        break;
11380|  1.53k|      }
11381|  2.41k|      case state::FILE: {
  ------------------
  |  Branch (11381:7): [True: 2.41k, False: 132k]
  ------------------
11382|  2.41k|        ada_log("FILE ", helpers::substring(url_data, input_position));
11383|  2.41k|        std::string_view file_view = url_data.substr(input_position);
11384|       |
11385|  2.41k|        url.set_protocol_as_file();
11386|  2.41k|        if constexpr (result_type_is_ada_url) {
11387|       |          // Set url's host to the empty string.
11388|  2.41k|          url.host = "";
11389|       |        } else {
11390|       |          url.update_base_hostname("");
11391|       |        }
11392|       |        // If c is U+002F (/) or U+005C (\), then:
11393|  2.41k|        if (input_position != input_size &&
  ------------------
  |  Branch (11393:13): [True: 2.27k, False: 141]
  ------------------
11394|  2.27k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11394:14): [True: 1.65k, False: 617]
  ------------------
11395|  1.65k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11395:14): [True: 6, False: 611]
  ------------------
11396|  1.65k|          ada_log("FILE c is U+002F or U+005C");
11397|       |          // Set state to file slash state.
11398|  1.65k|          state = state::FILE_SLASH;
11399|  1.65k|        }
11400|       |        // Otherwise, if base is non-null and base's scheme is "file":
11401|    752|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (11401:18): [True: 279, False: 473]
  |  Branch (11401:41): [True: 203, False: 76]
  ------------------
11402|       |          // Set url's host to base's host, url's path to a clone of base's
11403|       |          // path, and url's query to base's query.
11404|    203|          ada_log("FILE base non-null");
11405|    203|          if constexpr (result_type_is_ada_url) {
11406|    203|            url.host = base_url->host;
11407|    203|            url.path = base_url->path;
11408|    203|            url.query = base_url->query;
11409|       |          } else {
11410|       |            url.update_host_to_base_host(base_url->get_hostname());
11411|       |            url.update_base_pathname(base_url->get_pathname());
11412|       |            if (base_url->has_search()) {
11413|       |              // get_search() returns "" for an empty query string (URL ends
11414|       |              // with '?'). update_base_search("") would incorrectly clear the
11415|       |              // query, so pass "?" to preserve the empty query distinction.
11416|       |              auto s = base_url->get_search();
11417|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
11418|       |            }
11419|       |          }
11420|    203|          url.has_opaque_path = base_url->has_opaque_path;
11421|       |
11422|       |          // If c is U+003F (?), then set url's query to the empty string and
11423|       |          // state to query state.
11424|    203|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (11424:15): [True: 137, False: 66]
  |  Branch (11424:47): [True: 4, False: 133]
  ------------------
11425|      4|            state = state::QUERY;
11426|      4|          }
11427|       |          // Otherwise, if c is not the EOF code point:
11428|    199|          else if (input_position != input_size) {
  ------------------
  |  Branch (11428:20): [True: 133, False: 66]
  ------------------
11429|       |            // Set url's query to null.
11430|    133|            url.clear_search();
11431|       |            // If the code point substring from pointer to the end of input does
11432|       |            // not start with a Windows drive letter, then shorten url's path.
11433|    133|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (11433:17): [True: 113, False: 20]
  ------------------
11434|    113|              if constexpr (result_type_is_ada_url) {
11435|    113|                helpers::shorten_path(url.path, url.type);
11436|       |              } else {
11437|       |                std::string_view path = url.get_pathname();
11438|       |                if (helpers::shorten_path(path, url.type)) {
11439|       |                  url.update_base_pathname(std::move(std::string(path)));
11440|       |                }
11441|       |              }
11442|    113|            }
11443|       |            // Otherwise:
11444|     20|            else {
11445|       |              // Set url's path to an empty list.
11446|     20|              url.clear_pathname();
11447|     20|              url.has_opaque_path = true;
11448|     20|            }
11449|       |
11450|       |            // Set state to path state and decrease pointer by 1.
11451|    133|            state = state::PATH;
11452|    133|            break;
11453|    133|          }
11454|    203|        }
11455|       |        // Otherwise, set state to path state, and decrease pointer by 1.
11456|    549|        else {
11457|    549|          ada_log("FILE go to path");
11458|    549|          state = state::PATH;
11459|    549|          break;
11460|    549|        }
11461|       |
11462|  1.72k|        input_position++;
11463|  1.72k|        break;
11464|  2.41k|      }
11465|      0|      default:
  ------------------
  |  Branch (11465:7): [True: 0, False: 134k]
  ------------------
11466|      0|        unreachable();
11467|   134k|    }
11468|   134k|  }
11469|  10.5k|  if constexpr (store_values) {
11470|  10.5k|    if (fragment.has_value()) {
  ------------------
  |  Branch (11470:9): [True: 251, False: 10.3k]
  ------------------
11471|    251|      url.update_unencoded_base_hash(*fragment);
11472|    251|    }
11473|  10.5k|  }
11474|       |  // Check the resulting (normalized) URL size against the maximum input length.
11475|       |  // Normalization (percent-encoding, IDNA, etc.) can expand the URL beyond the
11476|       |  // original input size.
11477|  10.5k|  if constexpr (store_values) {
11478|  10.5k|    if (url.is_valid) {
  ------------------
  |  Branch (11478:9): [True: 10.0k, False: 528]
  ------------------
11479|       |      if constexpr (result_type_is_ada_url_aggregator) {
11480|       |        if (url.buffer.size() > max_input_length) {
11481|       |          url.is_valid = false;
11482|       |        }
11483|  10.0k|      } else {
11484|  10.0k|        if (url.get_href_size() > max_input_length) {
  ------------------
  |  Branch (11484:13): [True: 0, False: 10.0k]
  ------------------
11485|      0|          url.is_valid = false;
11486|      0|        }
11487|  10.0k|      }
11488|  10.0k|    }
11489|  10.5k|  }
11490|  10.5k|  return url;
11491|  35.5k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
10503|  35.5k|                           const result_type* base_url) {
10504|       |  // We can specialize the implementation per type.
10505|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
10506|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
10507|       |  // something else } is free (at runtime). This means that ada::url_aggregator
10508|       |  // and ada::url **do not have to support the exact same API**.
10509|  35.5k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
10510|  35.5k|  constexpr bool result_type_is_ada_url_aggregator =
10511|  35.5k|      std::is_same_v<url_aggregator, result_type>;
10512|  35.5k|  static_assert(result_type_is_ada_url ||
10513|  35.5k|                result_type_is_ada_url_aggregator);  // We don't support
10514|       |                                                     // anything else for now.
10515|       |
10516|  35.5k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
10517|  35.5k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
10518|  35.5k|          ")");
10519|       |
10520|  35.5k|  state state = state::SCHEME_START;
10521|  35.5k|  result_type url{};
10522|       |
10523|  35.5k|  const uint32_t max_input_length = ada::get_max_input_length();
10524|       |
10525|       |  // We refuse to parse URL strings that exceed the maximum input length.
10526|       |  // By default, this is 4GB but can be configured via
10527|       |  // ada::set_max_input_length().
10528|  35.5k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (10528:7): [True: 0, False: 35.5k]
  ------------------
10529|      0|    url.is_valid = false;
10530|      0|  }
10531|       |  // Going forward, user_input.size() is in [0,
10532|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
10533|       |  // base, or the optional_url was invalid, we must return.
10534|  35.5k|  if (base_url != nullptr) {
  ------------------
  |  Branch (10534:7): [True: 3.24k, False: 32.3k]
  ------------------
10535|  3.24k|    url.is_valid &= base_url->is_valid;
10536|  3.24k|  }
10537|  35.5k|  if (!url.is_valid) {
  ------------------
  |  Branch (10537:7): [True: 0, False: 35.5k]
  ------------------
10538|      0|    return url;
10539|      0|  }
10540|       |
10541|       |  // Simple absolute http(s) fast path (before tabs/newline scan).
10542|       |  // Skip digit-led hosts (IPv4) with a cheap peek.
10543|  35.5k|  if constexpr (store_values) {
10544|  35.5k|    if (base_url == nullptr) {
  ------------------
  |  Branch (10544:9): [True: 32.3k, False: 3.24k]
  ------------------
10545|  32.3k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
10546|  32.3k|      const size_t n = user_input.size();
10547|  32.3k|      const bool digit_led_host = (n >= 8 && p[4] == ':' && p[5] == '/' &&
  ------------------
  |  Branch (10547:36): [True: 11.6k, False: 20.6k]
  |  Branch (10547:46): [True: 2.90k, False: 8.77k]
  |  Branch (10547:61): [True: 2.13k, False: 774]
  ------------------
10548|  2.13k|                                   p[6] == '/' && p[7] >= '0' && p[7] <= '9') ||
  ------------------
  |  Branch (10548:36): [True: 2.10k, False: 27]
  |  Branch (10548:51): [True: 1.47k, False: 627]
  |  Branch (10548:66): [True: 522, False: 956]
  ------------------
10549|  31.7k|                                  (n >= 9 && p[5] == ':' && p[6] == '/' &&
  ------------------
  |  Branch (10549:36): [True: 10.1k, False: 21.6k]
  |  Branch (10549:46): [True: 262, False: 9.84k]
  |  Branch (10549:61): [True: 61, False: 201]
  ------------------
10550|     61|                                   p[7] == '/' && p[8] >= '0' && p[8] <= '9');
  ------------------
  |  Branch (10550:36): [True: 47, False: 14]
  |  Branch (10550:51): [True: 26, False: 21]
  |  Branch (10550:66): [True: 4, False: 22]
  ------------------
10551|  32.3k|      if (!digit_led_host && try_parse_simple_absolute(user_input, url)) {
  ------------------
  |  Branch (10551:11): [True: 31.7k, False: 526]
  |  Branch (10551:30): [True: 528, False: 31.2k]
  ------------------
10552|    528|        if constexpr (result_type_is_ada_url_aggregator) {
10553|    528|          if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (10553:15): [True: 0, False: 528]
  ------------------
10554|      0|            url.is_valid = false;
10555|      0|          }
10556|       |        } else {
10557|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
10558|       |            url.is_valid = false;
10559|       |          }
10560|       |        }
10561|    528|        return url;
10562|    528|      }
10563|  32.3k|    }
10564|  35.5k|  }
10565|       |
10566|  35.0k|  std::string tmp_buffer;
10567|  35.5k|  std::string_view url_data;
10568|  35.5k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (10568:7): [True: 220, False: 35.3k]
  ------------------
10569|    220|    tmp_buffer = user_input;
10570|       |    // Optimization opportunity: Instead of copying and then pruning, we could
10571|       |    // just directly build the string from user_input.
10572|    220|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
10573|    220|    url_data = tmp_buffer;
10574|  35.3k|  } else [[likely]] {
10575|  35.3k|    url_data = user_input;
10576|  35.3k|  }
10577|       |
10578|       |  // Leading and trailing control characters are uncommon and easy to deal with
10579|       |  // (no performance concern).
10580|  35.5k|  helpers::trim_c0_whitespace(url_data);
10581|       |
10582|  35.5k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
10583|       |    // Most of the time, we just need user_input.size().
10584|       |    // In some instances, we may need a bit more.
10585|       |    ///////////////////////////
10586|       |    // This is *very* important. This line should *not* be removed
10587|       |    // hastily. There are principled reasons why reserve is important
10588|       |    // for performance. If you have a benchmark with small inputs,
10589|       |    // it may not matter, but in other instances, it could.
10590|       |    ////
10591|       |    // This rounds up to the next power of two.
10592|       |    // We know that user_input.size() is in [0,
10593|       |    // std::numeric_limits<uint32_t>::max).
10594|  35.5k|    uint32_t reserve_capacity =
10595|  35.5k|        (0xFFFFFFFF >>
10596|  35.5k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
10597|  35.5k|        1;
10598|  35.5k|    url.reserve(reserve_capacity);
10599|  35.5k|  }
10600|       |
10601|       |  // Optimization opportunity. Most websites do not have fragment.
10602|  35.5k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
10603|       |  // We add it last so that an implementation like ada::url_aggregator
10604|       |  // can append it last to its internal buffer, thus improving performance.
10605|       |
10606|       |  // Here url_data no longer has its fragment.
10607|       |  // We are going to access the data from url_data (it is immutable).
10608|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
10609|       |  // The input_position variable should range from 0 to input_size.
10610|       |  // It is illegal to access url_data at input_size.
10611|  35.5k|  size_t input_position = 0;
10612|  35.5k|  const size_t input_size = url_data.size();
10613|       |  // Keep running the following state machine by switching on state.
10614|       |  // If after a run pointer points to the EOF code point, go to the next step.
10615|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
10616|       |  // We never decrement input_position.
10617|   145k|  while (input_position <= input_size) {
  ------------------
  |  Branch (10617:10): [True: 134k, False: 10.5k]
  ------------------
10618|   134k|    ada_log("In parsing at ", input_position, " out of ", input_size,
10619|   134k|            " in state ", ada::to_string(state));
10620|   134k|    switch (state) {
10621|  35.0k|      case state::SCHEME_START: {
  ------------------
  |  Branch (10621:7): [True: 35.0k, False: 99.5k]
  ------------------
10622|  35.0k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
10623|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
10624|       |        // state to scheme state.
10625|  35.0k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10625:13): [True: 19.5k, False: 15.4k]
  ------------------
10626|  19.5k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (10626:13): [True: 18.6k, False: 938]
  ------------------
10627|  18.6k|          state = state::SCHEME;
10628|  18.6k|          input_position++;
10629|  18.6k|        } else {
10630|       |          // Otherwise, if state override is not given, set state to no scheme
10631|       |          // state and decrease pointer by 1.
10632|  16.4k|          state = state::NO_SCHEME;
10633|  16.4k|        }
10634|  35.0k|        break;
10635|      0|      }
10636|  18.6k|      case state::SCHEME: {
  ------------------
  |  Branch (10636:7): [True: 18.6k, False: 115k]
  ------------------
10637|  18.6k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
10638|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
10639|       |        // append c, lowercased, to buffer.
10640|  45.6k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (10640:16): [True: 45.4k, False: 262]
  ------------------
10641|  45.4k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (10641:16): [True: 27.0k, False: 18.3k]
  ------------------
10642|  27.0k|          input_position++;
10643|  27.0k|        }
10644|       |        // Otherwise, if c is U+003A (:), then:
10645|  18.6k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10645:13): [True: 18.3k, False: 262]
  ------------------
10646|  18.3k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (10646:13): [True: 18.0k, False: 254]
  ------------------
10647|  18.0k|          ada_log("SCHEME the scheme should be ",
10648|  18.0k|                  url_data.substr(0, input_position));
10649|       |          if constexpr (result_type_is_ada_url) {
10650|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
10651|       |              return url;
10652|       |            }
10653|  18.0k|          } else {
10654|       |            // we pass the colon along instead of painfully adding it back.
10655|  18.0k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (10655:17): [True: 0, False: 18.0k]
  ------------------
10656|  18.0k|                    url_data.substr(0, input_position + 1))) {
10657|      0|              return url;
10658|      0|            }
10659|  18.0k|          }
10660|  18.0k|          ada_log("SCHEME the scheme is ", url.get_protocol());
10661|       |
10662|       |          // If url's scheme is "file", then:
10663|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
10664|  18.0k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (10664:15): [True: 2.13k, False: 15.9k]
  ------------------
10665|       |            // Set state to file state.
10666|  2.13k|            state = state::FILE;
10667|  2.13k|          }
10668|       |          // Otherwise, if url is special, base is non-null, and base's scheme
10669|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
10670|       |          // != nullptr is false.
10671|  15.9k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (10671:20): [True: 11.2k, False: 4.67k]
  |  Branch (10671:40): [True: 1.65k, False: 9.61k]
  ------------------
10672|  1.65k|                   base_url->type == url.type) {
  ------------------
  |  Branch (10672:20): [True: 124, False: 1.53k]
  ------------------
10673|       |            // Set state to special relative or authority state.
10674|    124|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
10675|    124|          }
10676|       |          // Otherwise, if url is special, set state to special authority
10677|       |          // slashes state.
10678|  15.8k|          else if (url.is_special()) {
  ------------------
  |  Branch (10678:20): [True: 11.1k, False: 4.67k]
  ------------------
10679|  11.1k|            state = state::SPECIAL_AUTHORITY_SLASHES;
10680|  11.1k|          }
10681|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
10682|       |          // path or authority state and increase pointer by 1.
10683|  4.67k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (10683:20): [True: 2.69k, False: 1.97k]
  ------------------
10684|  2.69k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (10684:20): [True: 2.02k, False: 673]
  ------------------
10685|  2.02k|            state = state::PATH_OR_AUTHORITY;
10686|  2.02k|            input_position++;
10687|  2.02k|          }
10688|       |          // Otherwise, set url's path to the empty string and set state to
10689|       |          // opaque path state.
10690|  2.65k|          else {
10691|  2.65k|            state = state::OPAQUE_PATH;
10692|  2.65k|          }
10693|  18.0k|        }
10694|       |        // Otherwise, if state override is not given, set buffer to the empty
10695|       |        // string, state to no scheme state, and start over (from the first code
10696|       |        // point in input).
10697|    516|        else {
10698|    516|          state = state::NO_SCHEME;
10699|    516|          input_position = 0;
10700|    516|          break;
10701|    516|        }
10702|  18.0k|        input_position++;
10703|  18.0k|        break;
10704|  18.6k|      }
10705|  16.9k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (10705:7): [True: 16.9k, False: 117k]
  ------------------
10706|  16.9k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
10707|       |        // The fragment was pruned from url_data before the state machine ran,
10708|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
10709|       |        // nothing else remains in front of it.
10710|  16.9k|        const bool c_is_hash =
10711|  16.9k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (10711:13): [True: 203, False: 16.7k]
  |  Branch (10711:37): [True: 180, False: 23]
  ------------------
10712|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
10713|       |        // validation error, return failure.
10714|  16.9k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (10714:13): [True: 15.9k, False: 942]
  |  Branch (10714:37): [True: 138, False: 804]
  |  Branch (10714:66): [True: 53, False: 85]
  ------------------
10715|  16.0k|          ada_log("NO_SCHEME validation error");
10716|  16.0k|          url.is_valid = false;
10717|  16.0k|          return url;
10718|  16.0k|        }
10719|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
10720|       |        // set url's scheme to base's scheme, url's path to base's path, url's
10721|       |        // query to base's query, and set state to fragment state.
10722|    889|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (10722:18): [True: 85, False: 804]
  |  Branch (10722:47): [True: 85, False: 0]
  ------------------
10723|     85|          ada_log("NO_SCHEME opaque base with fragment");
10724|     85|          url.copy_scheme(*base_url);
10725|     85|          url.has_opaque_path = base_url->has_opaque_path;
10726|       |
10727|       |          if constexpr (result_type_is_ada_url) {
10728|       |            url.path = base_url->path;
10729|       |            url.query = base_url->query;
10730|     85|          } else {
10731|     85|            url.update_base_pathname(base_url->get_pathname());
10732|     85|            if (base_url->has_search()) {
  ------------------
  |  Branch (10732:17): [True: 39, False: 46]
  ------------------
10733|       |              // get_search() returns "" for an empty query string (URL ends
10734|       |              // with '?'). update_base_search("") would incorrectly clear the
10735|       |              // query, so pass "?" to preserve the empty query distinction.
10736|     39|              auto s = base_url->get_search();
10737|     39|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (10737:38): [True: 15, False: 24]
  ------------------
10738|     39|            }
10739|     85|          }
10740|     85|          url.update_unencoded_base_hash(*fragment);
10741|     85|          return url;
10742|     85|        }
10743|       |        // Otherwise, if base's scheme is not "file", set state to relative
10744|       |        // state and decrease pointer by 1.
10745|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
10746|    804|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (10746:18): [True: 531, False: 273]
  ------------------
10747|    531|          ada_log("NO_SCHEME non-file relative path");
10748|    531|          state = state::RELATIVE_SCHEME;
10749|    531|        }
10750|       |        // Otherwise, set state to file state and decrease pointer by 1.
10751|    273|        else {
10752|    273|          ada_log("NO_SCHEME file base type");
10753|    273|          state = state::FILE;
10754|    273|        }
10755|    804|        break;
10756|  16.9k|      }
10757|  11.8k|      case state::AUTHORITY: {
  ------------------
  |  Branch (10757:7): [True: 11.8k, False: 122k]
  ------------------
10758|  11.8k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
10759|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
10760|       |        // about AUTHORITY. Of course, we could have @ and still not have to
10761|       |        // worry about AUTHORITY.
10762|       |        // TODO: Instead of just collecting a bool, collect the location of the
10763|       |        // '@' and do something useful with it.
10764|       |        // TODO: We could do various processing early on, using a single pass
10765|       |        // over the string to collect information about it, e.g., telling us
10766|       |        // whether there is a @ and if so, where (or how many).
10767|       |
10768|       |        // Check if url data contains an @.
10769|  11.8k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (10769:13): [True: 10.8k, False: 977]
  ------------------
10770|  10.8k|          state = state::HOST;
10771|  10.8k|          break;
10772|  10.8k|        }
10773|    977|        bool at_sign_seen{false};
10774|    977|        bool password_token_seen{false};
10775|       |        /**
10776|       |         * We expect something of the sort...
10777|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
10778|       |         * --------^
10779|       |         */
10780|  7.71k|        do {
10781|  7.71k|          std::string_view view = url_data.substr(input_position);
10782|       |          // The delimiters are @, /, ? \\.
10783|  7.71k|          size_t location =
10784|  7.71k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (10784:15): [True: 7.16k, False: 552]
  ------------------
10785|  7.71k|                               : helpers::find_authority_delimiter(view);
10786|  7.71k|          std::string_view authority_view = view.substr(0, location);
10787|  7.71k|          size_t end_of_authority = input_position + authority_view.size();
10788|       |          // If c is U+0040 (@), then:
10789|  7.71k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (10789:15): [True: 7.03k, False: 681]
  ------------------
10790|  7.03k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (10790:15): [True: 6.73k, False: 296]
  ------------------
10791|       |            // If atSignSeen is true, then prepend "%40" to buffer.
10792|  6.73k|            if (at_sign_seen) {
  ------------------
  |  Branch (10792:17): [True: 5.84k, False: 896]
  ------------------
10793|  5.84k|              if (password_token_seen) {
  ------------------
  |  Branch (10793:19): [True: 2.26k, False: 3.57k]
  ------------------
10794|       |                if constexpr (result_type_is_ada_url) {
10795|       |                  url.password += "%40";
10796|  2.26k|                } else {
10797|  2.26k|                  url.append_base_password("%40");
10798|  2.26k|                }
10799|  3.57k|              } else {
10800|       |                if constexpr (result_type_is_ada_url) {
10801|       |                  url.username += "%40";
10802|  3.57k|                } else {
10803|  3.57k|                  url.append_base_username("%40");
10804|  3.57k|                }
10805|  3.57k|              }
10806|  5.84k|            }
10807|       |
10808|  6.73k|            at_sign_seen = true;
10809|       |
10810|  6.73k|            if (!password_token_seen) {
  ------------------
  |  Branch (10810:17): [True: 4.47k, False: 2.26k]
  ------------------
10811|  4.47k|              size_t password_token_location = authority_view.find(':');
10812|  4.47k|              password_token_seen =
10813|  4.47k|                  password_token_location != std::string_view::npos;
10814|       |
10815|  4.47k|              if constexpr (store_values) {
10816|  4.47k|                if (!password_token_seen) {
  ------------------
  |  Branch (10816:21): [True: 4.09k, False: 373]
  ------------------
10817|       |                  if constexpr (result_type_is_ada_url) {
10818|       |                    url.username += unicode::percent_encode(
10819|       |                        authority_view,
10820|       |                        character_sets::USERINFO_PERCENT_ENCODE);
10821|  4.09k|                  } else {
10822|  4.09k|                    url.append_base_username(unicode::percent_encode(
10823|  4.09k|                        authority_view,
10824|  4.09k|                        character_sets::USERINFO_PERCENT_ENCODE));
10825|  4.09k|                  }
10826|  4.09k|                } else {
10827|       |                  if constexpr (result_type_is_ada_url) {
10828|       |                    url.username += unicode::percent_encode(
10829|       |                        authority_view.substr(0, password_token_location),
10830|       |                        character_sets::USERINFO_PERCENT_ENCODE);
10831|       |                    url.password += unicode::percent_encode(
10832|       |                        authority_view.substr(password_token_location + 1),
10833|       |                        character_sets::USERINFO_PERCENT_ENCODE);
10834|    373|                  } else {
10835|    373|                    url.append_base_username(unicode::percent_encode(
10836|    373|                        authority_view.substr(0, password_token_location),
10837|    373|                        character_sets::USERINFO_PERCENT_ENCODE));
10838|    373|                    url.append_base_password(unicode::percent_encode(
10839|    373|                        authority_view.substr(password_token_location + 1),
10840|    373|                        character_sets::USERINFO_PERCENT_ENCODE));
10841|    373|                  }
10842|    373|                }
10843|  4.47k|              }
10844|  4.47k|            } else if constexpr (store_values) {
10845|       |              if constexpr (result_type_is_ada_url) {
10846|       |                url.password += unicode::percent_encode(
10847|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
10848|  2.26k|              } else {
10849|  2.26k|                url.append_base_password(unicode::percent_encode(
10850|  2.26k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
10851|  2.26k|              }
10852|  2.26k|            }
10853|  6.73k|          }
10854|       |          // Otherwise, if one of the following is true:
10855|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
10856|       |          // - url is special and c is U+005C (\)
10857|    977|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (10857:20): [True: 681, False: 296]
  ------------------
10858|    296|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (10858:20): [True: 269, False: 27]
  ------------------
10859|     27|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (10859:20): [True: 20, False: 7]
  ------------------
10860|    977|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (10860:21): [True: 7, False: 0]
  |  Branch (10860:41): [True: 7, False: 0]
  ------------------
10861|       |            // If atSignSeen is true and authority_view is the empty string,
10862|       |            // validation error, return failure.
10863|    977|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (10863:17): [True: 896, False: 81]
  |  Branch (10863:33): [True: 345, False: 551]
  ------------------
10864|    345|              url.is_valid = false;
10865|    345|              return url;
10866|    345|            }
10867|    632|            state = state::HOST;
10868|    632|            break;
10869|    977|          }
10870|  6.73k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (10870:15): [True: 0, False: 6.73k]
  ------------------
10871|      0|            if constexpr (store_values) {
10872|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (10872:19): [True: 0, False: 0]
  ------------------
10873|      0|                url.update_unencoded_base_hash(*fragment);
10874|      0|              }
10875|      0|            }
10876|      0|            return url;
10877|      0|          }
10878|  6.73k|          input_position = end_of_authority + 1;
10879|  6.73k|        } while (true);
  ------------------
  |  Branch (10879:18): [True: 6.73k, Folded]
  ------------------
10880|       |
10881|    632|        break;
10882|    977|      }
10883|    632|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (10883:7): [True: 124, False: 134k]
  ------------------
10884|    124|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
10885|    124|                helpers::substring(url_data, input_position));
10886|       |
10887|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
10888|       |        // then set state to special authority ignore slashes state and increase
10889|       |        // pointer by 1.
10890|    124|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (10890:13): [True: 50, False: 74]
  ------------------
10891|     50|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
10892|     50|          input_position += 2;
10893|     74|        } else {
10894|       |          // Otherwise, validation error, set state to relative state and
10895|       |          // decrease pointer by 1.
10896|     74|          state = state::RELATIVE_SCHEME;
10897|     74|        }
10898|       |
10899|    124|        break;
10900|    977|      }
10901|  2.02k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (10901:7): [True: 2.02k, False: 132k]
  ------------------
10902|  2.02k|        ada_log("PATH_OR_AUTHORITY ",
10903|  2.02k|                helpers::substring(url_data, input_position));
10904|       |
10905|       |        // If c is U+002F (/), then set state to authority state.
10906|  2.02k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10906:13): [True: 1.89k, False: 131]
  ------------------
10907|  1.89k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (10907:13): [True: 639, False: 1.25k]
  ------------------
10908|    639|          state = state::AUTHORITY;
10909|    639|          input_position++;
10910|  1.38k|        } else {
10911|       |          // Otherwise, set state to path state, and decrease pointer by 1.
10912|  1.38k|          state = state::PATH;
10913|  1.38k|        }
10914|       |
10915|  2.02k|        break;
10916|    977|      }
10917|    605|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (10917:7): [True: 605, False: 133k]
  ------------------
10918|    605|        ada_log("RELATIVE_SCHEME ",
10919|    605|                helpers::substring(url_data, input_position));
10920|       |
10921|       |        // Set url's scheme to base's scheme.
10922|    605|        url.copy_scheme(*base_url);
10923|       |
10924|       |        // If c is U+002F (/), then set state to relative slash state.
10925|    605|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10925:13): [True: 439, False: 166]
  ------------------
10926|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
10927|    439|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (10927:13): [True: 70, False: 369]
  ------------------
10928|     70|          ada_log(
10929|     70|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
10930|     70|              "slash state");
10931|     70|          state = state::RELATIVE_SLASH;
10932|    535|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (10932:20): [True: 372, False: 163]
  |  Branch (10932:40): [True: 257, False: 115]
  ------------------
10933|    257|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (10933:20): [True: 5, False: 252]
  ------------------
10934|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
10935|       |          // set state to relative slash state.
10936|      5|          ada_log(
10937|      5|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
10938|      5|              "error, set state to relative slash state");
10939|      5|          state = state::RELATIVE_SLASH;
10940|    530|        } else {
10941|    530|          ada_log("RELATIVE_SCHEME otherwise");
10942|       |          // Set url's username to base's username, url's password to base's
10943|       |          // password, url's host to base's host, url's port to base's port,
10944|       |          // url's path to a clone of base's path, and url's query to base's
10945|       |          // query.
10946|       |          if constexpr (result_type_is_ada_url) {
10947|       |            url.username = base_url->username;
10948|       |            url.password = base_url->password;
10949|       |            url.host = base_url->host;
10950|       |            url.port = base_url->port;
10951|       |            // cloning the base path includes cloning the has_opaque_path flag
10952|       |            url.has_opaque_path = base_url->has_opaque_path;
10953|       |            url.path = base_url->path;
10954|       |            url.query = base_url->query;
10955|    530|          } else {
10956|    530|            url.update_base_authority(base_url->get_href(),
10957|    530|                                      base_url->get_components());
10958|    530|            url.update_host_to_base_host(base_url->get_hostname());
10959|    530|            url.update_base_port(base_url->retrieve_base_port());
10960|       |            // cloning the base path includes cloning the has_opaque_path flag
10961|    530|            url.has_opaque_path = base_url->has_opaque_path;
10962|    530|            url.update_base_pathname(base_url->get_pathname());
10963|    530|            if (base_url->has_search()) {
  ------------------
  |  Branch (10963:17): [True: 94, False: 436]
  ------------------
10964|       |              // get_search() returns "" for an empty query string (URL ends
10965|       |              // with '?'). update_base_search("") would incorrectly clear the
10966|       |              // query, so pass "?" to preserve the empty query distinction.
10967|     94|              auto s = base_url->get_search();
10968|     94|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (10968:38): [True: 37, False: 57]
  ------------------
10969|     94|            }
10970|    530|          }
10971|       |
10972|    530|          url.has_opaque_path = base_url->has_opaque_path;
10973|       |
10974|       |          // If c is U+003F (?), then set url's query to the empty string, and
10975|       |          // state to query state.
10976|    530|          if ((input_position != input_size) &&
  ------------------
  |  Branch (10976:15): [True: 364, False: 166]
  ------------------
10977|    364|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (10977:15): [True: 29, False: 335]
  ------------------
10978|     29|            state = state::QUERY;
10979|     29|          }
10980|       |          // Otherwise, if c is not the EOF code point:
10981|    501|          else if (input_position != input_size) {
  ------------------
  |  Branch (10981:20): [True: 335, False: 166]
  ------------------
10982|       |            // Set url's query to null.
10983|    335|            url.clear_search();
10984|       |            if constexpr (result_type_is_ada_url) {
10985|       |              // Shorten url's path.
10986|       |              helpers::shorten_path(url.path, url.type);
10987|    335|            } else {
10988|    335|              std::string_view path = url.get_pathname();
10989|    335|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (10989:19): [True: 331, False: 4]
  ------------------
10990|    331|                url.update_base_pathname(std::move(std::string(path)));
10991|    331|              }
10992|    335|            }
10993|       |            // Set state to path state and decrease pointer by 1.
10994|    335|            state = state::PATH;
10995|    335|            break;
10996|    335|          }
10997|    530|        }
10998|    270|        input_position++;
10999|    270|        break;
11000|    605|      }
11001|     75|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (11001:7): [True: 75, False: 134k]
  ------------------
11002|     75|        ada_log("RELATIVE_SLASH ",
11003|     75|                helpers::substring(url_data, input_position));
11004|       |
11005|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
11006|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
11007|     75|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (11007:13): [True: 49, False: 26]
  |  Branch (11007:33): [True: 24, False: 25]
  ------------------
11008|     24|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11008:14): [True: 3, False: 21]
  ------------------
11009|     21|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11009:14): [True: 1, False: 20]
  ------------------
11010|       |          // Set state to special authority ignore slashes state.
11011|      4|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
11012|      4|        }
11013|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
11014|     71|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (11014:18): [True: 38, False: 33]
  ------------------
11015|     38|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (11015:18): [True: 7, False: 31]
  ------------------
11016|      7|          state = state::AUTHORITY;
11017|      7|        }
11018|       |        // Otherwise, set
11019|       |        // - url's username to base's username,
11020|       |        // - url's password to base's password,
11021|       |        // - url's host to base's host,
11022|       |        // - url's port to base's port,
11023|       |        // - state to path state, and then, decrease pointer by 1.
11024|     64|        else {
11025|       |          if constexpr (result_type_is_ada_url) {
11026|       |            url.username = base_url->username;
11027|       |            url.password = base_url->password;
11028|       |            url.host = base_url->host;
11029|       |            url.port = base_url->port;
11030|     64|          } else {
11031|     64|            url.update_base_authority(base_url->get_href(),
11032|     64|                                      base_url->get_components());
11033|     64|            url.update_host_to_base_host(base_url->get_hostname());
11034|     64|            url.update_base_port(base_url->retrieve_base_port());
11035|     64|          }
11036|     64|          state = state::PATH;
11037|     64|          break;
11038|     64|        }
11039|       |
11040|     11|        input_position++;
11041|     11|        break;
11042|     75|      }
11043|  11.1k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (11043:7): [True: 11.1k, False: 123k]
  ------------------
11044|  11.1k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
11045|  11.1k|                helpers::substring(url_data, input_position));
11046|       |
11047|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
11048|       |        // then set state to special authority ignore slashes state and increase
11049|       |        // pointer by 1.
11050|  11.1k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (11050:13): [True: 3.41k, False: 7.73k]
  ------------------
11051|  3.41k|          input_position += 2;
11052|  3.41k|        }
11053|       |
11054|  11.1k|        [[fallthrough]];
11055|  11.1k|      }
11056|  11.2k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (11056:7): [True: 54, False: 134k]
  ------------------
11057|  11.2k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
11058|  11.2k|                helpers::substring(url_data, input_position));
11059|       |
11060|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
11061|       |        // authority state and decrease pointer by 1.
11062|  12.1k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (11062:16): [True: 12.1k, False: 68]
  ------------------
11063|  12.1k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (11063:17): [True: 588, False: 11.5k]
  ------------------
11064|  11.5k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (11064:17): [True: 398, False: 11.1k]
  ------------------
11065|    986|          input_position++;
11066|    986|        }
11067|  11.2k|        state = state::AUTHORITY;
11068|       |
11069|  11.2k|        break;
11070|  11.1k|      }
11071|    776|      case state::QUERY: {
  ------------------
  |  Branch (11071:7): [True: 776, False: 133k]
  ------------------
11072|    776|        ada_log("QUERY ", helpers::substring(url_data, input_position));
11073|    776|        if constexpr (store_values) {
11074|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
11075|       |          // if url is special; otherwise the query percent-encode set.
11076|    776|          const uint8_t* query_percent_encode_set =
11077|    776|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (11077:15): [True: 376, False: 400]
  ------------------
11078|    776|                               : character_sets::QUERY_PERCENT_ENCODE;
11079|       |
11080|       |          // Percent-encode after encoding, with encoding, buffer, and
11081|       |          // queryPercentEncodeSet, and append the result to url's query.
11082|    776|          url.update_base_search(url_data.substr(input_position),
11083|    776|                                 query_percent_encode_set);
11084|    776|          ada_log("QUERY update_base_search completed ");
11085|    776|          if (fragment.has_value()) {
  ------------------
  |  Branch (11085:15): [True: 124, False: 652]
  ------------------
11086|    124|            url.update_unencoded_base_hash(*fragment);
11087|    124|          }
11088|    776|        }
11089|    776|        return url;
11090|  11.1k|      }
11091|  11.5k|      case state::HOST: {
  ------------------
  |  Branch (11091:7): [True: 11.5k, False: 123k]
  ------------------
11092|  11.5k|        ada_log("HOST ", helpers::substring(url_data, input_position));
11093|       |
11094|  11.5k|        std::string_view host_view = url_data.substr(input_position);
11095|  11.5k|        auto [location, found_colon] =
11096|  11.5k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
11097|  11.5k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (11097:26): [True: 11.5k, False: 0]
  ------------------
11098|  11.5k|                             ? input_position + location
11099|  11.5k|                             : input_size;
11100|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
11101|       |        // Note: the 'found_colon' value is true if and only if a colon was
11102|       |        // encountered while not inside brackets.
11103|  11.5k|        if (found_colon) {
  ------------------
  |  Branch (11103:13): [True: 1.45k, False: 10.0k]
  ------------------
11104|       |          // If buffer is the empty string, validation error, return failure.
11105|       |          // Let host be the result of host parsing buffer with url is not
11106|       |          // special.
11107|  1.45k|          ada_log("HOST parsing ", host_view);
11108|  1.45k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (11108:15): [True: 123, False: 1.32k]
  ------------------
11109|    123|            return url;
11110|    123|          }
11111|  1.32k|          ada_log("HOST parsing results in ", url.get_hostname());
11112|       |          // Set url's host to host, buffer to the empty string, and state to
11113|       |          // port state.
11114|  1.32k|          state = state::PORT;
11115|  1.32k|          input_position++;
11116|  1.32k|        }
11117|       |        // Otherwise, if one of the following is true:
11118|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
11119|       |        // - url is special and c is U+005C (\)
11120|       |        // The get_host_delimiter_location function either brings us to
11121|       |        // the colon outside of the bracket, or to one of those characters.
11122|  10.0k|        else {
11123|       |          // If url is special and host_view is the empty string, validation
11124|       |          // error, return failure.
11125|  10.0k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (11125:15): [True: 147, False: 9.90k]
  |  Branch (11125:36): [True: 73, False: 74]
  ------------------
11126|     73|            url.is_valid = false;
11127|     73|            return url;
11128|     73|          }
11129|  9.98k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
11130|       |          // Let host be the result of host parsing host_view with url is not
11131|       |          // special.
11132|  9.98k|          if (host_view.empty()) {
  ------------------
  |  Branch (11132:15): [True: 74, False: 9.90k]
  ------------------
11133|     74|            url.update_base_hostname("");
11134|  9.90k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (11134:22): [True: 2.58k, False: 7.32k]
  ------------------
11135|  2.58k|            return url;
11136|  2.58k|          }
11137|  7.39k|          ada_log("HOST parsing results in ", url.get_hostname(),
11138|  7.39k|                  " href=", url.get_href());
11139|       |
11140|       |          // Set url's host to host, and state to path start state.
11141|  7.39k|          state = state::PATH_START;
11142|  7.39k|        }
11143|       |
11144|  8.72k|        break;
11145|  11.5k|      }
11146|  8.72k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (11146:7): [True: 2.65k, False: 131k]
  ------------------
11147|  2.65k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
11148|       |        // Opaque path, query, and fragment are structurally always valid:
11149|       |        // the parser would just percent-encode whatever is there. When we
11150|       |        // are not storing values (can_parse), we can return immediately.
11151|       |        // We must set has_opaque_path = true before returning so that when
11152|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
11153|       |        // correctly rejects relative inputs against an opaque-path base
11154|       |        // (e.g. can_parse("", &"W:") must return false).
11155|       |        if constexpr (!store_values) {
11156|       |          url.has_opaque_path = true;
11157|       |          return url;
11158|       |        }
11159|  2.65k|        std::string_view view = url_data.substr(input_position);
11160|       |        // If c is U+003F (?), then set url's query to the empty string and
11161|       |        // state to query state.
11162|  2.65k|        size_t location = view.find('?');
11163|  2.65k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (11163:13): [True: 231, False: 2.41k]
  ------------------
11164|    231|          view.remove_suffix(view.size() - location);
11165|    231|          state = state::QUERY;
11166|    231|          input_position += location + 1;
11167|  2.41k|        } else {
11168|  2.41k|          input_position = input_size + 1;
11169|  2.41k|        }
11170|  2.65k|        url.has_opaque_path = true;
11171|       |
11172|       |        // This is a really unlikely scenario in real world. We should not seek
11173|       |        // to optimize it.
11174|  2.65k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (11174:13): [True: 55, False: 2.59k]
  ------------------
11175|     55|          std::string modified_view =
11176|     55|              std::string(view.substr(0, view.size() - 1)) + "%20";
11177|     55|          url.update_base_pathname(unicode::percent_encode(
11178|     55|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
11179|  2.59k|        } else {
11180|  2.59k|          url.update_base_pathname(unicode::percent_encode(
11181|  2.59k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
11182|  2.59k|        }
11183|  2.65k|        break;
11184|  11.5k|      }
11185|  1.32k|      case state::PORT: {
  ------------------
  |  Branch (11185:7): [True: 1.32k, False: 133k]
  ------------------
11186|  1.32k|        ada_log("PORT ", helpers::substring(url_data, input_position));
11187|  1.32k|        std::string_view port_view = url_data.substr(input_position);
11188|  1.32k|        input_position += url.parse_port(port_view, true);
11189|  1.32k|        if (!url.is_valid) {
  ------------------
  |  Branch (11189:13): [True: 105, False: 1.22k]
  ------------------
11190|    105|          return url;
11191|    105|        }
11192|  1.22k|        state = state::PATH_START;
11193|  1.22k|        [[fallthrough]];
11194|  1.22k|      }
11195|  10.0k|      case state::PATH_START: {
  ------------------
  |  Branch (11195:7): [True: 8.81k, False: 125k]
  ------------------
11196|  10.0k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
11197|       |        // Path, query, and fragment are structurally always valid: the
11198|       |        // parser would just percent-encode whatever is there. When we are
11199|       |        // not storing values (can_parse), we can return immediately since
11200|       |        // no subsequent state can invalidate the URL.
11201|       |        if constexpr (!store_values) {
11202|       |          return url;
11203|       |        }
11204|       |
11205|       |        // If url is special, then:
11206|  10.0k|        if (url.is_special()) {
  ------------------
  |  Branch (11206:13): [True: 9.52k, False: 515]
  ------------------
11207|       |          // Set state to path state.
11208|  9.52k|          state = state::PATH;
11209|       |
11210|       |          // Optimization: Avoiding going into PATH state improves the
11211|       |          // performance of urls ending with /.
11212|  9.52k|          if (input_position == input_size) {
  ------------------
  |  Branch (11212:15): [True: 4.74k, False: 4.77k]
  ------------------
11213|  4.74k|            if constexpr (store_values) {
11214|  4.74k|              url.update_base_pathname("/");
11215|  4.74k|              if (fragment.has_value()) {
  ------------------
  |  Branch (11215:19): [True: 88, False: 4.65k]
  ------------------
11216|     88|                url.update_unencoded_base_hash(*fragment);
11217|     88|              }
11218|  4.74k|            }
11219|  4.74k|            return url;
11220|  4.74k|          }
11221|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
11222|       |          // by 1. We know that (input_position == input_size) is impossible
11223|       |          // here, because of the previous if-check.
11224|  4.77k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (11224:15): [True: 215, False: 4.55k]
  ------------------
11225|    215|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (11225:15): [True: 201, False: 14]
  ------------------
11226|    201|            break;
11227|    201|          }
11228|  4.77k|        }
11229|       |        // Otherwise, if state override is not given and c is U+003F (?),
11230|       |        // set url's query to the empty string and state to query state.
11231|    515|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (11231:18): [True: 151, False: 364]
  ------------------
11232|    151|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (11232:18): [True: 57, False: 94]
  ------------------
11233|     57|          state = state::QUERY;
11234|     57|        }
11235|       |        // Otherwise, if c is not the EOF code point:
11236|    458|        else if (input_position != input_size) {
  ------------------
  |  Branch (11236:18): [True: 94, False: 364]
  ------------------
11237|       |          // Set state to path state.
11238|     94|          state = state::PATH;
11239|       |
11240|       |          // If c is not U+002F (/), then decrease pointer by 1.
11241|     94|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (11241:15): [True: 0, False: 94]
  ------------------
11242|      0|            break;
11243|      0|          }
11244|     94|        }
11245|       |
11246|  5.08k|        input_position++;
11247|  5.08k|        break;
11248|  10.0k|      }
11249|  7.47k|      case state::PATH: {
  ------------------
  |  Branch (11249:7): [True: 7.47k, False: 127k]
  ------------------
11250|  7.47k|        ada_log("PATH ", helpers::substring(url_data, input_position));
11251|       |        // Path, query, and fragment are structurally always valid: the
11252|       |        // parser would just percent-encode whatever is there. When we are
11253|       |        // not storing values (can_parse), we can return immediately since
11254|       |        // no subsequent state can invalidate the URL.
11255|       |        if constexpr (!store_values) {
11256|       |          return url;
11257|       |        }
11258|  7.47k|        std::string_view view = url_data.substr(input_position);
11259|       |
11260|       |        // Most time, we do not need percent encoding.
11261|       |        // Furthermore, we can immediately locate the '?'.
11262|  7.47k|        size_t locofquestionmark = view.find('?');
11263|  7.47k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (11263:13): [True: 455, False: 7.01k]
  ------------------
11264|    455|          state = state::QUERY;
11265|    455|          view.remove_suffix(view.size() - locofquestionmark);
11266|    455|          input_position += locofquestionmark + 1;
11267|  7.01k|        } else {
11268|  7.01k|          input_position = input_size + 1;
11269|  7.01k|        }
11270|  7.47k|        if constexpr (store_values) {
11271|       |          if constexpr (result_type_is_ada_url) {
11272|       |            helpers::parse_prepared_path(view, url.type, url.path);
11273|  7.47k|          } else {
11274|  7.47k|            url.consume_prepared_path(view);
11275|  7.47k|            ADA_ASSERT_TRUE(url.validate());
11276|  7.47k|          }
11277|  7.47k|        }
11278|  7.47k|        break;
11279|  10.0k|      }
11280|  1.65k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (11280:7): [True: 1.65k, False: 132k]
  ------------------
11281|  1.65k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
11282|       |
11283|       |        // If c is U+002F (/) or U+005C (\), then:
11284|  1.65k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (11284:13): [True: 1.58k, False: 74]
  ------------------
11285|  1.58k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11285:14): [True: 1.52k, False: 59]
  ------------------
11286|  1.53k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11286:14): [True: 6, False: 53]
  ------------------
11287|  1.53k|          ada_log("FILE_SLASH c is U+002F or U+005C");
11288|       |          // Set state to file host state.
11289|  1.53k|          state = state::FILE_HOST;
11290|  1.53k|          input_position++;
11291|  1.53k|        } else {
11292|    127|          ada_log("FILE_SLASH otherwise");
11293|       |          // If base is non-null and base's scheme is "file", then:
11294|       |          // Note: it is unsafe to do base_url->scheme unless you know that
11295|       |          // base_url_has_value() is true.
11296|    127|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (11296:15): [True: 97, False: 30]
  |  Branch (11296:38): [True: 95, False: 2]
  ------------------
11297|       |            // Set url's host to base's host.
11298|       |            if constexpr (result_type_is_ada_url) {
11299|       |              url.host = base_url->host;
11300|     95|            } else {
11301|     95|              url.update_host_to_base_host(base_url->get_host());
11302|     95|            }
11303|       |            // If the code point substring from pointer to the end of input does
11304|       |            // not start with a Windows drive letter and base's path[0] is a
11305|       |            // normalized Windows drive letter, then append base's path[0] to
11306|       |            // url's path.
11307|     95|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (11307:17): [True: 95, False: 0]
  ------------------
11308|     95|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (11308:19): [True: 90, False: 5]
  ------------------
11309|     95|                      url_data.substr(input_position))) {
11310|     90|                std::string_view first_base_url_path =
11311|     90|                    base_url->get_pathname().substr(1);
11312|     90|                size_t loc = first_base_url_path.find('/');
11313|     90|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (11313:21): [True: 17, False: 73]
  ------------------
11314|     17|                  helpers::resize(first_base_url_path, loc);
11315|     17|                }
11316|     90|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (11316:21): [True: 2, False: 88]
  ------------------
11317|     90|                        first_base_url_path)) {
11318|       |                  if constexpr (result_type_is_ada_url) {
11319|       |                    url.path += '/';
11320|       |                    url.path += first_base_url_path;
11321|      2|                  } else {
11322|      2|                    url.append_base_pathname(
11323|      2|                        helpers::concat("/", first_base_url_path));
11324|      2|                  }
11325|      2|                }
11326|     90|              }
11327|     95|            }
11328|     95|          }
11329|       |
11330|       |          // Set state to path state, and decrease pointer by 1.
11331|    127|          state = state::PATH;
11332|    127|        }
11333|       |
11334|  1.65k|        break;
11335|  10.0k|      }
11336|  1.53k|      case state::FILE_HOST: {
  ------------------
  |  Branch (11336:7): [True: 1.53k, False: 133k]
  ------------------
11337|  1.53k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
11338|  1.53k|        std::string_view view = url_data.substr(input_position);
11339|       |
11340|  1.53k|        size_t location = view.find_first_of("/\\?");
11341|  1.53k|        std::string_view file_host_buffer = view.substr(
11342|  1.53k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (11342:16): [True: 732, False: 800]
  ------------------
11343|       |
11344|  1.53k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (11344:13): [True: 12, False: 1.52k]
  ------------------
11345|     12|          state = state::PATH;
11346|  1.52k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (11346:20): [True: 302, False: 1.21k]
  ------------------
11347|       |          // Set url's host to the empty string.
11348|       |          if constexpr (result_type_is_ada_url) {
11349|       |            url.host = "";
11350|    302|          } else {
11351|    302|            url.update_base_hostname("");
11352|    302|          }
11353|       |          // Set state to path start state.
11354|    302|          state = state::PATH_START;
11355|  1.21k|        } else {
11356|  1.21k|          size_t consumed_bytes = file_host_buffer.size();
11357|  1.21k|          input_position += consumed_bytes;
11358|       |          // Let host be the result of host parsing buffer with url is not
11359|       |          // special.
11360|  1.21k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (11360:15): [True: 106, False: 1.11k]
  ------------------
11361|    106|            return url;
11362|    106|          }
11363|       |
11364|       |          if constexpr (result_type_is_ada_url) {
11365|       |            // If host is "localhost", then set host to the empty string.
11366|       |            if (url.host.has_value() && url.host.value() == "localhost") {
11367|       |              url.host = "";
11368|       |            }
11369|  1.11k|          } else {
11370|  1.11k|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (11370:17): [True: 6, False: 1.10k]
  ------------------
11371|      6|              url.update_base_hostname("");
11372|      6|            }
11373|  1.11k|          }
11374|       |
11375|       |          // Set buffer to the empty string and state to path start state.
11376|  1.11k|          state = state::PATH_START;
11377|  1.11k|        }
11378|       |
11379|  1.42k|        break;
11380|  1.53k|      }
11381|  2.41k|      case state::FILE: {
  ------------------
  |  Branch (11381:7): [True: 2.41k, False: 132k]
  ------------------
11382|  2.41k|        ada_log("FILE ", helpers::substring(url_data, input_position));
11383|  2.41k|        std::string_view file_view = url_data.substr(input_position);
11384|       |
11385|  2.41k|        url.set_protocol_as_file();
11386|       |        if constexpr (result_type_is_ada_url) {
11387|       |          // Set url's host to the empty string.
11388|       |          url.host = "";
11389|  2.41k|        } else {
11390|  2.41k|          url.update_base_hostname("");
11391|  2.41k|        }
11392|       |        // If c is U+002F (/) or U+005C (\), then:
11393|  2.41k|        if (input_position != input_size &&
  ------------------
  |  Branch (11393:13): [True: 2.27k, False: 141]
  ------------------
11394|  2.27k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11394:14): [True: 1.65k, False: 617]
  ------------------
11395|  1.65k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11395:14): [True: 6, False: 611]
  ------------------
11396|  1.65k|          ada_log("FILE c is U+002F or U+005C");
11397|       |          // Set state to file slash state.
11398|  1.65k|          state = state::FILE_SLASH;
11399|  1.65k|        }
11400|       |        // Otherwise, if base is non-null and base's scheme is "file":
11401|    752|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (11401:18): [True: 279, False: 473]
  |  Branch (11401:41): [True: 203, False: 76]
  ------------------
11402|       |          // Set url's host to base's host, url's path to a clone of base's
11403|       |          // path, and url's query to base's query.
11404|    203|          ada_log("FILE base non-null");
11405|       |          if constexpr (result_type_is_ada_url) {
11406|       |            url.host = base_url->host;
11407|       |            url.path = base_url->path;
11408|       |            url.query = base_url->query;
11409|    203|          } else {
11410|    203|            url.update_host_to_base_host(base_url->get_hostname());
11411|    203|            url.update_base_pathname(base_url->get_pathname());
11412|    203|            if (base_url->has_search()) {
  ------------------
  |  Branch (11412:17): [True: 53, False: 150]
  ------------------
11413|       |              // get_search() returns "" for an empty query string (URL ends
11414|       |              // with '?'). update_base_search("") would incorrectly clear the
11415|       |              // query, so pass "?" to preserve the empty query distinction.
11416|     53|              auto s = base_url->get_search();
11417|     53|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (11417:38): [True: 21, False: 32]
  ------------------
11418|     53|            }
11419|    203|          }
11420|    203|          url.has_opaque_path = base_url->has_opaque_path;
11421|       |
11422|       |          // If c is U+003F (?), then set url's query to the empty string and
11423|       |          // state to query state.
11424|    203|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (11424:15): [True: 137, False: 66]
  |  Branch (11424:47): [True: 4, False: 133]
  ------------------
11425|      4|            state = state::QUERY;
11426|      4|          }
11427|       |          // Otherwise, if c is not the EOF code point:
11428|    199|          else if (input_position != input_size) {
  ------------------
  |  Branch (11428:20): [True: 133, False: 66]
  ------------------
11429|       |            // Set url's query to null.
11430|    133|            url.clear_search();
11431|       |            // If the code point substring from pointer to the end of input does
11432|       |            // not start with a Windows drive letter, then shorten url's path.
11433|    133|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (11433:17): [True: 113, False: 20]
  ------------------
11434|       |              if constexpr (result_type_is_ada_url) {
11435|       |                helpers::shorten_path(url.path, url.type);
11436|    113|              } else {
11437|    113|                std::string_view path = url.get_pathname();
11438|    113|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (11438:21): [True: 110, False: 3]
  ------------------
11439|    110|                  url.update_base_pathname(std::move(std::string(path)));
11440|    110|                }
11441|    113|              }
11442|    113|            }
11443|       |            // Otherwise:
11444|     20|            else {
11445|       |              // Set url's path to an empty list.
11446|     20|              url.clear_pathname();
11447|     20|              url.has_opaque_path = true;
11448|     20|            }
11449|       |
11450|       |            // Set state to path state and decrease pointer by 1.
11451|    133|            state = state::PATH;
11452|    133|            break;
11453|    133|          }
11454|    203|        }
11455|       |        // Otherwise, set state to path state, and decrease pointer by 1.
11456|    549|        else {
11457|    549|          ada_log("FILE go to path");
11458|    549|          state = state::PATH;
11459|    549|          break;
11460|    549|        }
11461|       |
11462|  1.72k|        input_position++;
11463|  1.72k|        break;
11464|  2.41k|      }
11465|      0|      default:
  ------------------
  |  Branch (11465:7): [True: 0, False: 134k]
  ------------------
11466|      0|        unreachable();
11467|   134k|    }
11468|   134k|  }
11469|  10.5k|  if constexpr (store_values) {
11470|  10.5k|    if (fragment.has_value()) {
  ------------------
  |  Branch (11470:9): [True: 251, False: 10.3k]
  ------------------
11471|    251|      url.update_unencoded_base_hash(*fragment);
11472|    251|    }
11473|  10.5k|  }
11474|       |  // Check the resulting (normalized) URL size against the maximum input length.
11475|       |  // Normalization (percent-encoding, IDNA, etc.) can expand the URL beyond the
11476|       |  // original input size.
11477|  10.5k|  if constexpr (store_values) {
11478|  10.5k|    if (url.is_valid) {
  ------------------
  |  Branch (11478:9): [True: 10.0k, False: 528]
  ------------------
11479|  10.0k|      if constexpr (result_type_is_ada_url_aggregator) {
11480|  10.0k|        if (url.buffer.size() > max_input_length) {
  ------------------
  |  Branch (11480:13): [True: 0, False: 10.0k]
  ------------------
11481|      0|          url.is_valid = false;
11482|      0|        }
11483|       |      } else {
11484|       |        if (url.get_href_size() > max_input_length) {
11485|       |          url.is_valid = false;
11486|       |        }
11487|       |      }
11488|  10.0k|    }
11489|  10.5k|  }
11490|  10.5k|  return url;
11491|  35.5k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb0EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
10503|  23.8k|                           const result_type* base_url) {
10504|       |  // We can specialize the implementation per type.
10505|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
10506|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
10507|       |  // something else } is free (at runtime). This means that ada::url_aggregator
10508|       |  // and ada::url **do not have to support the exact same API**.
10509|  23.8k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
10510|  23.8k|  constexpr bool result_type_is_ada_url_aggregator =
10511|  23.8k|      std::is_same_v<url_aggregator, result_type>;
10512|  23.8k|  static_assert(result_type_is_ada_url ||
10513|  23.8k|                result_type_is_ada_url_aggregator);  // We don't support
10514|       |                                                     // anything else for now.
10515|       |
10516|  23.8k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
10517|  23.8k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
10518|  23.8k|          ")");
10519|       |
10520|  23.8k|  state state = state::SCHEME_START;
10521|  23.8k|  result_type url{};
10522|       |
10523|  23.8k|  const uint32_t max_input_length = ada::get_max_input_length();
10524|       |
10525|       |  // We refuse to parse URL strings that exceed the maximum input length.
10526|       |  // By default, this is 4GB but can be configured via
10527|       |  // ada::set_max_input_length().
10528|  23.8k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (10528:7): [True: 0, False: 23.8k]
  ------------------
10529|      0|    url.is_valid = false;
10530|      0|  }
10531|       |  // Going forward, user_input.size() is in [0,
10532|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
10533|       |  // base, or the optional_url was invalid, we must return.
10534|  23.8k|  if (base_url != nullptr) {
  ------------------
  |  Branch (10534:7): [True: 3.24k, False: 20.6k]
  ------------------
10535|  3.24k|    url.is_valid &= base_url->is_valid;
10536|  3.24k|  }
10537|  23.8k|  if (!url.is_valid) {
  ------------------
  |  Branch (10537:7): [True: 0, False: 23.8k]
  ------------------
10538|      0|    return url;
10539|      0|  }
10540|       |
10541|       |  // Simple absolute http(s) fast path (before tabs/newline scan).
10542|       |  // Skip digit-led hosts (IPv4) with a cheap peek.
10543|       |  if constexpr (store_values) {
10544|       |    if (base_url == nullptr) {
10545|       |      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
10546|       |      const size_t n = user_input.size();
10547|       |      const bool digit_led_host = (n >= 8 && p[4] == ':' && p[5] == '/' &&
10548|       |                                   p[6] == '/' && p[7] >= '0' && p[7] <= '9') ||
10549|       |                                  (n >= 9 && p[5] == ':' && p[6] == '/' &&
10550|       |                                   p[7] == '/' && p[8] >= '0' && p[8] <= '9');
10551|       |      if (!digit_led_host && try_parse_simple_absolute(user_input, url)) {
10552|       |        if constexpr (result_type_is_ada_url_aggregator) {
10553|       |          if (url.buffer.size() > max_input_length) [[unlikely]] {
10554|       |            url.is_valid = false;
10555|       |          }
10556|       |        } else {
10557|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
10558|       |            url.is_valid = false;
10559|       |          }
10560|       |        }
10561|       |        return url;
10562|       |      }
10563|       |    }
10564|       |  }
10565|       |
10566|  23.8k|  std::string tmp_buffer;
10567|  23.8k|  std::string_view url_data;
10568|  23.8k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (10568:7): [True: 168, False: 23.7k]
  ------------------
10569|    168|    tmp_buffer = user_input;
10570|       |    // Optimization opportunity: Instead of copying and then pruning, we could
10571|       |    // just directly build the string from user_input.
10572|    168|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
10573|    168|    url_data = tmp_buffer;
10574|  23.7k|  } else [[likely]] {
10575|  23.7k|    url_data = user_input;
10576|  23.7k|  }
10577|       |
10578|       |  // Leading and trailing control characters are uncommon and easy to deal with
10579|       |  // (no performance concern).
10580|  23.8k|  helpers::trim_c0_whitespace(url_data);
10581|       |
10582|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
10583|       |    // Most of the time, we just need user_input.size().
10584|       |    // In some instances, we may need a bit more.
10585|       |    ///////////////////////////
10586|       |    // This is *very* important. This line should *not* be removed
10587|       |    // hastily. There are principled reasons why reserve is important
10588|       |    // for performance. If you have a benchmark with small inputs,
10589|       |    // it may not matter, but in other instances, it could.
10590|       |    ////
10591|       |    // This rounds up to the next power of two.
10592|       |    // We know that user_input.size() is in [0,
10593|       |    // std::numeric_limits<uint32_t>::max).
10594|       |    uint32_t reserve_capacity =
10595|       |        (0xFFFFFFFF >>
10596|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
10597|       |        1;
10598|       |    url.reserve(reserve_capacity);
10599|       |  }
10600|       |
10601|       |  // Optimization opportunity. Most websites do not have fragment.
10602|  23.8k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
10603|       |  // We add it last so that an implementation like ada::url_aggregator
10604|       |  // can append it last to its internal buffer, thus improving performance.
10605|       |
10606|       |  // Here url_data no longer has its fragment.
10607|       |  // We are going to access the data from url_data (it is immutable).
10608|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
10609|       |  // The input_position variable should range from 0 to input_size.
10610|       |  // It is illegal to access url_data at input_size.
10611|  23.8k|  size_t input_position = 0;
10612|  23.8k|  const size_t input_size = url_data.size();
10613|       |  // Keep running the following state machine by switching on state.
10614|       |  // If after a run pointer points to the EOF code point, go to the next step.
10615|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
10616|       |  // We never decrement input_position.
10617|   102k|  while (input_position <= input_size) {
  ------------------
  |  Branch (10617:10): [True: 102k, False: 232]
  ------------------
10618|   102k|    ada_log("In parsing at ", input_position, " out of ", input_size,
10619|   102k|            " in state ", ada::to_string(state));
10620|   102k|    switch (state) {
10621|  23.8k|      case state::SCHEME_START: {
  ------------------
  |  Branch (10621:7): [True: 23.8k, False: 78.5k]
  ------------------
10622|  23.8k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
10623|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
10624|       |        // state to scheme state.
10625|  23.8k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10625:13): [True: 17.8k, False: 6.01k]
  ------------------
10626|  17.8k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (10626:13): [True: 17.4k, False: 463]
  ------------------
10627|  17.4k|          state = state::SCHEME;
10628|  17.4k|          input_position++;
10629|  17.4k|        } else {
10630|       |          // Otherwise, if state override is not given, set state to no scheme
10631|       |          // state and decrease pointer by 1.
10632|  6.47k|          state = state::NO_SCHEME;
10633|  6.47k|        }
10634|  23.8k|        break;
10635|      0|      }
10636|  17.4k|      case state::SCHEME: {
  ------------------
  |  Branch (10636:7): [True: 17.4k, False: 85.0k]
  ------------------
10637|  17.4k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
10638|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
10639|       |        // append c, lowercased, to buffer.
10640|  43.3k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (10640:16): [True: 43.1k, False: 256]
  ------------------
10641|  43.1k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (10641:16): [True: 25.9k, False: 17.1k]
  ------------------
10642|  25.9k|          input_position++;
10643|  25.9k|        }
10644|       |        // Otherwise, if c is U+003A (:), then:
10645|  17.4k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10645:13): [True: 17.1k, False: 256]
  ------------------
10646|  17.1k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (10646:13): [True: 17.0k, False: 117]
  ------------------
10647|  17.0k|          ada_log("SCHEME the scheme should be ",
10648|  17.0k|                  url_data.substr(0, input_position));
10649|       |          if constexpr (result_type_is_ada_url) {
10650|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
10651|       |              return url;
10652|       |            }
10653|  17.0k|          } else {
10654|       |            // we pass the colon along instead of painfully adding it back.
10655|  17.0k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (10655:17): [True: 0, False: 17.0k]
  ------------------
10656|  17.0k|                    url_data.substr(0, input_position + 1))) {
10657|      0|              return url;
10658|      0|            }
10659|  17.0k|          }
10660|  17.0k|          ada_log("SCHEME the scheme is ", url.get_protocol());
10661|       |
10662|       |          // If url's scheme is "file", then:
10663|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
10664|  17.0k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (10664:15): [True: 2.13k, False: 14.9k]
  ------------------
10665|       |            // Set state to file state.
10666|  2.13k|            state = state::FILE;
10667|  2.13k|          }
10668|       |          // Otherwise, if url is special, base is non-null, and base's scheme
10669|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
10670|       |          // != nullptr is false.
10671|  14.9k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (10671:20): [True: 10.2k, False: 4.67k]
  |  Branch (10671:40): [True: 1.65k, False: 8.56k]
  ------------------
10672|  1.65k|                   base_url->type == url.type) {
  ------------------
  |  Branch (10672:20): [True: 124, False: 1.53k]
  ------------------
10673|       |            // Set state to special relative or authority state.
10674|    124|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
10675|    124|          }
10676|       |          // Otherwise, if url is special, set state to special authority
10677|       |          // slashes state.
10678|  14.7k|          else if (url.is_special()) {
  ------------------
  |  Branch (10678:20): [True: 10.1k, False: 4.67k]
  ------------------
10679|  10.1k|            state = state::SPECIAL_AUTHORITY_SLASHES;
10680|  10.1k|          }
10681|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
10682|       |          // path or authority state and increase pointer by 1.
10683|  4.67k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (10683:20): [True: 2.69k, False: 1.97k]
  ------------------
10684|  2.69k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (10684:20): [True: 2.02k, False: 673]
  ------------------
10685|  2.02k|            state = state::PATH_OR_AUTHORITY;
10686|  2.02k|            input_position++;
10687|  2.02k|          }
10688|       |          // Otherwise, set url's path to the empty string and set state to
10689|       |          // opaque path state.
10690|  2.65k|          else {
10691|  2.65k|            state = state::OPAQUE_PATH;
10692|  2.65k|          }
10693|  17.0k|        }
10694|       |        // Otherwise, if state override is not given, set buffer to the empty
10695|       |        // string, state to no scheme state, and start over (from the first code
10696|       |        // point in input).
10697|    373|        else {
10698|    373|          state = state::NO_SCHEME;
10699|    373|          input_position = 0;
10700|    373|          break;
10701|    373|        }
10702|  17.0k|        input_position++;
10703|  17.0k|        break;
10704|  17.4k|      }
10705|  6.84k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (10705:7): [True: 6.84k, False: 95.5k]
  ------------------
10706|  6.84k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
10707|       |        // The fragment was pruned from url_data before the state machine ran,
10708|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
10709|       |        // nothing else remains in front of it.
10710|  6.84k|        const bool c_is_hash =
10711|  6.84k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (10711:13): [True: 102, False: 6.74k]
  |  Branch (10711:37): [True: 91, False: 11]
  ------------------
10712|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
10713|       |        // validation error, return failure.
10714|  6.84k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (10714:13): [True: 5.90k, False: 942]
  |  Branch (10714:37): [True: 138, False: 804]
  |  Branch (10714:66): [True: 53, False: 85]
  ------------------
10715|  5.96k|          ada_log("NO_SCHEME validation error");
10716|  5.96k|          url.is_valid = false;
10717|  5.96k|          return url;
10718|  5.96k|        }
10719|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
10720|       |        // set url's scheme to base's scheme, url's path to base's path, url's
10721|       |        // query to base's query, and set state to fragment state.
10722|    889|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (10722:18): [True: 85, False: 804]
  |  Branch (10722:47): [True: 85, False: 0]
  ------------------
10723|     85|          ada_log("NO_SCHEME opaque base with fragment");
10724|     85|          url.copy_scheme(*base_url);
10725|     85|          url.has_opaque_path = base_url->has_opaque_path;
10726|       |
10727|       |          if constexpr (result_type_is_ada_url) {
10728|       |            url.path = base_url->path;
10729|       |            url.query = base_url->query;
10730|     85|          } else {
10731|     85|            url.update_base_pathname(base_url->get_pathname());
10732|     85|            if (base_url->has_search()) {
  ------------------
  |  Branch (10732:17): [True: 0, False: 85]
  ------------------
10733|       |              // get_search() returns "" for an empty query string (URL ends
10734|       |              // with '?'). update_base_search("") would incorrectly clear the
10735|       |              // query, so pass "?" to preserve the empty query distinction.
10736|      0|              auto s = base_url->get_search();
10737|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (10737:38): [True: 0, False: 0]
  ------------------
10738|      0|            }
10739|     85|          }
10740|     85|          url.update_unencoded_base_hash(*fragment);
10741|     85|          return url;
10742|     85|        }
10743|       |        // Otherwise, if base's scheme is not "file", set state to relative
10744|       |        // state and decrease pointer by 1.
10745|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
10746|    804|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (10746:18): [True: 531, False: 273]
  ------------------
10747|    531|          ada_log("NO_SCHEME non-file relative path");
10748|    531|          state = state::RELATIVE_SCHEME;
10749|    531|        }
10750|       |        // Otherwise, set state to file state and decrease pointer by 1.
10751|    273|        else {
10752|    273|          ada_log("NO_SCHEME file base type");
10753|    273|          state = state::FILE;
10754|    273|        }
10755|    804|        break;
10756|  6.84k|      }
10757|  10.8k|      case state::AUTHORITY: {
  ------------------
  |  Branch (10757:7): [True: 10.8k, False: 91.6k]
  ------------------
10758|  10.8k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
10759|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
10760|       |        // about AUTHORITY. Of course, we could have @ and still not have to
10761|       |        // worry about AUTHORITY.
10762|       |        // TODO: Instead of just collecting a bool, collect the location of the
10763|       |        // '@' and do something useful with it.
10764|       |        // TODO: We could do various processing early on, using a single pass
10765|       |        // over the string to collect information about it, e.g., telling us
10766|       |        // whether there is a @ and if so, where (or how many).
10767|       |
10768|       |        // Check if url data contains an @.
10769|  10.8k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (10769:13): [True: 9.83k, False: 974]
  ------------------
10770|  9.83k|          state = state::HOST;
10771|  9.83k|          break;
10772|  9.83k|        }
10773|    974|        bool at_sign_seen{false};
10774|    974|        bool password_token_seen{false};
10775|       |        /**
10776|       |         * We expect something of the sort...
10777|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
10778|       |         * --------^
10779|       |         */
10780|  7.71k|        do {
10781|  7.71k|          std::string_view view = url_data.substr(input_position);
10782|       |          // The delimiters are @, /, ? \\.
10783|  7.71k|          size_t location =
10784|  7.71k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (10784:15): [True: 7.16k, False: 552]
  ------------------
10785|  7.71k|                               : helpers::find_authority_delimiter(view);
10786|  7.71k|          std::string_view authority_view = view.substr(0, location);
10787|  7.71k|          size_t end_of_authority = input_position + authority_view.size();
10788|       |          // If c is U+0040 (@), then:
10789|  7.71k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (10789:15): [True: 7.03k, False: 681]
  ------------------
10790|  7.03k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (10790:15): [True: 6.73k, False: 293]
  ------------------
10791|       |            // If atSignSeen is true, then prepend "%40" to buffer.
10792|  6.73k|            if (at_sign_seen) {
  ------------------
  |  Branch (10792:17): [True: 5.84k, False: 896]
  ------------------
10793|  5.84k|              if (password_token_seen) {
  ------------------
  |  Branch (10793:19): [True: 2.26k, False: 3.57k]
  ------------------
10794|       |                if constexpr (result_type_is_ada_url) {
10795|       |                  url.password += "%40";
10796|  2.26k|                } else {
10797|  2.26k|                  url.append_base_password("%40");
10798|  2.26k|                }
10799|  3.57k|              } else {
10800|       |                if constexpr (result_type_is_ada_url) {
10801|       |                  url.username += "%40";
10802|  3.57k|                } else {
10803|  3.57k|                  url.append_base_username("%40");
10804|  3.57k|                }
10805|  3.57k|              }
10806|  5.84k|            }
10807|       |
10808|  6.73k|            at_sign_seen = true;
10809|       |
10810|  6.73k|            if (!password_token_seen) {
  ------------------
  |  Branch (10810:17): [True: 4.47k, False: 2.26k]
  ------------------
10811|  4.47k|              size_t password_token_location = authority_view.find(':');
10812|  4.47k|              password_token_seen =
10813|  4.47k|                  password_token_location != std::string_view::npos;
10814|       |
10815|       |              if constexpr (store_values) {
10816|       |                if (!password_token_seen) {
10817|       |                  if constexpr (result_type_is_ada_url) {
10818|       |                    url.username += unicode::percent_encode(
10819|       |                        authority_view,
10820|       |                        character_sets::USERINFO_PERCENT_ENCODE);
10821|       |                  } else {
10822|       |                    url.append_base_username(unicode::percent_encode(
10823|       |                        authority_view,
10824|       |                        character_sets::USERINFO_PERCENT_ENCODE));
10825|       |                  }
10826|       |                } else {
10827|       |                  if constexpr (result_type_is_ada_url) {
10828|       |                    url.username += unicode::percent_encode(
10829|       |                        authority_view.substr(0, password_token_location),
10830|       |                        character_sets::USERINFO_PERCENT_ENCODE);
10831|       |                    url.password += unicode::percent_encode(
10832|       |                        authority_view.substr(password_token_location + 1),
10833|       |                        character_sets::USERINFO_PERCENT_ENCODE);
10834|       |                  } else {
10835|       |                    url.append_base_username(unicode::percent_encode(
10836|       |                        authority_view.substr(0, password_token_location),
10837|       |                        character_sets::USERINFO_PERCENT_ENCODE));
10838|       |                    url.append_base_password(unicode::percent_encode(
10839|       |                        authority_view.substr(password_token_location + 1),
10840|       |                        character_sets::USERINFO_PERCENT_ENCODE));
10841|       |                  }
10842|       |                }
10843|       |              }
10844|  4.47k|            } else if constexpr (store_values) {
10845|  2.26k|              if constexpr (result_type_is_ada_url) {
10846|  2.26k|                url.password += unicode::percent_encode(
10847|  2.26k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
10848|  2.26k|              } else {
10849|  2.26k|                url.append_base_password(unicode::percent_encode(
10850|  2.26k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
10851|  2.26k|              }
10852|  2.26k|            }
10853|  6.73k|          }
10854|       |          // Otherwise, if one of the following is true:
10855|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
10856|       |          // - url is special and c is U+005C (\)
10857|    974|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (10857:20): [True: 681, False: 293]
  ------------------
10858|    293|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (10858:20): [True: 266, False: 27]
  ------------------
10859|     27|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (10859:20): [True: 20, False: 7]
  ------------------
10860|    974|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (10860:21): [True: 7, False: 0]
  |  Branch (10860:41): [True: 7, False: 0]
  ------------------
10861|       |            // If atSignSeen is true and authority_view is the empty string,
10862|       |            // validation error, return failure.
10863|    974|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (10863:17): [True: 896, False: 78]
  |  Branch (10863:33): [True: 345, False: 551]
  ------------------
10864|    345|              url.is_valid = false;
10865|    345|              return url;
10866|    345|            }
10867|    629|            state = state::HOST;
10868|    629|            break;
10869|    974|          }
10870|  6.73k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (10870:15): [True: 0, False: 6.73k]
  ------------------
10871|       |            if constexpr (store_values) {
10872|       |              if (fragment.has_value()) {
10873|       |                url.update_unencoded_base_hash(*fragment);
10874|       |              }
10875|       |            }
10876|      0|            return url;
10877|      0|          }
10878|  6.73k|          input_position = end_of_authority + 1;
10879|  6.73k|        } while (true);
  ------------------
  |  Branch (10879:18): [True: 6.73k, Folded]
  ------------------
10880|       |
10881|    629|        break;
10882|    974|      }
10883|    629|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (10883:7): [True: 124, False: 102k]
  ------------------
10884|    124|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
10885|    124|                helpers::substring(url_data, input_position));
10886|       |
10887|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
10888|       |        // then set state to special authority ignore slashes state and increase
10889|       |        // pointer by 1.
10890|    124|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (10890:13): [True: 50, False: 74]
  ------------------
10891|     50|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
10892|     50|          input_position += 2;
10893|     74|        } else {
10894|       |          // Otherwise, validation error, set state to relative state and
10895|       |          // decrease pointer by 1.
10896|     74|          state = state::RELATIVE_SCHEME;
10897|     74|        }
10898|       |
10899|    124|        break;
10900|    974|      }
10901|  2.02k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (10901:7): [True: 2.02k, False: 100k]
  ------------------
10902|  2.02k|        ada_log("PATH_OR_AUTHORITY ",
10903|  2.02k|                helpers::substring(url_data, input_position));
10904|       |
10905|       |        // If c is U+002F (/), then set state to authority state.
10906|  2.02k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10906:13): [True: 1.89k, False: 131]
  ------------------
10907|  1.89k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (10907:13): [True: 639, False: 1.25k]
  ------------------
10908|    639|          state = state::AUTHORITY;
10909|    639|          input_position++;
10910|  1.38k|        } else {
10911|       |          // Otherwise, set state to path state, and decrease pointer by 1.
10912|  1.38k|          state = state::PATH;
10913|  1.38k|        }
10914|       |
10915|  2.02k|        break;
10916|    974|      }
10917|    605|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (10917:7): [True: 605, False: 101k]
  ------------------
10918|    605|        ada_log("RELATIVE_SCHEME ",
10919|    605|                helpers::substring(url_data, input_position));
10920|       |
10921|       |        // Set url's scheme to base's scheme.
10922|    605|        url.copy_scheme(*base_url);
10923|       |
10924|       |        // If c is U+002F (/), then set state to relative slash state.
10925|    605|        if ((input_position != input_size) &&
  ------------------
  |  Branch (10925:13): [True: 439, False: 166]
  ------------------
10926|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
10927|    439|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (10927:13): [True: 70, False: 369]
  ------------------
10928|     70|          ada_log(
10929|     70|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
10930|     70|              "slash state");
10931|     70|          state = state::RELATIVE_SLASH;
10932|    535|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (10932:20): [True: 372, False: 163]
  |  Branch (10932:40): [True: 257, False: 115]
  ------------------
10933|    257|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (10933:20): [True: 5, False: 252]
  ------------------
10934|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
10935|       |          // set state to relative slash state.
10936|      5|          ada_log(
10937|      5|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
10938|      5|              "error, set state to relative slash state");
10939|      5|          state = state::RELATIVE_SLASH;
10940|    530|        } else {
10941|    530|          ada_log("RELATIVE_SCHEME otherwise");
10942|       |          // Set url's username to base's username, url's password to base's
10943|       |          // password, url's host to base's host, url's port to base's port,
10944|       |          // url's path to a clone of base's path, and url's query to base's
10945|       |          // query.
10946|       |          if constexpr (result_type_is_ada_url) {
10947|       |            url.username = base_url->username;
10948|       |            url.password = base_url->password;
10949|       |            url.host = base_url->host;
10950|       |            url.port = base_url->port;
10951|       |            // cloning the base path includes cloning the has_opaque_path flag
10952|       |            url.has_opaque_path = base_url->has_opaque_path;
10953|       |            url.path = base_url->path;
10954|       |            url.query = base_url->query;
10955|    530|          } else {
10956|    530|            url.update_base_authority(base_url->get_href(),
10957|    530|                                      base_url->get_components());
10958|    530|            url.update_host_to_base_host(base_url->get_hostname());
10959|    530|            url.update_base_port(base_url->retrieve_base_port());
10960|       |            // cloning the base path includes cloning the has_opaque_path flag
10961|    530|            url.has_opaque_path = base_url->has_opaque_path;
10962|    530|            url.update_base_pathname(base_url->get_pathname());
10963|    530|            if (base_url->has_search()) {
  ------------------
  |  Branch (10963:17): [True: 0, False: 530]
  ------------------
10964|       |              // get_search() returns "" for an empty query string (URL ends
10965|       |              // with '?'). update_base_search("") would incorrectly clear the
10966|       |              // query, so pass "?" to preserve the empty query distinction.
10967|      0|              auto s = base_url->get_search();
10968|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (10968:38): [True: 0, False: 0]
  ------------------
10969|      0|            }
10970|    530|          }
10971|       |
10972|    530|          url.has_opaque_path = base_url->has_opaque_path;
10973|       |
10974|       |          // If c is U+003F (?), then set url's query to the empty string, and
10975|       |          // state to query state.
10976|    530|          if ((input_position != input_size) &&
  ------------------
  |  Branch (10976:15): [True: 364, False: 166]
  ------------------
10977|    364|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (10977:15): [True: 29, False: 335]
  ------------------
10978|     29|            state = state::QUERY;
10979|     29|          }
10980|       |          // Otherwise, if c is not the EOF code point:
10981|    501|          else if (input_position != input_size) {
  ------------------
  |  Branch (10981:20): [True: 335, False: 166]
  ------------------
10982|       |            // Set url's query to null.
10983|    335|            url.clear_search();
10984|       |            if constexpr (result_type_is_ada_url) {
10985|       |              // Shorten url's path.
10986|       |              helpers::shorten_path(url.path, url.type);
10987|    335|            } else {
10988|    335|              std::string_view path = url.get_pathname();
10989|    335|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (10989:19): [True: 0, False: 335]
  ------------------
10990|      0|                url.update_base_pathname(std::move(std::string(path)));
10991|      0|              }
10992|    335|            }
10993|       |            // Set state to path state and decrease pointer by 1.
10994|    335|            state = state::PATH;
10995|    335|            break;
10996|    335|          }
10997|    530|        }
10998|    270|        input_position++;
10999|    270|        break;
11000|    605|      }
11001|     75|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (11001:7): [True: 75, False: 102k]
  ------------------
11002|     75|        ada_log("RELATIVE_SLASH ",
11003|     75|                helpers::substring(url_data, input_position));
11004|       |
11005|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
11006|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
11007|     75|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (11007:13): [True: 49, False: 26]
  |  Branch (11007:33): [True: 24, False: 25]
  ------------------
11008|     24|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11008:14): [True: 3, False: 21]
  ------------------
11009|     21|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11009:14): [True: 1, False: 20]
  ------------------
11010|       |          // Set state to special authority ignore slashes state.
11011|      4|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
11012|      4|        }
11013|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
11014|     71|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (11014:18): [True: 38, False: 33]
  ------------------
11015|     38|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (11015:18): [True: 7, False: 31]
  ------------------
11016|      7|          state = state::AUTHORITY;
11017|      7|        }
11018|       |        // Otherwise, set
11019|       |        // - url's username to base's username,
11020|       |        // - url's password to base's password,
11021|       |        // - url's host to base's host,
11022|       |        // - url's port to base's port,
11023|       |        // - state to path state, and then, decrease pointer by 1.
11024|     64|        else {
11025|       |          if constexpr (result_type_is_ada_url) {
11026|       |            url.username = base_url->username;
11027|       |            url.password = base_url->password;
11028|       |            url.host = base_url->host;
11029|       |            url.port = base_url->port;
11030|     64|          } else {
11031|     64|            url.update_base_authority(base_url->get_href(),
11032|     64|                                      base_url->get_components());
11033|     64|            url.update_host_to_base_host(base_url->get_hostname());
11034|     64|            url.update_base_port(base_url->retrieve_base_port());
11035|     64|          }
11036|     64|          state = state::PATH;
11037|     64|          break;
11038|     64|        }
11039|       |
11040|     11|        input_position++;
11041|     11|        break;
11042|     75|      }
11043|  10.1k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (11043:7): [True: 10.1k, False: 92.3k]
  ------------------
11044|  10.1k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
11045|  10.1k|                helpers::substring(url_data, input_position));
11046|       |
11047|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
11048|       |        // then set state to special authority ignore slashes state and increase
11049|       |        // pointer by 1.
11050|  10.1k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (11050:13): [True: 2.37k, False: 7.73k]
  ------------------
11051|  2.37k|          input_position += 2;
11052|  2.37k|        }
11053|       |
11054|  10.1k|        [[fallthrough]];
11055|  10.1k|      }
11056|  10.1k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (11056:7): [True: 54, False: 102k]
  ------------------
11057|  10.1k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
11058|  10.1k|                helpers::substring(url_data, input_position));
11059|       |
11060|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
11061|       |        // authority state and decrease pointer by 1.
11062|  10.8k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (11062:16): [True: 10.8k, False: 42]
  ------------------
11063|  10.8k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (11063:17): [True: 327, False: 10.5k]
  ------------------
11064|  10.5k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (11064:17): [True: 394, False: 10.1k]
  ------------------
11065|    721|          input_position++;
11066|    721|        }
11067|  10.1k|        state = state::AUTHORITY;
11068|       |
11069|  10.1k|        break;
11070|  10.1k|      }
11071|     33|      case state::QUERY: {
  ------------------
  |  Branch (11071:7): [True: 33, False: 102k]
  ------------------
11072|     33|        ada_log("QUERY ", helpers::substring(url_data, input_position));
11073|       |        if constexpr (store_values) {
11074|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
11075|       |          // if url is special; otherwise the query percent-encode set.
11076|       |          const uint8_t* query_percent_encode_set =
11077|       |              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
11078|       |                               : character_sets::QUERY_PERCENT_ENCODE;
11079|       |
11080|       |          // Percent-encode after encoding, with encoding, buffer, and
11081|       |          // queryPercentEncodeSet, and append the result to url's query.
11082|       |          url.update_base_search(url_data.substr(input_position),
11083|       |                                 query_percent_encode_set);
11084|       |          ada_log("QUERY update_base_search completed ");
11085|       |          if (fragment.has_value()) {
11086|       |            url.update_unencoded_base_hash(*fragment);
11087|       |          }
11088|       |        }
11089|     33|        return url;
11090|  10.1k|      }
11091|  10.4k|      case state::HOST: {
  ------------------
  |  Branch (11091:7): [True: 10.4k, False: 91.9k]
  ------------------
11092|  10.4k|        ada_log("HOST ", helpers::substring(url_data, input_position));
11093|       |
11094|  10.4k|        std::string_view host_view = url_data.substr(input_position);
11095|  10.4k|        auto [location, found_colon] =
11096|  10.4k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
11097|  10.4k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (11097:26): [True: 10.4k, False: 0]
  ------------------
11098|  10.4k|                             ? input_position + location
11099|  10.4k|                             : input_size;
11100|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
11101|       |        // Note: the 'found_colon' value is true if and only if a colon was
11102|       |        // encountered while not inside brackets.
11103|  10.4k|        if (found_colon) {
  ------------------
  |  Branch (11103:13): [True: 1.27k, False: 9.18k]
  ------------------
11104|       |          // If buffer is the empty string, validation error, return failure.
11105|       |          // Let host be the result of host parsing buffer with url is not
11106|       |          // special.
11107|  1.27k|          ada_log("HOST parsing ", host_view);
11108|  1.27k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (11108:15): [True: 114, False: 1.15k]
  ------------------
11109|    114|            return url;
11110|    114|          }
11111|  1.15k|          ada_log("HOST parsing results in ", url.get_hostname());
11112|       |          // Set url's host to host, buffer to the empty string, and state to
11113|       |          // port state.
11114|  1.15k|          state = state::PORT;
11115|  1.15k|          input_position++;
11116|  1.15k|        }
11117|       |        // Otherwise, if one of the following is true:
11118|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
11119|       |        // - url is special and c is U+005C (\)
11120|       |        // The get_host_delimiter_location function either brings us to
11121|       |        // the colon outside of the bracket, or to one of those characters.
11122|  9.18k|        else {
11123|       |          // If url is special and host_view is the empty string, validation
11124|       |          // error, return failure.
11125|  9.18k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (11125:15): [True: 119, False: 9.06k]
  |  Branch (11125:36): [True: 45, False: 74]
  ------------------
11126|     45|            url.is_valid = false;
11127|     45|            return url;
11128|     45|          }
11129|  9.14k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
11130|       |          // Let host be the result of host parsing host_view with url is not
11131|       |          // special.
11132|  9.14k|          if (host_view.empty()) {
  ------------------
  |  Branch (11132:15): [True: 74, False: 9.06k]
  ------------------
11133|     74|            url.update_base_hostname("");
11134|  9.06k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (11134:22): [True: 2.58k, False: 6.48k]
  ------------------
11135|  2.58k|            return url;
11136|  2.58k|          }
11137|  6.56k|          ada_log("HOST parsing results in ", url.get_hostname(),
11138|  6.56k|                  " href=", url.get_href());
11139|       |
11140|       |          // Set url's host to host, and state to path start state.
11141|  6.56k|          state = state::PATH_START;
11142|  6.56k|        }
11143|       |
11144|  7.71k|        break;
11145|  10.4k|      }
11146|  7.71k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (11146:7): [True: 2.65k, False: 99.7k]
  ------------------
11147|  2.65k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
11148|       |        // Opaque path, query, and fragment are structurally always valid:
11149|       |        // the parser would just percent-encode whatever is there. When we
11150|       |        // are not storing values (can_parse), we can return immediately.
11151|       |        // We must set has_opaque_path = true before returning so that when
11152|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
11153|       |        // correctly rejects relative inputs against an opaque-path base
11154|       |        // (e.g. can_parse("", &"W:") must return false).
11155|  2.65k|        if constexpr (!store_values) {
11156|  2.65k|          url.has_opaque_path = true;
11157|  2.65k|          return url;
11158|  2.65k|        }
11159|      0|        std::string_view view = url_data.substr(input_position);
11160|       |        // If c is U+003F (?), then set url's query to the empty string and
11161|       |        // state to query state.
11162|  2.65k|        size_t location = view.find('?');
11163|  2.65k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (11163:13): [True: 0, False: 2.65k]
  ------------------
11164|      0|          view.remove_suffix(view.size() - location);
11165|      0|          state = state::QUERY;
11166|      0|          input_position += location + 1;
11167|  2.65k|        } else {
11168|  2.65k|          input_position = input_size + 1;
11169|  2.65k|        }
11170|  2.65k|        url.has_opaque_path = true;
11171|       |
11172|       |        // This is a really unlikely scenario in real world. We should not seek
11173|       |        // to optimize it.
11174|  2.65k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (11174:13): [True: 0, False: 2.65k]
  ------------------
11175|      0|          std::string modified_view =
11176|      0|              std::string(view.substr(0, view.size() - 1)) + "%20";
11177|      0|          url.update_base_pathname(unicode::percent_encode(
11178|      0|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
11179|  2.65k|        } else {
11180|  2.65k|          url.update_base_pathname(unicode::percent_encode(
11181|  2.65k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
11182|  2.65k|        }
11183|  2.65k|        break;
11184|  10.4k|      }
11185|  1.15k|      case state::PORT: {
  ------------------
  |  Branch (11185:7): [True: 1.15k, False: 101k]
  ------------------
11186|  1.15k|        ada_log("PORT ", helpers::substring(url_data, input_position));
11187|  1.15k|        std::string_view port_view = url_data.substr(input_position);
11188|  1.15k|        input_position += url.parse_port(port_view, true);
11189|  1.15k|        if (!url.is_valid) {
  ------------------
  |  Branch (11189:13): [True: 86, False: 1.07k]
  ------------------
11190|     86|          return url;
11191|     86|        }
11192|  1.07k|        state = state::PATH_START;
11193|  1.07k|        [[fallthrough]];
11194|  1.07k|      }
11195|  9.04k|      case state::PATH_START: {
  ------------------
  |  Branch (11195:7): [True: 7.97k, False: 94.4k]
  ------------------
11196|  9.04k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
11197|       |        // Path, query, and fragment are structurally always valid: the
11198|       |        // parser would just percent-encode whatever is there. When we are
11199|       |        // not storing values (can_parse), we can return immediately since
11200|       |        // no subsequent state can invalidate the URL.
11201|  9.04k|        if constexpr (!store_values) {
11202|  9.04k|          return url;
11203|  9.04k|        }
11204|       |
11205|       |        // If url is special, then:
11206|  9.04k|        if (url.is_special()) {
  ------------------
  |  Branch (11206:13): [True: 0, False: 9.04k]
  ------------------
11207|       |          // Set state to path state.
11208|      0|          state = state::PATH;
11209|       |
11210|       |          // Optimization: Avoiding going into PATH state improves the
11211|       |          // performance of urls ending with /.
11212|      0|          if (input_position == input_size) {
  ------------------
  |  Branch (11212:15): [True: 0, False: 0]
  ------------------
11213|       |            if constexpr (store_values) {
11214|       |              url.update_base_pathname("/");
11215|       |              if (fragment.has_value()) {
11216|       |                url.update_unencoded_base_hash(*fragment);
11217|       |              }
11218|       |            }
11219|      0|            return url;
11220|      0|          }
11221|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
11222|       |          // by 1. We know that (input_position == input_size) is impossible
11223|       |          // here, because of the previous if-check.
11224|      0|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (11224:15): [True: 0, False: 0]
  ------------------
11225|      0|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (11225:15): [True: 0, False: 0]
  ------------------
11226|      0|            break;
11227|      0|          }
11228|      0|        }
11229|       |        // Otherwise, if state override is not given and c is U+003F (?),
11230|       |        // set url's query to the empty string and state to query state.
11231|  9.04k|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (11231:18): [True: 0, False: 9.04k]
  ------------------
11232|      0|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (11232:18): [True: 0, False: 0]
  ------------------
11233|      0|          state = state::QUERY;
11234|      0|        }
11235|       |        // Otherwise, if c is not the EOF code point:
11236|  9.04k|        else if (input_position != input_size) {
  ------------------
  |  Branch (11236:18): [True: 0, False: 9.04k]
  ------------------
11237|       |          // Set state to path state.
11238|      0|          state = state::PATH;
11239|       |
11240|       |          // If c is not U+002F (/), then decrease pointer by 1.
11241|      0|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (11241:15): [True: 0, False: 0]
  ------------------
11242|      0|            break;
11243|      0|          }
11244|      0|        }
11245|       |
11246|  9.04k|        input_position++;
11247|  9.04k|        break;
11248|  9.04k|      }
11249|  2.60k|      case state::PATH: {
  ------------------
  |  Branch (11249:7): [True: 2.60k, False: 99.8k]
  ------------------
11250|  2.60k|        ada_log("PATH ", helpers::substring(url_data, input_position));
11251|       |        // Path, query, and fragment are structurally always valid: the
11252|       |        // parser would just percent-encode whatever is there. When we are
11253|       |        // not storing values (can_parse), we can return immediately since
11254|       |        // no subsequent state can invalidate the URL.
11255|  2.60k|        if constexpr (!store_values) {
11256|  2.60k|          return url;
11257|  2.60k|        }
11258|      0|        std::string_view view = url_data.substr(input_position);
11259|       |
11260|       |        // Most time, we do not need percent encoding.
11261|       |        // Furthermore, we can immediately locate the '?'.
11262|  2.60k|        size_t locofquestionmark = view.find('?');
11263|  2.60k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (11263:13): [True: 0, False: 2.60k]
  ------------------
11264|      0|          state = state::QUERY;
11265|      0|          view.remove_suffix(view.size() - locofquestionmark);
11266|      0|          input_position += locofquestionmark + 1;
11267|  2.60k|        } else {
11268|  2.60k|          input_position = input_size + 1;
11269|  2.60k|        }
11270|       |        if constexpr (store_values) {
11271|       |          if constexpr (result_type_is_ada_url) {
11272|       |            helpers::parse_prepared_path(view, url.type, url.path);
11273|       |          } else {
11274|       |            url.consume_prepared_path(view);
11275|       |            ADA_ASSERT_TRUE(url.validate());
11276|       |          }
11277|       |        }
11278|  2.60k|        break;
11279|  9.04k|      }
11280|  1.65k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (11280:7): [True: 1.65k, False: 100k]
  ------------------
11281|  1.65k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
11282|       |
11283|       |        // If c is U+002F (/) or U+005C (\), then:
11284|  1.65k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (11284:13): [True: 1.58k, False: 74]
  ------------------
11285|  1.58k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11285:14): [True: 1.52k, False: 59]
  ------------------
11286|  1.53k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11286:14): [True: 6, False: 53]
  ------------------
11287|  1.53k|          ada_log("FILE_SLASH c is U+002F or U+005C");
11288|       |          // Set state to file host state.
11289|  1.53k|          state = state::FILE_HOST;
11290|  1.53k|          input_position++;
11291|  1.53k|        } else {
11292|    127|          ada_log("FILE_SLASH otherwise");
11293|       |          // If base is non-null and base's scheme is "file", then:
11294|       |          // Note: it is unsafe to do base_url->scheme unless you know that
11295|       |          // base_url_has_value() is true.
11296|    127|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (11296:15): [True: 97, False: 30]
  |  Branch (11296:38): [True: 95, False: 2]
  ------------------
11297|       |            // Set url's host to base's host.
11298|       |            if constexpr (result_type_is_ada_url) {
11299|       |              url.host = base_url->host;
11300|     95|            } else {
11301|     95|              url.update_host_to_base_host(base_url->get_host());
11302|     95|            }
11303|       |            // If the code point substring from pointer to the end of input does
11304|       |            // not start with a Windows drive letter and base's path[0] is a
11305|       |            // normalized Windows drive letter, then append base's path[0] to
11306|       |            // url's path.
11307|     95|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (11307:17): [True: 0, False: 95]
  ------------------
11308|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (11308:19): [True: 0, False: 0]
  ------------------
11309|      0|                      url_data.substr(input_position))) {
11310|      0|                std::string_view first_base_url_path =
11311|      0|                    base_url->get_pathname().substr(1);
11312|      0|                size_t loc = first_base_url_path.find('/');
11313|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (11313:21): [True: 0, False: 0]
  ------------------
11314|      0|                  helpers::resize(first_base_url_path, loc);
11315|      0|                }
11316|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (11316:21): [True: 0, False: 0]
  ------------------
11317|      0|                        first_base_url_path)) {
11318|       |                  if constexpr (result_type_is_ada_url) {
11319|       |                    url.path += '/';
11320|       |                    url.path += first_base_url_path;
11321|      0|                  } else {
11322|      0|                    url.append_base_pathname(
11323|      0|                        helpers::concat("/", first_base_url_path));
11324|      0|                  }
11325|      0|                }
11326|      0|              }
11327|      0|            }
11328|     95|          }
11329|       |
11330|       |          // Set state to path state, and decrease pointer by 1.
11331|    127|          state = state::PATH;
11332|    127|        }
11333|       |
11334|  1.65k|        break;
11335|  9.04k|      }
11336|  1.53k|      case state::FILE_HOST: {
  ------------------
  |  Branch (11336:7): [True: 1.53k, False: 100k]
  ------------------
11337|  1.53k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
11338|  1.53k|        std::string_view view = url_data.substr(input_position);
11339|       |
11340|  1.53k|        size_t location = view.find_first_of("/\\?");
11341|  1.53k|        std::string_view file_host_buffer = view.substr(
11342|  1.53k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (11342:16): [True: 732, False: 800]
  ------------------
11343|       |
11344|  1.53k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (11344:13): [True: 12, False: 1.52k]
  ------------------
11345|     12|          state = state::PATH;
11346|  1.52k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (11346:20): [True: 302, False: 1.21k]
  ------------------
11347|       |          // Set url's host to the empty string.
11348|       |          if constexpr (result_type_is_ada_url) {
11349|       |            url.host = "";
11350|    302|          } else {
11351|    302|            url.update_base_hostname("");
11352|    302|          }
11353|       |          // Set state to path start state.
11354|    302|          state = state::PATH_START;
11355|  1.21k|        } else {
11356|  1.21k|          size_t consumed_bytes = file_host_buffer.size();
11357|  1.21k|          input_position += consumed_bytes;
11358|       |          // Let host be the result of host parsing buffer with url is not
11359|       |          // special.
11360|  1.21k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (11360:15): [True: 106, False: 1.11k]
  ------------------
11361|    106|            return url;
11362|    106|          }
11363|       |
11364|       |          if constexpr (result_type_is_ada_url) {
11365|       |            // If host is "localhost", then set host to the empty string.
11366|       |            if (url.host.has_value() && url.host.value() == "localhost") {
11367|       |              url.host = "";
11368|       |            }
11369|  1.11k|          } else {
11370|  1.11k|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (11370:17): [True: 6, False: 1.10k]
  ------------------
11371|      6|              url.update_base_hostname("");
11372|      6|            }
11373|  1.11k|          }
11374|       |
11375|       |          // Set buffer to the empty string and state to path start state.
11376|  1.11k|          state = state::PATH_START;
11377|  1.11k|        }
11378|       |
11379|  1.42k|        break;
11380|  1.53k|      }
11381|  2.41k|      case state::FILE: {
  ------------------
  |  Branch (11381:7): [True: 2.41k, False: 100k]
  ------------------
11382|  2.41k|        ada_log("FILE ", helpers::substring(url_data, input_position));
11383|  2.41k|        std::string_view file_view = url_data.substr(input_position);
11384|       |
11385|  2.41k|        url.set_protocol_as_file();
11386|       |        if constexpr (result_type_is_ada_url) {
11387|       |          // Set url's host to the empty string.
11388|       |          url.host = "";
11389|  2.41k|        } else {
11390|  2.41k|          url.update_base_hostname("");
11391|  2.41k|        }
11392|       |        // If c is U+002F (/) or U+005C (\), then:
11393|  2.41k|        if (input_position != input_size &&
  ------------------
  |  Branch (11393:13): [True: 2.27k, False: 141]
  ------------------
11394|  2.27k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (11394:14): [True: 1.65k, False: 617]
  ------------------
11395|  1.65k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (11395:14): [True: 6, False: 611]
  ------------------
11396|  1.65k|          ada_log("FILE c is U+002F or U+005C");
11397|       |          // Set state to file slash state.
11398|  1.65k|          state = state::FILE_SLASH;
11399|  1.65k|        }
11400|       |        // Otherwise, if base is non-null and base's scheme is "file":
11401|    752|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (11401:18): [True: 279, False: 473]
  |  Branch (11401:41): [True: 203, False: 76]
  ------------------
11402|       |          // Set url's host to base's host, url's path to a clone of base's
11403|       |          // path, and url's query to base's query.
11404|    203|          ada_log("FILE base non-null");
11405|       |          if constexpr (result_type_is_ada_url) {
11406|       |            url.host = base_url->host;
11407|       |            url.path = base_url->path;
11408|       |            url.query = base_url->query;
11409|    203|          } else {
11410|    203|            url.update_host_to_base_host(base_url->get_hostname());
11411|    203|            url.update_base_pathname(base_url->get_pathname());
11412|    203|            if (base_url->has_search()) {
  ------------------
  |  Branch (11412:17): [True: 0, False: 203]
  ------------------
11413|       |              // get_search() returns "" for an empty query string (URL ends
11414|       |              // with '?'). update_base_search("") would incorrectly clear the
11415|       |              // query, so pass "?" to preserve the empty query distinction.
11416|      0|              auto s = base_url->get_search();
11417|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (11417:38): [True: 0, False: 0]
  ------------------
11418|      0|            }
11419|    203|          }
11420|    203|          url.has_opaque_path = base_url->has_opaque_path;
11421|       |
11422|       |          // If c is U+003F (?), then set url's query to the empty string and
11423|       |          // state to query state.
11424|    203|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (11424:15): [True: 137, False: 66]
  |  Branch (11424:47): [True: 4, False: 133]
  ------------------
11425|      4|            state = state::QUERY;
11426|      4|          }
11427|       |          // Otherwise, if c is not the EOF code point:
11428|    199|          else if (input_position != input_size) {
  ------------------
  |  Branch (11428:20): [True: 133, False: 66]
  ------------------
11429|       |            // Set url's query to null.
11430|    133|            url.clear_search();
11431|       |            // If the code point substring from pointer to the end of input does
11432|       |            // not start with a Windows drive letter, then shorten url's path.
11433|    133|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (11433:17): [True: 113, False: 20]
  ------------------
11434|       |              if constexpr (result_type_is_ada_url) {
11435|       |                helpers::shorten_path(url.path, url.type);
11436|    113|              } else {
11437|    113|                std::string_view path = url.get_pathname();
11438|    113|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (11438:21): [True: 0, False: 113]
  ------------------
11439|      0|                  url.update_base_pathname(std::move(std::string(path)));
11440|      0|                }
11441|    113|              }
11442|    113|            }
11443|       |            // Otherwise:
11444|     20|            else {
11445|       |              // Set url's path to an empty list.
11446|     20|              url.clear_pathname();
11447|     20|              url.has_opaque_path = true;
11448|     20|            }
11449|       |
11450|       |            // Set state to path state and decrease pointer by 1.
11451|    133|            state = state::PATH;
11452|    133|            break;
11453|    133|          }
11454|    203|        }
11455|       |        // Otherwise, set state to path state, and decrease pointer by 1.
11456|    549|        else {
11457|    549|          ada_log("FILE go to path");
11458|    549|          state = state::PATH;
11459|    549|          break;
11460|    549|        }
11461|       |
11462|  1.72k|        input_position++;
11463|  1.72k|        break;
11464|  2.41k|      }
11465|      0|      default:
  ------------------
  |  Branch (11465:7): [True: 0, False: 102k]
  ------------------
11466|      0|        unreachable();
11467|   102k|    }
11468|   102k|  }
11469|       |  if constexpr (store_values) {
11470|       |    if (fragment.has_value()) {
11471|       |      url.update_unencoded_base_hash(*fragment);
11472|       |    }
11473|       |  }
11474|       |  // Check the resulting (normalized) URL size against the maximum input length.
11475|       |  // Normalization (percent-encoding, IDNA, etc.) can expand the URL beyond the
11476|       |  // original input size.
11477|       |  if constexpr (store_values) {
11478|       |    if (url.is_valid) {
11479|       |      if constexpr (result_type_is_ada_url_aggregator) {
11480|       |        if (url.buffer.size() > max_input_length) {
11481|       |          url.is_valid = false;
11482|       |        }
11483|       |      } else {
11484|       |        if (url.get_href_size() > max_input_length) {
11485|       |          url.is_valid = false;
11486|       |        }
11487|       |      }
11488|       |    }
11489|       |  }
11490|    232|  return url;
11491|  23.8k|}
_ZNK3ada14url_aggregator8get_hostEv:
12398|    190|    ada_lifetime_bound {
12399|    190|  ada_log("url_aggregator::get_host");
12400|       |  // Technically, we should check if there is a hostname, but
12401|       |  // the code below works even if there isn't.
12402|       |  // if(!has_hostname()) { return ""; }
12403|    190|  size_t start = components.host_start;
12404|    190|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (12404:7): [True: 34, False: 156]
  ------------------
12405|     34|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (12405:7): [True: 0, False: 34]
  ------------------
12406|      0|    start++;
12407|      0|  }
12408|       |  // if we have an empty host, then the space between components.host_end and
12409|       |  // components.pathname_start may be occupied by /.
12410|    190|  if (start == components.host_end) {
  ------------------
  |  Branch (12410:7): [True: 156, False: 34]
  ------------------
12411|    156|    return {};
12412|    156|  }
12413|     34|  return helpers::substring(buffer, start, components.pathname_start);
12414|    190|}
_ZNK3ada14url_aggregator12get_hostnameEv:
12417|  27.3k|    ada_lifetime_bound {
12418|  27.3k|  ada_log("url_aggregator::get_hostname");
12419|       |  // Technically, we should check if there is a hostname, but
12420|       |  // the code below works even if there isn't.
12421|       |  // if(!has_hostname()) { return ""; }
12422|  27.3k|  size_t start = components.host_start;
12423|       |  // So host_start is not where the host begins.
12424|  27.3k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (12424:7): [True: 12.2k, False: 15.0k]
  ------------------
12425|  12.2k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (12425:7): [True: 795, False: 11.4k]
  ------------------
12426|    795|    start++;
12427|    795|  }
12428|  27.3k|  return helpers::substring(buffer, start, components.host_end);
12429|  27.3k|}
_ZNK3ada14url_aggregator10get_searchEv:
12432|    186|    ada_lifetime_bound {
12433|    186|  ada_log("url_aggregator::get_search");
12434|       |  // If this's URL's query is either null or the empty string, then return the
12435|       |  // empty string. Return U+003F (?), followed by this's URL's query.
12436|    186|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (12436:7): [True: 0, False: 186]
  ------------------
12437|      0|    return "";
12438|      0|  }
12439|    186|  auto ending_index = uint32_t(buffer.size());
12440|    186|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (12440:7): [True: 51, False: 135]
  ------------------
12441|     51|    ending_index = components.hash_start;
12442|     51|  }
12443|    186|  if (ending_index - components.search_start <= 1) {
  ------------------
  |  Branch (12443:7): [True: 73, False: 113]
  ------------------
12444|     73|    return "";
12445|     73|  }
12446|    113|  return helpers::substring(buffer, components.search_start, ending_index);
12447|    186|}
_ZNK3ada14url_aggregator12get_protocolEv:
12450|  1.38k|    ada_lifetime_bound {
12451|  1.38k|  ada_log("url_aggregator::get_protocol");
12452|  1.38k|  return helpers::substring(buffer, 0, components.protocol_end);
12453|  1.38k|}
_ZN3ada14url_aggregator10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
12562|  2.62k|bool url_aggregator::parse_ipv4(std::string_view input, bool in_place) {
12563|  2.62k|  ada_log("parse_ipv4 ", input, " [", input.size(),
12564|  2.62k|          " bytes], overlaps with buffer: ",
12565|  2.62k|          helpers::overlaps(input, buffer) ? "yes" : "no");
12566|  2.62k|  ADA_ASSERT_TRUE(validate());
12567|  2.62k|  if (input.empty()) {
  ------------------
  |  Branch (12567:7): [True: 0, False: 2.62k]
  ------------------
12568|      0|    return is_valid = false;
12569|      0|  }
12570|  2.62k|  const bool trailing_dot = (input.back() == '.');
12571|  2.62k|  if (trailing_dot) {
  ------------------
  |  Branch (12571:7): [True: 74, False: 2.55k]
  ------------------
12572|     74|    input.remove_suffix(1);
12573|     74|    if (input.empty()) {
  ------------------
  |  Branch (12573:9): [True: 0, False: 74]
  ------------------
12574|      0|      return is_valid = false;
12575|      0|    }
12576|     74|  }
12577|       |
12578|  2.62k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
12579|  2.62k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (12579:7): [True: 24, False: 2.60k]
  ------------------
12580|       |    // Pure decimal: keep the buffer when it already holds the address.
12581|       |    // Otherwise write the (trailing-dot-stripped) input.
12582|     24|    if (!(in_place && !trailing_dot)) {
  ------------------
  |  Branch (12582:11): [True: 0, False: 24]
  |  Branch (12582:23): [True: 0, False: 0]
  ------------------
12583|     24|      update_base_hostname(input);
12584|     24|    }
12585|     24|    host_type = IPV4;
12586|     24|    ADA_ASSERT_TRUE(validate());
12587|     24|    return true;
12588|     24|  }
12589|       |
12590|  2.60k|  const char* p = input.data();
12591|  2.60k|  const char* end = p + input.size();
12592|  2.60k|  uint64_t ipv4 = 0;
12593|  2.60k|  int digit_count = 0;
12594|  2.60k|  int pure_decimal_count = 0;
12595|       |
12596|  3.55k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (12596:10): [True: 3.54k, False: 14]
  |  Branch (12596:29): [True: 3.54k, False: 0]
  ------------------
12597|  3.54k|    uint64_t segment = 0;
12598|  3.54k|    bool pure = false;
12599|  3.54k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (12599:9): [True: 332, False: 3.21k]
  ------------------
12600|    332|      return is_valid = false;
12601|    332|    }
12602|  3.21k|    if (pure) {
  ------------------
  |  Branch (12602:9): [True: 2.30k, False: 906]
  ------------------
12603|  2.30k|      ++pure_decimal_count;
12604|  2.30k|    }
12605|  3.21k|    if (p >= end) {
  ------------------
  |  Branch (12605:9): [True: 2.19k, False: 1.01k]
  ------------------
12606|  2.19k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
12607|  2.19k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (12607:11): [True: 54, False: 2.14k]
  ------------------
12608|     54|        return is_valid = false;
12609|     54|      }
12610|  2.14k|      ipv4 = (ipv4 << shift) | segment;
12611|  2.14k|      goto ipv4_done;
12612|  2.19k|    }
12613|  1.01k|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (12613:9): [True: 58, False: 958]
  |  Branch (12613:26): [True: 0, False: 958]
  ------------------
12614|     58|      return is_valid = false;
12615|     58|    }
12616|    958|    ipv4 = (ipv4 << 8) | segment;
12617|    958|    ++p;
12618|    958|  }
12619|     14|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (12619:7): [True: 0, False: 14]
  |  Branch (12619:27): [True: 14, False: 0]
  ------------------
12620|     14|    return is_valid = false;
12621|     14|  }
12622|  2.14k|ipv4_done:
12623|  2.14k|  ada_log("url_aggregator::parse_ipv4 completed ", get_href(),
12624|  2.14k|          " host: ", get_host());
12625|  2.14k|  if (in_place && pure_decimal_count == 4 && !trailing_dot) {
  ------------------
  |  Branch (12625:7): [True: 1.73k, False: 410]
  |  Branch (12625:19): [True: 0, False: 1.73k]
  |  Branch (12625:46): [True: 0, False: 0]
  ------------------
12626|      0|    ada_log(
12627|      0|        "url_aggregator::parse_ipv4 completed and was already correct in the "
12628|      0|        "buffer");
12629|  2.14k|  } else {
12630|  2.14k|    update_base_hostname(ada::serializers::ipv4(ipv4));
12631|  2.14k|  }
12632|  2.14k|  host_type = IPV4;
12633|  2.14k|  ADA_ASSERT_TRUE(validate());
12634|  2.14k|  return true;
12635|     14|}
_ZN3ada14url_aggregator10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12637|  1.13k|bool url_aggregator::parse_ipv6(std::string_view input) {
12638|  1.13k|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
12639|  1.13k|  ADA_ASSERT_TRUE(validate());
12640|  1.13k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
12641|  1.13k|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (12641:7): [True: 42, False: 1.09k]
  |  Branch (12641:24): [True: 32, False: 1.06k]
  ------------------
12642|     74|    return is_valid = false;
12643|     74|  }
12644|       |#if defined(ADA_AVX512)
12645|       |  if (!detail::ipv6_structure_plausible(input.data(), input.size()))
12646|       |      [[unlikely]] {
12647|       |    return is_valid = false;
12648|       |  }
12649|       |#endif
12650|       |
12651|  1.06k|  std::array<uint16_t, 8> address{};
12652|  1.06k|  const char* pointer = input.data();
12653|  1.06k|  const char* const end = pointer + input.size();
12654|  1.06k|  int piece_index = 0;
12655|  1.06k|  int compress = -1;
12656|       |
12657|  1.06k|  if (*pointer == ':') {
  ------------------
  |  Branch (12657:7): [True: 404, False: 658]
  ------------------
12658|    404|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (12658:9): [True: 6, False: 398]
  |  Branch (12658:30): [True: 20, False: 378]
  ------------------
12659|     26|      return is_valid = false;
12660|     26|    }
12661|    378|    pointer += 2;
12662|    378|    compress = ++piece_index;
12663|    378|  }
12664|       |
12665|  2.84k|  while (pointer != end) {
  ------------------
  |  Branch (12665:10): [True: 2.13k, False: 704]
  ------------------
12666|  2.13k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (12666:9): [True: 6, False: 2.13k]
  ------------------
12667|      6|      return is_valid = false;
12668|      6|    }
12669|  2.13k|    if (*pointer == ':') {
  ------------------
  |  Branch (12669:9): [True: 288, False: 1.84k]
  ------------------
12670|    288|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (12670:11): [True: 6, False: 282]
  ------------------
12671|      6|        return is_valid = false;
12672|      6|      }
12673|    282|      ++pointer;
12674|    282|      compress = ++piece_index;
12675|    282|      continue;
12676|    288|    }
12677|       |
12678|  1.84k|    uint16_t value = 0;
12679|  1.84k|    const int length = detail::parse_hex_piece(pointer, end, value);
12680|       |
12681|  1.84k|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (12681:9): [True: 1.48k, False: 356]
  |  Branch (12681:27): [True: 226, False: 1.26k]
  ------------------
12682|    226|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (12682:11): [True: 6, False: 220]
  ------------------
12683|      6|        return is_valid = false;
12684|      6|      }
12685|    220|      pointer -= length;
12686|    220|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (12686:11): [True: 6, False: 214]
  ------------------
12687|      6|        return is_valid = false;
12688|      6|      }
12689|       |
12690|    214|      int numbers_seen = 0;
12691|    586|      while (pointer != end) {
  ------------------
  |  Branch (12691:14): [True: 526, False: 60]
  ------------------
12692|    526|        int ipv4_piece = -1;
12693|    526|        if (numbers_seen > 0) {
  ------------------
  |  Branch (12693:13): [True: 312, False: 214]
  ------------------
12694|    312|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (12694:15): [True: 270, False: 42]
  |  Branch (12694:34): [True: 260, False: 10]
  ------------------
12695|    260|            ++pointer;
12696|    260|          } else {
12697|     52|            return is_valid = false;
12698|     52|          }
12699|    312|        }
12700|    474|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (12700:13): [True: 48, False: 426]
  |  Branch (12700:31): [True: 14, False: 412]
  |  Branch (12700:49): [True: 16, False: 396]
  ------------------
12701|     78|          return is_valid = false;
12702|     78|        }
12703|    396|        ipv4_piece = *pointer - '0';
12704|    396|        ++pointer;
12705|    396|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (12705:13): [True: 360, False: 36]
  |  Branch (12705:31): [True: 156, False: 204]
  |  Branch (12705:50): [True: 146, False: 10]
  ------------------
12706|    146|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (12706:15): [True: 8, False: 138]
  ------------------
12707|      8|            return is_valid = false;
12708|      8|          }
12709|    138|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
12710|    138|          ++pointer;
12711|    138|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (12711:15): [True: 126, False: 12]
  |  Branch (12711:33): [True: 72, False: 54]
  |  Branch (12711:52): [True: 64, False: 8]
  ------------------
12712|     64|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
12713|     64|            ++pointer;
12714|     64|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (12714:17): [True: 16, False: 48]
  ------------------
12715|     16|              return is_valid = false;
12716|     16|            }
12717|     64|          }
12718|    138|        }
12719|    372|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
12720|    372|            address[static_cast<size_t>(piece_index)] * 0x100 +
12721|    372|            static_cast<uint16_t>(ipv4_piece));
12722|    372|        ++numbers_seen;
12723|    372|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (12723:13): [True: 110, False: 262]
  |  Branch (12723:34): [True: 34, False: 228]
  ------------------
12724|    144|          ++piece_index;
12725|    144|        }
12726|    372|      }
12727|     60|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (12727:11): [True: 36, False: 24]
  ------------------
12728|     36|        return is_valid = false;
12729|     36|      }
12730|     24|      break;
12731|     60|    }
12732|       |
12733|  1.61k|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (12733:9): [True: 80, False: 1.53k]
  ------------------
12734|     80|      return is_valid = false;
12735|     80|    }
12736|       |
12737|  1.53k|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (12737:9): [True: 1.18k, False: 356]
  |  Branch (12737:27): [True: 1.17k, False: 8]
  ------------------
12738|  1.17k|      ++pointer;
12739|  1.17k|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (12739:11): [True: 6, False: 1.16k]
  ------------------
12740|      6|        return is_valid = false;
12741|      6|      }
12742|  1.17k|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (12742:16): [True: 8, False: 356]
  ------------------
12743|      8|      return is_valid = false;
12744|      8|    }
12745|       |
12746|  1.52k|    address[static_cast<size_t>(piece_index)] = value;
12747|  1.52k|    ++piece_index;
12748|  1.52k|  }
12749|       |
12750|    728|  if (compress != -1) {
  ------------------
  |  Branch (12750:7): [True: 642, False: 86]
  ------------------
12751|    642|    const int right = piece_index - compress;
12752|    642|    if (right > 0) {
  ------------------
  |  Branch (12752:9): [True: 294, False: 348]
  ------------------
12753|    294|      const size_t dest = static_cast<size_t>(8 - right);
12754|    294|      const size_t src = static_cast<size_t>(compress);
12755|    294|      if (dest != src) {
  ------------------
  |  Branch (12755:11): [True: 276, False: 18]
  ------------------
12756|    776|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (12756:53): [True: 500, False: 276]
  ------------------
12757|    500|          address[dest + i] = address[src + i];
12758|    500|          address[src + i] = 0;
12759|    500|        }
12760|    276|      }
12761|    294|    }
12762|    642|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (12762:14): [True: 58, False: 28]
  ------------------
12763|     58|    return is_valid = false;
12764|     58|  }
12765|       |
12766|       |  // Serialize once; skip rewrite when hostname is already canonical.
12767|    670|  const std::string serialized = ada::serializers::ipv6(address);
12768|    670|  const std::string_view current = get_hostname();
12769|    670|  if (current.size() == serialized.size() &&
  ------------------
  |  Branch (12769:7): [True: 0, False: 670]
  ------------------
12770|      0|      std::memcmp(current.data(), serialized.data(), serialized.size()) == 0) {
  ------------------
  |  Branch (12770:7): [True: 0, False: 0]
  ------------------
12771|      0|    ada_log("parse_ipv6 in-place canonical match");
12772|    670|  } else {
12773|    670|    update_base_hostname(serialized);
12774|    670|  }
12775|    670|  ada_log("parse_ipv6 ", get_hostname());
12776|    670|  ADA_ASSERT_TRUE(validate());
12777|    670|  host_type = IPV6;
12778|    670|  return true;
12779|    728|}
_ZN3ada14url_aggregator17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12781|    822|bool url_aggregator::parse_opaque_host(std::string_view input) {
12782|    822|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
12783|    822|  ADA_ASSERT_TRUE(validate());
12784|    822|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
12785|    822|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (12785:7): [True: 18, False: 804]
  ------------------
12786|     18|    return is_valid = false;
12787|     18|  }
12788|       |
12789|       |  // Return the result of running UTF-8 percent-encode on input using the C0
12790|       |  // control percent-encode set.
12791|    804|  size_t idx = ada::unicode::percent_encode_index(
12792|    804|      input, character_sets::C0_CONTROL_PERCENT_ENCODE);
12793|    804|  if (idx == input.size()) {
  ------------------
  |  Branch (12793:7): [True: 490, False: 314]
  ------------------
12794|    490|    update_base_hostname(input);
12795|    490|  } else {
12796|       |    // We only create a temporary string if we need to.
12797|    314|    update_base_hostname(ada::unicode::percent_encode(
12798|    314|        input, character_sets::C0_CONTROL_PERCENT_ENCODE, idx));
12799|    314|  }
12800|    804|  ADA_ASSERT_TRUE(validate());
12801|    804|  return true;
12802|    822|}
_ZN3ada14url_aggregator15delete_dash_dotEv:
12961|      9|void url_aggregator::delete_dash_dot() {
12962|      9|  ada_log("url_aggregator::delete_dash_dot");
12963|      9|  ADA_ASSERT_TRUE(validate());
12964|      9|  ADA_ASSERT_TRUE(has_dash_dot());
12965|      9|  buffer.erase(components.host_end, 2);
12966|      9|  components.pathname_start -= 2;
12967|      9|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (12967:7): [True: 0, False: 9]
  ------------------
12968|      0|    components.search_start -= 2;
12969|      0|  }
12970|      9|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (12970:7): [True: 0, False: 9]
  ------------------
12971|      0|    components.hash_start -= 2;
12972|      0|  }
12973|      9|  ADA_ASSERT_TRUE(validate());
12974|      9|  ADA_ASSERT_TRUE(!has_dash_dot());
12975|      9|}
can_parse.cc:_ZZN3ada4idna22utf32_length_from_utf8EPKcmENK3$_0clEa:
  284|   422k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  285|       |    // -65 is 0b10111111, anything larger in two-complement's
  286|       |    // should start a new code point.
  287|   422k|    return c > -65;
  288|   422k|  });
_ZN3ada4idna7deflate22make_fixed_litlen_huffEv:
  490|      2|inline Huff make_fixed_litlen_huff() {
  491|      2|  Huff h;
  492|      2|  uint8_t lens[288];
  493|       |  // Fixed Huffman: 0-143:8, 144-255:9, 256-279:7, 280-287:8
  494|    290|  for (int i = 0; i <= 143; i++) lens[i] = 8;
  ------------------
  |  Branch (494:19): [True: 288, False: 2]
  ------------------
  495|    226|  for (int i = 144; i <= 255; i++) lens[i] = 9;
  ------------------
  |  Branch (495:21): [True: 224, False: 2]
  ------------------
  496|     50|  for (int i = 256; i <= 279; i++) lens[i] = 7;
  ------------------
  |  Branch (496:21): [True: 48, False: 2]
  ------------------
  497|     18|  for (int i = 280; i <= 287; i++) lens[i] = 8;
  ------------------
  |  Branch (497:21): [True: 16, False: 2]
  ------------------
  498|      2|  h.build(lens, 288);
  499|      2|  return h;
  500|      2|}
_ZN3ada4idna7deflate4Huff5buildEPKhi:
  439|     16|  bool build(const uint8_t* lens, int n) {
  440|     16|    std::memset(counts, 0, sizeof(counts));
  441|     16|    std::memset(first_code, 0, sizeof(first_code));
  442|     16|    std::memset(base_idx, 0, sizeof(base_idx));
  443|     16|    nsymbols = n;
  444|     16|    max_bits = 0;
  445|  1.98k|    for (int i = 0; i < n; i++) {
  ------------------
  |  Branch (445:21): [True: 1.96k, False: 16]
  ------------------
  446|  1.96k|      lengths[i] = lens[i];
  447|  1.96k|      if (lens[i]) {
  ------------------
  |  Branch (447:11): [True: 1.91k, False: 49]
  ------------------
  448|  1.91k|        counts[lens[i]]++;
  449|  1.91k|        if (lens[i] > max_bits) max_bits = lens[i];
  ------------------
  |  Branch (449:13): [True: 46, False: 1.87k]
  ------------------
  450|  1.91k|      }
  451|  1.96k|    }
  452|       |    // Canonical first code for each bit length (RFC 1951).
  453|     16|    int code = 0;
  454|    256|    for (int bits = 1; bits <= MAXBITS; bits++) {
  ------------------
  |  Branch (454:24): [True: 240, False: 16]
  ------------------
  455|    240|      code = (code + counts[bits - 1]) << 1;
  456|    240|      first_code[bits] = code;
  457|    240|    }
  458|     16|    int idx = 0;
  459|    158|    for (int bits = 1; bits <= max_bits; bits++) {
  ------------------
  |  Branch (459:24): [True: 142, False: 16]
  ------------------
  460|    142|      base_idx[bits] = idx;
  461|       |      // assign symbols in symbol order for codes of this length
  462|  21.6k|      for (int sym = 0; sym < n; sym++) {
  ------------------
  |  Branch (462:25): [True: 21.5k, False: 142]
  ------------------
  463|  21.5k|        if (lengths[sym] == bits) {
  ------------------
  |  Branch (463:13): [True: 1.91k, False: 19.6k]
  ------------------
  464|  1.91k|          symbols[idx++] = static_cast<int16_t>(sym);
  465|  1.91k|        }
  466|  21.5k|      }
  467|    142|    }
  468|     16|    return true;
  469|     16|  }
_ZN3ada4idna7deflate20make_fixed_dist_huffEv:
  502|      2|inline Huff make_fixed_dist_huff() {
  503|      2|  Huff h;
  504|      2|  uint8_t lens[32];
  505|     66|  for (int i = 0; i < 32; i++) lens[i] = 5;
  ------------------
  |  Branch (505:19): [True: 64, False: 2]
  ------------------
  506|      2|  h.build(lens, 32);
  507|      2|  return h;
  508|      2|}
can_parse.cc:_ZZN3ada4idna9ascii_mapEPcmENK3$_0clEh:
 5134|  21.5k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5135|  21.5k|    return 0x101010101010101ull * v;
 5136|  21.5k|  };
_ZN3ada4idna13ensure_tablesEv:
 4885|  63.0k|[[nodiscard]] inline bool ensure_tables() noexcept {
 4886|  63.0k|  uint8_t state = tables_init_state.load(std::memory_order_acquire);
 4887|  63.0k|  if (state == kTablesReady) {
  ------------------
  |  Branch (4887:7): [True: 63.0k, False: 1]
  ------------------
 4888|  63.0k|    return true;
 4889|  63.0k|  }
 4890|      1|  if (state == kTablesFailed) {
  ------------------
  |  Branch (4890:7): [True: 0, False: 1]
  ------------------
 4891|      0|    return false;
 4892|      0|  }
 4893|       |
 4894|      1|  uint8_t expected = kTablesUninit;
 4895|      1|  if (tables_init_state.compare_exchange_strong(expected, kTablesInProgress,
  ------------------
  |  Branch (4895:7): [True: 1, False: 0]
  ------------------
 4896|      1|                                                std::memory_order_acq_rel,
 4897|      1|                                                std::memory_order_acquire)) {
 4898|      1|    uint8_t* buffer = new (std::nothrow) uint8_t[table_blob::uncompressed_size];
 4899|      1|    if (buffer == nullptr) {
  ------------------
  |  Branch (4899:9): [True: 0, False: 1]
  ------------------
 4900|      0|      tables_init_state.store(kTablesFailed, std::memory_order_release);
 4901|      0|      return false;
 4902|      0|    }
 4903|       |
 4904|      1|    const size_t n = deflate::inflate_raw(table_blob::compressed,
 4905|      1|                                          table_blob::compressed_size, buffer,
 4906|      1|                                          table_blob::uncompressed_size);
 4907|      1|    if (n != table_blob::uncompressed_size ||
  ------------------
  |  Branch (4907:9): [True: 0, False: 1]
  ------------------
 4908|      1|        crc32_ieee(buffer, n) != table_blob::uncompressed_crc32) {
  ------------------
  |  Branch (4908:9): [True: 0, False: 1]
  ------------------
 4909|      0|      delete[] buffer;
 4910|      0|      tables_init_state.store(kTablesFailed, std::memory_order_release);
 4911|      0|      return false;
 4912|      0|    }
 4913|       |
 4914|       |    // LE payload verified; convert multi-byte fields for big-endian hosts.
 4915|      1|    detail::convert_table_blob_to_host_endian(buffer);
 4916|       |
 4917|      1|    auto at = [&](size_t off) noexcept -> const uint8_t* {
 4918|      1|      return buffer + off;
 4919|      1|    };
 4920|       |
 4921|       |    // Publish all non-atomic pointers before READY. Waiters synchronize via
 4922|       |    // acquire on tables_init_state and then observe these writes.
 4923|      1|    idna_stage1 =
 4924|      1|        reinterpret_cast<const uint16_t*>(at(table_blob::off_idna_stage1));
 4925|      1|    idna_stage2 =
 4926|      1|        reinterpret_cast<const uint16_t*>(at(table_blob::off_idna_stage2));
 4927|      1|    idna_bool_blocks =
 4928|      1|        reinterpret_cast<const uint64_t*>(at(table_blob::off_idna_bool_blocks));
 4929|      1|    idna_utf8_mappings = at(table_blob::off_idna_utf8_mappings);
 4930|       |
 4931|      1|    decomposition_index = at(table_blob::off_decomposition_index);
 4932|      1|    decomposition_block_flat = reinterpret_cast<const uint16_t*>(
 4933|      1|        at(table_blob::off_decomposition_block));
 4934|      1|    decomposition_data = reinterpret_cast<const char32_t*>(
 4935|      1|        at(table_blob::off_decomposition_data));
 4936|      1|    ccc_index = at(table_blob::off_ccc_index);
 4937|      1|    ccc_block_flat = at(table_blob::off_ccc_block);
 4938|      1|    composition_index = at(table_blob::off_composition_index);
 4939|      1|    composition_block_flat = reinterpret_cast<const uint16_t*>(
 4940|      1|        at(table_blob::off_composition_block));
 4941|      1|    composition_data =
 4942|      1|        reinterpret_cast<const char32_t*>(at(table_blob::off_composition_data));
 4943|       |
 4944|      1|    id_continue =
 4945|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_id_continue_flat));
 4946|      1|    id_start =
 4947|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_id_start_flat));
 4948|       |
 4949|      1|    dir_start =
 4950|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_dir_start));
 4951|      1|    dir_final =
 4952|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_dir_final));
 4953|      1|    dir_value = at(table_blob::off_dir_value);
 4954|      1|    combining_ranges =
 4955|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_combining_flat));
 4956|       |
 4957|      1|    tables_buffer = buffer;
 4958|      1|    tables_init_state.store(kTablesReady, std::memory_order_release);
 4959|      1|    return true;
 4960|      1|  }
 4961|       |
 4962|       |  // Another thread owns init (or finished between our loads).
 4963|      0|  for (uint64_t spins = 0; spins < kTablesSpinLimit; ++spins) {
  ------------------
  |  Branch (4963:28): [True: 0, False: 0]
  ------------------
 4964|      0|    state = tables_init_state.load(std::memory_order_acquire);
 4965|      0|    if (state == kTablesReady) {
  ------------------
  |  Branch (4965:9): [True: 0, False: 0]
  ------------------
 4966|      0|      return true;
 4967|      0|    }
 4968|      0|    if (state == kTablesFailed) {
  ------------------
  |  Branch (4968:9): [True: 0, False: 0]
  ------------------
 4969|      0|      return false;
 4970|      0|    }
 4971|      0|#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
 4972|      0|    defined(_M_IX86)
 4973|      0|#if defined(__GNUC__) || defined(__clang__)
 4974|      0|    __builtin_ia32_pause();
 4975|      0|#endif
 4976|       |#elif defined(__aarch64__) || defined(_M_ARM64)
 4977|       |#if defined(__GNUC__) || defined(__clang__)
 4978|       |    asm volatile("yield" ::: "memory");
 4979|       |#endif
 4980|       |#endif
 4981|      0|  }
 4982|       |  // Timed out waiting for a peer - treat as failure rather than hang.
 4983|      0|  return false;
 4984|      0|}
_ZN3ada4idna7deflate11inflate_rawEPKhmPhm:
  535|      1|                          size_t dst_cap) {
  536|      1|  BitReader br(src, src_len);
  537|      1|  size_t out = 0;
  538|      4|  for (;;) {
  539|      4|    uint32_t bfinal = br.get(1);
  540|      4|    uint32_t btype = br.get(2);
  541|      4|    if (bfinal == UINT32_MAX || btype == UINT32_MAX) return 0;
  ------------------
  |  Branch (541:9): [True: 0, False: 4]
  |  Branch (541:33): [True: 0, False: 4]
  ------------------
  542|      4|    if (btype == 0) {
  ------------------
  |  Branch (542:9): [True: 0, False: 4]
  ------------------
  543|       |      // stored
  544|      0|      br.align_byte();
  545|      0|      if (!br.ensure(16)) return 0;
  ------------------
  |  Branch (545:11): [True: 0, False: 0]
  ------------------
  546|       |      // read 16+16 from bit buffer awkwardly - use byte path
  547|       |      // Align: bits is multiple of 8
  548|       |      // Get len from remaining bits then bytes
  549|      0|      uint32_t len = br.get(16);
  550|      0|      uint32_t nlen = br.get(16);
  551|      0|      if (len == UINT32_MAX || nlen == UINT32_MAX) return 0;
  ------------------
  |  Branch (551:11): [True: 0, False: 0]
  |  Branch (551:32): [True: 0, False: 0]
  ------------------
  552|      0|      if ((len ^ 0xFFFF) != nlen) return 0;
  ------------------
  |  Branch (552:11): [True: 0, False: 0]
  ------------------
  553|       |      // After get, may have bits in acc from previous - for stored, should be
  554|       |      // byte aligned Copy len bytes from p
  555|      0|      if (br.bits != 0) {
  ------------------
  |  Branch (555:11): [True: 0, False: 0]
  ------------------
  556|       |        // leftover bits should be 0 if aligned
  557|      0|      }
  558|      0|      if (size_t(br.end - br.p) < len) return 0;
  ------------------
  |  Branch (558:11): [True: 0, False: 0]
  ------------------
  559|      0|      if (out + len > dst_cap) return 0;
  ------------------
  |  Branch (559:11): [True: 0, False: 0]
  ------------------
  560|      0|      std::memcpy(dst + out, br.p, len);
  561|      0|      br.p += len;
  562|      0|      out += len;
  563|      4|    } else if (btype == 1 || btype == 2) {
  ------------------
  |  Branch (563:16): [True: 0, False: 4]
  |  Branch (563:30): [True: 4, False: 0]
  ------------------
  564|      4|      Huff lit, dist;
  565|      4|      if (btype == 1) {
  ------------------
  |  Branch (565:11): [True: 0, False: 4]
  ------------------
  566|       |        // use fixed via functions
  567|      4|      } else {
  568|       |        // dynamic
  569|      4|        uint32_t hlit = br.get(5);
  570|      4|        if (hlit == UINT32_MAX) return 0;
  ------------------
  |  Branch (570:13): [True: 0, False: 4]
  ------------------
  571|      4|        hlit += 257;
  572|      4|        uint32_t hdist = br.get(5);
  573|      4|        if (hdist == UINT32_MAX) return 0;
  ------------------
  |  Branch (573:13): [True: 0, False: 4]
  ------------------
  574|      4|        hdist += 1;
  575|      4|        uint32_t hclen = br.get(4);
  576|      4|        if (hclen == UINT32_MAX) return 0;
  ------------------
  |  Branch (576:13): [True: 0, False: 4]
  ------------------
  577|      4|        hclen += 4;
  578|      4|        static const int order[19] = {16, 17, 18, 0, 8,  7, 9,  6, 10, 5,
  579|      4|                                      11, 4,  12, 3, 13, 2, 14, 1, 15};
  580|      4|        uint8_t clen[19]{};
  581|     69|        for (uint32_t i = 0; i < hclen; i++) {
  ------------------
  |  Branch (581:30): [True: 65, False: 4]
  ------------------
  582|     65|          uint32_t v = br.get(3);
  583|     65|          if (v == UINT32_MAX) return 0;
  ------------------
  |  Branch (583:15): [True: 0, False: 65]
  ------------------
  584|     65|          clen[order[i]] = uint8_t(v);
  585|     65|        }
  586|      4|        Huff cl;
  587|      4|        if (!cl.build(clen, 19)) return 0;
  ------------------
  |  Branch (587:13): [True: 0, False: 4]
  ------------------
  588|      4|        uint8_t lens[288 + 32]{};
  589|      4|        uint32_t n = hlit + hdist;
  590|  1.09k|        for (uint32_t i = 0; i < n;) {
  ------------------
  |  Branch (590:30): [True: 1.08k, False: 4]
  ------------------
  591|  1.08k|          int sym = cl.decode(br);
  592|  1.08k|          if (sym < 0) return 0;
  ------------------
  |  Branch (592:15): [True: 0, False: 1.08k]
  ------------------
  593|  1.08k|          if (sym < 16) {
  ------------------
  |  Branch (593:15): [True: 1.03k, False: 49]
  ------------------
  594|  1.03k|            lens[i++] = uint8_t(sym);
  595|  1.03k|          } else if (sym == 16) {
  ------------------
  |  Branch (595:22): [True: 49, False: 0]
  ------------------
  596|     49|            uint32_t rep = br.get(2);
  597|     49|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (597:17): [True: 0, False: 49]
  ------------------
  598|     49|            rep += 3;
  599|     49|            if (i == 0) return 0;
  ------------------
  |  Branch (599:17): [True: 0, False: 49]
  ------------------
  600|     49|            uint8_t v = lens[i - 1];
  601|    263|            while (rep--) lens[i++] = v;
  ------------------
  |  Branch (601:20): [True: 214, False: 49]
  ------------------
  602|     49|          } else if (sym == 17) {
  ------------------
  |  Branch (602:22): [True: 0, False: 0]
  ------------------
  603|      0|            uint32_t rep = br.get(3);
  604|      0|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (604:17): [True: 0, False: 0]
  ------------------
  605|      0|            rep += 3;
  606|      0|            while (rep--) lens[i++] = 0;
  ------------------
  |  Branch (606:20): [True: 0, False: 0]
  ------------------
  607|      0|          } else if (sym == 18) {
  ------------------
  |  Branch (607:22): [True: 0, False: 0]
  ------------------
  608|      0|            uint32_t rep = br.get(7);
  609|      0|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (609:17): [True: 0, False: 0]
  ------------------
  610|      0|            rep += 11;
  611|      0|            while (rep--) lens[i++] = 0;
  ------------------
  |  Branch (611:20): [True: 0, False: 0]
  ------------------
  612|      0|          } else
  613|      0|            return 0;
  614|  1.08k|        }
  615|      4|        if (!lit.build(lens, int(hlit))) return 0;
  ------------------
  |  Branch (615:13): [True: 0, False: 4]
  ------------------
  616|      4|        if (!dist.build(lens + hlit, int(hdist))) return 0;
  ------------------
  |  Branch (616:13): [True: 0, False: 4]
  ------------------
  617|      4|      }
  618|  52.2k|      for (;;) {
  619|  52.2k|        int sym;
  620|  52.2k|        if (btype == 1)
  ------------------
  |  Branch (620:13): [True: 0, False: 52.2k]
  ------------------
  621|      0|          sym = fixed_litlen(br);
  622|  52.2k|        else
  623|  52.2k|          sym = lit.decode(br);
  624|  52.2k|        if (sym < 0) return 0;
  ------------------
  |  Branch (624:13): [True: 0, False: 52.2k]
  ------------------
  625|  52.2k|        if (sym < 256) {
  ------------------
  |  Branch (625:13): [True: 37.2k, False: 14.9k]
  ------------------
  626|  37.2k|          if (out >= dst_cap) return 0;
  ------------------
  |  Branch (626:15): [True: 0, False: 37.2k]
  ------------------
  627|  37.2k|          dst[out++] = uint8_t(sym);
  628|  37.2k|        } else if (sym == 256) {
  ------------------
  |  Branch (628:20): [True: 4, False: 14.9k]
  ------------------
  629|      4|          break;
  630|  14.9k|        } else {
  631|  14.9k|          int len_code = sym - 257;
  632|  14.9k|          if (len_code < 0 || len_code > 28) return 0;
  ------------------
  |  Branch (632:15): [True: 0, False: 14.9k]
  |  Branch (632:31): [True: 0, False: 14.9k]
  ------------------
  633|  14.9k|          uint32_t extra = br.get(len_extra[len_code]);
  634|  14.9k|          if (len_extra[len_code] && extra == UINT32_MAX) return 0;
  ------------------
  |  Branch (634:15): [True: 2.03k, False: 12.9k]
  |  Branch (634:38): [True: 0, False: 2.03k]
  ------------------
  635|  14.9k|          uint32_t length =
  636|  14.9k|              len_base[len_code] + (len_extra[len_code] ? extra : 0);
  ------------------
  |  Branch (636:37): [True: 2.03k, False: 12.9k]
  ------------------
  637|  14.9k|          int dsym;
  638|  14.9k|          if (btype == 1)
  ------------------
  |  Branch (638:15): [True: 0, False: 14.9k]
  ------------------
  639|      0|            dsym = fixed_dist(br);
  640|  14.9k|          else
  641|  14.9k|            dsym = dist.decode(br);
  642|  14.9k|          if (dsym < 0 || dsym > 29) return 0;
  ------------------
  |  Branch (642:15): [True: 0, False: 14.9k]
  |  Branch (642:27): [True: 0, False: 14.9k]
  ------------------
  643|  14.9k|          uint32_t dextra = br.get(dist_extra[dsym]);
  644|  14.9k|          if (dist_extra[dsym] && dextra == UINT32_MAX) return 0;
  ------------------
  |  Branch (644:15): [True: 9.97k, False: 5.01k]
  |  Branch (644:35): [True: 0, False: 9.97k]
  ------------------
  645|  14.9k|          uint32_t distance = dist_base[dsym] + (dist_extra[dsym] ? dextra : 0);
  ------------------
  |  Branch (645:50): [True: 9.97k, False: 5.01k]
  ------------------
  646|  14.9k|          if (distance > out || out + length > dst_cap) return 0;
  ------------------
  |  Branch (646:15): [True: 0, False: 14.9k]
  |  Branch (646:33): [True: 0, False: 14.9k]
  ------------------
  647|   202k|          for (uint32_t k = 0; k < length; k++) {
  ------------------
  |  Branch (647:32): [True: 187k, False: 14.9k]
  ------------------
  648|   187k|            dst[out] = dst[out - distance];
  649|   187k|            out++;
  650|   187k|          }
  651|  14.9k|        }
  652|  52.2k|      }
  653|      4|    } else {
  654|      0|      return 0;  // reserved
  655|      0|    }
  656|      4|    if (bfinal) break;
  ------------------
  |  Branch (656:9): [True: 1, False: 3]
  ------------------
  657|      4|  }
  658|      1|  return out;
  659|      1|}
_ZN3ada4idna7deflate9BitReaderC2EPKhm:
  402|      1|      : p(data), end(data + len) {}
_ZN3ada4idna7deflate9BitReader3getEi:
  412|   457k|  uint32_t get(int n) {
  413|   457k|    if (!ensure(n)) return UINT32_MAX;
  ------------------
  |  Branch (413:9): [True: 0, False: 457k]
  ------------------
  414|   457k|    uint32_t v = acc & ((1u << n) - 1);
  415|   457k|    acc >>= n;
  416|   457k|    bits -= n;
  417|   457k|    return v;
  418|   457k|  }
_ZN3ada4idna7deflate9BitReader6ensureEi:
  404|   457k|  bool ensure(int n) {
  405|   520k|    while (bits < n) {
  ------------------
  |  Branch (405:12): [True: 62.3k, False: 457k]
  ------------------
  406|  62.3k|      if (p >= end) return false;
  ------------------
  |  Branch (406:11): [True: 0, False: 62.3k]
  ------------------
  407|  62.3k|      acc |= uint32_t(*p++) << bits;
  408|  62.3k|      bits += 8;
  409|  62.3k|    }
  410|   457k|    return true;
  411|   457k|  }
_ZNK3ada4idna7deflate4Huff6decodeERNS1_9BitReaderE:
  471|  68.3k|  int decode(BitReader& br) const {
  472|  68.3k|    if (max_bits == 0) return -1;
  ------------------
  |  Branch (472:9): [True: 0, False: 68.3k]
  ------------------
  473|  68.3k|    int code = 0;
  474|   427k|    for (int len = 1; len <= max_bits; len++) {
  ------------------
  |  Branch (474:23): [True: 427k, False: 0]
  ------------------
  475|   427k|      uint32_t b = br.get(1);
  476|   427k|      if (b == UINT32_MAX) return -1;
  ------------------
  |  Branch (476:11): [True: 0, False: 427k]
  ------------------
  477|   427k|      code = (code << 1) | int(b);
  478|   427k|      int first = first_code[len];
  479|   427k|      int cnt = counts[len];
  480|   427k|      if (cnt && code - first < cnt) {
  ------------------
  |  Branch (480:11): [True: 263k, False: 164k]
  |  Branch (480:18): [True: 68.3k, False: 195k]
  ------------------
  481|  68.3k|        return symbols[base_idx[len] + (code - first)];
  482|  68.3k|      }
  483|   427k|    }
  484|      0|    return -1;
  485|  68.3k|  }
_ZN3ada4idna10crc32_ieeeEPKhm:
 4868|      1|                                         size_t len) noexcept {
 4869|      1|  uint32_t c = 0xFFFFFFFFu;
 4870|   224k|  for (size_t i = 0; i < len; ++i) {
  ------------------
  |  Branch (4870:22): [True: 224k, False: 1]
  ------------------
 4871|   224k|    c ^= data[i];
 4872|  2.02M|    for (int k = 0; k < 8; ++k) {
  ------------------
  |  Branch (4872:21): [True: 1.79M, False: 224k]
  ------------------
 4873|  1.79M|      const uint32_t mask = 0u - (c & 1u);
 4874|  1.79M|      c = (c >> 1) ^ (0xEDB88320u & mask);
 4875|  1.79M|    }
 4876|   224k|  }
 4877|      1|  return ~c;
 4878|      1|}
_ZN3ada4idna6detail33convert_table_blob_to_host_endianEPh:
 4691|      1|inline void convert_table_blob_to_host_endian(uint8_t* buffer) noexcept {
 4692|      1|  if constexpr (std::endian::native == std::endian::little) {
 4693|      1|    (void)buffer;
 4694|      1|    return;
 4695|      1|  }
 4696|      0|  auto u16 = [&](size_t off, size_t count) {
 4697|      1|    bswap_inplace(reinterpret_cast<uint16_t*>(buffer + off), count);
 4698|      1|  };
 4699|      1|  auto u32 = [&](size_t off, size_t count) {
 4700|      1|    bswap_inplace(reinterpret_cast<uint32_t*>(buffer + off), count);
 4701|      1|  };
 4702|      1|  auto u64 = [&](size_t off, size_t count) {
 4703|      1|    bswap_inplace(reinterpret_cast<uint64_t*>(buffer + off), count);
 4704|      1|  };
 4705|       |
 4706|      1|  u16(table_blob::off_idna_stage1, table_blob::count_idna_stage1);
 4707|      1|  u16(table_blob::off_idna_stage2, table_blob::count_idna_stage2);
 4708|      1|  u64(table_blob::off_idna_bool_blocks, table_blob::count_idna_bool_blocks);
 4709|      1|  u16(table_blob::off_decomposition_block,
 4710|      1|      table_blob::count_decomposition_block);
 4711|      1|  u32(table_blob::off_decomposition_data, table_blob::count_decomposition_data);
 4712|      1|  u16(table_blob::off_composition_block, table_blob::count_composition_block);
 4713|      1|  u32(table_blob::off_composition_data, table_blob::count_composition_data);
 4714|      1|  u32(table_blob::off_id_continue_flat, table_blob::count_id_continue_flat);
 4715|      1|  u32(table_blob::off_id_start_flat, table_blob::count_id_start_flat);
 4716|      1|  u32(table_blob::off_dir_start, table_blob::count_dir_start);
 4717|      1|  u32(table_blob::off_dir_final, table_blob::count_dir_final);
 4718|      1|  u32(table_blob::off_combining_flat, table_blob::count_combining_flat);
 4719|      1|}
_ZZN3ada4idna13ensure_tablesEvENKUlmE_clEm:
 4917|     18|    auto at = [&](size_t off) noexcept -> const uint8_t* {
 4918|     18|      return buffer + off;
 4919|     18|    };
can_parse.cc:_ZN3ada4idnaL11idna_lookupEj:
 5072|   638k|static uint16_t idna_lookup(uint32_t cp) noexcept {
 5073|   638k|  if (cp < IDNA_LOW_RANGE_END) {
  ------------------
  |  Branch (5073:7): [True: 633k, False: 5.37k]
  ------------------
 5074|   633k|    uint16_t ref = idna_stage1[cp >> IDNA_BLOCK_BITS];
 5075|   633k|    if (ref & IDNA_BOOL_FLAG) {
  ------------------
  |  Branch (5075:9): [True: 273k, False: 359k]
  ------------------
 5076|   273k|      uint32_t bit_idx =
 5077|   273k|          static_cast<uint32_t>(ref & ~IDNA_BOOL_FLAG) * IDNA_BLOCK_SIZE +
 5078|   273k|          (cp & IDNA_BLOCK_MASK);
 5079|   273k|      bool is_valid = (idna_bool_blocks[bit_idx >> 6] >> (bit_idx & 63u)) & 1u;
 5080|   273k|      return is_valid ? IDNA_VALID : IDNA_DISALLOWED;
  ------------------
  |  Branch (5080:14): [True: 273k, False: 45]
  ------------------
 5081|   273k|    }
 5082|   359k|    return idna_stage2[ref + (cp & IDNA_BLOCK_MASK)];
 5083|   633k|  }
 5084|       |
 5085|  5.37k|  if (cp >= IDNA_HIGH_IGNORED_START && cp < IDNA_HIGH_IGNORED_END) {
  ------------------
  |  Branch (5085:7): [True: 5.28k, False: 93]
  |  Branch (5085:40): [True: 4.89k, False: 393]
  ------------------
 5086|  4.89k|    return IDNA_IGNORED;
 5087|  4.89k|  }
 5088|       |
 5089|    486|  return IDNA_DISALLOWED;
 5090|  5.37k|}
can_parse.cc:_ZN3ada4idnaL21utf8_count_codepointsEPKh:
 5115|   104k|static size_t utf8_count_codepoints(const uint8_t* ptr) noexcept {
 5116|   104k|  size_t n = 0;
 5117|   582k|  while (*ptr != 0) {
  ------------------
  |  Branch (5117:10): [True: 478k, False: 104k]
  ------------------
 5118|   478k|    ++n;
 5119|   478k|    if (*ptr < 0x80u) {
  ------------------
  |  Branch (5119:9): [True: 120k, False: 357k]
  ------------------
 5120|   120k|      ++ptr;
 5121|   357k|    } else if (*ptr < 0xE0u) {
  ------------------
  |  Branch (5121:16): [True: 227k, False: 130k]
  ------------------
 5122|   227k|      ptr += 2;
 5123|   227k|    } else if (*ptr < 0xF0u) {
  ------------------
  |  Branch (5123:16): [True: 127k, False: 2.69k]
  ------------------
 5124|   127k|      ptr += 3;
 5125|   127k|    } else {
 5126|  2.69k|      ptr += 4;
 5127|  2.69k|    }
 5128|   478k|  }
 5129|   104k|  return n;
 5130|   104k|}
can_parse.cc:_ZN3ada4idnaL9utf8_nextERPKh:
 5093|   476k|static char32_t utf8_next(const uint8_t*& ptr) noexcept {
 5094|   476k|  uint8_t b0 = *ptr++;
 5095|   476k|  if (b0 < 0x80u) return static_cast<char32_t>(b0);
  ------------------
  |  Branch (5095:7): [True: 120k, False: 356k]
  ------------------
 5096|   356k|  if (b0 < 0xE0u) {
  ------------------
  |  Branch (5096:7): [True: 227k, False: 128k]
  ------------------
 5097|   227k|    uint32_t cp = (b0 & 0x1Fu) << 6;
 5098|   227k|    cp |= (*ptr++ & 0x3Fu);
 5099|   227k|    return static_cast<char32_t>(cp);
 5100|   227k|  }
 5101|   128k|  if (b0 < 0xF0u) {
  ------------------
  |  Branch (5101:7): [True: 126k, False: 2.62k]
  ------------------
 5102|   126k|    uint32_t cp = (b0 & 0x0Fu) << 12;
 5103|   126k|    cp |= ((*ptr++ & 0x3Fu) << 6);
 5104|   126k|    cp |= (*ptr++ & 0x3Fu);
 5105|   126k|    return static_cast<char32_t>(cp);
 5106|   126k|  }
 5107|  2.62k|  uint32_t cp = (b0 & 0x07u) << 18;
 5108|  2.62k|  cp |= ((*ptr++ & 0x3Fu) << 12);
 5109|  2.62k|  cp |= ((*ptr++ & 0x3Fu) << 6);
 5110|  2.62k|  cp |= (*ptr++ & 0x3Fu);
 5111|  2.62k|  return static_cast<char32_t>(cp);
 5112|   128k|}
_ZN3ada4idna23decomposition_block_rowEh:
 4989|   617k|inline const uint16_t* decomposition_block_row(uint8_t bi) noexcept {
 4990|   617k|  if (bi >= kDecompBlockRows) {
  ------------------
  |  Branch (4990:7): [True: 0, False: 617k]
  ------------------
 4991|      0|    bi = 0;
 4992|      0|  }
 4993|   617k|  return decomposition_block_flat + static_cast<size_t>(bi) * kDecompBlockCols;
 4994|   617k|}
_ZN3ada4idna13ccc_block_rowEh:
 4996|  1.11M|inline const uint8_t* ccc_block_row(uint8_t bi) noexcept {
 4997|  1.11M|  if (bi >= kCccBlockRows) {
  ------------------
  |  Branch (4997:7): [True: 0, False: 1.11M]
  ------------------
 4998|      0|    bi = 0;
 4999|      0|  }
 5000|  1.11M|  return ccc_block_flat + static_cast<size_t>(bi) * kCccBlockCols;
 5001|  1.11M|}
_ZN3ada4idna16tables_are_readyEv:
 4880|  15.6k|[[nodiscard]] inline bool tables_are_ready() noexcept {
 4881|  15.6k|  return tables_init_state.load(std::memory_order_acquire) == kTablesReady;
 4882|  15.6k|}
can_parse.cc:_ZN3ada4idnaL23canonical_decomp_lengthEDi:
 5272|   526k|static size_t canonical_decomp_length(char32_t current_character) noexcept {
 5273|   526k|  if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5273:7): [True: 28.3k, False: 498k]
  ------------------
 5274|  28.3k|      current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5274:7): [True: 15.9k, False: 12.4k]
  ------------------
 5275|       |    // Hangul precomposed syllables are NFC.
 5276|  15.9k|    return 0;
 5277|  15.9k|  }
 5278|   510k|  if (current_character >= 0x110000) {
  ------------------
  |  Branch (5278:7): [True: 0, False: 510k]
  ------------------
 5279|      0|    return 0;
 5280|      0|  }
 5281|   510k|  const uint8_t di = decomposition_index[current_character >> 8];
 5282|   510k|  const uint16_t* const decomposition =
 5283|   510k|      decomposition_block_row(di) + (current_character % 256);
 5284|   510k|  size_t decomposition_length =
 5285|   510k|      (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5286|   510k|  if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5286:7): [True: 12.0k, False: 498k]
  |  Branch (5286:37): [True: 0, False: 12.0k]
  ------------------
 5287|      0|    decomposition_length = 0;  // compatibility-only: ignored for NFC
 5288|      0|  }
 5289|   510k|  return decomposition_length;
 5290|   510k|}
can_parse.cc:_ZN3ada4idnaL13would_composeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5404|  14.4k|static bool would_compose(std::u32string_view input) noexcept {
 5405|   461k|  for (size_t input_count = 0; input_count < input.size();) {
  ------------------
  |  Branch (5405:32): [True: 451k, False: 9.99k]
  ------------------
 5406|   451k|    const char32_t current = input[input_count];
 5407|   451k|    if (current >= hangul_lbase && current < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5407:9): [True: 155k, False: 296k]
  |  Branch (5407:36): [True: 5.00k, False: 150k]
  ------------------
 5408|  5.00k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5408:11): [True: 4.78k, False: 219]
  ------------------
 5409|  4.78k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5409:11): [True: 1.52k, False: 3.26k]
  ------------------
 5410|  1.52k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5410:11): [True: 366, False: 1.16k]
  ------------------
 5411|    366|        return true;
 5412|    366|      }
 5413|  4.64k|      ++input_count;
 5414|  4.64k|      continue;
 5415|  5.00k|    }
 5416|   446k|    if (current >= hangul_sbase && current < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5416:9): [True: 14.3k, False: 432k]
  |  Branch (5416:36): [True: 9.03k, False: 5.29k]
  ------------------
 5417|  9.03k|      if ((current - hangul_sbase) % hangul_tcount &&
  ------------------
  |  Branch (5417:11): [True: 6.65k, False: 2.38k]
  ------------------
 5418|  6.65k|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (5418:11): [True: 6.25k, False: 393]
  ------------------
 5419|  6.25k|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5419:11): [True: 5.43k, False: 822]
  ------------------
 5420|  5.43k|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5420:11): [True: 1.38k, False: 4.05k]
  ------------------
 5421|  1.38k|        return true;
 5422|  1.38k|      }
 5423|  7.65k|      ++input_count;
 5424|  7.65k|      continue;
 5425|  9.03k|    }
 5426|   437k|    if (current < 0x110000) {
  ------------------
  |  Branch (5426:9): [True: 437k, False: 0]
  ------------------
 5427|   437k|      const uint16_t* composition =
 5428|   437k|          composition_block_row(composition_index[current >> 8]) +
 5429|   437k|          (current % 256);
 5430|   437k|      int32_t previous_ccc = -1;
 5431|   437k|      size_t j = input_count;
 5432|   444k|      for (; j + 1 < input.size(); ++j) {
  ------------------
  |  Branch (5432:14): [True: 435k, False: 9.27k]
  ------------------
 5433|   435k|        uint8_t ccc = get_ccc(input[j + 1]);
 5434|   435k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (5434:13): [True: 86.6k, False: 348k]
  |  Branch (5434:49): [True: 83.2k, False: 3.42k]
  ------------------
 5435|  83.2k|          int left = composition[0];
 5436|  83.2k|          int right = composition[1];
 5437|  83.2k|          if (left < 0 || right < left ||
  ------------------
  |  Branch (5437:15): [True: 0, False: 83.2k]
  |  Branch (5437:27): [True: 0, False: 83.2k]
  ------------------
 5438|  83.2k|              static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5438:15): [True: 0, False: 83.2k]
  ------------------
 5439|      0|            break;
 5440|      0|          }
 5441|   226k|          while (left + 2 < right) {
  ------------------
  |  Branch (5441:18): [True: 142k, False: 83.2k]
  ------------------
 5442|   142k|            int middle = left + (((right - left) >> 1) & ~1);
 5443|   142k|            if (composition_data[static_cast<size_t>(middle)] <= input[j + 1]) {
  ------------------
  |  Branch (5443:17): [True: 13.5k, False: 129k]
  ------------------
 5444|  13.5k|              left = middle;
 5445|  13.5k|            }
 5446|   142k|            if (composition_data[static_cast<size_t>(middle)] >= input[j + 1]) {
  ------------------
  |  Branch (5446:17): [True: 131k, False: 11.7k]
  ------------------
 5447|   131k|              right = middle;
 5448|   131k|            }
 5449|   142k|          }
 5450|  83.2k|          if (static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5450:15): [True: 83.2k, False: 0]
  ------------------
 5451|  83.2k|              composition_data[static_cast<size_t>(left)] == input[j + 1]) {
  ------------------
  |  Branch (5451:15): [True: 2.70k, False: 80.5k]
  ------------------
 5452|  2.70k|            return true;
 5453|  2.70k|          }
 5454|  83.2k|        }
 5455|   432k|        if (ccc == 0) {
  ------------------
  |  Branch (5455:13): [True: 425k, False: 7.22k]
  ------------------
 5456|   425k|          break;
 5457|   425k|        }
 5458|  7.22k|        previous_ccc = ccc;
 5459|  7.22k|      }
 5460|   434k|      input_count = j + 1;
 5461|   434k|      continue;
 5462|   437k|    }
 5463|      0|    ++input_count;
 5464|      0|  }
 5465|  9.99k|  return false;
 5466|  14.4k|}
_ZN3ada4idna21composition_block_rowEh:
 5003|   497k|inline const uint16_t* composition_block_row(uint8_t bi) noexcept {
 5004|   497k|  if (bi >= kCompBlockRows) {
  ------------------
  |  Branch (5004:7): [True: 0, False: 497k]
  ------------------
 5005|      0|    bi = 0;
 5006|      0|  }
 5007|   497k|  return composition_block_flat + static_cast<size_t>(bi) * kCompBlockCols;
 5008|   497k|}
can_parse.cc:_ZN3ada4idnaL19char_to_digit_valueEc:
 5632|  83.0k|static constexpr int32_t char_to_digit_value(char value) {
 5633|  83.0k|  if (value >= 'a' && value <= 'z') return value - 'a';
  ------------------
  |  Branch (5633:7): [True: 72.6k, False: 10.4k]
  |  Branch (5633:23): [True: 72.6k, False: 15]
  ------------------
 5634|  10.4k|  if (value >= '0' && value <= '9') return value - '0' + 26;
  ------------------
  |  Branch (5634:7): [True: 10.4k, False: 54]
  |  Branch (5634:23): [True: 10.3k, False: 18]
  ------------------
 5635|     72|  return -1;
 5636|  10.4k|}
can_parse.cc:_ZN3ada4idnaL5adaptEiib:
 5642|   265k|static constexpr int32_t adapt(int32_t d, int32_t n, bool firsttime) {
 5643|   265k|  if (firsttime) {
  ------------------
  |  Branch (5643:7): [True: 42.9k, False: 222k]
  ------------------
 5644|  42.9k|    d = d / damp;
 5645|   222k|  } else {
 5646|   222k|    d = d / 2;
 5647|   222k|  }
 5648|   265k|  d += d / n;
 5649|   265k|  int32_t k = 0;
 5650|   364k|  while (d > ((base - tmin) * tmax) / 2) {
  ------------------
  |  Branch (5650:10): [True: 98.6k, False: 265k]
  ------------------
 5651|  98.6k|    d /= base - tmin;
 5652|  98.6k|    k += base;
 5653|  98.6k|  }
 5654|   265k|  return k + (((base - tmin + 1) * d) / (d + skew));
 5655|   265k|}
can_parse.cc:_ZN3ada4idnaL13digit_to_charEi:
 5638|   506k|static constexpr char digit_to_char(int32_t digit) {
 5639|   506k|  return digit < 26 ? char(digit + 97) : char(digit + 22);
  ------------------
  |  Branch (5639:10): [True: 358k, False: 147k]
  ------------------
 5640|   506k|}
can_parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_0clIA2_jEEDaRKT_:
 5977|   358k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
can_parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_1clEj:
 6062|  5.40k|      auto is_l_or_d = [](uint32_t code) {
 6063|  5.40k|        return std::ranges::binary_search(L, code) ||
  ------------------
  |  Branch (6063:16): [True: 288, False: 5.11k]
  ------------------
 6064|  5.11k|               std::ranges::binary_search(D, code);
  ------------------
  |  Branch (6064:16): [True: 1.48k, False: 3.62k]
  ------------------
 6065|  5.40k|      };
can_parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_2clEj:
 6066|  6.69k|      auto is_r_or_d = [](uint32_t code) {
 6067|  6.69k|        return std::ranges::binary_search(R, code) ||
  ------------------
  |  Branch (6067:16): [True: 876, False: 5.82k]
  ------------------
 6068|  5.82k|               std::ranges::binary_search(D, code);
  ------------------
  |  Branch (6068:16): [True: 606, False: 5.21k]
  ------------------
 6069|  6.69k|      };
can_parse.cc:_ZN3ada4idnaL20find_last_not_of_nsmENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5924|  40.2k|    const std::u32string_view label) noexcept {
 5925|  44.2k|  for (int i = static_cast<int>(label.size() - 1); i >= 0; i--)
  ------------------
  |  Branch (5925:52): [True: 44.2k, False: 0]
  ------------------
 5926|  44.2k|    if (find_direction(label[i]) != direction::NSM) return i;
  ------------------
  |  Branch (5926:9): [True: 40.2k, False: 4.02k]
  ------------------
 5927|       |
 5928|      0|  return std::u32string_view::npos;
 5929|  40.2k|}
can_parse.cc:_ZN3ada4idnaL12is_rtl_labelENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5933|  40.2k|inline static bool is_rtl_label(const std::u32string_view label) noexcept {
 5934|  40.2k|  const size_t mask =
 5935|  40.2k|      (1u << direction::R) | (1u << direction::AL) | (1u << direction::AN);
 5936|       |
 5937|  40.2k|  size_t directions = 0;
 5938|   278k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (5938:22): [True: 237k, False: 40.2k]
  ------------------
 5939|   237k|    directions |= 1u << find_direction(label[i]);
 5940|   237k|  }
 5941|  40.2k|  return (directions & mask) != 0;
 5942|  40.2k|}
can_parse.cc:_ZN3ada4idnaL14find_directionEj:
 5905|   317k|inline static direction find_direction(uint32_t code_point) noexcept {
 5906|       |  // Binary search on dir_final (sorted upper bounds).
 5907|   317k|  size_t lo = 0, hi = dir_table_count;
 5908|  3.57M|  while (lo < hi) {
  ------------------
  |  Branch (5908:10): [True: 3.25M, False: 317k]
  ------------------
 5909|  3.25M|    size_t mid = lo + (hi - lo) / 2;
 5910|  3.25M|    if (dir_final[mid] < code_point) {
  ------------------
  |  Branch (5910:9): [True: 1.39M, False: 1.86M]
  ------------------
 5911|  1.39M|      lo = mid + 1;
 5912|  1.86M|    } else {
 5913|  1.86M|      hi = mid;
 5914|  1.86M|    }
 5915|  3.25M|  }
 5916|   317k|  if (lo == dir_table_count) {
  ------------------
  |  Branch (5916:7): [True: 0, False: 317k]
  ------------------
 5917|      0|    return direction::NONE;
 5918|      0|  }
 5919|   317k|  return code_point >= dir_start[lo] ? static_cast<direction>(dir_value[lo])
  ------------------
  |  Branch (5919:10): [True: 315k, False: 2.49k]
  ------------------
 5920|   317k|                                     : direction::NONE;
 5921|   317k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6290|  20.2k|bool constexpr is_ascii(std::string_view view) {
 6291|   508k|  for (uint8_t c : view) {
  ------------------
  |  Branch (6291:18): [True: 508k, False: 7.17k]
  ------------------
 6292|   508k|    if (c >= 0x80) {
  ------------------
  |  Branch (6292:9): [True: 13.0k, False: 495k]
  ------------------
 6293|  13.0k|      return false;
 6294|  13.0k|    }
 6295|   508k|  }
 6296|  7.17k|  return true;
 6297|  20.2k|}
can_parse.cc:_ZN3ada4idnaL19from_ascii_to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6327|  7.17k|static void from_ascii_to_ascii(std::string_view ut8_string, std::string& out) {
 6328|  7.17k|  out.assign(ut8_string);
 6329|  7.17k|  ascii_map(out.data(), out.size());
 6330|  7.17k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6281|  66.2k|bool constexpr is_ascii(std::u32string_view view) {
 6282|   243k|  for (uint32_t c : view) {
  ------------------
  |  Branch (6282:19): [True: 243k, False: 8.58k]
  ------------------
 6283|   243k|    if (c >= 0x80) {
  ------------------
  |  Branch (6283:9): [True: 57.6k, False: 186k]
  ------------------
 6284|  57.6k|      return false;
 6285|  57.6k|    }
 6286|   243k|  }
 6287|  8.58k|  return true;
 6288|  66.2k|}
can_parse.cc:_ZN3ada4idnaL13is_ace_prefixENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6343|  50.9k|static bool is_ace_prefix(std::u32string_view label) noexcept {
 6344|  50.9k|  return label.size() >= 4 && label[0] == U'x' && label[1] == U'n' &&
  ------------------
  |  Branch (6344:10): [True: 36.9k, False: 14.0k]
  |  Branch (6344:31): [True: 7.33k, False: 29.5k]
  |  Branch (6344:51): [True: 6.35k, False: 984]
  ------------------
 6345|  6.35k|         label[2] == U'-' && label[3] == U'-';
  ------------------
  |  Branch (6345:10): [True: 5.81k, False: 537]
  |  Branch (6345:30): [True: 5.28k, False: 528]
  ------------------
 6346|  50.9k|}
can_parse.cc:_ZN3ada4idnaL18append_ascii_labelERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS1_17basic_string_viewIDiNS3_IDiEEEE:
 6333|  10.7k|static void append_ascii_label(std::string& out, std::u32string_view label) {
 6334|  10.7k|  const size_t old = out.size();
 6335|  10.7k|  out.resize(old + label.size());
 6336|  10.7k|  char* dest = out.data() + old;
 6337|   147k|  for (char32_t c : label) {
  ------------------
  |  Branch (6337:19): [True: 147k, False: 10.7k]
  ------------------
 6338|   147k|    *dest++ = static_cast<char>(c);
 6339|   147k|  }
 6340|  10.7k|}
_ZN3ada7unicode18is_ascii_hex_digitEc:
 7075|  14.5k|ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept {
 7076|  14.5k|  return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
  ------------------
  |  Branch (7076:11): [True: 7.45k, False: 7.11k]
  |  Branch (7076:23): [True: 3.31k, False: 4.14k]
  |  Branch (7076:37): [True: 4.12k, False: 7.12k]
  |  Branch (7076:49): [True: 2.55k, False: 1.56k]
  ------------------
 7077|  8.69k|         (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7077:11): [True: 1.51k, False: 7.17k]
  |  Branch (7077:23): [True: 1.28k, False: 237]
  ------------------
 7078|  14.5k|}
_ZN3ada7unicode21convert_hex_to_binaryEc:
 7165|  4.01k|unsigned constexpr convert_hex_to_binary(const char c) noexcept {
 7166|  4.01k|  return hex_to_binary_table[c - '0'];
 7167|  4.01k|}
can_parse.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
 7306|  56.9k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7307|  56.9k|    return character_sets::bit_at(character_set, c);
 7308|  56.9k|  });
_ZN3ada7unicode36contains_forbidden_domain_code_pointEPKcm:
 7002|  25.5k|    const char* input, size_t length) noexcept {
 7003|  25.5k|  size_t i = 0;
 7004|  25.5k|  uint8_t accumulator{};
 7005|   395k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7005:10): [True: 369k, False: 25.5k]
  ------------------
 7006|   369k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7007|   369k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 1])];
 7008|   369k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 2])];
 7009|   369k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 3])];
 7010|   369k|  }
 7011|  63.9k|  for (; i < length; i++) {
  ------------------
  |  Branch (7011:10): [True: 38.4k, False: 25.5k]
  ------------------
 7012|  38.4k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7013|  38.4k|  }
 7014|  25.5k|  return accumulator;
 7015|  25.5k|}
can_parse.cc:_ZN3ada11serializers12_GLOBAL__N_113write_hex_u16EPct:
 7437|  2.10k|ada_really_inline char* write_hex_u16(char* p, uint16_t v) noexcept {
 7438|  2.10k|  static constexpr char kHex[] = "0123456789abcdef";
 7439|  2.10k|  if (v >= 0x1000) {
  ------------------
  |  Branch (7439:7): [True: 231, False: 1.87k]
  ------------------
 7440|    231|    *p++ = kHex[(v >> 12) & 0xf];
 7441|    231|    *p++ = kHex[(v >> 8) & 0xf];
 7442|    231|    *p++ = kHex[(v >> 4) & 0xf];
 7443|    231|    *p++ = kHex[v & 0xf];
 7444|  1.87k|  } else if (v >= 0x100) {
  ------------------
  |  Branch (7444:14): [True: 258, False: 1.62k]
  ------------------
 7445|    258|    *p++ = kHex[(v >> 8) & 0xf];
 7446|    258|    *p++ = kHex[(v >> 4) & 0xf];
 7447|    258|    *p++ = kHex[v & 0xf];
 7448|  1.62k|  } else if (v >= 0x10) {
  ------------------
  |  Branch (7448:14): [True: 231, False: 1.38k]
  ------------------
 7449|    231|    *p++ = kHex[(v >> 4) & 0xf];
 7450|    231|    *p++ = kHex[v & 0xf];
 7451|  1.38k|  } else {
 7452|  1.38k|    *p++ = kHex[v & 0xf];
 7453|  1.38k|  }
 7454|  2.10k|  return p;
 7455|  2.10k|}
can_parse.cc:_ZN3ada11serializers12_GLOBAL__N_18write_u8EPch:
 7423|  12.8k|ada_really_inline char* write_u8(char* p, uint8_t v) noexcept {
 7424|  12.8k|  if (v < 10) {
  ------------------
  |  Branch (7424:7): [True: 10.3k, False: 2.52k]
  ------------------
 7425|  10.3k|    *p++ = static_cast<char>('0' + v);
 7426|  10.3k|  } else if (v < 100) {
  ------------------
  |  Branch (7426:14): [True: 1.08k, False: 1.44k]
  ------------------
 7427|  1.08k|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v) * 2], 2);
 7428|  1.08k|    p += 2;
 7429|  1.44k|  } else {
 7430|  1.44k|    *p++ = static_cast<char>('0' + v / 100);
 7431|  1.44k|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v % 100) * 2], 2);
 7432|  1.44k|    p += 2;
 7433|  1.44k|  }
 7434|  12.8k|  return p;
 7435|  12.8k|}
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6851|  93.9k|    std::string_view user_input) noexcept {
 6852|       |  // first check for short strings in which case we do it naively.
 6853|  93.9k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (6853:7): [True: 74.0k, False: 19.8k]
  ------------------
 6854|  74.0k|    return std::ranges::any_of(user_input, is_tabs_or_newline);
 6855|  74.0k|  }
 6856|       |  // fast path for long strings (expected to be common)
 6857|  19.8k|  size_t i = 0;
 6858|  19.8k|  const __m128i mask1 = _mm_set1_epi8('\r');
 6859|  19.8k|  const __m128i mask2 = _mm_set1_epi8('\n');
 6860|  19.8k|  const __m128i mask3 = _mm_set1_epi8('\t');
 6861|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
 6862|  19.8k|  __m128i running{0};
 6863|   108k|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (6863:10): [True: 88.2k, False: 19.8k]
  ------------------
 6864|  88.2k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
 6865|  88.2k|    running = _mm_or_si128(
 6866|  88.2k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 6867|  88.2k|                                           _mm_cmpeq_epi8(word, mask2))),
 6868|  88.2k|        _mm_cmpeq_epi8(word, mask3));
 6869|  88.2k|  }
 6870|  19.8k|  if (i < user_input.size()) {
  ------------------
  |  Branch (6870:7): [True: 17.8k, False: 2.07k]
  ------------------
 6871|  17.8k|    __m128i word = _mm_loadu_si128(
 6872|  17.8k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
 6873|  17.8k|    running = _mm_or_si128(
 6874|  17.8k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 6875|  17.8k|                                           _mm_cmpeq_epi8(word, mask2))),
 6876|  17.8k|        _mm_cmpeq_epi8(word, mask3));
 6877|  17.8k|  }
 6878|  19.8k|  return _mm_movemask_epi8(running) != 0;
 6879|  93.9k|}
_ZN3ada7unicode18is_tabs_or_newlineEc:
 6742|   277k|constexpr bool is_tabs_or_newline(char c) noexcept {
 6743|   277k|  return c == '\r' || c == '\n' || c == '\t';
  ------------------
  |  Branch (6743:10): [True: 120, False: 277k]
  |  Branch (6743:23): [True: 164, False: 276k]
  |  Branch (6743:36): [True: 70, False: 276k]
  ------------------
 6744|   277k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_124try_can_parse_clean_httpENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7585|  23.2k|std::optional<bool> try_can_parse_clean_http(std::string_view input) noexcept {
 7586|  23.2k|  const auto* bytes = reinterpret_cast<const uint8_t*>(input.data());
 7587|  23.2k|  const size_t length = input.size();
 7588|       |
 7589|  23.2k|  size_t authority_start;
 7590|  23.2k|  if (length >= 8 && input.starts_with("https://")) {
  ------------------
  |  Branch (7590:7): [True: 10.5k, False: 12.7k]
  |  Branch (7590:22): [True: 35, False: 10.5k]
  ------------------
 7591|     35|    authority_start = 8;
 7592|  23.2k|  } else if (length >= 7 && input.starts_with("http://")) {
  ------------------
  |  Branch (7592:14): [True: 11.5k, False: 11.7k]
  |  Branch (7592:29): [True: 696, False: 10.8k]
  ------------------
 7593|    696|    authority_start = 7;
 7594|  22.5k|  } else {
 7595|  22.5k|    return std::nullopt;
 7596|  22.5k|  }
 7597|       |
 7598|    731|  if (authority_start >= length) {
  ------------------
  |  Branch (7598:7): [True: 3, False: 728]
  ------------------
 7599|      3|    return false;
 7600|      3|  }
 7601|       |
 7602|    728|  size_t cursor = authority_start;
 7603|  2.44k|  while (cursor + 8 <= length && eight_clean_http_host_bytes(bytes + cursor)) {
  ------------------
  |  Branch (7603:10): [True: 1.92k, False: 525]
  |  Branch (7603:34): [True: 1.72k, False: 203]
  ------------------
 7604|  1.72k|    cursor += 8;
 7605|  1.72k|  }
 7606|  1.92k|  while (cursor < length) {
  ------------------
  |  Branch (7606:10): [True: 1.86k, False: 64]
  ------------------
 7607|  1.86k|    const uint8_t c = bytes[cursor];
 7608|  1.86k|    if (c == '/' || c == '?' || c == '#') {
  ------------------
  |  Branch (7608:9): [True: 443, False: 1.42k]
  |  Branch (7608:21): [True: 18, False: 1.40k]
  |  Branch (7608:33): [True: 14, False: 1.39k]
  ------------------
 7609|    475|      break;
 7610|    475|    }
 7611|  1.39k|    if (!clean_http_host_byte[c]) {
  ------------------
  |  Branch (7611:9): [True: 189, False: 1.20k]
  ------------------
 7612|    189|      return std::nullopt;
 7613|    189|    }
 7614|  1.20k|    ++cursor;
 7615|  1.20k|  }
 7616|       |
 7617|    539|  const size_t host_length = cursor - authority_start;
 7618|    539|  if (host_length == 0) {
  ------------------
  |  Branch (7618:7): [True: 5, False: 534]
  ------------------
 7619|       |    // Extra special-scheme slashes are normalization, not an empty host.
 7620|      5|    if (bytes[authority_start] == '/' || bytes[authority_start] == '\\') {
  ------------------
  |  Branch (7620:9): [True: 3, False: 2]
  |  Branch (7620:42): [True: 0, False: 2]
  ------------------
 7621|      3|      return std::nullopt;
 7622|      3|    }
 7623|      2|    return false;
 7624|      5|  }
 7625|    534|  if (host_length > 253 || bytes[cursor - 1] == '.') {
  ------------------
  |  Branch (7625:7): [True: 13, False: 521]
  |  Branch (7625:28): [True: 71, False: 450]
  ------------------
 7626|     84|    return std::nullopt;
 7627|     84|  }
 7628|       |
 7629|    450|  const std::string_view host(input.data() + authority_start, host_length);
 7630|    450|  if (checkers::is_ipv4(host) || host.find("xn-") != std::string_view::npos) {
  ------------------
  |  Branch (7630:7): [True: 22, False: 428]
  |  Branch (7630:34): [True: 51, False: 377]
  ------------------
 7631|     73|    return std::nullopt;
 7632|     73|  }
 7633|       |
 7634|       |  // Once a special URL has a valid host, path/query/fragment bytes cannot make
 7635|       |  // it structurally invalid: the full parser percent-encodes them as needed.
 7636|    377|  return true;
 7637|    450|}
can_parse.cc:_ZN3ada12_GLOBAL__N_127eight_clean_http_host_bytesEPKh:
 7574|  1.92k|    const uint8_t* input) noexcept {
 7575|  1.92k|  return clean_http_host_byte[input[0]] & clean_http_host_byte[input[1]] &
 7576|  1.92k|         clean_http_host_byte[input[2]] & clean_http_host_byte[input[3]] &
 7577|  1.92k|         clean_http_host_byte[input[4]] & clean_http_host_byte[input[5]] &
 7578|  1.92k|         clean_http_host_byte[input[6]] & clean_http_host_byte[input[7]];
 7579|  1.92k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_127try_can_parse_absolute_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7652|  22.9k|    std::string_view input) noexcept {
 7653|  22.9k|  const uint8_t* b = reinterpret_cast<const uint8_t*>(input.data());
 7654|  22.9k|  size_t len = input.size();
 7655|       |
 7656|       |  // -- Inline C0 whitespace trim (no allocation) --------------------------
 7657|       |  // Note: \t (0x09), \n (0x0a), \r (0x0d) are all <= 0x20, so any
 7658|       |  // leading/trailing tabs or newlines are correctly stripped here, matching
 7659|       |  // the WHATWG spec's "remove leading/trailing C0 control and space" step.
 7660|  24.2k|  while (len > 0 && b[0] <= 0x20) {
  ------------------
  |  Branch (7660:10): [True: 14.8k, False: 9.38k]
  |  Branch (7660:21): [True: 1.32k, False: 13.5k]
  ------------------
 7661|  1.32k|    b++;
 7662|  1.32k|    len--;
 7663|  1.32k|  }
 7664|  23.6k|  while (len > 0 && b[len - 1] <= 0x20) {
  ------------------
  |  Branch (7664:10): [True: 14.2k, False: 9.38k]
  |  Branch (7664:21): [True: 722, False: 13.5k]
  ------------------
 7665|    722|    len--;
 7666|    722|  }
 7667|  22.9k|  if (len == 0) return false;
  ------------------
  |  Branch (7667:7): [True: 9.38k, False: 13.5k]
  ------------------
 7668|       |
 7669|       |  // -- Scheme detection -----------------------------------------------------
 7670|       |  // Fast path for HTTP and HTTPS (covers ~90%+ of real-world URLs).
 7671|       |  // Avoids the general scheme loop, buffer copy, and perfect hash lookup.
 7672|       |  // We know HTTP and HTTPS are special non-file schemes, so no further
 7673|       |  // scheme_type checks are needed on the fast path -- only `pos` matters.
 7674|  13.5k|  size_t pos;
 7675|       |
 7676|  13.5k|  if (len >= 7 && (b[0] | 0x20) == 'h' && (b[1] | 0x20) == 't' &&
  ------------------
  |  Branch (7676:7): [True: 11.1k, False: 2.41k]
  |  Branch (7676:19): [True: 869, False: 10.2k]
  |  Branch (7676:43): [True: 744, False: 125]
  ------------------
 7677|    744|      (b[2] | 0x20) == 't' && (b[3] | 0x20) == 'p') {
  ------------------
  |  Branch (7677:7): [True: 722, False: 22]
  |  Branch (7677:31): [True: 697, False: 25]
  ------------------
 7678|    697|    if (b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (7678:9): [True: 596, False: 101]
  |  Branch (7678:24): [True: 345, False: 251]
  |  Branch (7678:39): [True: 329, False: 16]
  ------------------
 7679|    329|      pos = 7;
 7680|    329|      goto skip_extra_slashes;
 7681|    329|    }
 7682|    368|    if (len >= 8 && (b[4] | 0x20) == 's' && b[5] == ':' && b[6] == '/' &&
  ------------------
  |  Branch (7682:9): [True: 323, False: 45]
  |  Branch (7682:21): [True: 63, False: 260]
  |  Branch (7682:45): [True: 46, False: 17]
  |  Branch (7682:60): [True: 30, False: 16]
  ------------------
 7683|     30|        b[7] == '/') {
  ------------------
  |  Branch (7683:9): [True: 21, False: 9]
  ------------------
 7684|     21|      pos = 8;
 7685|     21|      goto skip_extra_slashes;
 7686|     21|    }
 7687|       |    // Fall through: could be "httpe://", tabs in scheme, etc.
 7688|    368|  }
 7689|       |
 7690|  13.1k|  {
 7691|       |    // General scheme detection for ws, wss, ftp, and edge cases.
 7692|  13.1k|    if (!checkers::is_alpha(static_cast<char>(b[0]))) return false;
  ------------------
  |  Branch (7692:9): [True: 564, False: 12.6k]
  ------------------
 7693|       |
 7694|       |    // Scan for ':' within the first 7 bytes. All special schemes are <= 5
 7695|       |    // chars ("https"), so any URL whose first ':' is beyond byte 6 is either
 7696|       |    // non-special or relative -- both require the full parser.
 7697|  12.6k|    size_t colon_pos = 0;
 7698|  27.5k|    for (size_t i = 1;; ++i) {
 7699|  27.5k|      if (i >= 7 || i >= len) return std::nullopt;
  ------------------
  |  Branch (7699:11): [True: 77, False: 27.4k]
  |  Branch (7699:21): [True: 153, False: 27.3k]
  ------------------
 7700|  27.3k|      const char c = static_cast<char>(b[i]);
 7701|  27.3k|      if (c == ':') {
  ------------------
  |  Branch (7701:11): [True: 12.2k, False: 15.1k]
  ------------------
 7702|  12.2k|        colon_pos = i;
 7703|  12.2k|        break;
 7704|  12.2k|      }
 7705|       |      // Tabs/newlines in the scheme require the full parser to strip them.
 7706|  15.1k|      if (c == '\t' || c == '\n' || c == '\r') return std::nullopt;
  ------------------
  |  Branch (7706:11): [True: 8, False: 15.1k]
  |  Branch (7706:24): [True: 7, False: 15.0k]
  |  Branch (7706:37): [True: 11, False: 15.0k]
  ------------------
 7707|  15.0k|      if (!unicode::is_alnum_plus(c)) return false;
  ------------------
  |  Branch (7707:11): [True: 143, False: 14.9k]
  ------------------
 7708|  15.0k|    }
 7709|       |
 7710|       |    // Lowercase scheme bytes inline and classify via the existing perfect
 7711|       |    // hash.
 7712|  12.2k|    char scheme_buf[6];
 7713|  12.2k|    scheme_buf[0] = static_cast<char>(b[0] | 0x20);
 7714|  26.5k|    for (size_t i = 1; i < colon_pos; ++i)
  ------------------
  |  Branch (7714:24): [True: 14.3k, False: 12.2k]
  ------------------
 7715|  14.3k|      scheme_buf[i] = static_cast<char>(b[i] | 0x20);
 7716|       |
 7717|  12.2k|    const ada::scheme::type scheme_type =
 7718|  12.2k|        ada::scheme::get_scheme_type({scheme_buf, colon_pos});
 7719|       |
 7720|       |    // Only handle special, non-file schemes.
 7721|  12.2k|    if (scheme_type == ada::scheme::NOT_SPECIAL) return std::nullopt;
  ------------------
  |  Branch (7721:9): [True: 2.18k, False: 10.0k]
  ------------------
 7722|  10.0k|    if (scheme_type == ada::scheme::FILE) return std::nullopt;
  ------------------
  |  Branch (7722:9): [True: 1.46k, False: 8.55k]
  ------------------
 7723|       |
 7724|       |    // Per WHATWG, special URLs don't require "//": "http:example.com" is valid
 7725|       |    // (SPECIAL_AUTHORITY_IGNORE_SLASHES just skips leading slashes and
 7726|       |    // proceeds to AUTHORITY).  Defer to the inline fallback for any input
 7727|       |    // without "://".
 7728|  8.55k|    pos = colon_pos + 1;
 7729|  8.55k|    if (pos + 2 > len || b[pos] != '/' || b[pos + 1] != '/') {
  ------------------
  |  Branch (7729:9): [True: 130, False: 8.42k]
  |  Branch (7729:26): [True: 5.24k, False: 3.18k]
  |  Branch (7729:43): [True: 42, False: 3.14k]
  ------------------
 7730|  5.41k|      return std::nullopt;
 7731|  5.41k|    }
 7732|  3.14k|    pos += 2;
 7733|  3.14k|  }
 7734|       |
 7735|  3.49k|skip_extra_slashes:
 7736|       |  // SPECIAL_AUTHORITY_IGNORE_SLASHES: the full parser skips any additional
 7737|       |  // leading '/' or '\' after the initial "//".  Mirror that here so we don't
 7738|       |  // mis-identify the host as empty when there are extra slashes.
 7739|  3.99k|  while (pos < len && (b[pos] == '/' || b[pos] == '\\')) {
  ------------------
  |  Branch (7739:10): [True: 3.97k, False: 21]
  |  Branch (7739:24): [True: 325, False: 3.65k]
  |  Branch (7739:41): [True: 181, False: 3.47k]
  ------------------
 7740|    506|    ++pos;
 7741|    506|  }
 7742|       |
 7743|       |  // Early IPv6 bail-out: if the authority starts with '[', it's an IPv6
 7744|       |  // literal which requires the full parser.  Checking here avoids scanning
 7745|       |  // the entire bracketed address only to bail out afterward.
 7746|  3.49k|  if (pos < len && b[pos] == '[') return std::nullopt;
  ------------------
  |  Branch (7746:7): [True: 3.47k, False: 21]
  |  Branch (7746:20): [True: 95, False: 3.37k]
  ------------------
 7747|       |
 7748|       |  // -- Merged authority + host scan ------------------------------------------
 7749|       |  // A single forward pass over the authority bytes that simultaneously:
 7750|       |  //   - finds the authority end and port colon
 7751|       |  //   - validates host characters (forbidden domain code points)
 7752|       |  //   - tracks IPv4 indicators (all-decimal-dots, last non-dot char)
 7753|       |  //   - detects xn-- prefixes (IDNA punycode)
 7754|       |  //   - detects tabs/newlines (which require the full parser to strip)
 7755|       |  // This replaces 4 separate scans over the host bytes.
 7756|  3.39k|  const size_t auth_start = pos;
 7757|  3.39k|  size_t auth_end = pos;
 7758|  3.39k|  size_t port_colon = SIZE_MAX;
 7759|  3.39k|  bool all_dec_dots = true;
 7760|  3.39k|  uint8_t last_non_dot = 0;
 7761|       |
 7762|  22.8k|  for (; auth_end < len; ++auth_end) {
  ------------------
  |  Branch (7762:10): [True: 22.7k, False: 176]
  ------------------
 7763|  22.7k|    const uint8_t c = b[auth_end];
 7764|       |
 7765|       |    // Non-ASCII -> needs IDNA processing -> full parser.
 7766|  22.7k|    if (c >= 0x80) return std::nullopt;
  ------------------
  |  Branch (7766:9): [True: 17, False: 22.7k]
  ------------------
 7767|       |
 7768|       |    // Authority delimiters.
 7769|  22.7k|    if (c == '/' || c == '?' || c == '#' || c == '\\') break;
  ------------------
  |  Branch (7769:9): [True: 1.69k, False: 21.0k]
  |  Branch (7769:21): [True: 26, False: 20.9k]
  |  Branch (7769:33): [True: 17, False: 20.9k]
  |  Branch (7769:45): [True: 2, False: 20.9k]
  ------------------
 7770|       |
 7771|       |    // Port separator.
 7772|  20.9k|    if (c == ':') {
  ------------------
  |  Branch (7772:9): [True: 564, False: 20.3k]
  ------------------
 7773|    564|      if (port_colon == SIZE_MAX) port_colon = auth_end;
  ------------------
  |  Branch (7773:11): [True: 360, False: 204]
  ------------------
 7774|    564|      continue;
 7775|    564|    }
 7776|       |
 7777|       |    // Credentials or percent-encoding -> full parser.
 7778|  20.3k|    if (c == '@' || c == '%') return std::nullopt;
  ------------------
  |  Branch (7778:9): [True: 47, False: 20.3k]
  |  Branch (7778:21): [True: 135, False: 20.2k]
  ------------------
 7779|       |
 7780|       |    // Tabs/newlines anywhere in the authority require the full parser to
 7781|       |    // strip them before validation.  Without this, a tab in the port (e.g.
 7782|       |    // "http://host:8\t0/") would be mis-rejected by port validation.
 7783|  20.2k|    if (c == '\t' || c == '\n' || c == '\r') return std::nullopt;
  ------------------
  |  Branch (7783:9): [True: 1, False: 20.2k]
  |  Branch (7783:22): [True: 1, False: 20.2k]
  |  Branch (7783:35): [True: 1, False: 20.2k]
  ------------------
 7784|       |
 7785|       |    // Skip remaining host-specific checks for port bytes.  Port digits are
 7786|       |    // validated separately below, and no forbidden-domain-code-point check
 7787|       |    // is needed on port characters.
 7788|  20.2k|    if (port_colon != SIZE_MAX) continue;
  ------------------
  |  Branch (7788:9): [True: 1.34k, False: 18.8k]
  ------------------
 7789|       |
 7790|       |    // -- Host byte validation (inlined) ------------------------------------
 7791|       |    // Forbidden domain code points that are not already caught above:
 7792|       |    //   C0 controls and space (0x00-0x20), DEL (0x7F), <, >, [, ], ^, |.
 7793|       |    // At this stage, the input may still be userinfo or be normalized later
 7794|       |    // (e.g., percent-encoded), so we do not reject here and defer to the
 7795|       |    // parser. Characters already caught: >= 0x80 (non-ASCII), '/' '?' '#' '\\'
 7796|       |    // (delimiters), ':' (port), '@' '%' (bail), '\t' '\n' '\r' (bail).
 7797|  18.8k|    if (c <= 0x20 || c == 0x7F || c == '<' || c == '>' || c == '[' ||
  ------------------
  |  Branch (7797:9): [True: 9, False: 18.8k]
  |  Branch (7797:22): [True: 2, False: 18.8k]
  |  Branch (7797:35): [True: 1, False: 18.8k]
  |  Branch (7797:47): [True: 1, False: 18.8k]
  |  Branch (7797:59): [True: 1, False: 18.8k]
  ------------------
 7798|  18.8k|        c == ']' || c == '^' || c == '|') {
  ------------------
  |  Branch (7798:9): [True: 2, False: 18.8k]
  |  Branch (7798:21): [True: 1, False: 18.8k]
  |  Branch (7798:33): [True: 1, False: 18.8k]
  ------------------
 7799|     18|      return std::nullopt;
 7800|     18|    }
 7801|       |
 7802|       |    // Track whether host is all decimal digits and dots (potential IPv4).
 7803|  18.8k|    if (c != '.' && (c < '0' || c > '9')) all_dec_dots = false;
  ------------------
  |  Branch (7803:9): [True: 15.8k, False: 3.00k]
  |  Branch (7803:22): [True: 1.23k, False: 14.6k]
  |  Branch (7803:33): [True: 8.80k, False: 5.81k]
  ------------------
 7804|       |
 7805|       |    // Track last non-dot character for the IPv4 hex/octal heuristic.
 7806|  18.8k|    if (c != '.') last_non_dot = c;
  ------------------
  |  Branch (7806:9): [True: 15.8k, False: 3.00k]
  ------------------
 7807|       |
 7808|       |    // Detect xn-- prefix inline (IDNA punycode -> needs full parser).
 7809|       |    // Checking at every position mirrors the original behavior: any
 7810|       |    // occurrence of "xn--" in the host (not just at label boundaries)
 7811|       |    // triggers a bail-out to the full IDNA validator.
 7812|  18.8k|    if ((c | 0x20) == 'x' && auth_end + 4 <= len &&
  ------------------
  |  Branch (7812:9): [True: 2.63k, False: 16.2k]
  |  Branch (7812:30): [True: 2.51k, False: 127]
  ------------------
 7813|  2.51k|        (b[auth_end + 1] | 0x20) == 'n' && b[auth_end + 2] == '-' &&
  ------------------
  |  Branch (7813:9): [True: 1.81k, False: 700]
  |  Branch (7813:44): [True: 1.58k, False: 232]
  ------------------
 7814|  1.58k|        b[auth_end + 3] == '-') {
  ------------------
  |  Branch (7814:9): [True: 1.26k, False: 319]
  ------------------
 7815|  1.26k|      return std::nullopt;
 7816|  1.26k|    }
 7817|  18.8k|  }
 7818|       |
 7819|  1.91k|  const size_t host_end = (port_colon != SIZE_MAX) ? port_colon : auth_end;
  ------------------
  |  Branch (7819:27): [True: 325, False: 1.59k]
  ------------------
 7820|       |
 7821|       |  // Empty host is invalid for special URLs.
 7822|  1.91k|  if (auth_start == host_end) return false;
  ------------------
  |  Branch (7822:7): [True: 32, False: 1.88k]
  ------------------
 7823|       |
 7824|       |  // -- IPv4 handling ---------------------------------------------------------
 7825|  1.88k|  const char* host_ptr = reinterpret_cast<const char*>(b + auth_start);
 7826|  1.88k|  const size_t host_len = host_end - auth_start;
 7827|       |
 7828|  1.88k|  if (all_dec_dots) {
  ------------------
  |  Branch (7828:7): [True: 804, False: 1.07k]
  ------------------
 7829|       |    // Host is all decimal digits and dots -> try the fast IPv4 parser.
 7830|    804|    if (checkers::try_parse_ipv4_fast({host_ptr, host_len}) !=
  ------------------
  |  Branch (7830:9): [True: 571, False: 233]
  ------------------
 7831|    804|        checkers::ipv4_fast_fail) {
 7832|       |      // Valid decimal IPv4 host.  Do NOT return true yet: the port still
 7833|       |      // needs to be validated below before we can declare the URL valid.
 7834|    571|      goto validate_port;
 7835|    571|    }
 7836|       |    // Fast IPv4 parsing failed (e.g. host is ".", "..", "1.2.3.500").
 7837|       |    // Such hosts may still be valid domain names; defer to the full parser.
 7838|    233|    return std::nullopt;
 7839|    804|  }
 7840|       |
 7841|       |  // Last-significant-character heuristic for non-decimal IPv4 (hex/octal):
 7842|       |  // if the last non-dot char is a digit, 'a'-'f', or 'x' the host might be
 7843|       |  // an IPv4 address that the fast path can't validate -- fall through.
 7844|       |  // last_non_dot was tracked during the authority scan above.
 7845|  1.07k|  {
 7846|  1.07k|    const uint8_t lc = last_non_dot | 0x20;
 7847|  1.07k|    if ((last_non_dot >= '0' && last_non_dot <= '9') ||
  ------------------
  |  Branch (7847:10): [True: 814, False: 265]
  |  Branch (7847:33): [True: 131, False: 683]
  ------------------
 7848|    948|        (lc >= 'a' && lc <= 'f') || lc == 'x') {
  ------------------
  |  Branch (7848:10): [True: 660, False: 288]
  |  Branch (7848:23): [True: 196, False: 464]
  |  Branch (7848:37): [True: 163, False: 589]
  ------------------
 7849|    490|      return std::nullopt;
 7850|    490|    }
 7851|  1.07k|  }
 7852|       |
 7853|       |  // -- Port validation -------------------------------------------------------
 7854|  1.16k|validate_port:
 7855|  1.16k|  if (port_colon != SIZE_MAX) {
  ------------------
  |  Branch (7855:7): [True: 170, False: 990]
  ------------------
 7856|    170|    const uint8_t* pp = b + port_colon + 1;
 7857|    170|    size_t pl = auth_end - port_colon - 1;
 7858|    170|    if (pl > 0) {
  ------------------
  |  Branch (7858:9): [True: 168, False: 2]
  ------------------
 7859|       |      // Strip leading zeros: "0000001" == 1, "0000000000000" == 0, both valid.
 7860|       |      // Only the significant digits count toward the 5-digit maximum.
 7861|    431|      while (pl > 0 && *pp == '0') {
  ------------------
  |  Branch (7861:14): [True: 399, False: 32]
  |  Branch (7861:24): [True: 263, False: 136]
  ------------------
 7862|    263|        ++pp;
 7863|    263|        --pl;
 7864|    263|      }
 7865|    168|      if (pl > 5) return false;  // significant digits > 99999
  ------------------
  |  Branch (7865:11): [True: 5, False: 163]
  ------------------
 7866|    163|      uint32_t pv = 0;
 7867|    503|      for (size_t i = 0; i < pl; ++i) {
  ------------------
  |  Branch (7867:26): [True: 352, False: 151]
  ------------------
 7868|    352|        if (pp[i] < '0' || pp[i] > '9') return false;
  ------------------
  |  Branch (7868:13): [True: 3, False: 349]
  |  Branch (7868:28): [True: 9, False: 340]
  ------------------
 7869|    340|        pv = pv * 10 + (pp[i] - '0');
 7870|    340|      }
 7871|    151|      if (pv > 65535) return false;
  ------------------
  |  Branch (7871:11): [True: 2, False: 149]
  ------------------
 7872|    151|    }
 7873|    170|  }
 7874|       |
 7875|       |  // Path, query, and fragment are structurally always valid for can_parse --
 7876|       |  // the parser would encode whatever is there.
 7877|  1.14k|  return true;
 7878|  1.16k|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 8174|    608|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
 8175|       |  // if this ever becomes a performance issue, we could use an approach similar
 8176|       |  // to has_tabs_or_newline
 8177|    608|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
 8178|    608|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
 7096|  26.1k|    const char c) noexcept {
 7097|  26.1k|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (7097:10): [True: 1.64k, False: 24.4k]
  |  Branch (7097:23): [True: 1.46k, False: 22.9k]
  |  Branch (7097:36): [True: 504, False: 22.4k]
  ------------------
 7098|  26.1k|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
 7091|   119k|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
 7092|   119k|  return (unsigned char)c <= ' ';
 7093|   119k|}
_ZN3ada7helpers19parse_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 8871|  7.47k|                                           std::string& path) {
 8872|  7.47k|  ada_log("parse_prepared_path ", input);
 8873|  7.47k|  uint8_t accumulator = checkers::path_signature(input);
 8874|       |  // Let us first detect a trivial case.
 8875|       |  // If it is special, we check that we have no dot, no %,  no \ and no
 8876|       |  // character needing percent encoding. Otherwise, we check that we have no %,
 8877|       |  // no dot, and no character needing percent encoding.
 8878|  7.47k|  constexpr uint8_t need_encoding = 1;
 8879|  7.47k|  constexpr uint8_t backslash_char = 2;
 8880|  7.47k|  constexpr uint8_t dot_char = 4;
 8881|  7.47k|  constexpr uint8_t percent_char = 8;
 8882|  7.47k|  bool special = type != ada::scheme::NOT_SPECIAL;
 8883|  7.47k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8883:39): [True: 1.54k, False: 5.92k]
  ------------------
 8884|  1.54k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (8884:39): [True: 141, False: 1.40k]
  ------------------
 8885|  7.47k|  bool trivial_path =
 8886|  7.47k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (8886:7): [True: 4.57k, False: 2.90k]
  |  Branch (8886:8): [True: 5.88k, False: 1.59k]
  ------------------
 8887|  7.47k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
 8888|  1.59k|                  0)) &&
 8889|  4.57k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (8889:7): [True: 4.47k, False: 97]
  ------------------
 8890|  7.47k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (8890:7): [True: 997, False: 6.47k]
  |  Branch (8890:34): [True: 973, False: 24]
  ------------------
 8891|       |    // '4' means that we have at least one dot, but nothing that requires
 8892|       |    // percent encoding or decoding. The only part that is not trivial is
 8893|       |    // that we may have single dots and double dots path segments.
 8894|       |    // If we have such segments, then we either have a path that begins
 8895|       |    // with '.' (easy to check), or we have the sequence './'.
 8896|       |    // Note: input cannot be empty, it must at least contain one character ('.')
 8897|       |    // Note: we know that '\' is not present.
 8898|    973|    if (input[0] != '.') {
  ------------------
  |  Branch (8898:9): [True: 436, False: 537]
  ------------------
 8899|    436|      size_t slashdot = 0;
 8900|    436|      bool dot_is_file = true;
 8901|  2.06k|      for (;;) {
 8902|  2.06k|        slashdot = input.find("/.", slashdot);
 8903|  2.06k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (8903:13): [True: 436, False: 1.62k]
  ------------------
 8904|    436|          break;
 8905|  1.62k|        } else {  // uncommon
 8906|       |          // only three cases matter: /./, /.. or a final /
 8907|  1.62k|          slashdot += 2;
 8908|  1.62k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (8908:28): [True: 53, False: 1.57k]
  |  Branch (8908:56): [True: 1.13k, False: 441]
  ------------------
 8909|    441|                           input[slashdot] == '/');
  ------------------
  |  Branch (8909:28): [True: 160, False: 281]
  ------------------
 8910|  1.62k|        }
 8911|  2.06k|      }
 8912|    436|      trivial_path = dot_is_file;
 8913|    436|    }
 8914|    973|  }
 8915|  7.47k|  if (trivial_path) {
  ------------------
  |  Branch (8915:7): [True: 4.61k, False: 2.86k]
  ------------------
 8916|  4.61k|    ada_log("parse_path trivial");
 8917|  4.61k|    path += '/';
 8918|  4.61k|    path += input;
 8919|  4.61k|    return;
 8920|  4.61k|  }
 8921|       |  // We are going to need to look a bit at the path, but let us see if we can
 8922|       |  // ignore percent encoding *and* backslashes *and* percent characters.
 8923|       |  // Except for the trivial case, this is likely to capture 99% of paths out
 8924|       |  // there.
 8925|  2.86k|  bool fast_path =
 8926|  2.86k|      (special &&
  ------------------
  |  Branch (8926:8): [True: 1.72k, False: 1.13k]
  ------------------
 8927|  1.72k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (8927:8): [True: 795, False: 932]
  ------------------
 8928|    795|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (8928:7): [True: 609, False: 186]
  ------------------
 8929|  2.86k|  if (fast_path) {
  ------------------
  |  Branch (8929:7): [True: 609, False: 2.25k]
  ------------------
 8930|    609|    ada_log("parse_prepared_path fast");
 8931|       |    // Here we don't need to worry about \ or percent encoding.
 8932|       |    // We also do not have a file protocol. We might have dots, however,
 8933|       |    // but dots must as appear as '.', and they cannot be encoded because
 8934|       |    // the symbol '%' is not present.
 8935|    609|    size_t previous_location = 0;  // We start at 0.
 8936|  7.21k|    do {
 8937|  7.21k|      size_t new_location = input.find('/', previous_location);
 8938|       |      // std::string_view path_view = input;
 8939|       |      //  We process the last segment separately:
 8940|  7.21k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (8940:11): [True: 609, False: 6.60k]
  ------------------
 8941|    609|        std::string_view path_view = input.substr(previous_location);
 8942|    609|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (8942:13): [True: 69, False: 540]
  ------------------
 8943|       |          // e.g., if you receive ".." with an empty path, you go to "/".
 8944|     69|          if (path.empty()) {
  ------------------
  |  Branch (8944:15): [True: 20, False: 49]
  ------------------
 8945|     20|            path = '/';
 8946|     20|            return;
 8947|     20|          }
 8948|       |          // Fast case where we have nothing to do:
 8949|     49|          if (path.back() == '/') {
  ------------------
  |  Branch (8949:15): [True: 17, False: 32]
  ------------------
 8950|     17|            return;
 8951|     17|          }
 8952|       |          // If you have the path "/joe/myfriend",
 8953|       |          // then you delete 'myfriend'.
 8954|     32|          path.resize(path.rfind('/') + 1);
 8955|     32|          return;
 8956|     49|        }
 8957|    540|        path += '/';
 8958|    540|        if (path_view != ".") {
  ------------------
  |  Branch (8958:13): [True: 477, False: 63]
  ------------------
 8959|    477|          path.append(path_view);
 8960|    477|        }
 8961|    540|        return;
 8962|  6.60k|      } else {
 8963|       |        // This is a non-final segment.
 8964|  6.60k|        std::string_view path_view =
 8965|  6.60k|            input.substr(previous_location, new_location - previous_location);
 8966|  6.60k|        previous_location = new_location + 1;
 8967|  6.60k|        if (path_view == "..") {
  ------------------
  |  Branch (8967:13): [True: 970, False: 5.63k]
  ------------------
 8968|    970|          size_t last_delimiter = path.rfind('/');
 8969|    970|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (8969:15): [True: 511, False: 459]
  ------------------
 8970|    511|            path.erase(last_delimiter);
 8971|    511|          }
 8972|  5.63k|        } else if (path_view != ".") {
  ------------------
  |  Branch (8972:20): [True: 5.37k, False: 260]
  ------------------
 8973|  5.37k|          path += '/';
 8974|  5.37k|          path.append(path_view);
 8975|  5.37k|        }
 8976|  6.60k|      }
 8977|  7.21k|    } while (true);
  ------------------
  |  Branch (8977:14): [True: 6.60k, Folded]
  ------------------
 8978|  2.25k|  } else {
 8979|  2.25k|    ada_log("parse_path slow");
 8980|       |    // we have reached the general case
 8981|  2.25k|    bool needs_percent_encoding = (accumulator & 1);
 8982|  2.25k|    std::string path_buffer_tmp;
 8983|  13.0k|    do {
 8984|  13.0k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (8984:26): [True: 6.44k, False: 6.59k]
  |  Branch (8984:37): [True: 380, False: 6.06k]
  ------------------
 8985|  13.0k|                            ? input.find_first_of("/\\")
 8986|  13.0k|                            : input.find('/');
 8987|  13.0k|      std::string_view path_view = input;
 8988|  13.0k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (8988:11): [True: 10.7k, False: 2.25k]
  ------------------
 8989|  10.7k|        path_view.remove_suffix(path_view.size() - location);
 8990|  10.7k|        input.remove_prefix(location + 1);
 8991|  10.7k|      }
 8992|       |      // path_buffer is either path_view or it might point at a percent encoded
 8993|       |      // temporary file.
 8994|  13.0k|      std::string_view path_buffer =
 8995|  13.0k|          (needs_percent_encoding &&
  ------------------
  |  Branch (8995:12): [True: 7.09k, False: 5.93k]
  ------------------
 8996|  7.09k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (8996:12): [True: 2.94k, False: 4.14k]
  ------------------
 8997|  7.09k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
 8998|  13.0k|              ? path_buffer_tmp
 8999|  13.0k|              : path_view;
 9000|  13.0k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (9000:11): [True: 2.63k, False: 10.3k]
  ------------------
 9001|  2.63k|        helpers::shorten_path(path, type);
 9002|  2.63k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9002:13): [True: 195, False: 2.43k]
  ------------------
 9003|    195|          path += '/';
 9004|    195|        }
 9005|  10.3k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (9005:18): [True: 669, False: 9.72k]
  ------------------
 9006|    669|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (9006:18): [True: 96, False: 573]
  ------------------
 9007|     96|        path += '/';
 9008|     96|      }
 9009|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
 9010|  10.3k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (9010:16): [True: 9.72k, False: 573]
  ------------------
 9011|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
 9012|       |        // Windows drive letter, then replace the second code point in
 9013|       |        // path_buffer with U+003A (:).
 9014|  9.72k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (9014:13): [True: 2.31k, False: 7.41k]
  |  Branch (9014:48): [True: 1.10k, False: 1.20k]
  ------------------
 9015|  1.10k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (9015:13): [True: 165, False: 943]
  ------------------
 9016|    165|          path += '/';
 9017|    165|          path += path_buffer[0];
 9018|    165|          path += ':';
 9019|    165|          path_buffer.remove_prefix(2);
 9020|    165|          path.append(path_buffer);
 9021|  9.56k|        } else {
 9022|       |          // Append path_buffer to url's path.
 9023|  9.56k|          path += '/';
 9024|  9.56k|          path.append(path_buffer);
 9025|  9.56k|        }
 9026|  9.72k|      }
 9027|  13.0k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (9027:11): [True: 2.25k, False: 10.7k]
  ------------------
 9028|  2.25k|        return;
 9029|  2.25k|      }
 9030|  13.0k|    } while (true);
  ------------------
  |  Branch (9030:14): [True: 10.7k, Folded]
  ------------------
 9031|  2.25k|  }
 9032|  2.86k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   87|  14.9k|    std::string_view input) noexcept {
   88|       |  // The path percent-encode set is the query percent-encode set and U+003F (?),
   89|       |  // U+0060 (`), U+007B ({), and U+007D (}). The query percent-encode set is the
   90|       |  // C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+0023 (#),
   91|       |  // U+003C (<), and U+003E (>). The C0 control percent-encode set are the C0
   92|       |  // controls and all code points greater than U+007E (~).
   93|  14.9k|  size_t i = 0;
   94|  14.9k|  uint8_t accumulator{};
   95|  41.2k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (95:10): [True: 26.3k, False: 14.9k]
  ------------------
   96|  26.3k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   97|  26.3k|                           path_signature_table[uint8_t(input[i + 1])] |
   98|  26.3k|                           path_signature_table[uint8_t(input[i + 2])] |
   99|  26.3k|                           path_signature_table[uint8_t(input[i + 3])] |
  100|  26.3k|                           path_signature_table[uint8_t(input[i + 4])] |
  101|  26.3k|                           path_signature_table[uint8_t(input[i + 5])] |
  102|  26.3k|                           path_signature_table[uint8_t(input[i + 6])] |
  103|  26.3k|                           path_signature_table[uint8_t(input[i + 7])]);
  104|  26.3k|  }
  105|  35.5k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (105:10): [True: 20.5k, False: 14.9k]
  ------------------
  106|  20.5k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
  107|  20.5k|  }
  108|  14.9k|  return accumulator;
  109|  14.9k|}
_ZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7332|  14.1k|                    std::string& out) {
 7333|  14.1k|  ada_log("percent_encode ", input, " to output string while ",
 7334|  14.1k|          append ? "appending" : "overwriting");
 7335|  14.1k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7336|  14.1k|    return character_sets::bit_at(character_set, c);
 7337|  14.1k|  });
 7338|  14.1k|  ada_log("percent_encode done checking, moved to ",
 7339|  14.1k|          std::distance(input.begin(), pointer));
 7340|       |
 7341|       |  // Optimization: Don't iterate if percent encode is not required
 7342|  14.1k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7342:7): [True: 8.29k, False: 5.89k]
  ------------------
 7343|  8.29k|    ada_log("percent_encode encoding not needed.");
 7344|  8.29k|    return false;
 7345|  8.29k|  }
 7346|  5.89k|  if constexpr (!append) {
 7347|  5.89k|    out.clear();
 7348|  5.89k|  }
 7349|  5.89k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7350|  5.89k|          " bytes");
 7351|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7352|  5.89k|  out.append(input.data(), std::distance(input.begin(), pointer));
 7353|  5.89k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7354|  5.89k|          " bytes");
 7355|  80.8k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7355:10): [True: 74.9k, False: 5.89k]
  ------------------
 7356|  74.9k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7356:9): [True: 72.4k, False: 2.56k]
  ------------------
 7357|  72.4k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7358|  72.4k|    } else {
 7359|  2.56k|      out += *pointer;
 7360|  2.56k|    }
 7361|  74.9k|  }
 7362|  5.89k|  return true;
 7363|  14.1k|}
_ZZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7335|  16.8k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7336|  16.8k|    return character_sets::bit_at(character_set, c);
 7337|  16.8k|  });
_ZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7104|  26.1k|    std::string_view input) noexcept {
 7105|       |  // This will catch most cases:
 7106|       |  // The length must be 2,4 or 6.
 7107|       |  // We divide by two and require
 7108|       |  // that the result be between 1 and 3 inclusively.
 7109|  26.1k|  uint64_t half_length = uint64_t(input.size()) / 2;
 7110|  26.1k|  if (half_length - 1 > 2) {
  ------------------
  |  Branch (7110:7): [True: 12.5k, False: 13.5k]
  ------------------
 7111|  12.5k|    return false;
 7112|  12.5k|  }
 7113|       |  // We have a string of length 2, 4 or 6.
 7114|       |  // We now check the first character:
 7115|  13.5k|  if ((input[0] != '.') && (input[0] != '%')) {
  ------------------
  |  Branch (7115:7): [True: 6.55k, False: 6.97k]
  |  Branch (7115:28): [True: 1.77k, False: 4.78k]
  ------------------
 7116|  1.77k|    return false;
 7117|  1.77k|  }
 7118|       |  // We are unlikely the get beyond this point.
 7119|  11.7k|  int hash_value = (input.size() + (unsigned)(input[0])) & 3;
 7120|  11.7k|  const std::string_view target = table_is_double_dot_path_segment[hash_value];
 7121|  11.7k|  if (target.size() != input.size()) {
  ------------------
  |  Branch (7121:7): [True: 4.02k, False: 7.73k]
  ------------------
 7122|  4.02k|    return false;
 7123|  4.02k|  }
 7124|       |  // We almost never get here.
 7125|       |  // Optimizing the rest is relatively unimportant.
 7126|  7.73k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7127|  7.73k|    uint16_t A, B;
 7128|  7.73k|    memcpy(&A, a.data(), sizeof(A));
 7129|  7.73k|    memcpy(&B, b.data(), sizeof(B));
 7130|  7.73k|    return A == B;
 7131|  7.73k|  };
 7132|  7.73k|  if (!prefix_equal_unsafe(input, target)) {
  ------------------
  |  Branch (7132:7): [True: 950, False: 6.78k]
  ------------------
 7133|    950|    return false;
 7134|    950|  }
 7135|  9.03k|  for (size_t i = 2; i < input.size(); i++) {
  ------------------
  |  Branch (7135:22): [True: 3.77k, False: 5.26k]
  ------------------
 7136|  3.77k|    char c = input[i];
 7137|  3.77k|    if ((uint8_t((c | 0x20) - 0x61) <= 25 ? (c | 0x20) : c) != target[i]) {
  ------------------
  |  Branch (7137:9): [True: 1.52k, False: 2.25k]
  |  Branch (7137:10): [True: 956, False: 2.81k]
  ------------------
 7138|  1.52k|      return false;
 7139|  1.52k|    }
 7140|  3.77k|  }
 7141|  5.26k|  return true;
 7142|       |  // The above code might be a bit better than the code below. Compilers
 7143|       |  // are not stupid and may use the fact that these strings have length 2,4 and
 7144|       |  // 6 and other tricks.
 7145|       |  // return input == ".." ||
 7146|       |  //  input == ".%2e" || input == ".%2E" ||
 7147|       |  //  input == "%2e." || input == "%2E." ||
 7148|       |  //  input == "%2e%2e" || input == "%2E%2E" || input == "%2E%2e" || input ==
 7149|       |  //  "%2e%2E";
 7150|  6.78k|}
_ZZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_S5_E_clES5_S5_:
 7126|  7.73k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7127|  7.73k|    uint16_t A, B;
 7128|  7.73k|    memcpy(&A, a.data(), sizeof(A));
 7129|  7.73k|    memcpy(&B, b.data(), sizeof(B));
 7130|  7.73k|    return A == B;
 7131|  7.73k|  };
_ZN3ada7unicode26is_single_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7153|  41.4k|    std::string_view input) noexcept {
 7154|  41.4k|  return input == "." || input == "%2e" || input == "%2E";
  ------------------
  |  Branch (7154:10): [True: 1.58k, False: 39.8k]
  |  Branch (7154:26): [True: 894, False: 39.0k]
  |  Branch (7154:44): [True: 10, False: 38.9k]
  ------------------
 7155|  41.4k|}
_ZN3ada7unicode28is_forbidden_host_code_pointEc:
 6974|  32.3k|    const char c) noexcept {
 6975|  32.3k|  return is_forbidden_host_code_point_table[uint8_t(c)];
 6976|  32.3k|}
_ZN3ada6detail17parse_ipv4_numberERPKcS2_RmRb:
 9149|  5.31k|                              bool& is_pure_decimal) noexcept {
 9150|  5.31k|  is_pure_decimal = false;
 9151|  5.31k|  if (p >= end) [[unlikely]] {
  ------------------
  |  Branch (9151:7): [True: 0, False: 5.31k]
  ------------------
 9152|      0|    return false;
 9153|      0|  }
 9154|  5.31k|  if (end - p >= 2 && p[0] == '0' && ((p[1] | 0x20) == 'x')) {
  ------------------
  |  Branch (9154:7): [True: 3.82k, False: 1.49k]
  |  Branch (9154:23): [True: 1.78k, False: 2.04k]
  |  Branch (9154:38): [True: 963, False: 819]
  ------------------
 9155|    963|    p += 2;
 9156|    963|    if (p == end || *p == '.') {
  ------------------
  |  Branch (9156:9): [True: 201, False: 762]
  |  Branch (9156:21): [True: 63, False: 699]
  ------------------
 9157|    264|      value = 0;
 9158|    264|      return true;
 9159|    264|    }
 9160|    699|    uint64_t v = 0;
 9161|    699|    int digits = 0;
 9162|  3.65k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9162:12): [True: 3.08k, False: 570]
  |  Branch (9162:23): [True: 2.99k, False: 96]
  ------------------
 9163|  2.99k|      const uint8_t nib = hex_nibble[static_cast<unsigned char>(*p)];
 9164|  2.99k|      if (nib == 0xff) [[unlikely]] {
  ------------------
  |  Branch (9164:11): [True: 15, False: 2.97k]
  ------------------
 9165|     15|        return false;
 9166|     15|      }
 9167|  2.97k|      if (v > (0xFFFFFFFFULL >> 4)) [[unlikely]] {
  ------------------
  |  Branch (9167:11): [True: 18, False: 2.95k]
  ------------------
 9168|     18|        return false;
 9169|     18|      }
 9170|  2.95k|      v = (v << 4) | nib;
 9171|  2.95k|      ++p;
 9172|  2.95k|      ++digits;
 9173|  2.95k|    }
 9174|    666|    if (digits == 0) [[unlikely]] {
  ------------------
  |  Branch (9174:9): [True: 0, False: 666]
  ------------------
 9175|      0|      return false;
 9176|      0|    }
 9177|    666|    value = v;
 9178|    666|    return true;
 9179|    666|  }
 9180|  4.35k|  if (end - p >= 2 && p[0] == '0' && p[1] >= '0' && p[1] <= '9') {
  ------------------
  |  Branch (9180:7): [True: 2.86k, False: 1.49k]
  |  Branch (9180:23): [True: 819, False: 2.04k]
  |  Branch (9180:38): [True: 501, False: 318]
  |  Branch (9180:53): [True: 477, False: 24]
  ------------------
 9181|    477|    ++p;
 9182|    477|    uint64_t v = 0;
 9183|  4.53k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9183:12): [True: 4.18k, False: 351]
  |  Branch (9183:23): [True: 4.11k, False: 78]
  ------------------
 9184|  4.11k|      const char c = *p;
 9185|  4.11k|      if (c < '0' || c > '7') [[unlikely]] {
  ------------------
  |  Branch (9185:11): [True: 15, False: 4.09k]
  |  Branch (9185:22): [True: 15, False: 4.08k]
  ------------------
 9186|     30|        return false;
 9187|     30|      }
 9188|  4.08k|      if (v > (0xFFFFFFFFULL >> 3)) [[unlikely]] {
  ------------------
  |  Branch (9188:11): [True: 18, False: 4.06k]
  ------------------
 9189|     18|        return false;
 9190|     18|      }
 9191|  4.06k|      v = (v << 3) | static_cast<uint64_t>(c - '0');
 9192|  4.06k|      ++p;
 9193|  4.06k|    }
 9194|    429|    value = v;
 9195|    429|    return true;
 9196|    477|  }
 9197|  3.87k|  if (*p < '0' || *p > '9') [[unlikely]] {
  ------------------
  |  Branch (9197:7): [True: 96, False: 3.78k]
  |  Branch (9197:19): [True: 192, False: 3.58k]
  ------------------
 9198|    288|    return false;
 9199|    288|  }
 9200|  3.58k|  is_pure_decimal = true;
 9201|  3.58k|  uint64_t v = static_cast<uint64_t>(*p - '0');
 9202|  3.58k|  ++p;
 9203|  7.21k|  while (p < end && *p != '.') {
  ------------------
  |  Branch (9203:10): [True: 5.04k, False: 2.17k]
  |  Branch (9203:21): [True: 3.75k, False: 1.28k]
  ------------------
 9204|  3.75k|    const char c = *p;
 9205|  3.75k|    if (c < '0' || c > '9') [[unlikely]] {
  ------------------
  |  Branch (9205:9): [True: 33, False: 3.72k]
  |  Branch (9205:20): [True: 60, False: 3.66k]
  ------------------
 9206|     93|      return false;
 9207|     93|    }
 9208|  3.66k|    if (v > 429496729ULL) [[unlikely]] {
  ------------------
  |  Branch (9208:9): [True: 24, False: 3.63k]
  ------------------
 9209|     24|      return false;
 9210|     24|    }
 9211|  3.63k|    v = v * 10u + static_cast<uint64_t>(c - '0');
 9212|  3.63k|    if (v > 0xFFFFFFFFULL) [[unlikely]] {
  ------------------
  |  Branch (9212:9): [True: 12, False: 3.62k]
  ------------------
 9213|     12|      return false;
 9214|     12|    }
 9215|  3.62k|    ++p;
 9216|  3.62k|  }
 9217|  3.45k|  value = v;
 9218|  3.45k|  return true;
 9219|  3.58k|}
_ZN3ada6detail15parse_hex_pieceERPKcS2_Rt:
 9223|  2.76k|                           uint16_t& value) noexcept {
 9224|  2.76k|  if (pointer == end) {
  ------------------
  |  Branch (9224:7): [True: 0, False: 2.76k]
  ------------------
 9225|      0|    return 0;
 9226|      0|  }
 9227|  2.76k|  const uint8_t n0 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9228|  2.76k|  if (n0 == 0xff) {
  ------------------
  |  Branch (9228:7): [True: 129, False: 2.63k]
  ------------------
 9229|    129|    return 0;
 9230|    129|  }
 9231|  2.63k|  uint32_t v = n0;
 9232|  2.63k|  ++pointer;
 9233|  2.63k|  int length = 1;
 9234|  2.63k|  if (pointer != end) {
  ------------------
  |  Branch (9234:7): [True: 2.37k, False: 258]
  ------------------
 9235|  2.37k|    const uint8_t n1 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9236|  2.37k|    if (n1 != 0xff) {
  ------------------
  |  Branch (9236:9): [True: 885, False: 1.49k]
  ------------------
 9237|    885|      v = (v << 4) | n1;
 9238|    885|      ++pointer;
 9239|    885|      ++length;
 9240|    885|      if (pointer != end) {
  ------------------
  |  Branch (9240:11): [True: 810, False: 75]
  ------------------
 9241|    810|        const uint8_t n2 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9242|    810|        if (n2 != 0xff) {
  ------------------
  |  Branch (9242:13): [True: 576, False: 234]
  ------------------
 9243|    576|          v = (v << 4) | n2;
 9244|    576|          ++pointer;
 9245|    576|          ++length;
 9246|    576|          if (pointer != end) {
  ------------------
  |  Branch (9246:15): [True: 483, False: 93]
  ------------------
 9247|    483|            const uint8_t n3 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9248|    483|            if (n3 != 0xff) {
  ------------------
  |  Branch (9248:17): [True: 246, False: 237]
  ------------------
 9249|    246|              v = (v << 4) | n3;
 9250|    246|              ++pointer;
 9251|    246|              ++length;
 9252|    246|            }
 9253|    483|          }
 9254|    576|        }
 9255|    810|      }
 9256|    885|    }
 9257|  2.37k|  }
 9258|  2.63k|  value = static_cast<uint16_t>(v);
 9259|  2.63k|  return length;
 9260|  2.76k|}
_ZN3ada7unicode13is_alnum_plusEc:
 7068|   149k|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
 7069|   149k|  return is_alnum_plus_table[uint8_t(c)];
 7070|       |  // A table is almost surely much faster than the
 7071|       |  // following under most compilers: return
 7072|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
 7073|   149k|}
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8114|  93.9k|    std::string_view& input) noexcept {
 8115|       |  // compiles down to 20--30 instructions including a class to memchr (C
 8116|       |  // function). this function should be quite fast.
 8117|  93.9k|  size_t location_of_first = input.find('#');
 8118|  93.9k|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (8118:7): [True: 92.0k, False: 1.89k]
  ------------------
 8119|  92.0k|    return std::nullopt;
 8120|  92.0k|  }
 8121|  1.89k|  std::string_view hash = input;
 8122|  1.89k|  hash.remove_prefix(location_of_first + 1);
 8123|  1.89k|  input.remove_suffix(input.size() - location_of_first);
 8124|  1.89k|  return hash;
 8125|  93.9k|}
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9067|  21.4k|find_authority_delimiter_special(std::string_view view) noexcept {
 9068|       |  // performance note: we might be able to gain further performance
 9069|       |  // with SIMD intrinsics.
 9070|   132k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9070:33): [True: 130k, False: 1.84k]
  ------------------
 9071|   130k|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (9071:9): [True: 19.6k, False: 111k]
  ------------------
 9072|  19.6k|      return pos - view.begin();
 9073|  19.6k|    }
 9074|   130k|  }
 9075|  1.84k|  return size_t(view.size());
 9076|  21.4k|}
_ZN3ada7helpers24find_authority_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9088|  1.65k|find_authority_delimiter(std::string_view view) noexcept {
 9089|       |  // performance note: we might be able to gain further performance
 9090|       |  // with SIMD instrinsics.
 9091|  8.38k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9091:33): [True: 8.18k, False: 201]
  ------------------
 9092|  8.18k|    if (authority_delimiter[(uint8_t)*pos]) {
  ------------------
  |  Branch (9092:9): [True: 1.45k, False: 6.73k]
  ------------------
 9093|  1.45k|      return pos - view.begin();
 9094|  1.45k|    }
 9095|  8.18k|  }
 9096|    201|  return size_t(view.size());
 9097|  1.65k|}
_ZN3ada7helpers12shorten_pathERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_6scheme4typeE:
 8127|  5.71k|ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) {
 8128|       |  // Let path be url's path.
 8129|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
 8130|       |  // Windows drive letter, then return.
 8131|  5.71k|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8131:7): [True: 3.35k, False: 2.36k]
  ------------------
 8132|  3.35k|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (8132:7): [True: 2.52k, False: 823]
  |  Branch (8132:54): [True: 1.77k, False: 750]
  ------------------
 8133|  1.77k|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (8133:9): [True: 743, False: 1.03k]
  ------------------
 8134|  1.77k|            helpers::substring(path, 1))) {
 8135|    743|      return false;
 8136|    743|    }
 8137|  1.77k|  }
 8138|       |
 8139|       |  // Remove path's last item, if any.
 8140|  4.97k|  size_t last_delimiter = path.rfind('/');
 8141|  4.97k|  if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (8141:7): [True: 3.31k, False: 1.65k]
  ------------------
 8142|  3.31k|    path.erase(last_delimiter);
 8143|  3.31k|    return true;
 8144|  3.31k|  }
 8145|       |
 8146|  1.65k|  return false;
 8147|  4.97k|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8780|  33.4k|    const bool is_special, std::string_view& view) noexcept {
 8781|       |  /**
 8782|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
 8783|       |   * compute a variable called insideBrackets but this variable is only used
 8784|       |   * once, to check whether a ':' character was found outside brackets. Exact
 8785|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
 8786|       |   * It is conceptually simpler and arguably more efficient to just return a
 8787|       |   * Boolean indicating whether ':' was found outside brackets.
 8788|       |   */
 8789|  33.4k|  const size_t view_size = view.size();
 8790|  33.4k|  size_t location = 0;
 8791|  33.4k|  bool found_colon = false;
 8792|       |  /**
 8793|       |   * Performance analysis:
 8794|       |   *
 8795|       |   * We are basically seeking the end of the hostname which can be indicated
 8796|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
 8797|       |   * (where '\\' is only applicable for special URLs). However, these must
 8798|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
 8799|       |   * '?' does not count.
 8800|       |   *
 8801|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
 8802|       |   * the set of delimiters, and that we handle it first.
 8803|       |   *
 8804|       |   * So the trick is to have a fast function that locates the next delimiter.
 8805|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
 8806|       |   * function would be provided by the C++ standard library, but it seems that
 8807|       |   * find_first_of is not very fast, so we are forced to roll our own.
 8808|       |   *
 8809|       |   * We do not break into two loops for speed, but for clarity.
 8810|       |   */
 8811|  33.4k|  if (is_special) {
  ------------------
  |  Branch (8811:7): [True: 31.5k, False: 1.88k]
  ------------------
 8812|       |    // We move to the next delimiter.
 8813|  31.5k|    location = find_next_host_delimiter_special(view, location);
 8814|       |    // Unless we find '[' then we are going only going to have to call
 8815|       |    // find_next_host_delimiter_special once.
 8816|  35.0k|    for (; location < view_size;
  ------------------
  |  Branch (8816:12): [True: 17.7k, False: 17.2k]
  ------------------
 8817|  31.5k|         location = find_next_host_delimiter_special(view, location)) {
 8818|  17.7k|      if (view[location] == '[') {
  ------------------
  |  Branch (8818:11): [True: 3.66k, False: 14.0k]
  ------------------
 8819|  3.66k|        location = view.find(']', location);
 8820|  3.66k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (8820:13): [True: 228, False: 3.44k]
  ------------------
 8821|       |          // performance: view.find might get translated to a memchr, which
 8822|       |          // has no notion of std::string_view::npos, so the code does not
 8823|       |          // reflect the assembly.
 8824|    228|          location = view_size;
 8825|    228|          break;
 8826|    228|        }
 8827|  14.0k|      } else {
 8828|  14.0k|        found_colon = view[location] == ':';
 8829|  14.0k|        break;
 8830|  14.0k|      }
 8831|  17.7k|    }
 8832|  31.5k|  } else {
 8833|       |    // We move to the next delimiter.
 8834|  1.88k|    location = find_next_host_delimiter(view, location);
 8835|       |    // Unless we find '[' then we are going only going to have to call
 8836|       |    // find_next_host_delimiter_special once.
 8837|  3.57k|    for (; location < view_size;
  ------------------
  |  Branch (8837:12): [True: 2.51k, False: 1.06k]
  ------------------
 8838|  2.51k|         location = find_next_host_delimiter(view, location)) {
 8839|  2.51k|      if (view[location] == '[') {
  ------------------
  |  Branch (8839:11): [True: 1.75k, False: 756]
  ------------------
 8840|  1.75k|        location = view.find(']', location);
 8841|  1.75k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (8841:13): [True: 66, False: 1.68k]
  ------------------
 8842|       |          // performance: view.find might get translated to a memchr, which
 8843|       |          // has no notion of std::string_view::npos, so the code does not
 8844|       |          // reflect the assembly.
 8845|     66|          location = view_size;
 8846|     66|          break;
 8847|     66|        }
 8848|  1.75k|      } else {
 8849|    756|        found_colon = view[location] == ':';
 8850|    756|        break;
 8851|    756|      }
 8852|  2.51k|    }
 8853|  1.88k|  }
 8854|       |  // performance: remove_suffix may translate into a single instruction.
 8855|  33.4k|  view.remove_suffix(view_size - location);
 8856|  33.4k|  return {location, found_colon};
 8857|  33.4k|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8370|  35.0k|    std::string_view view, size_t location) noexcept {
 8371|       |  // first check for short strings in which case we do it naively.
 8372|  35.0k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (8372:7): [True: 24.0k, False: 11.0k]
  ------------------
 8373|   135k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (8373:31): [True: 122k, False: 12.4k]
  ------------------
 8374|   122k|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (8374:11): [True: 3.20k, False: 119k]
  |  Branch (8374:29): [True: 6.06k, False: 113k]
  |  Branch (8374:47): [True: 33, False: 113k]
  ------------------
 8375|   113k|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (8375:11): [True: 440, False: 112k]
  |  Branch (8375:29): [True: 1.79k, False: 111k]
  ------------------
 8376|  11.5k|        return i;
 8377|  11.5k|      }
 8378|   122k|    }
 8379|  12.4k|    return size_t(view.size());
 8380|  24.0k|  }
 8381|       |  // fast path for long strings (expected to be common)
 8382|  11.0k|  size_t i = location;
 8383|  11.0k|  const __m128i mask1 = _mm_set1_epi8(':');
 8384|  11.0k|  const __m128i mask2 = _mm_set1_epi8('/');
 8385|  11.0k|  const __m128i mask3 = _mm_set1_epi8('\\');
 8386|  11.0k|  const __m128i mask4 = _mm_set1_epi8('?');
 8387|  11.0k|  const __m128i mask5 = _mm_set1_epi8('[');
 8388|       |
 8389|  50.7k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (8389:10): [True: 43.6k, False: 7.17k]
  ------------------
 8390|  43.6k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 8391|  43.6k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 8392|  43.6k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 8393|  43.6k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 8394|  43.6k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 8395|  43.6k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 8396|  43.6k|    __m128i m = _mm_or_si128(
 8397|  43.6k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 8398|  43.6k|    int mask = _mm_movemask_epi8(m);
 8399|  43.6k|    if (mask != 0) {
  ------------------
  |  Branch (8399:9): [True: 3.85k, False: 39.7k]
  ------------------
 8400|  3.85k|      return i + trailing_zeroes(mask);
 8401|  3.85k|    }
 8402|  43.6k|  }
 8403|  7.17k|  if (i < view.size()) {
  ------------------
  |  Branch (8403:7): [True: 6.69k, False: 473]
  ------------------
 8404|  6.69k|    __m128i word =
 8405|  6.69k|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 8406|  6.69k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 8407|  6.69k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 8408|  6.69k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 8409|  6.69k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 8410|  6.69k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 8411|  6.69k|    __m128i m = _mm_or_si128(
 8412|  6.69k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 8413|  6.69k|    int mask = _mm_movemask_epi8(m);
 8414|  6.69k|    if (mask != 0) {
  ------------------
  |  Branch (8414:9): [True: 2.38k, False: 4.31k]
  ------------------
 8415|  2.38k|      return view.length() - 16 + trailing_zeroes(mask);
 8416|  2.38k|    }
 8417|  6.69k|  }
 8418|  4.78k|  return size_t(view.length());
 8419|  7.17k|}
_ZN3ada7helpers15trailing_zeroesEj:
 8195|  7.69k|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
 8196|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 8197|       |  unsigned long ret;
 8198|       |  // Search the mask data from least significant bit (LSB)
 8199|       |  // to the most significant bit (MSB) for a set bit (1).
 8200|       |  _BitScanForward(&ret, input_num);
 8201|       |  return (int)ret;
 8202|       |#else   // ADA_REGULAR_VISUAL_STUDIO
 8203|  7.69k|  return __builtin_ctzl(input_num);
 8204|  7.69k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 8205|  7.69k|}
_ZN3ada7helpers24find_next_host_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8646|  3.57k|                                                  size_t location) noexcept {
 8647|       |  // first check for short strings in which case we do it naively.
 8648|  3.57k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (8648:7): [True: 1.73k, False: 1.84k]
  ------------------
 8649|  4.85k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (8649:31): [True: 4.17k, False: 687]
  ------------------
 8650|  4.17k|      if (view[i] == ':' || view[i] == '/' || view[i] == '?' ||
  ------------------
  |  Branch (8650:11): [True: 267, False: 3.90k]
  |  Branch (8650:29): [True: 141, False: 3.76k]
  |  Branch (8650:47): [True: 111, False: 3.65k]
  ------------------
 8651|  3.65k|          view[i] == '[') {
  ------------------
  |  Branch (8651:11): [True: 528, False: 3.12k]
  ------------------
 8652|  1.04k|        return i;
 8653|  1.04k|      }
 8654|  4.17k|    }
 8655|    687|    return size_t(view.size());
 8656|  1.73k|  }
 8657|       |  // fast path for long strings (expected to be common)
 8658|  1.84k|  size_t i = location;
 8659|  1.84k|  const __m128i mask1 = _mm_set1_epi8(':');
 8660|  1.84k|  const __m128i mask2 = _mm_set1_epi8('/');
 8661|  1.84k|  const __m128i mask4 = _mm_set1_epi8('?');
 8662|  1.84k|  const __m128i mask5 = _mm_set1_epi8('[');
 8663|       |
 8664|  3.84k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (8664:10): [True: 3.37k, False: 477]
  ------------------
 8665|  3.37k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 8666|  3.37k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 8667|  3.37k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 8668|  3.37k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 8669|  3.37k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 8670|  3.37k|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 8671|  3.37k|    int mask = _mm_movemask_epi8(m);
 8672|  3.37k|    if (mask != 0) {
  ------------------
  |  Branch (8672:9): [True: 1.36k, False: 2.00k]
  ------------------
 8673|  1.36k|      return i + trailing_zeroes(mask);
 8674|  1.36k|    }
 8675|  3.37k|  }
 8676|    477|  if (i < view.size()) {
  ------------------
  |  Branch (8676:7): [True: 354, False: 123]
  ------------------
 8677|    354|    __m128i word =
 8678|    354|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 8679|    354|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 8680|    354|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 8681|    354|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 8682|    354|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 8683|    354|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 8684|    354|    int mask = _mm_movemask_epi8(m);
 8685|    354|    if (mask != 0) {
  ------------------
  |  Branch (8685:9): [True: 99, False: 255]
  ------------------
 8686|     99|      return view.length() - 16 + trailing_zeroes(mask);
 8687|     99|    }
 8688|    354|  }
 8689|    378|  return size_t(view.length());
 8690|    477|}
_ZN3ada3url10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9625|  12.5k|ada_really_inline bool url::parse_host(std::string_view input) {
 9626|  12.5k|  ada_log("parse_host ", input, " [", input.size(), " bytes]");
 9627|  12.5k|  if (input.empty()) {
  ------------------
  |  Branch (9627:7): [True: 15, False: 12.5k]
  ------------------
 9628|     15|    return is_valid = false;
 9629|     15|  }  // technically unnecessary.
 9630|       |  // If input starts with U+005B ([), then:
 9631|  12.5k|  if (input[0] == '[') {
  ------------------
  |  Branch (9631:7): [True: 745, False: 11.8k]
  ------------------
 9632|       |    // If input does not end with U+005D (]), validation error, return failure.
 9633|    745|    if (input.back() != ']') {
  ------------------
  |  Branch (9633:9): [True: 177, False: 568]
  ------------------
 9634|    177|      return is_valid = false;
 9635|    177|    }
 9636|    568|    ada_log("parse_host ipv6");
 9637|       |
 9638|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
 9639|       |    // trailing U+005D (]) removed.
 9640|    568|    input.remove_prefix(1);
 9641|    568|    input.remove_suffix(1);
 9642|    568|    return parse_ipv6(input);
 9643|    745|  }
 9644|       |
 9645|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
 9646|       |  // input.
 9647|  11.8k|  if (!is_special()) {
  ------------------
  |  Branch (9647:7): [True: 411, False: 11.4k]
  ------------------
 9648|    411|    return parse_opaque_host(input);
 9649|    411|  }
 9650|       |
 9651|       |  // Fast path: try to parse as pure decimal IPv4(a.b.c.d) first.
 9652|  11.4k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
 9653|  11.4k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (9653:7): [True: 782, False: 10.6k]
  ------------------
 9654|       |    // Fast path succeeded - input is pure decimal IPv4
 9655|    782|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (9655:9): [True: 782, False: 0]
  |  Branch (9655:27): [True: 23, False: 759]
  ------------------
 9656|     23|      host = input.substr(0, input.size() - 1);
 9657|    759|    } else {
 9658|    759|      host = input;
 9659|    759|    }
 9660|    782|    host_type = IPV4;
 9661|    782|    is_valid = true;
 9662|    782|    ada_log("parse_host fast path decimal ipv4");
 9663|    782|    return true;
 9664|    782|  }
 9665|       |  // Let domain be the result of running UTF-8 decode without BOM on the
 9666|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
 9667|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
 9668|       |  // which case we do not need to call the expensive 'to_ascii' if a few
 9669|       |  // conditions are met: no '%' and no 'xn-' subsequence.
 9670|  10.6k|  std::string buffer = std::string(input);
 9671|       |  // This next function checks that the result is ascii, but we are going to
 9672|       |  // to check anyhow with is_forbidden.
 9673|       |  // bool is_ascii =
 9674|  10.6k|  unicode::to_lower_ascii(buffer.data(), buffer.size());
 9675|  10.6k|  bool is_forbidden = unicode::contains_forbidden_domain_code_point(
 9676|  10.6k|      buffer.data(), buffer.size());
 9677|  10.6k|  static constexpr std::string_view xn_dash{"xn-", 3};
 9678|  10.6k|  if (is_forbidden == 0 && buffer.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (9678:7): [True: 6.06k, False: 4.55k]
  |  Branch (9678:28): [True: 4.29k, False: 1.77k]
  ------------------
 9679|       |    // fast path
 9680|  4.29k|    host = std::move(buffer);
 9681|       |
 9682|       |    // Check for other IPv4 formats (hex, octal, etc.)
 9683|  4.29k|    if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (9683:9): [True: 1.12k, False: 3.16k]
  ------------------
 9684|  1.12k|      ada_log("parse_host fast path ipv4");
 9685|  1.12k|      return parse_ipv4(host.value());
 9686|  1.12k|    }
 9687|  3.16k|    ada_log("parse_host fast path ", *host);
 9688|  3.16k|    is_valid = true;
 9689|  3.16k|    return true;
 9690|  4.29k|  }
 9691|  6.33k|  ada_log("parse_host calling to_ascii");
 9692|  6.33k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
 9693|  6.33k|  if (!is_valid || !host.has_value()) {
  ------------------
  |  Branch (9693:7): [True: 2.14k, False: 4.18k]
  |  Branch (9693:20): [True: 0, False: 4.18k]
  ------------------
 9694|  2.14k|    ada_log("parse_host to_ascii returns false");
 9695|  2.14k|    return is_valid = false;
 9696|  2.14k|  }
 9697|  4.18k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
 9698|  4.18k|          " bytes]");
 9699|       |
 9700|  4.18k|  if (std::any_of(host->begin(), host->end(),
  ------------------
  |  Branch (9700:7): [True: 0, False: 4.18k]
  ------------------
 9701|  4.18k|                  ada::unicode::is_forbidden_domain_code_point)) {
 9702|      0|    host = std::nullopt;
 9703|      0|    return is_valid = false;
 9704|      0|  }
 9705|       |
 9706|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
 9707|       |  // asciiDomain.
 9708|  4.18k|  if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (9708:7): [True: 183, False: 4.00k]
  ------------------
 9709|    183|    ada_log("parse_host got ipv4 ", *host);
 9710|    183|    return parse_ipv4(*host);
 9711|    183|  }
 9712|       |
 9713|  4.00k|  return true;
 9714|  4.18k|}
_ZN3ada7unicode30is_forbidden_domain_code_pointEc:
 6997|  1.09M|    const char c) noexcept {
 6998|  1.09M|  return is_forbidden_domain_code_point_table[uint8_t(c)];
 6999|  1.09M|}
_ZN3ada7helpers6resizeERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8188|     34|ada_really_inline void resize(std::string_view& input, size_t pos) noexcept {
 8189|     34|  ADA_ASSERT_TRUE(pos <= input.size());
 8190|     34|  input.remove_suffix(input.size() - pos);
 8191|     34|}
_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7332|  1.40k|                    std::string& out) {
 7333|  1.40k|  ada_log("percent_encode ", input, " to output string while ",
 7334|  1.40k|          append ? "appending" : "overwriting");
 7335|  1.40k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7336|  1.40k|    return character_sets::bit_at(character_set, c);
 7337|  1.40k|  });
 7338|  1.40k|  ada_log("percent_encode done checking, moved to ",
 7339|  1.40k|          std::distance(input.begin(), pointer));
 7340|       |
 7341|       |  // Optimization: Don't iterate if percent encode is not required
 7342|  1.40k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7342:7): [True: 1.00k, False: 402]
  ------------------
 7343|  1.00k|    ada_log("percent_encode encoding not needed.");
 7344|  1.00k|    return false;
 7345|  1.00k|  }
 7346|       |  if constexpr (!append) {
 7347|       |    out.clear();
 7348|       |  }
 7349|    402|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7350|    402|          " bytes");
 7351|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7352|    402|  out.append(input.data(), std::distance(input.begin(), pointer));
 7353|    402|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7354|    402|          " bytes");
 7355|  10.9k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7355:10): [True: 10.5k, False: 402]
  ------------------
 7356|  10.5k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7356:9): [True: 9.66k, False: 899]
  ------------------
 7357|  9.66k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7358|  9.66k|    } else {
 7359|    899|      out += *pointer;
 7360|    899|    }
 7361|  10.5k|  }
 7362|    402|  return true;
 7363|  1.40k|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7335|  8.63k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7336|  8.63k|    return character_sets::bit_at(character_set, c);
 7337|  8.63k|  });
_ZN3ada7helpers12shorten_pathERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeE:
 8150|    896|                                    ada::scheme::type type) {
 8151|       |  // Let path be url's path.
 8152|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
 8153|       |  // Windows drive letter, then return.
 8154|    896|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8154:7): [True: 226, False: 670]
  ------------------
 8155|    226|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (8155:7): [True: 199, False: 27]
  |  Branch (8155:54): [True: 86, False: 113]
  ------------------
 8156|     86|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (8156:9): [True: 3, False: 83]
  ------------------
 8157|     86|            helpers::substring(path, 1))) {
 8158|      3|      return false;
 8159|      3|    }
 8160|     86|  }
 8161|       |
 8162|       |  // Remove path's last item, if any.
 8163|    893|  if (!path.empty()) {
  ------------------
  |  Branch (8163:7): [True: 441, False: 452]
  ------------------
 8164|    441|    size_t slash_loc = path.rfind('/');
 8165|    441|    if (slash_loc != std::string_view::npos) {
  ------------------
  |  Branch (8165:9): [True: 441, False: 0]
  ------------------
 8166|    441|      path.remove_suffix(path.size() - slash_loc);
 8167|    441|      return true;
 8168|    441|    }
 8169|    441|  }
 8170|       |
 8171|    452|  return false;
 8172|    893|}
_ZN3ada14url_aggregator11copy_schemeERKS0_:
11697|  1.38k|inline void url_aggregator::copy_scheme(const url_aggregator& u) {
11698|  1.38k|  ada_log("url_aggregator::copy_scheme ", u.buffer);
11699|  1.38k|  ADA_ASSERT_TRUE(validate());
11700|       |  // next line could overflow but unsigned arithmetic has well-defined
11701|       |  // overflows.
11702|  1.38k|  uint32_t new_difference = u.components.protocol_end - components.protocol_end;
11703|       |
11704|  1.38k|  type = u.type;
11705|  1.38k|  buffer.erase(0, components.protocol_end);
11706|  1.38k|  buffer.insert(0, u.get_protocol());
11707|  1.38k|  components.protocol_end = u.components.protocol_end;
11708|       |
11709|       |  // No need to update the components
11710|  1.38k|  if (new_difference == 0) {
  ------------------
  |  Branch (11710:7): [True: 148, False: 1.23k]
  ------------------
11711|    148|    return;
11712|    148|  }
11713|       |
11714|  1.23k|  apply_shifted_non_scheme_offsets(components, new_difference);
11715|  1.23k|  ADA_ASSERT_TRUE(validate());
11716|  1.23k|}
can_parse.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
11575|  36.3k|    ada::url_components& components, uint32_t new_difference) {
11576|  36.3k|  components.username_end += new_difference;
11577|  36.3k|  components.host_start += new_difference;
11578|  36.3k|  components.host_end += new_difference;
11579|  36.3k|  components.pathname_start += new_difference;
11580|  36.3k|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (11580:7): [True: 0, False: 36.3k]
  ------------------
11581|      0|    components.search_start += new_difference;
11582|      0|  }
11583|  36.3k|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (11583:7): [True: 0, False: 36.3k]
  ------------------
11584|      0|    components.hash_start += new_difference;
11585|      0|  }
11586|  36.3k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12064|  24.1k|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
12065|  24.1k|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
12066|  24.1k|          " bytes]");
12067|  24.1k|  ADA_ASSERT_TRUE(validate());
12068|  24.1k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
12069|  24.1k|  if (input.empty()) {
  ------------------
  |  Branch (12069:7): [True: 21, False: 24.1k]
  ------------------
12070|     21|    return is_valid = false;
12071|     21|  }  // technically unnecessary.
12072|       |  // If input starts with U+005B ([), then:
12073|  24.1k|  if (input[0] == '[') {
  ------------------
  |  Branch (12073:7): [True: 1.49k, False: 22.6k]
  ------------------
12074|       |    // If input does not end with U+005D (]), validation error, return failure.
12075|  1.49k|    if (input.back() != ']') {
  ------------------
  |  Branch (12075:9): [True: 354, False: 1.13k]
  ------------------
12076|    354|      return is_valid = false;
12077|    354|    }
12078|  1.13k|    ada_log("parse_host ipv6");
12079|       |
12080|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
12081|       |    // trailing U+005D (]) removed.
12082|  1.13k|    input.remove_prefix(1);
12083|  1.13k|    input.remove_suffix(1);
12084|  1.13k|    return parse_ipv6(input);
12085|  1.49k|  }
12086|       |
12087|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
12088|       |  // input.
12089|  22.6k|  if (!is_special()) {
  ------------------
  |  Branch (12089:7): [True: 822, False: 21.8k]
  ------------------
12090|    822|    return parse_opaque_host(input);
12091|    822|  }
12092|       |  // Let domain be the result of running UTF-8 decode without BOM on the
12093|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
12094|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
12095|       |  // which case we do not need to call the expensive 'to_ascii' if a few
12096|       |  // conditions are met: no '%' and no 'xn-' subsequence.
12097|       |
12098|       |  // Often, the input does not contain any forbidden code points, and no upper
12099|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
12100|       |  // optimize for such a common case.
12101|       |
12102|       |  // Fast path: try to parse as pure decimal IPv4(a.b.c.d) first.
12103|  21.8k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
12104|  21.8k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (12104:7): [True: 993, False: 20.8k]
  ------------------
12105|       |    // Fast path succeeded - input is pure decimal IPv4
12106|    993|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (12106:9): [True: 993, False: 0]
  |  Branch (12106:27): [True: 45, False: 948]
  ------------------
12107|     45|      update_base_hostname(input.substr(0, input.size() - 1));
12108|    948|    } else {
12109|    948|      update_base_hostname(input);
12110|    948|    }
12111|    993|    host_type = IPV4;
12112|    993|    is_valid = true;
12113|    993|    ada_log("parse_host fast path decimal ipv4");
12114|    993|    ADA_ASSERT_TRUE(validate());
12115|    993|    return true;
12116|    993|  }
12117|  20.8k|  uint8_t is_forbidden_or_upper =
12118|  20.8k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
12119|  20.8k|                                                             input.size());
12120|       |  // Minor optimization opportunity:
12121|       |  // contains_forbidden_domain_code_point_or_upper could be extend to check for
12122|       |  // the presence of characters that cannot appear in the ipv4 address and we
12123|       |  // could also check whether x and n and - are present, and so we could skip
12124|       |  // some of the checks below. However, the gains are likely to be small, and
12125|       |  // the code would be more complex.
12126|  20.8k|  static constexpr std::string_view xn_dash{"xn-", 3};
12127|  20.8k|  if (is_forbidden_or_upper == 0 &&
  ------------------
  |  Branch (12127:7): [True: 10.2k, False: 10.5k]
  ------------------
12128|  10.2k|      input.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (12128:7): [True: 6.87k, False: 3.36k]
  ------------------
12129|       |    // fast path
12130|  6.87k|    update_base_hostname(input);
12131|       |
12132|       |    // Check for other IPv4 formats (hex, octal, etc.)
12133|  6.87k|    if (checkers::is_ipv4(get_hostname())) {
  ------------------
  |  Branch (12133:9): [True: 2.01k, False: 4.86k]
  ------------------
12134|  2.01k|      ada_log("parse_host fast path ipv4");
12135|  2.01k|      return parse_ipv4(get_hostname(), true);
12136|  2.01k|    }
12137|  4.86k|    ada_log("parse_host fast path ", get_hostname());
12138|  4.86k|    is_valid = true;
12139|  4.86k|    return true;
12140|  6.87k|  }
12141|       |  // We have encountered at least one forbidden code point or the input contains
12142|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
12143|       |  // conversion.
12144|       |
12145|  13.9k|  ada_log("parse_host calling to_ascii");
12146|  13.9k|  std::optional<std::string> host = std::string(get_hostname());
12147|  13.9k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
12148|  13.9k|  if (!is_valid) {
  ------------------
  |  Branch (12148:7): [True: 4.29k, False: 9.63k]
  ------------------
12149|  4.29k|    ada_log("parse_host to_ascii returns false");
12150|  4.29k|    return is_valid = false;
12151|  4.29k|  }
12152|  9.63k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
12153|  9.63k|          " bytes]");
12154|       |
12155|  9.63k|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (12155:7): [True: 0, False: 9.63k]
  ------------------
12156|  9.63k|                          ada::unicode::is_forbidden_domain_code_point)) {
12157|      0|    return is_valid = false;
12158|      0|  }
12159|       |
12160|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
12161|       |  // asciiDomain.
12162|  9.63k|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (12162:7): [True: 614, False: 9.02k]
  ------------------
12163|    614|    ada_log("parse_host got ipv4 ", *host);
12164|    614|    return parse_ipv4(host.value(), false);
12165|    614|  }
12166|       |
12167|  9.02k|  update_base_hostname(host.value());
12168|  9.02k|  ADA_ASSERT_TRUE(validate());
12169|  9.02k|  return true;
12170|  9.63k|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
 7038|  20.8k|                                              size_t length) noexcept {
 7039|  20.8k|  size_t i = 0;
 7040|  20.8k|  uint8_t accumulator{};
 7041|   164k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7041:10): [True: 143k, False: 20.8k]
  ------------------
 7042|   143k|    accumulator |=
 7043|   143k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7044|   143k|    accumulator |=
 7045|   143k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
 7046|   143k|    accumulator |=
 7047|   143k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
 7048|   143k|    accumulator |=
 7049|   143k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
 7050|   143k|  }
 7051|  51.7k|  for (; i < length; i++) {
  ------------------
  |  Branch (7051:10): [True: 30.8k, False: 20.8k]
  ------------------
 7052|  30.8k|    accumulator |=
 7053|  30.8k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7054|  30.8k|  }
 7055|  20.8k|  return accumulator;
 7056|  20.8k|}
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8181|  1.86k|                                                       size_t pos) {
 8182|  1.86k|  ADA_ASSERT_TRUE(pos <= input.size());
 8183|       |  // The following is safer but unneeded if we have the above line:
 8184|       |  // return pos > input.size() ? std::string_view() : input.substr(pos);
 8185|  1.86k|  return input.substr(pos);
 8186|  1.86k|}
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
12977|  7.47k|inline void url_aggregator::consume_prepared_path(std::string_view input) {
12978|  7.47k|  ada_log("url_aggregator::consume_prepared_path ", input);
12979|       |  /***
12980|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
12981|       |   * unfortunate. This particular function is nearly identical, except that it
12982|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
12983|       |   * very common) merely appends to the buffer. This is the same trivial path as
12984|       |   * with helpers::parse_prepared_path, except that we have the additional check
12985|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
12986|       |   * modify it, and then insert it back into the buffer.
12987|       |   */
12988|  7.47k|  uint8_t accumulator = checkers::path_signature(input);
12989|       |  // Let us first detect a trivial case.
12990|       |  // If it is special, we check that we have no dot, no %,  no \ and no
12991|       |  // character needing percent encoding. Otherwise, we check that we have no %,
12992|       |  // no dot, and no character needing percent encoding.
12993|  7.47k|  constexpr uint8_t need_encoding = 1;
12994|  7.47k|  constexpr uint8_t backslash_char = 2;
12995|  7.47k|  constexpr uint8_t dot_char = 4;
12996|  7.47k|  constexpr uint8_t percent_char = 8;
12997|  7.47k|  bool special = type != ada::scheme::NOT_SPECIAL;
12998|  7.47k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (12998:39): [True: 1.54k, False: 5.92k]
  ------------------
12999|  1.54k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (12999:39): [True: 141, False: 1.40k]
  ------------------
13000|  7.47k|  bool trivial_path =
13001|  7.47k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (13001:7): [True: 4.57k, False: 2.90k]
  |  Branch (13001:8): [True: 5.88k, False: 1.59k]
  ------------------
13002|  7.47k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
13003|  1.59k|                  0)) &&
13004|  4.57k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (13004:7): [True: 4.47k, False: 97]
  ------------------
13005|  7.47k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (13005:7): [True: 997, False: 6.47k]
  |  Branch (13005:34): [True: 973, False: 24]
  ------------------
13006|       |    // '4' means that we have at least one dot, but nothing that requires
13007|       |    // percent encoding or decoding. The only part that is not trivial is
13008|       |    // that we may have single dots and double dots path segments.
13009|       |    // If we have such segments, then we either have a path that begins
13010|       |    // with '.' (easy to check), or we have the sequence './'.
13011|       |    // Note: input cannot be empty, it must at least contain one character ('.')
13012|       |    // Note: we know that '\' is not present.
13013|    973|    if (input[0] != '.') {
  ------------------
  |  Branch (13013:9): [True: 436, False: 537]
  ------------------
13014|    436|      size_t slashdot = 0;
13015|    436|      bool dot_is_file = true;
13016|  2.06k|      for (;;) {
13017|  2.06k|        slashdot = input.find("/.", slashdot);
13018|  2.06k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (13018:13): [True: 436, False: 1.62k]
  ------------------
13019|    436|          break;
13020|  1.62k|        } else {  // uncommon
13021|       |          // only three cases matter: /./, /.. or a final /
13022|  1.62k|          slashdot += 2;
13023|  1.62k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (13023:28): [True: 53, False: 1.57k]
  |  Branch (13023:56): [True: 1.13k, False: 441]
  ------------------
13024|    441|                           input[slashdot] == '/');
  ------------------
  |  Branch (13024:28): [True: 160, False: 281]
  ------------------
13025|  1.62k|        }
13026|  2.06k|      }
13027|    436|      trivial_path = dot_is_file;
13028|    436|    }
13029|    973|  }
13030|  7.47k|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (13030:7): [True: 4.61k, False: 2.86k]
  |  Branch (13030:23): [True: 4.51k, False: 95]
  ------------------
13031|  4.51k|    ada_log("parse_path trivial");
13032|  4.51k|    buffer += '/';
13033|  4.51k|    buffer += input;
13034|  4.51k|    return;
13035|  4.51k|  }
13036|  2.95k|  std::string path = std::string(get_pathname());
13037|       |  // We are going to need to look a bit at the path, but let us see if we can
13038|       |  // ignore percent encoding *and* backslashes *and* percent characters.
13039|       |  // Except for the trivial case, this is likely to capture 99% of paths out
13040|       |  // there.
13041|  2.95k|  bool fast_path =
13042|  2.95k|      (special &&
  ------------------
  |  Branch (13042:8): [True: 1.79k, False: 1.16k]
  ------------------
13043|  1.79k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (13043:8): [True: 864, False: 932]
  ------------------
13044|    864|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (13044:7): [True: 664, False: 200]
  ------------------
13045|  2.95k|  if (fast_path) {
  ------------------
  |  Branch (13045:7): [True: 664, False: 2.29k]
  ------------------
13046|    664|    ada_log("parse_prepared_path fast");
13047|       |    // Here we don't need to worry about \ or percent encoding.
13048|       |    // We also do not have a file protocol. We might have dots, however,
13049|       |    // but dots must as appear as '.', and they cannot be encoded because
13050|       |    // the symbol '%' is not present.
13051|    664|    size_t previous_location = 0;  // We start at 0.
13052|  7.34k|    do {
13053|  7.34k|      size_t new_location = input.find('/', previous_location);
13054|       |      // std::string_view path_view = input;
13055|       |      //  We process the last segment separately:
13056|  7.34k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (13056:11): [True: 664, False: 6.68k]
  ------------------
13057|    664|        std::string_view path_view = input.substr(previous_location);
13058|    664|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (13058:13): [True: 69, False: 595]
  ------------------
13059|       |          // e.g., if you receive ".." with an empty path, you go to "/".
13060|     69|          if (path.empty()) {
  ------------------
  |  Branch (13060:15): [True: 20, False: 49]
  ------------------
13061|     20|            path = '/';
13062|     20|            update_base_pathname(path);
13063|     20|            return;
13064|     20|          }
13065|       |          // Fast case where we have nothing to do:
13066|     49|          if (path.back() == '/') {
  ------------------
  |  Branch (13066:15): [True: 17, False: 32]
  ------------------
13067|     17|            update_base_pathname(path);
13068|     17|            return;
13069|     17|          }
13070|       |          // If you have the path "/joe/myfriend",
13071|       |          // then you delete 'myfriend'.
13072|     32|          path.resize(path.rfind('/') + 1);
13073|     32|          update_base_pathname(path);
13074|     32|          return;
13075|     49|        }
13076|    595|        path += '/';
13077|    595|        if (path_view != ".") {
  ------------------
  |  Branch (13077:13): [True: 532, False: 63]
  ------------------
13078|    532|          path.append(path_view);
13079|    532|        }
13080|    595|        update_base_pathname(path);
13081|    595|        return;
13082|  6.68k|      } else {
13083|       |        // This is a non-final segment.
13084|  6.68k|        std::string_view path_view =
13085|  6.68k|            input.substr(previous_location, new_location - previous_location);
13086|  6.68k|        previous_location = new_location + 1;
13087|  6.68k|        if (path_view == "..") {
  ------------------
  |  Branch (13087:13): [True: 970, False: 5.71k]
  ------------------
13088|    970|          size_t last_delimiter = path.rfind('/');
13089|    970|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (13089:15): [True: 511, False: 459]
  ------------------
13090|    511|            path.erase(last_delimiter);
13091|    511|          }
13092|  5.71k|        } else if (path_view != ".") {
  ------------------
  |  Branch (13092:20): [True: 5.45k, False: 260]
  ------------------
13093|  5.45k|          path += '/';
13094|  5.45k|          path.append(path_view);
13095|  5.45k|        }
13096|  6.68k|      }
13097|  7.34k|    } while (true);
  ------------------
  |  Branch (13097:14): [True: 6.68k, Folded]
  ------------------
13098|  2.29k|  } else {
13099|  2.29k|    ada_log("parse_path slow");
13100|       |    // we have reached the general case
13101|  2.29k|    bool needs_percent_encoding = (accumulator & 1);
13102|  2.29k|    std::string path_buffer_tmp;
13103|  13.0k|    do {
13104|  13.0k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (13104:26): [True: 6.45k, False: 6.61k]
  |  Branch (13104:37): [True: 380, False: 6.07k]
  ------------------
13105|  13.0k|                            ? input.find_first_of("/\\")
13106|  13.0k|                            : input.find('/');
13107|  13.0k|      std::string_view path_view = input;
13108|  13.0k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (13108:11): [True: 10.7k, False: 2.29k]
  ------------------
13109|  10.7k|        path_view.remove_suffix(path_view.size() - location);
13110|  10.7k|        input.remove_prefix(location + 1);
13111|  10.7k|      }
13112|       |      // path_buffer is either path_view or it might point at a percent encoded
13113|       |      // temporary string.
13114|  13.0k|      std::string_view path_buffer =
13115|  13.0k|          (needs_percent_encoding &&
  ------------------
  |  Branch (13115:12): [True: 7.09k, False: 5.97k]
  ------------------
13116|  7.09k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (13116:12): [True: 2.94k, False: 4.14k]
  ------------------
13117|  7.09k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
13118|  13.0k|              ? path_buffer_tmp
13119|  13.0k|              : path_view;
13120|  13.0k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (13120:11): [True: 2.63k, False: 10.4k]
  ------------------
13121|  2.63k|        helpers::shorten_path(path, type);
13122|  2.63k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (13122:13): [True: 195, False: 2.43k]
  ------------------
13123|    195|          path += '/';
13124|    195|        }
13125|  10.4k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (13125:18): [True: 669, False: 9.76k]
  ------------------
13126|    669|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (13126:18): [True: 96, False: 573]
  ------------------
13127|     96|        path += '/';
13128|     96|      }
13129|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
13130|  10.3k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (13130:16): [True: 9.76k, False: 573]
  ------------------
13131|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
13132|       |        // Windows drive letter, then replace the second code point in
13133|       |        // path_buffer with U+003A (:).
13134|  9.76k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (13134:13): [True: 2.32k, False: 7.44k]
  |  Branch (13134:48): [True: 1.10k, False: 1.21k]
  ------------------
13135|  1.10k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (13135:13): [True: 165, False: 943]
  ------------------
13136|    165|          path += '/';
13137|    165|          path += path_buffer[0];
13138|    165|          path += ':';
13139|    165|          path_buffer.remove_prefix(2);
13140|    165|          path.append(path_buffer);
13141|  9.60k|        } else {
13142|       |          // Append path_buffer to url's path.
13143|  9.60k|          path += '/';
13144|  9.60k|          path.append(path_buffer);
13145|  9.60k|        }
13146|  9.76k|      }
13147|  13.0k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (13147:11): [True: 2.29k, False: 10.7k]
  ------------------
13148|  2.29k|        update_base_pathname(path);
13149|  2.29k|        return;
13150|  2.29k|      }
13151|  13.0k|    } while (true);
  ------------------
  |  Branch (13151:14): [True: 10.7k, Folded]
  ------------------
13152|  2.29k|  }
13153|  2.95k|}
_ZN3ada7unicode14to_lower_asciiEPcm:
 6750|  39.5k|constexpr bool to_lower_ascii(char* input, size_t length) noexcept {
 6751|  39.5k|  uint64_t broadcast_80 = broadcast(0x80);
 6752|  39.5k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 6753|  39.5k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 6754|  39.5k|  uint64_t non_ascii = 0;
 6755|  39.5k|  size_t i = 0;
 6756|       |
 6757|  75.6k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (6757:10): [True: 36.1k, False: 39.5k]
  ------------------
 6758|  36.1k|    uint64_t word{};
 6759|  36.1k|    memcpy(&word, input + i, sizeof(word));
 6760|  36.1k|    non_ascii |= (word & broadcast_80);
 6761|  36.1k|    word ^=
 6762|  36.1k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 6763|  36.1k|    memcpy(input + i, &word, sizeof(word));
 6764|  36.1k|  }
 6765|  39.5k|  if (i < length) {
  ------------------
  |  Branch (6765:7): [True: 38.5k, False: 1.03k]
  ------------------
 6766|  38.5k|    uint64_t word{};
 6767|  38.5k|    memcpy(&word, input + i, length - i);
 6768|  38.5k|    non_ascii |= (word & broadcast_80);
 6769|  38.5k|    word ^=
 6770|  38.5k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 6771|  38.5k|    memcpy(input + i, &word, length - i);
 6772|  38.5k|  }
 6773|  39.5k|  return non_ascii == 0;
 6774|  39.5k|}
_ZN3ada7unicode9broadcastEh:
 6746|   118k|constexpr uint64_t broadcast(uint8_t v) noexcept {
 6747|   118k|  return 0x101010101010101ull * v;
 6748|   118k|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   12|  26.7k|ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept {
   13|       |  // The string is not empty and does not contain upper case ASCII characters.
   14|       |  //
   15|       |  // Optimization. To be considered as a possible ipv4, the string must end
   16|       |  // with 'x' or a lowercase hex character.
   17|       |  // Most of the time, this will be false so this simple check will save a lot
   18|       |  // of effort.
   19|       |  // If the address ends with a dot, we need to prune it (special case).
   20|  26.7k|  if (view.ends_with('.')) {
  ------------------
  |  Branch (20:7): [True: 2.38k, False: 24.3k]
  ------------------
   21|  2.38k|    view.remove_suffix(1);
   22|  2.38k|    if (view.empty()) {
  ------------------
  |  Branch (22:9): [True: 1.25k, False: 1.13k]
  ------------------
   23|  1.25k|      return false;
   24|  1.25k|    }
   25|  2.38k|  }
   26|  25.5k|  char last_char = view.back();
   27|  25.5k|  bool possible_ipv4 = (last_char >= '0' && last_char <= '9') ||
  ------------------
  |  Branch (27:25): [True: 23.1k, False: 2.33k]
  |  Branch (27:45): [True: 4.91k, False: 18.2k]
  ------------------
   28|  20.6k|                       (last_char >= 'a' && last_char <= 'f') ||
  ------------------
  |  Branch (28:25): [True: 17.9k, False: 2.64k]
  |  Branch (28:45): [True: 7.89k, False: 10.0k]
  ------------------
   29|  12.7k|                       last_char == 'x';
  ------------------
  |  Branch (29:24): [True: 2.18k, False: 10.5k]
  ------------------
   30|  25.5k|  if (!possible_ipv4) {
  ------------------
  |  Branch (30:7): [True: 10.5k, False: 14.9k]
  ------------------
   31|  10.5k|    return false;
   32|  10.5k|  }
   33|       |  // From the last character, find the last dot.
   34|  14.9k|  size_t last_dot = view.rfind('.');
   35|  14.9k|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (35:7): [True: 4.01k, False: 10.9k]
  ------------------
   36|       |    // We have at least one dot.
   37|  4.01k|    view.remove_prefix(last_dot + 1);
   38|  4.01k|  }
   39|       |  /** Optimization opportunity: we have basically identified the last number of
   40|       |     the ipv4 if we return true here. We might as well parse it and have at
   41|       |     least one number parsed when we get to parse_ipv4. */
   42|  14.9k|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (42:7): [True: 3.15k, False: 11.8k]
  ------------------
   43|  3.15k|    return true;
   44|  3.15k|  }
   45|       |  // It could be hex (0x), but not if there is a single character.
   46|  11.8k|  if (view.size() == 1) {
  ------------------
  |  Branch (46:7): [True: 2.02k, False: 9.81k]
  ------------------
   47|  2.02k|    return false;
   48|  2.02k|  }
   49|       |  // It must start with 0x.
   50|  9.81k|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (50:7): [True: 7.47k, False: 2.33k]
  ------------------
   51|  7.47k|    return false;
   52|  7.47k|  }
   53|       |  // We must allow "0x".
   54|  2.33k|  if (view.size() == 2) {
  ------------------
  |  Branch (54:7): [True: 219, False: 2.11k]
  ------------------
   55|    219|    return true;
   56|    219|  }
   57|       |  // We have 0x followed by some characters, we need to check that they are
   58|       |  // hexadecimals.
   59|  2.11k|  view.remove_prefix(2);
   60|  2.11k|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   61|  2.33k|}
_ZN3ada7unicode16is_lowercase_hexEc:
 7157|  14.7k|ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept {
 7158|  14.7k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7158:11): [True: 14.4k, False: 363]
  |  Branch (7158:23): [True: 7.31k, False: 7.11k]
  |  Branch (7158:37): [True: 7.07k, False: 405]
  |  Branch (7158:49): [True: 5.97k, False: 1.10k]
  ------------------
 7159|  14.7k|}
_ZN3ada6parser25try_parse_simple_absoluteINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
10248|  31.7k|                                                result_type& out) {
10249|  31.7k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
10250|  31.7k|  constexpr bool is_aggregator =
10251|  31.7k|      std::is_same_v<result_type, ada::url_aggregator>;
10252|  31.7k|  static_assert(is_ada_url || is_aggregator);
10253|       |
10254|  31.7k|  const size_t len = input.size();
10255|  31.7k|  if (len < 8) [[unlikely]] {
  ------------------
  |  Branch (10255:7): [True: 20.6k, False: 11.1k]
  ------------------
10256|  20.6k|    return false;
10257|  20.6k|  }
10258|  11.1k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
10259|       |
10260|  11.1k|  size_t pos;
10261|  11.1k|  ada::scheme::type scheme_type;
10262|  11.1k|  uint32_t protocol_end;
10263|  11.1k|  if (b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p') {
  ------------------
  |  Branch (10263:7): [True: 1.19k, False: 9.95k]
  |  Branch (10263:22): [True: 1.07k, False: 118]
  |  Branch (10263:37): [True: 1.05k, False: 22]
  |  Branch (10263:52): [True: 1.02k, False: 30]
  ------------------
10264|  1.02k|    if (b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (10264:9): [True: 922, False: 101]
  |  Branch (10264:24): [True: 706, False: 216]
  |  Branch (10264:39): [True: 696, False: 10]
  ------------------
10265|    696|      pos = 7;
10266|    696|      scheme_type = ada::scheme::type::HTTP;
10267|    696|      protocol_end = 5;
10268|    696|    } else if (len >= 8 && b[4] == 's' && b[5] == ':' && b[6] == '/' &&
  ------------------
  |  Branch (10268:16): [True: 327, False: 0]
  |  Branch (10268:28): [True: 80, False: 247]
  |  Branch (10268:43): [True: 62, False: 18]
  |  Branch (10268:58): [True: 46, False: 16]
  ------------------
10269|     46|               b[7] == '/') {
  ------------------
  |  Branch (10269:16): [True: 35, False: 11]
  ------------------
10270|     35|      pos = 8;
10271|     35|      scheme_type = ada::scheme::type::HTTPS;
10272|     35|      protocol_end = 6;
10273|    292|    } else {
10274|    292|      return false;
10275|    292|    }
10276|  10.1k|  } else {
10277|  10.1k|    return false;
10278|  10.1k|  }
10279|    731|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (10279:7): [True: 727, False: 4]
  |  Branch (10279:21): [True: 4, False: 723]
  |  Branch (10279:38): [True: 3, False: 720]
  ------------------
10280|      7|    return false;
10281|      7|  }
10282|       |
10283|       |  // Digit-led hosts are IPv4/numeric; skip before scanning.
10284|    724|  if (pos < len && b[pos] >= '0' && b[pos] <= '9') {
  ------------------
  |  Branch (10284:7): [True: 720, False: 4]
  |  Branch (10284:20): [True: 464, False: 256]
  |  Branch (10284:37): [True: 0, False: 464]
  ------------------
10285|      0|    return false;
10286|      0|  }
10287|       |
10288|    724|  const size_t host_start = pos;
10289|    724|  bool has_upper = false;
10290|    724|  size_t i = pos;
10291|  17.5k|  for (; i < len; ++i) {
  ------------------
  |  Branch (10291:10): [True: 17.4k, False: 88]
  ------------------
10292|  17.4k|    const uint8_t c = b[i];
10293|  17.4k|    const uint8_t cls = k_host_class[c];
10294|  17.4k|    if (cls == 1) {
  ------------------
  |  Branch (10294:9): [True: 604, False: 16.8k]
  ------------------
10295|    604|      break;
10296|    604|    }
10297|  16.8k|    if (cls == 2) [[unlikely]] {
  ------------------
  |  Branch (10297:9): [True: 32, False: 16.7k]
  ------------------
10298|     32|      return false;
10299|     32|    }
10300|  16.7k|    if (c >= 'A' && c <= 'Z') {
  ------------------
  |  Branch (10300:9): [True: 10.1k, False: 6.61k]
  |  Branch (10300:21): [True: 509, False: 9.65k]
  ------------------
10301|    509|      has_upper = true;
10302|    509|    }
10303|  16.7k|  }
10304|    692|  const size_t host_end = i;
10305|    692|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (10305:7): [True: 7, False: 685]
  ------------------
10306|      7|    return false;
10307|      7|  }
10308|    685|  const size_t host_len = host_end - host_start;
10309|    685|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (10309:7): [True: 15, False: 670]
  ------------------
10310|     15|    return false;
10311|     15|  }
10312|    670|  {
10313|    670|    std::string_view hv(input.data() + host_start, host_len);
10314|    670|    char host_buf[256];
10315|    670|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (10315:9): [True: 77, False: 593]
  ------------------
10316|     77|      std::memcpy(host_buf, input.data() + host_start, host_len);
10317|     77|      unicode::to_lower_ascii(host_buf, host_len);
10318|     77|      hv = std::string_view(host_buf, host_len);
10319|     77|    }
10320|    670|    if (checkers::is_ipv4(hv)) [[unlikely]] {
  ------------------
  |  Branch (10320:9): [True: 10, False: 660]
  ------------------
10321|     10|      return false;
10322|     10|    }
10323|    660|    static constexpr std::string_view xn{"xn-", 3};
10324|    660|    if (hv.find(xn) != std::string_view::npos) [[unlikely]] {
  ------------------
  |  Branch (10324:9): [True: 67, False: 593]
  ------------------
10325|     67|      return false;
10326|     67|    }
10327|    660|  }
10328|       |
10329|    593|  size_t path_start = host_end;
10330|    593|  size_t path_end = host_end;
10331|    593|  size_t query_start = std::string_view::npos;
10332|    593|  size_t hash_start = std::string_view::npos;
10333|    593|  bool has_path = false;
10334|    593|  bool path_has_dot = false;
10335|       |
10336|    593|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (10336:7): [True: 527, False: 66]
  |  Branch (10336:18): [True: 453, False: 74]
  ------------------
10337|    453|    has_path = true;
10338|    453|    path_start = i;
10339|    453|    ++i;
10340|  2.96k|    for (; i < len; ++i) {
  ------------------
  |  Branch (10340:12): [True: 2.68k, False: 278]
  ------------------
10341|  2.68k|      const uint8_t c = b[i];
10342|  2.68k|      const uint8_t cls = k_rest[c];
10343|  2.68k|      if (cls == 0) {
  ------------------
  |  Branch (10343:11): [True: 2.51k, False: 175]
  ------------------
10344|  2.51k|        path_has_dot |= (c == '.');
10345|  2.51k|        continue;
10346|  2.51k|      }
10347|    175|      if (cls == 1) {
  ------------------
  |  Branch (10347:11): [True: 160, False: 15]
  ------------------
10348|    160|        path_end = i;
10349|    160|        if (c == '?') {
  ------------------
  |  Branch (10349:13): [True: 92, False: 68]
  ------------------
10350|     92|          query_start = i;
10351|     92|          ++i;
10352|     92|          goto scan_query;
10353|     92|        }
10354|     68|        hash_start = i;
10355|     68|        ++i;
10356|     68|        goto scan_hash;
10357|    160|      }
10358|     15|      return false;
10359|    175|    }
10360|    278|    path_end = i;
10361|    278|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (10361:14): [True: 74, False: 66]
  |  Branch (10361:25): [True: 44, False: 30]
  ------------------
10362|     44|    query_start = i;
10363|     44|    ++i;
10364|     44|    goto scan_query;
10365|     96|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (10365:14): [True: 30, False: 66]
  |  Branch (10365:25): [True: 30, False: 0]
  ------------------
10366|     30|    hash_start = i;
10367|     30|    ++i;
10368|     30|    goto scan_hash;
10369|     30|  }
10370|    344|  goto after_rest;
10371|       |
10372|    344|scan_query:
10373|  4.75k|  for (; i < len; ++i) {
  ------------------
  |  Branch (10373:10): [True: 4.66k, False: 96]
  ------------------
10374|  4.66k|    const uint8_t c = b[i];
10375|  4.66k|    if (c == '#') {
  ------------------
  |  Branch (10375:9): [True: 26, False: 4.63k]
  ------------------
10376|     26|      hash_start = i;
10377|     26|      ++i;
10378|     26|      goto scan_hash;
10379|     26|    }
10380|  4.63k|    if (c == '?' || c == '%') {
  ------------------
  |  Branch (10380:9): [True: 309, False: 4.32k]
  |  Branch (10380:21): [True: 1.50k, False: 2.82k]
  ------------------
10381|  1.81k|      continue;
10382|  1.81k|    }
10383|  2.82k|    if (k_rest[c] == 2) [[unlikely]] {
  ------------------
  |  Branch (10383:9): [True: 14, False: 2.80k]
  ------------------
10384|     14|      return false;
10385|     14|    }
10386|  2.82k|  }
10387|     96|  goto after_rest;
10388|       |
10389|    124|scan_hash:
10390|  2.95k|  for (; i < len; ++i) {
  ------------------
  |  Branch (10390:10): [True: 2.83k, False: 115]
  ------------------
10391|  2.83k|    const uint8_t c = b[i];
10392|  2.83k|    if (c == '?' || c == '#' || c == '%') {
  ------------------
  |  Branch (10392:9): [True: 270, False: 2.56k]
  |  Branch (10392:21): [True: 205, False: 2.36k]
  |  Branch (10392:33): [True: 888, False: 1.47k]
  ------------------
10393|  1.36k|      continue;
10394|  1.36k|    }
10395|  1.47k|    if (k_rest[c] == 2) [[unlikely]] {
  ------------------
  |  Branch (10395:9): [True: 9, False: 1.46k]
  ------------------
10396|      9|      return false;
10397|      9|    }
10398|  1.47k|  }
10399|       |
10400|    555|after_rest:
10401|    555|  if (path_has_dot) [[unlikely]] {
  ------------------
  |  Branch (10401:7): [True: 109, False: 446]
  ------------------
10402|    109|    const std::string_view path_body(input.data() + path_start,
10403|    109|                                     path_end - path_start);
10404|    109|    if (path_body.size() >= 2 && path_body[1] == '.') {
  ------------------
  |  Branch (10404:9): [True: 109, False: 0]
  |  Branch (10404:34): [True: 44, False: 65]
  ------------------
10405|     44|      if (path_body.size() == 2 || path_body[2] == '/' ||
  ------------------
  |  Branch (10405:11): [True: 5, False: 39]
  |  Branch (10405:36): [True: 3, False: 36]
  ------------------
10406|     36|          (path_body.size() >= 3 && path_body[2] == '.' &&
  ------------------
  |  Branch (10406:12): [True: 36, False: 0]
  |  Branch (10406:37): [True: 14, False: 22]
  ------------------
10407|     14|           (path_body.size() == 3 || path_body[3] == '/'))) {
  ------------------
  |  Branch (10407:13): [True: 3, False: 11]
  |  Branch (10407:38): [True: 3, False: 8]
  ------------------
10408|     14|        return false;
10409|     14|      }
10410|     44|    }
10411|     95|    static constexpr std::string_view slash_dot{"/.", 2};
10412|     95|    size_t p = 1;
10413|    644|    while ((p = path_body.find(slash_dot, p)) != std::string_view::npos) {
  ------------------
  |  Branch (10413:12): [True: 562, False: 82]
  ------------------
10414|    562|      const size_t after = p + 2;
10415|    562|      if (after == path_body.size() || path_body[after] == '/' ||
  ------------------
  |  Branch (10415:11): [True: 3, False: 559]
  |  Branch (10415:40): [True: 4, False: 555]
  ------------------
10416|    555|          (after + 1 <= path_body.size() && path_body[after] == '.' &&
  ------------------
  |  Branch (10416:12): [True: 555, False: 0]
  |  Branch (10416:45): [True: 204, False: 351]
  ------------------
10417|    204|           (after + 1 == path_body.size() || path_body[after + 1] == '/'))) {
  ------------------
  |  Branch (10417:13): [True: 3, False: 201]
  |  Branch (10417:46): [True: 3, False: 198]
  ------------------
10418|     13|        return false;
10419|     13|      }
10420|    549|      p = after;
10421|    549|    }
10422|     95|  }
10423|       |
10424|    528|  const bool need_slash = !has_path;
10425|    528|  out.type = scheme_type;
10426|    528|  out.is_valid = true;
10427|    528|  out.has_opaque_path = false;
10428|    528|  out.host_type = DEFAULT;
10429|       |
10430|       |  if constexpr (is_aggregator) {
10431|       |    if (!need_slash) {
10432|       |      out.buffer.resize(len);
10433|       |      // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
10434|       |      std::memcpy(out.buffer.data(), input.data(), len);
10435|       |      if (has_upper) {
10436|       |        unicode::to_lower_ascii(out.buffer.data() + host_start,
10437|       |                                host_end - host_start);
10438|       |      }
10439|       |      out.components.protocol_end = protocol_end;
10440|       |      out.components.username_end = protocol_end + 2;
10441|       |      out.components.host_start = protocol_end + 2;
10442|       |      out.components.host_end = static_cast<uint32_t>(host_end);
10443|       |      out.components.port = url_components::omitted;
10444|       |      out.components.pathname_start = static_cast<uint32_t>(path_start);
10445|       |      out.components.search_start = (query_start != std::string_view::npos)
10446|       |                                        ? static_cast<uint32_t>(query_start)
10447|       |                                        : url_components::omitted;
10448|       |      out.components.hash_start = (hash_start != std::string_view::npos)
10449|       |                                      ? static_cast<uint32_t>(hash_start)
10450|       |                                      : url_components::omitted;
10451|       |    } else {
10452|       |      out.buffer.resize(len + 1);
10453|       |      // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
10454|       |      std::memcpy(out.buffer.data(), input.data(), host_end);
10455|       |      out.buffer[host_end] = '/';
10456|       |      if (host_end < len) {
10457|       |        std::memcpy(out.buffer.data() + host_end + 1, input.data() + host_end,
10458|       |                    len - host_end);
10459|       |      }
10460|       |      if (has_upper) {
10461|       |        unicode::to_lower_ascii(out.buffer.data() + host_start,
10462|       |                                host_end - host_start);
10463|       |      }
10464|       |      out.components.protocol_end = protocol_end;
10465|       |      out.components.username_end = protocol_end + 2;
10466|       |      out.components.host_start = protocol_end + 2;
10467|       |      out.components.host_end = static_cast<uint32_t>(host_end);
10468|       |      out.components.port = url_components::omitted;
10469|       |      out.components.pathname_start = static_cast<uint32_t>(host_end);
10470|       |      out.components.search_start = (query_start != std::string_view::npos)
10471|       |                                        ? static_cast<uint32_t>(query_start + 1)
10472|       |                                        : url_components::omitted;
10473|       |      out.components.hash_start = (hash_start != std::string_view::npos)
10474|       |                                      ? static_cast<uint32_t>(hash_start + 1)
10475|       |                                      : url_components::omitted;
10476|       |    }
10477|    528|  } else {
10478|    528|    std::string host_str(input.substr(host_start, host_end - host_start));
10479|    528|    if (has_upper) {
  ------------------
  |  Branch (10479:9): [True: 59, False: 469]
  ------------------
10480|     59|      unicode::to_lower_ascii(host_str.data(), host_str.size());
10481|     59|    }
10482|    528|    out.host = std::move(host_str);
10483|    528|    if (need_slash) {
  ------------------
  |  Branch (10483:9): [True: 126, False: 402]
  ------------------
10484|    126|      out.path = "/";
10485|    402|    } else {
10486|    402|      out.path.assign(input.data() + path_start, path_end - path_start);
10487|    402|    }
10488|    528|    if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (10488:9): [True: 122, False: 406]
  ------------------
10489|    122|      const size_t q_end =
10490|    122|          (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (10490:11): [True: 26, False: 96]
  ------------------
10491|    122|      out.query.emplace(input.data() + query_start + 1,
10492|    122|                        q_end - query_start - 1);
10493|    122|    }
10494|    528|    if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (10494:9): [True: 115, False: 413]
  ------------------
10495|    115|      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
10496|    115|    }
10497|    528|  }
10498|    528|  return true;
10499|    555|}
_ZN3ada3url12parse_schemeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
 9527|  18.0k|ada_really_inline bool url::parse_scheme(const std::string_view input) {
 9528|  18.0k|  auto parsed_type = ada::scheme::get_scheme_type(input);
 9529|  18.0k|  bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
 9530|       |  /**
 9531|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
 9532|       |   *http, https), in which case, we can go really fast.
 9533|       |   **/
 9534|  18.0k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (9534:7): [True: 8.51k, False: 9.57k]
  ------------------
 9535|       |    if constexpr (has_state_override) {
 9536|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
 9537|       |      // then return.
 9538|       |      if (is_special() != is_input_special) {
 9539|       |        return false;
 9540|       |      }
 9541|       |
 9542|       |      // If url includes credentials or has a non-null port, and buffer is
 9543|       |      // "file", then return.
 9544|       |      if ((has_credentials() || port.has_value()) &&
 9545|       |          parsed_type == ada::scheme::type::FILE) {
 9546|       |        return false;
 9547|       |      }
 9548|       |
 9549|       |      // If url's scheme is "file" and its host is an empty host, then return.
 9550|       |      // An empty host is the empty string.
 9551|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
 9552|       |          host->empty()) {
 9553|       |        return false;
 9554|       |      }
 9555|       |    }
 9556|       |
 9557|  8.51k|    type = parsed_type;
 9558|       |
 9559|       |    if constexpr (has_state_override) {
 9560|       |      // This is uncommon.
 9561|       |      uint16_t urls_scheme_port = get_special_port();
 9562|       |
 9563|       |      if (urls_scheme_port) {
 9564|       |        // If url's port is url's scheme's default port, then set url's port to
 9565|       |        // null.
 9566|       |        if (port.has_value() && *port == urls_scheme_port) {
 9567|       |          port = std::nullopt;
 9568|       |        }
 9569|       |      }
 9570|       |    }
 9571|  9.57k|  } else {  // slow path
 9572|  9.57k|    std::string _buffer(input);
 9573|       |    // Next function is only valid if the input is ASCII and returns false
 9574|       |    // otherwise, but it seems that we always have ascii content so we do not
 9575|       |    // need to check the return value.
 9576|       |    // bool is_ascii =
 9577|  9.57k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
 9578|       |
 9579|       |    if constexpr (has_state_override) {
 9580|       |      // The state-override validation errors below ("return" in the WHATWG URL
 9581|       |      // parser) leave the URL unchanged. The setter contract is
 9582|       |      // "true on success, false if the scheme is invalid" -- the fast path
 9583|       |      // above already returns false here, so the slow path must agree.
 9584|       |
 9585|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
 9586|       |      // then return. If url's scheme is not a special scheme and buffer is a
 9587|       |      // special scheme, then return.
 9588|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
 9589|       |        return false;
 9590|       |      }
 9591|       |
 9592|       |      // If url includes credentials or has a non-null port, and buffer is
 9593|       |      // "file", then return.
 9594|       |      if ((has_credentials() || port.has_value()) && _buffer == "file") {
 9595|       |        return false;
 9596|       |      }
 9597|       |
 9598|       |      // If url's scheme is "file" and its host is an empty host, then return.
 9599|       |      // An empty host is the empty string.
 9600|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
 9601|       |          host->empty()) {
 9602|       |        return false;
 9603|       |      }
 9604|       |    }
 9605|       |
 9606|  9.57k|    set_scheme(std::move(_buffer));
 9607|       |
 9608|       |    if constexpr (has_state_override) {
 9609|       |      // This is uncommon.
 9610|       |      uint16_t urls_scheme_port = get_special_port();
 9611|       |
 9612|       |      if (urls_scheme_port) {
 9613|       |        // If url's port is url's scheme's default port, then set url's port to
 9614|       |        // null.
 9615|       |        if (port.has_value() && *port == urls_scheme_port) {
 9616|       |          port = std::nullopt;
 9617|       |        }
 9618|       |      }
 9619|       |    }
 9620|  9.57k|  }
 9621|       |
 9622|  18.0k|  return true;
 9623|  18.0k|}
_ZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
10248|  31.7k|                                                result_type& out) {
10249|  31.7k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
10250|  31.7k|  constexpr bool is_aggregator =
10251|  31.7k|      std::is_same_v<result_type, ada::url_aggregator>;
10252|  31.7k|  static_assert(is_ada_url || is_aggregator);
10253|       |
10254|  31.7k|  const size_t len = input.size();
10255|  31.7k|  if (len < 8) [[unlikely]] {
  ------------------
  |  Branch (10255:7): [True: 20.6k, False: 11.1k]
  ------------------
10256|  20.6k|    return false;
10257|  20.6k|  }
10258|  11.1k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
10259|       |
10260|  11.1k|  size_t pos;
10261|  11.1k|  ada::scheme::type scheme_type;
10262|  11.1k|  uint32_t protocol_end;
10263|  11.1k|  if (b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p') {
  ------------------
  |  Branch (10263:7): [True: 1.19k, False: 9.95k]
  |  Branch (10263:22): [True: 1.07k, False: 118]
  |  Branch (10263:37): [True: 1.05k, False: 22]
  |  Branch (10263:52): [True: 1.02k, False: 30]
  ------------------
10264|  1.02k|    if (b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (10264:9): [True: 922, False: 101]
  |  Branch (10264:24): [True: 706, False: 216]
  |  Branch (10264:39): [True: 696, False: 10]
  ------------------
10265|    696|      pos = 7;
10266|    696|      scheme_type = ada::scheme::type::HTTP;
10267|    696|      protocol_end = 5;
10268|    696|    } else if (len >= 8 && b[4] == 's' && b[5] == ':' && b[6] == '/' &&
  ------------------
  |  Branch (10268:16): [True: 327, False: 0]
  |  Branch (10268:28): [True: 80, False: 247]
  |  Branch (10268:43): [True: 62, False: 18]
  |  Branch (10268:58): [True: 46, False: 16]
  ------------------
10269|     46|               b[7] == '/') {
  ------------------
  |  Branch (10269:16): [True: 35, False: 11]
  ------------------
10270|     35|      pos = 8;
10271|     35|      scheme_type = ada::scheme::type::HTTPS;
10272|     35|      protocol_end = 6;
10273|    292|    } else {
10274|    292|      return false;
10275|    292|    }
10276|  10.1k|  } else {
10277|  10.1k|    return false;
10278|  10.1k|  }
10279|    731|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (10279:7): [True: 727, False: 4]
  |  Branch (10279:21): [True: 4, False: 723]
  |  Branch (10279:38): [True: 3, False: 720]
  ------------------
10280|      7|    return false;
10281|      7|  }
10282|       |
10283|       |  // Digit-led hosts are IPv4/numeric; skip before scanning.
10284|    724|  if (pos < len && b[pos] >= '0' && b[pos] <= '9') {
  ------------------
  |  Branch (10284:7): [True: 720, False: 4]
  |  Branch (10284:20): [True: 464, False: 256]
  |  Branch (10284:37): [True: 0, False: 464]
  ------------------
10285|      0|    return false;
10286|      0|  }
10287|       |
10288|    724|  const size_t host_start = pos;
10289|    724|  bool has_upper = false;
10290|    724|  size_t i = pos;
10291|  17.5k|  for (; i < len; ++i) {
  ------------------
  |  Branch (10291:10): [True: 17.4k, False: 88]
  ------------------
10292|  17.4k|    const uint8_t c = b[i];
10293|  17.4k|    const uint8_t cls = k_host_class[c];
10294|  17.4k|    if (cls == 1) {
  ------------------
  |  Branch (10294:9): [True: 604, False: 16.8k]
  ------------------
10295|    604|      break;
10296|    604|    }
10297|  16.8k|    if (cls == 2) [[unlikely]] {
  ------------------
  |  Branch (10297:9): [True: 32, False: 16.7k]
  ------------------
10298|     32|      return false;
10299|     32|    }
10300|  16.7k|    if (c >= 'A' && c <= 'Z') {
  ------------------
  |  Branch (10300:9): [True: 10.1k, False: 6.61k]
  |  Branch (10300:21): [True: 509, False: 9.65k]
  ------------------
10301|    509|      has_upper = true;
10302|    509|    }
10303|  16.7k|  }
10304|    692|  const size_t host_end = i;
10305|    692|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (10305:7): [True: 7, False: 685]
  ------------------
10306|      7|    return false;
10307|      7|  }
10308|    685|  const size_t host_len = host_end - host_start;
10309|    685|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (10309:7): [True: 15, False: 670]
  ------------------
10310|     15|    return false;
10311|     15|  }
10312|    670|  {
10313|    670|    std::string_view hv(input.data() + host_start, host_len);
10314|    670|    char host_buf[256];
10315|    670|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (10315:9): [True: 77, False: 593]
  ------------------
10316|     77|      std::memcpy(host_buf, input.data() + host_start, host_len);
10317|     77|      unicode::to_lower_ascii(host_buf, host_len);
10318|     77|      hv = std::string_view(host_buf, host_len);
10319|     77|    }
10320|    670|    if (checkers::is_ipv4(hv)) [[unlikely]] {
  ------------------
  |  Branch (10320:9): [True: 10, False: 660]
  ------------------
10321|     10|      return false;
10322|     10|    }
10323|    660|    static constexpr std::string_view xn{"xn-", 3};
10324|    660|    if (hv.find(xn) != std::string_view::npos) [[unlikely]] {
  ------------------
  |  Branch (10324:9): [True: 67, False: 593]
  ------------------
10325|     67|      return false;
10326|     67|    }
10327|    660|  }
10328|       |
10329|    593|  size_t path_start = host_end;
10330|    593|  size_t path_end = host_end;
10331|    593|  size_t query_start = std::string_view::npos;
10332|    593|  size_t hash_start = std::string_view::npos;
10333|    593|  bool has_path = false;
10334|    593|  bool path_has_dot = false;
10335|       |
10336|    593|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (10336:7): [True: 527, False: 66]
  |  Branch (10336:18): [True: 453, False: 74]
  ------------------
10337|    453|    has_path = true;
10338|    453|    path_start = i;
10339|    453|    ++i;
10340|  2.96k|    for (; i < len; ++i) {
  ------------------
  |  Branch (10340:12): [True: 2.68k, False: 278]
  ------------------
10341|  2.68k|      const uint8_t c = b[i];
10342|  2.68k|      const uint8_t cls = k_rest[c];
10343|  2.68k|      if (cls == 0) {
  ------------------
  |  Branch (10343:11): [True: 2.51k, False: 175]
  ------------------
10344|  2.51k|        path_has_dot |= (c == '.');
10345|  2.51k|        continue;
10346|  2.51k|      }
10347|    175|      if (cls == 1) {
  ------------------
  |  Branch (10347:11): [True: 160, False: 15]
  ------------------
10348|    160|        path_end = i;
10349|    160|        if (c == '?') {
  ------------------
  |  Branch (10349:13): [True: 92, False: 68]
  ------------------
10350|     92|          query_start = i;
10351|     92|          ++i;
10352|     92|          goto scan_query;
10353|     92|        }
10354|     68|        hash_start = i;
10355|     68|        ++i;
10356|     68|        goto scan_hash;
10357|    160|      }
10358|     15|      return false;
10359|    175|    }
10360|    278|    path_end = i;
10361|    278|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (10361:14): [True: 74, False: 66]
  |  Branch (10361:25): [True: 44, False: 30]
  ------------------
10362|     44|    query_start = i;
10363|     44|    ++i;
10364|     44|    goto scan_query;
10365|     96|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (10365:14): [True: 30, False: 66]
  |  Branch (10365:25): [True: 30, False: 0]
  ------------------
10366|     30|    hash_start = i;
10367|     30|    ++i;
10368|     30|    goto scan_hash;
10369|     30|  }
10370|    344|  goto after_rest;
10371|       |
10372|    344|scan_query:
10373|  4.75k|  for (; i < len; ++i) {
  ------------------
  |  Branch (10373:10): [True: 4.66k, False: 96]
  ------------------
10374|  4.66k|    const uint8_t c = b[i];
10375|  4.66k|    if (c == '#') {
  ------------------
  |  Branch (10375:9): [True: 26, False: 4.63k]
  ------------------
10376|     26|      hash_start = i;
10377|     26|      ++i;
10378|     26|      goto scan_hash;
10379|     26|    }
10380|  4.63k|    if (c == '?' || c == '%') {
  ------------------
  |  Branch (10380:9): [True: 309, False: 4.32k]
  |  Branch (10380:21): [True: 1.50k, False: 2.82k]
  ------------------
10381|  1.81k|      continue;
10382|  1.81k|    }
10383|  2.82k|    if (k_rest[c] == 2) [[unlikely]] {
  ------------------
  |  Branch (10383:9): [True: 14, False: 2.80k]
  ------------------
10384|     14|      return false;
10385|     14|    }
10386|  2.82k|  }
10387|     96|  goto after_rest;
10388|       |
10389|    124|scan_hash:
10390|  2.95k|  for (; i < len; ++i) {
  ------------------
  |  Branch (10390:10): [True: 2.83k, False: 115]
  ------------------
10391|  2.83k|    const uint8_t c = b[i];
10392|  2.83k|    if (c == '?' || c == '#' || c == '%') {
  ------------------
  |  Branch (10392:9): [True: 270, False: 2.56k]
  |  Branch (10392:21): [True: 205, False: 2.36k]
  |  Branch (10392:33): [True: 888, False: 1.47k]
  ------------------
10393|  1.36k|      continue;
10394|  1.36k|    }
10395|  1.47k|    if (k_rest[c] == 2) [[unlikely]] {
  ------------------
  |  Branch (10395:9): [True: 9, False: 1.46k]
  ------------------
10396|      9|      return false;
10397|      9|    }
10398|  1.47k|  }
10399|       |
10400|    555|after_rest:
10401|    555|  if (path_has_dot) [[unlikely]] {
  ------------------
  |  Branch (10401:7): [True: 109, False: 446]
  ------------------
10402|    109|    const std::string_view path_body(input.data() + path_start,
10403|    109|                                     path_end - path_start);
10404|    109|    if (path_body.size() >= 2 && path_body[1] == '.') {
  ------------------
  |  Branch (10404:9): [True: 109, False: 0]
  |  Branch (10404:34): [True: 44, False: 65]
  ------------------
10405|     44|      if (path_body.size() == 2 || path_body[2] == '/' ||
  ------------------
  |  Branch (10405:11): [True: 5, False: 39]
  |  Branch (10405:36): [True: 3, False: 36]
  ------------------
10406|     36|          (path_body.size() >= 3 && path_body[2] == '.' &&
  ------------------
  |  Branch (10406:12): [True: 36, False: 0]
  |  Branch (10406:37): [True: 14, False: 22]
  ------------------
10407|     14|           (path_body.size() == 3 || path_body[3] == '/'))) {
  ------------------
  |  Branch (10407:13): [True: 3, False: 11]
  |  Branch (10407:38): [True: 3, False: 8]
  ------------------
10408|     14|        return false;
10409|     14|      }
10410|     44|    }
10411|     95|    static constexpr std::string_view slash_dot{"/.", 2};
10412|     95|    size_t p = 1;
10413|    644|    while ((p = path_body.find(slash_dot, p)) != std::string_view::npos) {
  ------------------
  |  Branch (10413:12): [True: 562, False: 82]
  ------------------
10414|    562|      const size_t after = p + 2;
10415|    562|      if (after == path_body.size() || path_body[after] == '/' ||
  ------------------
  |  Branch (10415:11): [True: 3, False: 559]
  |  Branch (10415:40): [True: 4, False: 555]
  ------------------
10416|    555|          (after + 1 <= path_body.size() && path_body[after] == '.' &&
  ------------------
  |  Branch (10416:12): [True: 555, False: 0]
  |  Branch (10416:45): [True: 204, False: 351]
  ------------------
10417|    204|           (after + 1 == path_body.size() || path_body[after + 1] == '/'))) {
  ------------------
  |  Branch (10417:13): [True: 3, False: 201]
  |  Branch (10417:46): [True: 3, False: 198]
  ------------------
10418|     13|        return false;
10419|     13|      }
10420|    549|      p = after;
10421|    549|    }
10422|     95|  }
10423|       |
10424|    528|  const bool need_slash = !has_path;
10425|    528|  out.type = scheme_type;
10426|    528|  out.is_valid = true;
10427|    528|  out.has_opaque_path = false;
10428|    528|  out.host_type = DEFAULT;
10429|       |
10430|    528|  if constexpr (is_aggregator) {
10431|    528|    if (!need_slash) {
  ------------------
  |  Branch (10431:9): [True: 402, False: 126]
  ------------------
10432|    402|      out.buffer.resize(len);
10433|       |      // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
10434|    402|      std::memcpy(out.buffer.data(), input.data(), len);
10435|    402|      if (has_upper) {
  ------------------
  |  Branch (10435:11): [True: 11, False: 391]
  ------------------
10436|     11|        unicode::to_lower_ascii(out.buffer.data() + host_start,
10437|     11|                                host_end - host_start);
10438|     11|      }
10439|    402|      out.components.protocol_end = protocol_end;
10440|    402|      out.components.username_end = protocol_end + 2;
10441|    402|      out.components.host_start = protocol_end + 2;
10442|    402|      out.components.host_end = static_cast<uint32_t>(host_end);
10443|    402|      out.components.port = url_components::omitted;
10444|    402|      out.components.pathname_start = static_cast<uint32_t>(path_start);
10445|    402|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (10445:37): [True: 87, False: 315]
  ------------------
10446|    402|                                        ? static_cast<uint32_t>(query_start)
10447|    402|                                        : url_components::omitted;
10448|    402|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (10448:35): [True: 84, False: 318]
  ------------------
10449|    402|                                      ? static_cast<uint32_t>(hash_start)
10450|    402|                                      : url_components::omitted;
10451|    402|    } else {
10452|    126|      out.buffer.resize(len + 1);
10453|       |      // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
10454|    126|      std::memcpy(out.buffer.data(), input.data(), host_end);
10455|    126|      out.buffer[host_end] = '/';
10456|    126|      if (host_end < len) {
  ------------------
  |  Branch (10456:11): [True: 60, False: 66]
  ------------------
10457|     60|        std::memcpy(out.buffer.data() + host_end + 1, input.data() + host_end,
10458|     60|                    len - host_end);
10459|     60|      }
10460|    126|      if (has_upper) {
  ------------------
  |  Branch (10460:11): [True: 48, False: 78]
  ------------------
10461|     48|        unicode::to_lower_ascii(out.buffer.data() + host_start,
10462|     48|                                host_end - host_start);
10463|     48|      }
10464|    126|      out.components.protocol_end = protocol_end;
10465|    126|      out.components.username_end = protocol_end + 2;
10466|    126|      out.components.host_start = protocol_end + 2;
10467|    126|      out.components.host_end = static_cast<uint32_t>(host_end);
10468|    126|      out.components.port = url_components::omitted;
10469|    126|      out.components.pathname_start = static_cast<uint32_t>(host_end);
10470|    126|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (10470:37): [True: 35, False: 91]
  ------------------
10471|    126|                                        ? static_cast<uint32_t>(query_start + 1)
10472|    126|                                        : url_components::omitted;
10473|    126|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (10473:35): [True: 31, False: 95]
  ------------------
10474|    126|                                      ? static_cast<uint32_t>(hash_start + 1)
10475|    126|                                      : url_components::omitted;
10476|    126|    }
10477|       |  } else {
10478|       |    std::string host_str(input.substr(host_start, host_end - host_start));
10479|       |    if (has_upper) {
10480|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
10481|       |    }
10482|       |    out.host = std::move(host_str);
10483|       |    if (need_slash) {
10484|       |      out.path = "/";
10485|       |    } else {
10486|       |      out.path.assign(input.data() + path_start, path_end - path_start);
10487|       |    }
10488|       |    if (query_start != std::string_view::npos) {
10489|       |      const size_t q_end =
10490|       |          (hash_start != std::string_view::npos) ? hash_start : len;
10491|       |      out.query.emplace(input.data() + query_start + 1,
10492|       |                        q_end - query_start - 1);
10493|       |    }
10494|       |    if (hash_start != std::string_view::npos) {
10495|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
10496|       |    }
10497|       |  }
10498|    528|  return true;
10499|    555|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
11593|  35.1k|    const std::string_view input_with_colon) {
11594|  35.1k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
11595|  35.1k|  ADA_ASSERT_TRUE(validate());
11596|  35.1k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
11597|  35.1k|  std::string_view input{input_with_colon};
11598|  35.1k|  input.remove_suffix(1);
11599|  35.1k|  auto parsed_type = ada::scheme::get_scheme_type(input);
11600|  35.1k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
11601|       |  /**
11602|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
11603|       |   *http, https), in which case, we can go really fast.
11604|       |   **/
11605|  35.1k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (11605:7): [True: 16.0k, False: 19.0k]
  ------------------
11606|       |    if constexpr (has_state_override) {
11607|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
11608|       |      // then return.
11609|       |      if (is_special() != is_input_special) {
11610|       |        return false;
11611|       |      }
11612|       |
11613|       |      // If url includes credentials or has a non-null port, and buffer is
11614|       |      // "file", then return.
11615|       |      if ((has_credentials() || components.port != url_components::omitted) &&
11616|       |          parsed_type == ada::scheme::type::FILE) {
11617|       |        return false;
11618|       |      }
11619|       |
11620|       |      // If url's scheme is "file" and its host is an empty host, then return.
11621|       |      // An empty host is the empty string.
11622|       |      if (type == ada::scheme::type::FILE &&
11623|       |          components.host_start == components.host_end) {
11624|       |        return false;
11625|       |      }
11626|       |    }
11627|       |
11628|  16.0k|    type = parsed_type;
11629|  16.0k|    set_scheme_from_view_with_colon(input_with_colon);
11630|       |
11631|       |    if constexpr (has_state_override) {
11632|       |      // This is uncommon.
11633|       |      uint16_t urls_scheme_port = get_special_port();
11634|       |
11635|       |      if (urls_scheme_port) {
11636|       |        // If url's port is url's scheme's default port, then set url's port to
11637|       |        // null.
11638|       |        if (components.port == urls_scheme_port) {
11639|       |          clear_port();
11640|       |        }
11641|       |      }
11642|       |    }
11643|  19.0k|  } else {  // slow path
11644|  19.0k|    std::string _buffer(input);
11645|       |    // Next function is only valid if the input is ASCII and returns false
11646|       |    // otherwise, but it seems that we always have ascii content so we do not
11647|       |    // need to check the return value.
11648|  19.0k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
11649|       |
11650|       |    if constexpr (has_state_override) {
11651|       |      // The state-override validation errors below ("return" in the WHATWG URL
11652|       |      // parser) leave the URL unchanged. The setter contract is
11653|       |      // "true on success, false if the scheme is invalid" -- the fast path
11654|       |      // above already returns false here, so the slow path must agree.
11655|       |
11656|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
11657|       |      // then return. If url's scheme is not a special scheme and buffer is a
11658|       |      // special scheme, then return.
11659|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
11660|       |        return false;
11661|       |      }
11662|       |
11663|       |      // If url includes credentials or has a non-null port, and buffer is
11664|       |      // "file", then return.
11665|       |      if ((has_credentials() || components.port != url_components::omitted) &&
11666|       |          _buffer == "file") {
11667|       |        return false;
11668|       |      }
11669|       |
11670|       |      // If url's scheme is "file" and its host is an empty host, then return.
11671|       |      // An empty host is the empty string.
11672|       |      if (type == ada::scheme::type::FILE &&
11673|       |          components.host_start == components.host_end) {
11674|       |        return false;
11675|       |      }
11676|       |    }
11677|       |
11678|  19.0k|    set_scheme(_buffer);
11679|       |
11680|       |    if constexpr (has_state_override) {
11681|       |      // This is uncommon.
11682|       |      uint16_t urls_scheme_port = get_special_port();
11683|       |
11684|       |      if (urls_scheme_port) {
11685|       |        // If url's port is url's scheme's default port, then set url's port to
11686|       |        // null.
11687|       |        if (components.port == urls_scheme_port) {
11688|       |          clear_port();
11689|       |        }
11690|       |      }
11691|       |    }
11692|  19.0k|  }
11693|  35.1k|  ADA_ASSERT_TRUE(validate());
11694|  35.1k|  return true;
11695|  35.1k|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
11719|  16.0k|    std::string_view new_scheme_with_colon) {
11720|  16.0k|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
11721|  16.0k|          new_scheme_with_colon);
11722|  16.0k|  ADA_ASSERT_TRUE(validate());
11723|  16.0k|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
11724|  16.0k|                  new_scheme_with_colon.back() == ':');
11725|       |  // next line could overflow but unsigned arithmetic has well-defined
11726|       |  // overflows.
11727|  16.0k|  uint32_t new_difference =
11728|  16.0k|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
11729|       |
11730|  16.0k|  if (buffer.empty()) {
  ------------------
  |  Branch (11730:7): [True: 16.0k, False: 0]
  ------------------
11731|  16.0k|    buffer.append(new_scheme_with_colon);
11732|  16.0k|  } else {
11733|      0|    buffer.erase(0, components.protocol_end);
11734|      0|    buffer.insert(0, new_scheme_with_colon);
11735|      0|  }
11736|  16.0k|  components.protocol_end += new_difference;
11737|       |
11738|  16.0k|  apply_shifted_non_scheme_offsets(components, new_difference);
11739|  16.0k|  ADA_ASSERT_TRUE(validate());
11740|  16.0k|}
_ZN3ada14url_aggregator10set_schemeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
11742|  19.0k|inline void url_aggregator::set_scheme(std::string_view new_scheme) {
11743|  19.0k|  ada_log("url_aggregator::set_scheme ", new_scheme);
11744|  19.0k|  ADA_ASSERT_TRUE(validate());
11745|  19.0k|  ADA_ASSERT_TRUE(new_scheme.empty() || new_scheme.back() != ':');
11746|       |  // next line could overflow but unsigned arithmetic has well-defined
11747|       |  // overflows.
11748|  19.0k|  uint32_t new_difference =
11749|  19.0k|      uint32_t(new_scheme.size()) - components.protocol_end + 1;
11750|       |
11751|  19.0k|  type = ada::scheme::get_scheme_type(new_scheme);
11752|  19.0k|  if (buffer.empty()) {
  ------------------
  |  Branch (11752:7): [True: 19.0k, False: 0]
  ------------------
11753|  19.0k|    buffer.append(helpers::concat(new_scheme, ":"));
11754|  19.0k|  } else {
11755|      0|    buffer.erase(0, components.protocol_end);
11756|      0|    buffer.insert(0, helpers::concat(new_scheme, ":"));
11757|      0|  }
11758|  19.0k|  components.protocol_end = uint32_t(new_scheme.size() + 1);
11759|       |
11760|  19.0k|  apply_shifted_non_scheme_offsets(components, new_difference);
11761|  19.0k|  ADA_ASSERT_TRUE(validate());
11762|  19.0k|}

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5632|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1074|   236k|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1075|   236k|  return !!(a[i >> 3] & (1 << (i & 7)));
 1076|   236k|}
_ZN2tl10unexpectedIN3ada6errorsEEC2EOS2_:
 2087|  77.5k|  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:
 3573|  19.3k|      : impl_base(unexpect, std::move(e.value())),
 3574|  19.3k|        ctor_base(detail::default_constructor_tag{}) {}
_ZNR2tl10unexpectedIN3ada6errorsEE5valueEv:
 2101|  38.7k|  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2559|  19.3k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada3urlENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3177|  35.5k|  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_:
 3659|  16.1k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3532|  16.1k|      : impl_base(in_place, std::forward<Args>(args)...),
 3533|  16.1k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2547|  16.1k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada3urlC2EOS0_:
 4928|  16.1k|  url(url&& u) noexcept = default;
_ZN3ada8url_baseD2Ev:
 1591|   147k|  virtual ~url_base() = default;
_ZN3ada3urlD2Ev:
 4931|  51.7k|  ~url() override = default;
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3573|  19.3k|      : impl_base(unexpect, std::move(e.value())),
 3574|  19.3k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2559|  19.3k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3177|  35.5k|  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_:
 3659|  16.1k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3532|  16.1k|      : impl_base(in_place, std::forward<Args>(args)...),
 3533|  16.1k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2547|  16.1k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorC2EOS0_:
 7645|  16.1k|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZN3ada14url_aggregatorD2Ev:
 7648|  96.2k|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6663|  94.0k|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6664|  94.0k|  if (scheme.empty()) {
  ------------------
  |  Branch (6664:7): [True: 0, False: 94.0k]
  ------------------
 6665|      0|    return ada::scheme::NOT_SPECIAL;
 6666|      0|  }
 6667|  94.0k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6668|  94.0k|  const std::string_view target = details::is_special_list[hash_value];
 6669|  94.0k|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6669:7): [True: 73.9k, False: 20.1k]
  ------------------
 6670|  73.9k|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6670:7): [True: 49.2k, False: 24.6k]
  ------------------
 6671|  73.9k|          details::scheme_keys[hash_value]) {
 6672|  49.2k|    return ada::scheme::type(hash_value);
 6673|  49.2k|  } else {
 6674|  44.8k|    return ada::scheme::NOT_SPECIAL;
 6675|  44.8k|  }
 6676|  94.0k|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6604|  73.9k|inline uint64_t branchless_load5(const char* p, size_t n) {
 6605|  73.9k|  uint64_t input = (uint8_t)p[0];
 6606|  73.9k|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6607|  73.9k|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6608|  73.9k|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6609|  73.9k|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6610|  73.9k|  return input;
 6611|  73.9k|}
_ZN3ada14url_aggregatorC2Ev:
 7643|  80.0k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 4837|  80.0k|  url_components() = default;
_ZN3ada14url_aggregatoraSEOS0_:
 7646|  9.02k|  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1344|  37.9k|try_parse_ipv4_fast(std::string_view input) noexcept {
 1345|  37.9k|  const size_t len = input.size();
 1346|       |  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
 1347|  37.9k|  if (len < 7 || len > 16) [[unlikely]] {
  ------------------
  |  Branch (1347:7): [True: 18.0k, False: 19.9k]
  |  Branch (1347:18): [True: 8.07k, False: 11.8k]
  ------------------
 1348|  26.0k|    return ipv4_fast_fail;
 1349|  26.0k|  }
 1350|  11.8k|  const char* data = input.data();
 1351|       |
 1352|       |#if defined(ADA_AVX512)
 1353|       |  return detail::try_parse_ipv4_avx512(data, len);
 1354|       |#else
 1355|  11.8k|  return detail::parse_ipv4_decimal_scalar(data, data + len);
 1356|  37.9k|#endif
 1357|  37.9k|}
_ZN3ada8checkers6detail25parse_ipv4_decimal_scalarEPKcS3_:
 1244|  11.8k|parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
 1245|  11.8k|  uint32_t ipv4 = 0;
 1246|  25.6k|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1246:19): [True: 22.6k, False: 2.98k]
  ------------------
 1247|  22.6k|    if (p == pend) [[unlikely]] {
  ------------------
  |  Branch (1247:9): [True: 24, False: 22.6k]
  ------------------
 1248|     24|      return ipv4_fast_fail;
 1249|     24|    }
 1250|  22.6k|    uint32_t val;
 1251|  22.6k|    char c = *p;
 1252|  22.6k|    if (c >= '0' && c <= '9') [[likely]] {
  ------------------
  |  Branch (1252:9): [True: 20.6k, False: 2.00k]
  |  Branch (1252:21): [True: 16.8k, False: 3.82k]
  ------------------
 1253|  16.8k|      val = static_cast<uint32_t>(c - '0');
 1254|  16.8k|      ++p;
 1255|  16.8k|    } else {
 1256|  5.83k|      return ipv4_fast_fail;
 1257|  5.83k|    }
 1258|  16.8k|    if (p < pend) {
  ------------------
  |  Branch (1258:9): [True: 15.4k, False: 1.41k]
  ------------------
 1259|  15.4k|      c = *p;
 1260|  15.4k|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1260:11): [True: 6.78k, False: 8.63k]
  |  Branch (1260:23): [True: 5.67k, False: 1.10k]
  ------------------
 1261|  5.67k|        if (val == 0) [[unlikely]] {
  ------------------
  |  Branch (1261:13): [True: 531, False: 5.14k]
  ------------------
 1262|    531|          return ipv4_fast_fail;
 1263|    531|        }
 1264|  5.14k|        val = val * 10u + static_cast<uint32_t>(c - '0');
 1265|  5.14k|        ++p;
 1266|  5.14k|        if (p < pend) {
  ------------------
  |  Branch (1266:13): [True: 4.55k, False: 581]
  ------------------
 1267|  4.55k|          c = *p;
 1268|  4.55k|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1268:15): [True: 3.11k, False: 1.44k]
  |  Branch (1268:27): [True: 2.96k, False: 153]
  ------------------
 1269|  2.96k|            val = val * 10u + static_cast<uint32_t>(c - '0');
 1270|  2.96k|            ++p;
 1271|  2.96k|            if (val > 255u) [[unlikely]] {
  ------------------
  |  Branch (1271:17): [True: 699, False: 2.26k]
  ------------------
 1272|    699|              return ipv4_fast_fail;
 1273|    699|            }
 1274|  2.96k|          }
 1275|  4.55k|        }
 1276|  5.14k|      }
 1277|  15.4k|    }
 1278|  15.6k|    ipv4 = (ipv4 << 8) | val;
 1279|  15.6k|    if (i < 3) {
  ------------------
  |  Branch (1279:9): [True: 12.6k, False: 2.98k]
  ------------------
 1280|  12.6k|      if (p == pend || *p != '.') [[unlikely]] {
  ------------------
  |  Branch (1280:11): [True: 388, False: 12.2k]
  |  Branch (1280:24): [True: 1.39k, False: 10.8k]
  ------------------
 1281|  1.78k|        return ipv4_fast_fail;
 1282|  1.78k|      }
 1283|  10.8k|      ++p;
 1284|  10.8k|    }
 1285|  15.6k|  }
 1286|  2.98k|  if (p != pend) {
  ------------------
  |  Branch (1286:7): [True: 669, False: 2.31k]
  ------------------
 1287|    669|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1287:9): [True: 406, False: 263]
  |  Branch (1287:26): [True: 69, False: 337]
  ------------------
 1288|     69|      return ipv4;
 1289|     69|    }
 1290|    600|    return ipv4_fast_fail;
 1291|    669|  }
 1292|  2.31k|  return ipv4;
 1293|  2.98k|}
_ZNK3ada3url15has_credentialsEv:
 7181|  10.1k|[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
 7182|  10.1k|  return !username.empty() || !password.empty();
  ------------------
  |  Branch (7182:10): [True: 1.07k, False: 9.09k]
  |  Branch (7182:31): [True: 197, False: 8.89k]
  ------------------
 7183|  10.1k|}
_ZNK3ada8url_base10is_specialEv:
 7146|   249k|    const noexcept {
 7147|   249k|  return type != ada::scheme::NOT_SPECIAL;
 7148|   249k|}
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEEcvbEv:
 3972|  19.9k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEptEv:
 3941|  12.9k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 3942|  12.9k|    TL_ASSERT(has_value());
  ------------------
  |  | 1994|  12.9k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3942:5): [True: 12.9k, False: 0]
  ------------------
 3943|  12.9k|    return valptr();
 3944|  12.9k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE6valptrEv:
 3237|  12.9k|  T* valptr() { return std::addressof(this->m_val); }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EED2Ev:
 2569|  35.5k|  ~expected_storage_base() {
 2570|  35.5k|    if (m_has_val) {
  ------------------
  |  Branch (2570:9): [True: 16.1k, False: 19.3k]
  ------------------
 2571|  16.1k|      m_val.~T();
 2572|  16.1k|    }
 2573|  35.5k|  }
_ZNK3ada3url13get_href_sizeEv:
 7436|  14.5k|[[nodiscard]] inline size_t url::get_href_size() const noexcept {
 7437|  14.5k|  size_t size = 0;
 7438|  14.5k|  if (is_special()) {
  ------------------
  |  Branch (7438:7): [True: 7.48k, False: 7.10k]
  ------------------
 7439|  7.48k|    size += ada::scheme::details::is_special_list[type].size() + 1;
 7440|  7.48k|  } else {
 7441|  7.10k|    size += non_special_scheme.size() + 1;
 7442|  7.10k|  }
 7443|  14.5k|  if (host.has_value()) {
  ------------------
  |  Branch (7443:7): [True: 8.43k, False: 6.16k]
  ------------------
 7444|  8.43k|    size += host->size();
 7445|  8.43k|    size += 2;
 7446|  8.43k|    if (has_credentials()) {
  ------------------
  |  Branch (7446:9): [True: 780, False: 7.65k]
  ------------------
 7447|    780|      size += username.size();
 7448|    780|      if (!password.empty()) {
  ------------------
  |  Branch (7448:11): [True: 273, False: 507]
  ------------------
 7449|    273|        size += 1 + password.size();
 7450|    273|      }
 7451|    780|      size += 1;
 7452|    780|    }
 7453|  8.43k|    if (port.has_value()) {
  ------------------
  |  Branch (7453:9): [True: 1.34k, False: 7.08k]
  ------------------
 7454|  1.34k|      size += 1;
 7455|  1.34k|      uint16_t p = *port;
 7456|  1.34k|      size += (p >= 10000)  ? 5
  ------------------
  |  Branch (7456:15): [True: 87, False: 1.26k]
  ------------------
 7457|  1.34k|              : (p >= 1000) ? 4
  ------------------
  |  Branch (7457:17): [True: 91, False: 1.16k]
  ------------------
 7458|  1.26k|              : (p >= 100)  ? 3
  ------------------
  |  Branch (7458:17): [True: 92, False: 1.07k]
  ------------------
 7459|  1.16k|              : (p >= 10)   ? 2
  ------------------
  |  Branch (7459:17): [True: 66, False: 1.01k]
  ------------------
 7460|  1.07k|                            : 1;
 7461|  1.34k|    }
 7462|  8.43k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7462:14): [True: 2.79k, False: 3.36k]
  |  Branch (7462:34): [True: 100, False: 2.69k]
  ------------------
 7463|    100|    size += 2;
 7464|    100|  }
 7465|  14.5k|  size += path.size();
 7466|  14.5k|  if (query.has_value()) {
  ------------------
  |  Branch (7466:7): [True: 602, False: 13.9k]
  ------------------
 7467|    602|    size += 1 + query->size();
 7468|    602|  }
 7469|  14.5k|  if (hash.has_value()) {
  ------------------
  |  Branch (7469:7): [True: 647, False: 13.9k]
  ------------------
 7470|    647|    size += 1 + hash->size();
 7471|    647|  }
 7472|  14.5k|  return size;
 7473|  14.5k|}
_ZN3ada8checkers8is_alphaEc:
 1224|  78.1k|constexpr bool is_alpha(char x) noexcept {
 1225|  78.1k|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1225:10): [True: 71.1k, False: 6.94k]
  |  Branch (1225:34): [True: 70.8k, False: 288]
  ------------------
 1226|  78.1k|}
_ZN3ada8checkers8to_lowerEc:
 1222|   149k|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3954|  3.24k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 3955|  3.24k|    TL_ASSERT(has_value());
  ------------------
  |  | 1994|  3.24k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3955:5): [True: 3.24k, False: 0]
  ------------------
 3956|  3.24k|    return val();
 3957|  3.24k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3246|  3.24k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3247|  3.24k|    return this->m_val;
 3248|  3.24k|  }
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEE9has_valueEv:
 3971|  55.5k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN3ada3urlC2Ev:
 4926|  35.5k|  url() = default;
_ZN3ada3url11copy_schemeERKS0_:
 7344|    690|constexpr void url::copy_scheme(const ada::url& u) {
 7345|    690|  non_special_scheme = u.non_special_scheme;
 7346|    690|  type = u.type;
 7347|    690|}
_ZN3ada3url26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7286|    548|inline void url::update_unencoded_base_hash(std::string_view input) {
 7287|       |  // We do the percent encoding
 7288|    548|  hash = unicode::percent_encode(input,
 7289|    548|                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
 7290|    548|}
_ZN3ada3url12clear_searchEv:
 7319|    468|constexpr void url::clear_search() { query = std::nullopt; }
_ZN3ada3url18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7293|    776|                                    const uint8_t query_percent_encode_set[]) {
 7294|    776|  query = ada::unicode::percent_encode(input, query_percent_encode_set);
 7295|    776|}
_ZN3ada3url20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7284|     74|inline void url::update_base_hostname(std::string_view input) { host = input; }
_ZN3ada3url20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7301|  7.39k|inline void url::update_base_pathname(const std::string_view input) {
 7302|  7.39k|  path = input;
 7303|  7.39k|}
_ZN3ada3url10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 7476|  1.32k|                                         bool check_trailing_content) noexcept {
 7477|  1.32k|  ada_log("parse_port('", view, "') ", view.size());
 7478|  1.32k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (7478:7): [True: 1.03k, False: 294]
  |  Branch (7478:24): [True: 4, False: 1.03k]
  ------------------
 7479|      4|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 7480|      4|    is_valid = false;
 7481|      4|    return 0;
 7482|      4|  }
 7483|  1.32k|  uint16_t parsed_port{};
 7484|  1.32k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 7485|  1.32k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (7485:7): [True: 33, False: 1.29k]
  ------------------
 7486|     33|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 7487|     33|    is_valid = false;
 7488|     33|    return 0;
 7489|     33|  }
 7490|  1.29k|  ada_log("parse_port: ", parsed_port);
 7491|  1.29k|  const auto consumed = size_t(r.ptr - view.data());
 7492|  1.29k|  ada_log("parse_port: consumed ", consumed);
 7493|  1.29k|  if (check_trailing_content) {
  ------------------
  |  Branch (7493:7): [True: 1.29k, False: 0]
  ------------------
 7494|  1.29k|    is_valid &=
 7495|  1.29k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (7495:10): [True: 818, False: 473]
  |  Branch (7495:37): [True: 385, False: 88]
  ------------------
 7496|     88|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (7496:10): [True: 19, False: 69]
  |  Branch (7496:36): [True: 64, False: 5]
  |  Branch (7496:52): [True: 1, False: 63]
  ------------------
 7497|  1.29k|  }
 7498|  1.29k|  ada_log("parse_port: is_valid = ", is_valid);
 7499|  1.29k|  if (is_valid) {
  ------------------
  |  Branch (7499:7): [True: 1.22k, False: 68]
  ------------------
 7500|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 7501|  1.22k|    auto default_port = scheme_default_port();
 7502|  1.22k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (7502:27): [True: 76, False: 1.14k]
  |  Branch (7502:48): [True: 25, False: 51]
  ------------------
 7503|  1.19k|                         (default_port != parsed_port);
  ------------------
  |  Branch (7503:26): [True: 1.19k, False: 6]
  ------------------
 7504|  1.22k|    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
  ------------------
  |  Branch (7504:13): [True: 921, False: 302]
  |  Branch (7504:36): [True: 915, False: 6]
  ------------------
 7505|  1.22k|                                                  : std::nullopt;
 7506|  1.22k|  }
 7507|  1.29k|  return consumed;
 7508|  1.32k|}
_ZNK3ada8url_base19scheme_default_portEv:
 7155|  3.51k|url_base::scheme_default_port() const noexcept {
 7156|  3.51k|  return scheme::get_special_port(type);
 7157|  3.51k|}
_ZN3ada6scheme16get_special_portENS0_4typeE:
 6660|  3.51k|constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
 6661|  3.51k|  return details::special_ports[int(type)];
 6662|  3.51k|}
_ZNK3ada3url12get_pathnameEv:
 7207|    185|[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
 7208|    185|  return path;
 7209|    185|}
_ZN3ada8checkers23is_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1228|  10.4k|constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
 1229|  10.4k|  return input.size() >= 2 &&
  ------------------
  |  Branch (1229:10): [True: 6.99k, False: 3.49k]
  ------------------
 1230|  6.99k|         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
  ------------------
  |  Branch (1230:11): [True: 2.81k, False: 4.18k]
  |  Branch (1230:34): [True: 956, False: 1.85k]
  |  Branch (1230:55): [True: 489, False: 1.36k]
  ------------------
 1231|  1.44k|         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
  ------------------
  |  Branch (1231:11): [True: 532, False: 913]
  |  Branch (1231:35): [True: 151, False: 762]
  |  Branch (1231:54): [True: 30, False: 732]
  ------------------
 1232|    732|                                  input[2] == '?' || input[2] == '#'));
  ------------------
  |  Branch (1232:35): [True: 5, False: 727]
  |  Branch (1232:54): [True: 0, False: 727]
  ------------------
 1233|  10.4k|}
_ZN3ada8checkers34is_normalized_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1236|  2.04k|    std::string_view input) noexcept {
 1237|  2.04k|  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
  ------------------
  |  Branch (1237:10): [True: 992, False: 1.05k]
  |  Branch (1237:32): [True: 848, False: 144]
  |  Branch (1237:54): [True: 750, False: 98]
  ------------------
 1238|  2.04k|}
_ZN3ada3url20set_protocol_as_fileEv:
 7329|  2.41k|constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
_ZN3ada3url14clear_pathnameEv:
 7317|     20|constexpr void url::clear_pathname() { path.clear(); }
_ZN3ada7helpers14leading_zeroesEj:
 1876|  35.0k|inline int leading_zeroes(uint32_t input_num) noexcept {
 1877|       |#if ADA_REGULAR_VISUAL_STUDIO
 1878|       |  unsigned long leading_zero(0);
 1879|       |  unsigned long in(input_num);
 1880|       |  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
 1881|       |#else
 1882|  35.0k|  return __builtin_clz(input_num);
 1883|  35.0k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 1884|  35.0k|}
_ZN3ada14url_aggregator7reserveEj:
 8866|  35.0k|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 8867|  35.0k|  buffer.reserve(capacity);
 8868|  35.0k|}
_ZN3ada14url_aggregator20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8356|  12.4k|inline void url_aggregator::update_base_pathname(const std::string_view input) {
 8357|  12.4k|  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
 8358|  12.4k|          " bytes] \n", to_diagram());
 8359|  12.4k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8360|  12.4k|  ADA_ASSERT_TRUE(validate());
 8361|       |
 8362|  12.4k|  const bool begins_with_dashdash = input.starts_with("//");
 8363|  12.4k|  if (!begins_with_dashdash && has_dash_dot()) {
  ------------------
  |  Branch (8363:7): [True: 12.1k, False: 295]
  |  Branch (8363:32): [True: 9, False: 12.1k]
  ------------------
 8364|       |    // We must delete the ./
 8365|      9|    delete_dash_dot();
 8366|      9|  }
 8367|       |
 8368|  12.4k|  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
  ------------------
  |  Branch (8368:7): [True: 295, False: 12.1k]
  |  Branch (8368:31): [True: 295, False: 0]
  |  Branch (8368:51): [True: 74, False: 221]
  ------------------
 8369|     74|      !has_dash_dot()) {
  ------------------
  |  Branch (8369:7): [True: 68, False: 6]
  ------------------
 8370|       |    // If url's host is null, url does not have an opaque path, url's path's
 8371|       |    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
 8372|       |    // output.
 8373|     68|    buffer.insert(components.pathname_start, "/.");
 8374|     68|    components.pathname_start += 2;
 8375|     68|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8375:9): [True: 0, False: 68]
  ------------------
 8376|      0|      components.search_start += 2;
 8377|      0|    }
 8378|     68|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8378:9): [True: 0, False: 68]
  ------------------
 8379|      0|      components.hash_start += 2;
 8380|      0|    }
 8381|     68|  }
 8382|       |
 8383|  12.4k|  uint32_t difference = replace_and_resize(
 8384|  12.4k|      components.pathname_start,
 8385|  12.4k|      components.pathname_start + get_pathname_length(), input);
 8386|  12.4k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8386:7): [True: 0, False: 12.4k]
  ------------------
 8387|      0|    components.search_start += difference;
 8388|      0|  }
 8389|  12.4k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8389:7): [True: 0, False: 12.4k]
  ------------------
 8390|      0|    components.hash_start += difference;
 8391|      0|  }
 8392|  12.4k|  ADA_ASSERT_TRUE(validate());
 8393|  12.4k|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8199|  39.9k|    uint32_t start, uint32_t end, std::string_view input) {
 8200|  39.9k|  uint32_t current_length = end - start;
 8201|  39.9k|  uint32_t input_size = uint32_t(input.size());
 8202|  39.9k|  uint32_t new_difference = input_size - current_length;
 8203|       |
 8204|  39.9k|  if (current_length == 0) {
  ------------------
  |  Branch (8204:7): [True: 36.8k, False: 3.10k]
  ------------------
 8205|  36.8k|    buffer.insert(start, input);
 8206|  36.8k|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8206:14): [True: 410, False: 2.69k]
  ------------------
 8207|    410|    buffer.replace(start, input_size, input);
 8208|  2.69k|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8208:14): [True: 631, False: 2.06k]
  ------------------
 8209|    631|    buffer.erase(start, current_length - input_size);
 8210|    631|    buffer.replace(start, input_size, input);
 8211|  2.06k|  } else {
 8212|  2.06k|    buffer.replace(start, current_length, input.substr(0, current_length));
 8213|  2.06k|    buffer.insert(start + current_length, input.substr(current_length));
 8214|  2.06k|  }
 8215|       |
 8216|  39.9k|  return new_difference;
 8217|  39.9k|}
_ZNK3ada14url_aggregator19get_pathname_lengthEv:
 8248|  12.4k|url_aggregator::get_pathname_length() const noexcept {
 8249|  12.4k|  ada_log("url_aggregator::get_pathname_length");
 8250|  12.4k|  uint32_t ending_index = uint32_t(buffer.size());
 8251|  12.4k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8251:7): [True: 0, False: 12.4k]
  ------------------
 8252|      0|    ending_index = components.search_start;
 8253|  12.4k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8253:14): [True: 0, False: 12.4k]
  ------------------
 8254|      0|    ending_index = components.hash_start;
 8255|      0|  }
 8256|  12.4k|  return ending_index - components.pathname_start;
 8257|  12.4k|}
_ZNK3ada14url_aggregator12get_pathnameEv:
 9193|  5.77k|    ada_lifetime_bound {
 9194|  5.77k|  ada_log("url_aggregator::get_pathname pathname_start = ",
 9195|  5.77k|          components.pathname_start, " buffer.size() = ", buffer.size(),
 9196|  5.77k|          " components.search_start = ", components.search_start,
 9197|  5.77k|          " components.hash_start = ", components.hash_start);
 9198|  5.77k|  auto ending_index = uint32_t(buffer.size());
 9199|  5.77k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9199:7): [True: 200, False: 5.57k]
  ------------------
 9200|    200|    ending_index = components.search_start;
 9201|  5.57k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9201:14): [True: 68, False: 5.50k]
  ------------------
 9202|     68|    ending_index = components.hash_start;
 9203|     68|  }
 9204|  5.77k|  return helpers::substring(buffer, components.pathname_start, ending_index);
 9205|  5.77k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8264|    186|inline void url_aggregator::update_base_search(std::string_view input) {
 8265|    186|  ada_log("url_aggregator::update_base_search ", input);
 8266|    186|  ADA_ASSERT_TRUE(validate());
 8267|    186|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8268|    186|  if (input.empty()) {
  ------------------
  |  Branch (8268:7): [True: 0, False: 186]
  ------------------
 8269|      0|    clear_search();
 8270|      0|    return;
 8271|      0|  }
 8272|       |
 8273|    186|  if (input[0] == '?') {
  ------------------
  |  Branch (8273:7): [True: 186, False: 0]
  ------------------
 8274|    186|    input.remove_prefix(1);
 8275|    186|  }
 8276|       |
 8277|    186|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8277:7): [True: 186, False: 0]
  ------------------
 8278|    186|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8278:9): [True: 186, False: 0]
  ------------------
 8279|    186|      components.search_start = uint32_t(buffer.size());
 8280|    186|      buffer += "?";
 8281|    186|    } else {
 8282|      0|      buffer.resize(components.search_start + 1);
 8283|      0|    }
 8284|       |
 8285|    186|    buffer.append(input);
 8286|    186|  } else {
 8287|      0|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8287:9): [True: 0, False: 0]
  ------------------
 8288|      0|      components.search_start = components.hash_start;
 8289|      0|    } else {
 8290|      0|      buffer.erase(components.search_start,
 8291|      0|                   components.hash_start - components.search_start);
 8292|      0|      components.hash_start = components.search_start;
 8293|      0|    }
 8294|       |
 8295|      0|    buffer.insert(components.search_start, "?");
 8296|      0|    buffer.insert(components.search_start + 1, input);
 8297|      0|    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8298|      0|  }
 8299|       |
 8300|    186|  ADA_ASSERT_TRUE(validate());
 8301|    186|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8175|    633|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8176|    633|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8177|    633|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8178|    633|          " bytes] components.hash_start = ", components.hash_start);
 8179|    633|  ADA_ASSERT_TRUE(validate());
 8180|    633|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8181|    633|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8181:7): [True: 0, False: 633]
  ------------------
 8182|      0|    buffer.resize(components.hash_start);
 8183|      0|  }
 8184|    633|  components.hash_start = uint32_t(buffer.size());
 8185|    633|  buffer += "#";
 8186|    633|  bool encoding_required = unicode::percent_encode<true>(
 8187|    633|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8188|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8189|       |  // encoding was not deemed required.
 8190|    633|  if (!encoding_required) {
  ------------------
  |  Branch (8190:7): [True: 482, False: 151]
  ------------------
 8191|    482|    buffer.append(input);
 8192|    482|  }
 8193|    633|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8194|    633|          buffer, "' [", buffer.size(), " bytes]");
 8195|    633|  ADA_ASSERT_TRUE(validate());
 8196|    633|}
_ZN3ada14url_aggregator20append_base_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8584|  7.17k|inline void url_aggregator::append_base_password(const std::string_view input) {
 8585|  7.17k|  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
 8586|  7.17k|          "\n", to_diagram());
 8587|  7.17k|  ADA_ASSERT_TRUE(validate());
 8588|  7.17k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8589|       |#if ADA_DEVELOPMENT_CHECKS
 8590|       |  // computing the expected password.
 8591|       |  std::string password_expected = std::string(get_password());
 8592|       |  password_expected.append(input);
 8593|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8594|  7.17k|  add_authority_slashes_if_needed();
 8595|       |
 8596|       |  // If input is empty, do nothing.
 8597|  7.17k|  if (input.empty()) {
  ------------------
  |  Branch (8597:7): [True: 1.92k, False: 5.24k]
  ------------------
 8598|  1.92k|    return;
 8599|  1.92k|  }
 8600|       |
 8601|  5.24k|  uint32_t difference = uint32_t(input.size());
 8602|  5.24k|  if (has_password()) {
  ------------------
  |  Branch (8602:7): [True: 4.69k, False: 548]
  ------------------
 8603|  4.69k|    buffer.insert(components.host_start, input);
 8604|  4.69k|  } else {
 8605|    548|    difference++;  // Increment for ":"
 8606|    548|    buffer.insert(components.username_end, ":");
 8607|    548|    buffer.insert(components.username_end + 1, input);
 8608|    548|  }
 8609|  5.24k|  components.host_start += difference;
 8610|       |
 8611|       |  // The following line is required to add "@" to hostname. When updating
 8612|       |  // password if hostname does not start with "@", it is "append_base_password"s
 8613|       |  // responsibility to set it.
 8614|  5.24k|  if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8614:7): [True: 397, False: 4.85k]
  ------------------
 8615|    397|    buffer.insert(components.host_start, "@");
 8616|    397|    difference++;
 8617|    397|  }
 8618|       |
 8619|  5.24k|  components.host_end += difference;
 8620|  5.24k|  components.pathname_start += difference;
 8621|  5.24k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8621:7): [True: 0, False: 5.24k]
  ------------------
 8622|      0|    components.search_start += difference;
 8623|      0|  }
 8624|  5.24k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8624:7): [True: 0, False: 5.24k]
  ------------------
 8625|      0|    components.hash_start += difference;
 8626|      0|  }
 8627|       |#if ADA_DEVELOPMENT_CHECKS
 8628|       |  std::string password_after(get_password());
 8629|       |  ADA_ASSERT_EQUAL(
 8630|       |      password_expected, password_after,
 8631|       |      "append_base_password problem after inserting " + std::string(input));
 8632|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8633|  5.24k|  ADA_ASSERT_TRUE(validate());
 8634|  5.24k|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 8840|  46.3k|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 8841|  46.3k|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 8842|  46.3k|  ADA_ASSERT_TRUE(validate());
 8843|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 8844|       |  // to insert
 8845|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 8846|  46.3k|  if (has_authority()) {
  ------------------
  |  Branch (8846:7): [True: 24.1k, False: 22.2k]
  ------------------
 8847|  24.1k|    return;
 8848|  24.1k|  }
 8849|       |  // Performance: the common case is components.protocol_end == buffer.size()
 8850|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 8851|       |  // the insert could be fused with another insert.
 8852|  22.2k|  buffer.insert(components.protocol_end, "//");
 8853|  22.2k|  components.username_end += 2;
 8854|  22.2k|  components.host_start += 2;
 8855|  22.2k|  components.host_end += 2;
 8856|  22.2k|  components.pathname_start += 2;
 8857|  22.2k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8857:7): [True: 0, False: 22.2k]
  ------------------
 8858|      0|    components.search_start += 2;
 8859|      0|  }
 8860|  22.2k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8860:7): [True: 0, False: 22.2k]
  ------------------
 8861|      0|    components.hash_start += 2;
 8862|      0|  }
 8863|  22.2k|  ADA_ASSERT_TRUE(validate());
 8864|  22.2k|}
_ZN3ada14url_aggregator20append_base_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8466|  11.6k|inline void url_aggregator::append_base_username(const std::string_view input) {
 8467|  11.6k|  ada_log("url_aggregator::append_base_username ", input);
 8468|  11.6k|  ADA_ASSERT_TRUE(validate());
 8469|  11.6k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8470|       |#if ADA_DEVELOPMENT_CHECKS
 8471|       |  // computing the expected password.
 8472|       |  std::string username_expected(get_username());
 8473|       |  username_expected.append(input);
 8474|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8475|  11.6k|  add_authority_slashes_if_needed();
 8476|       |
 8477|       |  // If input is empty, do nothing.
 8478|  11.6k|  if (input.empty()) {
  ------------------
  |  Branch (8478:7): [True: 3.36k, False: 8.25k]
  ------------------
 8479|  3.36k|    return;
 8480|  3.36k|  }
 8481|       |
 8482|  8.25k|  uint32_t difference = uint32_t(input.size());
 8483|  8.25k|  buffer.insert(components.username_end, input);
 8484|  8.25k|  components.username_end += difference;
 8485|  8.25k|  components.host_start += difference;
 8486|       |
 8487|  8.25k|  if (buffer[components.host_start] != '@' &&
  ------------------
  |  Branch (8487:7): [True: 935, False: 7.32k]
  ------------------
 8488|    935|      components.host_start != components.host_end) {
  ------------------
  |  Branch (8488:7): [True: 935, False: 0]
  ------------------
 8489|    935|    buffer.insert(components.host_start, "@");
 8490|    935|    difference++;
 8491|    935|  }
 8492|       |
 8493|  8.25k|  components.host_end += difference;
 8494|  8.25k|  components.pathname_start += difference;
 8495|  8.25k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8495:7): [True: 0, False: 8.25k]
  ------------------
 8496|      0|    components.search_start += difference;
 8497|      0|  }
 8498|  8.25k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8498:7): [True: 0, False: 8.25k]
  ------------------
 8499|      0|    components.hash_start += difference;
 8500|      0|  }
 8501|       |#if ADA_DEVELOPMENT_CHECKS
 8502|       |  std::string username_after(get_username());
 8503|       |  ADA_ASSERT_EQUAL(
 8504|       |      username_expected, username_after,
 8505|       |      "append_base_username problem after inserting " + std::string(input));
 8506|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8507|  8.25k|  ADA_ASSERT_TRUE(validate());
 8508|  8.25k|}
_ZN3ada14url_aggregator21update_base_authorityENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS_14url_componentsE:
 8117|  1.18k|    std::string_view base_buffer, const ada::url_components& base) {
 8118|  1.18k|  std::string_view input = base_buffer.substr(
 8119|  1.18k|      base.protocol_end, base.host_start - base.protocol_end);
 8120|  1.18k|  ada_log("url_aggregator::update_base_authority ", input);
 8121|       |
 8122|  1.18k|  bool input_starts_with_dash = input.starts_with("//");
 8123|  1.18k|  uint32_t diff = components.host_start - components.protocol_end;
 8124|       |
 8125|  1.18k|  buffer.erase(components.protocol_end,
 8126|  1.18k|               components.host_start - components.protocol_end);
 8127|  1.18k|  components.username_end = components.protocol_end;
 8128|       |
 8129|  1.18k|  if (input_starts_with_dash) {
  ------------------
  |  Branch (8129:7): [True: 866, False: 322]
  ------------------
 8130|    866|    input.remove_prefix(2);
 8131|    866|    diff += 2;  // add "//"
 8132|    866|    buffer.insert(components.protocol_end, "//");
 8133|    866|    components.username_end += 2;
 8134|    866|  }
 8135|       |
 8136|  1.18k|  size_t password_delimiter = input.find(':');
 8137|       |
 8138|       |  // Check if input contains both username and password by checking the
 8139|       |  // delimiter: ":" A typical input that contains authority would be "user:pass"
 8140|  1.18k|  if (password_delimiter != std::string_view::npos) {
  ------------------
  |  Branch (8140:7): [True: 47, False: 1.14k]
  ------------------
 8141|       |    // Insert both username and password
 8142|     47|    std::string_view username = input.substr(0, password_delimiter);
 8143|     47|    std::string_view password = input.substr(password_delimiter + 1);
 8144|       |
 8145|     47|    buffer.insert(components.protocol_end + diff, username);
 8146|     47|    diff += uint32_t(username.size());
 8147|     47|    buffer.insert(components.protocol_end + diff, ":");
 8148|     47|    components.username_end = components.protocol_end + diff;
 8149|     47|    buffer.insert(components.protocol_end + diff + 1, password);
 8150|     47|    diff += uint32_t(password.size()) + 1;
 8151|  1.14k|  } else if (!input.empty()) {
  ------------------
  |  Branch (8151:14): [True: 51, False: 1.09k]
  ------------------
 8152|       |    // Insert only username
 8153|     51|    buffer.insert(components.protocol_end + diff, input);
 8154|     51|    components.username_end =
 8155|     51|        components.protocol_end + diff + uint32_t(input.size());
 8156|     51|    diff += uint32_t(input.size());
 8157|     51|  }
 8158|       |
 8159|  1.18k|  components.host_start += diff;
 8160|       |
 8161|  1.18k|  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
  ------------------
  |  Branch (8161:7): [True: 0, False: 1.18k]
  |  Branch (8161:42): [True: 0, False: 0]
  ------------------
 8162|      0|    buffer.insert(components.host_start, "@");
 8163|      0|    diff++;
 8164|      0|  }
 8165|  1.18k|  components.host_end += diff;
 8166|  1.18k|  components.pathname_start += diff;
 8167|  1.18k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8167:7): [True: 0, False: 1.18k]
  ------------------
 8168|      0|    components.search_start += diff;
 8169|      0|  }
 8170|  1.18k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8170:7): [True: 0, False: 1.18k]
  ------------------
 8171|      0|    components.hash_start += diff;
 8172|      0|  }
 8173|  1.18k|}
_ZNK3ada14url_aggregator8get_hrefEv:
 8944|  19.3k|    const noexcept ada_lifetime_bound {
 8945|  19.3k|  ada_log("url_aggregator::get_href");
 8946|  19.3k|  return buffer;
 8947|  19.3k|}
_ZNK3ada14url_aggregator14get_componentsEv:
 8826|  1.18k|url_aggregator::get_components() const noexcept {
 8827|  1.18k|  return components;
 8828|  1.18k|}
_ZN3ada14url_aggregator24update_host_to_base_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9212|  1.78k|void url_aggregator::update_host_to_base_host(const std::string_view input) {
 9213|  1.78k|  ada_log("url_aggregator::update_host_to_base_host ", input);
 9214|  1.78k|  ADA_ASSERT_TRUE(validate());
 9215|  1.78k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 9216|  1.78k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (9216:7): [True: 1.18k, False: 596]
  ------------------
 9217|       |    // Let host be the result of host parsing host_view with url is not special.
 9218|  1.18k|    if (input.empty() && !is_special()) {
  ------------------
  |  Branch (9218:9): [True: 340, False: 848]
  |  Branch (9218:26): [True: 340, False: 0]
  ------------------
 9219|    340|      if (has_hostname()) {
  ------------------
  |  Branch (9219:11): [True: 18, False: 322]
  ------------------
 9220|     18|        clear_hostname();
 9221|    322|      } else if (has_dash_dot()) {
  ------------------
  |  Branch (9221:18): [True: 0, False: 322]
  ------------------
 9222|      0|        add_authority_slashes_if_needed();
 9223|      0|        delete_dash_dot();
 9224|      0|      }
 9225|    340|      return;
 9226|    340|    }
 9227|  1.18k|  }
 9228|  1.44k|  update_base_hostname(input);
 9229|  1.44k|  ADA_ASSERT_TRUE(validate());
 9230|  1.44k|  return;
 9231|  1.78k|}
_ZN3ada14url_aggregator14clear_hostnameEv:
 8767|     18|constexpr void url_aggregator::clear_hostname() {
 8768|     18|  ada_log("url_aggregator::clear_hostname");
 8769|     18|  ADA_ASSERT_TRUE(validate());
 8770|     18|  if (!has_authority()) {
  ------------------
  |  Branch (8770:7): [True: 0, False: 18]
  ------------------
 8771|      0|    return;
 8772|      0|  }
 8773|     18|  ADA_ASSERT_TRUE(has_authority());
 8774|       |
 8775|     18|  uint32_t hostname_length = components.host_end - components.host_start;
 8776|     18|  uint32_t start = components.host_start;
 8777|       |
 8778|       |  // If hostname starts with "@", we should not remove that character.
 8779|     18|  if (hostname_length > 0 && buffer[start] == '@') {
  ------------------
  |  Branch (8779:7): [True: 0, False: 18]
  |  Branch (8779:30): [True: 0, False: 0]
  ------------------
 8780|      0|    start++;
 8781|      0|    hostname_length--;
 8782|      0|  }
 8783|     18|  buffer.erase(start, hostname_length);
 8784|     18|  components.host_end = start;
 8785|     18|  components.pathname_start -= hostname_length;
 8786|     18|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8786:7): [True: 0, False: 18]
  ------------------
 8787|      0|    components.search_start -= hostname_length;
 8788|      0|  }
 8789|     18|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8789:7): [True: 0, False: 18]
  ------------------
 8790|      0|    components.hash_start -= hostname_length;
 8791|      0|  }
 8792|       |#if ADA_DEVELOPMENT_CHECKS
 8793|       |  ADA_ASSERT_EQUAL(get_hostname(), "",
 8794|       |                   "hostname should have been cleared on buffer=" + buffer +
 8795|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8796|       |#endif
 8797|     18|  ADA_ASSERT_TRUE(has_authority());
 8798|     18|  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
 8799|     18|                   "hostname should have been cleared on buffer=" + buffer +
 8800|     18|                       " with " + components.to_string() + "\n" + to_diagram());
 8801|     18|  ADA_ASSERT_TRUE(validate());
 8802|     18|}
_ZN3ada14url_aggregator16update_base_portEj:
 8636|  2.86k|inline void url_aggregator::update_base_port(uint32_t input) {
 8637|  2.86k|  ada_log("url_aggregator::update_base_port");
 8638|  2.86k|  ADA_ASSERT_TRUE(validate());
 8639|  2.86k|  if (input == url_components::omitted) {
  ------------------
  |  Branch (8639:7): [True: 1.16k, False: 1.70k]
  ------------------
 8640|  1.16k|    clear_port();
 8641|  1.16k|    return;
 8642|  1.16k|  }
 8643|       |  // calling std::to_string(input.value()) is unfortunate given that the port
 8644|       |  // value is probably already available as a string.
 8645|  1.70k|  std::string value = helpers::concat(":", std::to_string(input));
 8646|  1.70k|  uint32_t difference = uint32_t(value.size());
 8647|       |
 8648|  1.70k|  if (components.port != url_components::omitted) {
  ------------------
  |  Branch (8648:7): [True: 0, False: 1.70k]
  ------------------
 8649|      0|    difference -= components.pathname_start - components.host_end;
 8650|      0|    buffer.erase(components.host_end,
 8651|      0|                 components.pathname_start - components.host_end);
 8652|      0|  }
 8653|       |
 8654|  1.70k|  buffer.insert(components.host_end, value);
 8655|  1.70k|  components.pathname_start += difference;
 8656|  1.70k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8656:7): [True: 0, False: 1.70k]
  ------------------
 8657|      0|    components.search_start += difference;
 8658|      0|  }
 8659|  1.70k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8659:7): [True: 0, False: 1.70k]
  ------------------
 8660|      0|    components.hash_start += difference;
 8661|      0|  }
 8662|  1.70k|  components.port = input;
 8663|  1.70k|  ADA_ASSERT_TRUE(validate());
 8664|  1.70k|}
_ZN3ada7helpers6concatIJPKcNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEESA_DpT_:
 1866|  1.70k|std::string concat(Args... args) {
 1867|  1.70k|  std::string answer;
 1868|  1.70k|  inner_concat(answer, args...);
 1869|  1.70k|  return answer;
 1870|  1.70k|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEEvRSA_T_DpT0_:
 1855|  1.70k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1856|  1.70k|  buffer.append(t);
 1857|  1.70k|  return inner_concat(buffer, args...);
 1858|  1.70k|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJEEEvRS8_T_:
 1847|  1.70k|inline void inner_concat(std::string& buffer, T t) {
 1848|  1.70k|  buffer.append(t);
 1849|  1.70k|}
_ZNK3ada14url_aggregator18retrieve_base_portEv:
 8685|  1.18k|[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
 8686|  1.18k|  ada_log("url_aggregator::retrieve_base_port");
 8687|  1.18k|  return components.port;
 8688|  1.18k|}
_ZN3ada14url_aggregator12clear_searchEv:
 8690|    936|inline void url_aggregator::clear_search() {
 8691|    936|  ada_log("url_aggregator::clear_search");
 8692|    936|  ADA_ASSERT_TRUE(validate());
 8693|    936|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8693:7): [True: 888, False: 48]
  ------------------
 8694|    888|    return;
 8695|    888|  }
 8696|       |
 8697|     48|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8697:7): [True: 48, False: 0]
  ------------------
 8698|     48|    buffer.resize(components.search_start);
 8699|     48|  } else {
 8700|      0|    buffer.erase(components.search_start,
 8701|      0|                 components.hash_start - components.search_start);
 8702|      0|    components.hash_start = components.search_start;
 8703|      0|  }
 8704|       |
 8705|     48|  components.search_start = url_components::omitted;
 8706|       |
 8707|       |#if ADA_DEVELOPMENT_CHECKS
 8708|       |  ADA_ASSERT_EQUAL(get_search(), "",
 8709|       |                   "search should have been cleared on buffer=" + buffer +
 8710|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8711|       |#endif
 8712|     48|  ADA_ASSERT_TRUE(validate());
 8713|     48|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8304|    776|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8305|    776|  ada_log("url_aggregator::update_base_search ", input,
 8306|    776|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8307|    776|  ADA_ASSERT_TRUE(validate());
 8308|    776|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8309|       |
 8310|    776|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8310:7): [True: 776, False: 0]
  ------------------
 8311|    776|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8311:9): [True: 756, False: 20]
  ------------------
 8312|    756|      components.search_start = uint32_t(buffer.size());
 8313|    756|      buffer += "?";
 8314|    756|    } else {
 8315|     20|      buffer.resize(components.search_start + 1);
 8316|     20|    }
 8317|       |
 8318|    776|    bool encoding_required =
 8319|    776|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8320|       |    // When encoding_required is false, then buffer is left unchanged, and
 8321|       |    // percent encoding was not deemed required.
 8322|    776|    if (!encoding_required) {
  ------------------
  |  Branch (8322:9): [True: 525, False: 251]
  ------------------
 8323|    525|      buffer.append(input);
 8324|    525|    }
 8325|    776|  } else {
 8326|      0|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8326:9): [True: 0, False: 0]
  ------------------
 8327|      0|      components.search_start = components.hash_start;
 8328|      0|    } else {
 8329|      0|      buffer.erase(components.search_start,
 8330|      0|                   components.hash_start - components.search_start);
 8331|      0|      components.hash_start = components.search_start;
 8332|      0|    }
 8333|       |
 8334|      0|    buffer.insert(components.search_start, "?");
 8335|      0|    size_t idx =
 8336|      0|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8337|      0|    if (idx == input.size()) {
  ------------------
  |  Branch (8337:9): [True: 0, False: 0]
  ------------------
 8338|      0|      buffer.insert(components.search_start + 1, input);
 8339|      0|      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8340|      0|    } else {
 8341|      0|      buffer.insert(components.search_start + 1, input, 0, idx);
 8342|      0|      input.remove_prefix(idx);
 8343|       |      // We only create a temporary string if we need percent encoding and
 8344|       |      // we attempt to create as small a temporary string as we can.
 8345|      0|      std::string encoded =
 8346|      0|          ada::unicode::percent_encode(input, query_percent_encode_set);
 8347|      0|      buffer.insert(components.search_start + idx + 1, encoded);
 8348|      0|      components.hash_start +=
 8349|      0|          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
 8350|      0|    }
 8351|      0|  }
 8352|       |
 8353|    776|  ADA_ASSERT_TRUE(validate());
 8354|    776|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8219|  27.5k|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8220|  27.5k|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8221|  27.5k|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8222|  27.5k|  ADA_ASSERT_TRUE(validate());
 8223|  27.5k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8224|       |
 8225|       |  // This next line is required for when parsing a URL like `foo://`
 8226|  27.5k|  add_authority_slashes_if_needed();
 8227|       |
 8228|  27.5k|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8229|  27.5k|  uint32_t new_difference =
 8230|  27.5k|      replace_and_resize(components.host_start, components.host_end, input);
 8231|       |
 8232|  27.5k|  if (has_credentials) {
  ------------------
  |  Branch (8232:7): [True: 934, False: 26.6k]
  ------------------
 8233|    934|    buffer.insert(components.host_start, "@");
 8234|    934|    new_difference++;
 8235|    934|  }
 8236|  27.5k|  components.host_end += new_difference;
 8237|  27.5k|  components.pathname_start += new_difference;
 8238|  27.5k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8238:7): [True: 0, False: 27.5k]
  ------------------
 8239|      0|    components.search_start += new_difference;
 8240|      0|  }
 8241|  27.5k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8241:7): [True: 0, False: 27.5k]
  ------------------
 8242|      0|    components.hash_start += new_difference;
 8243|      0|  }
 8244|  27.5k|  ADA_ASSERT_TRUE(validate());
 8245|  27.5k|}
_ZN3ada14url_aggregator10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 8954|  2.48k|url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
 8955|  2.48k|  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
 8956|  2.48k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (8956:7): [True: 1.90k, False: 586]
  |  Branch (8956:24): [True: 7, False: 1.89k]
  ------------------
 8957|      7|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 8958|      7|    is_valid = false;
 8959|      7|    return 0;
 8960|      7|  }
 8961|  2.47k|  uint16_t parsed_port{};
 8962|  2.47k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 8963|  2.47k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (8963:7): [True: 60, False: 2.41k]
  ------------------
 8964|     60|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 8965|     60|    is_valid = false;
 8966|     60|    return 0;
 8967|     60|  }
 8968|  2.41k|  ada_log("parse_port: ", parsed_port);
 8969|  2.41k|  const size_t consumed = size_t(r.ptr - view.data());
 8970|  2.41k|  ada_log("parse_port: consumed ", consumed);
 8971|  2.41k|  if (check_trailing_content) {
  ------------------
  |  Branch (8971:7): [True: 2.41k, False: 0]
  ------------------
 8972|  2.41k|    is_valid &=
 8973|  2.41k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (8973:10): [True: 1.62k, False: 797]
  |  Branch (8973:37): [True: 633, False: 164]
  ------------------
 8974|    164|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (8974:10): [True: 38, False: 126]
  |  Branch (8974:36): [True: 116, False: 10]
  |  Branch (8974:52): [True: 2, False: 114]
  ------------------
 8975|  2.41k|  }
 8976|  2.41k|  ada_log("parse_port: is_valid = ", is_valid);
 8977|  2.41k|  if (is_valid) {
  ------------------
  |  Branch (8977:7): [True: 2.29k, False: 124]
  ------------------
 8978|  2.29k|    ada_log("parse_port", r.ec == std::errc());
 8979|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 8980|  2.29k|    auto default_port = scheme_default_port();
 8981|  2.29k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (8981:27): [True: 152, False: 2.14k]
  |  Branch (8981:48): [True: 50, False: 102]
  ------------------
 8982|  2.24k|                         (default_port != parsed_port);
  ------------------
  |  Branch (8982:26): [True: 2.23k, False: 12]
  ------------------
 8983|  2.29k|    if (r.ec == std::errc() && is_port_valid) {
  ------------------
  |  Branch (8983:9): [True: 1.69k, False: 602]
  |  Branch (8983:32): [True: 1.68k, False: 12]
  ------------------
 8984|  1.68k|      update_base_port(parsed_port);
 8985|  1.68k|    } else {
 8986|    614|      clear_port();
 8987|    614|    }
 8988|  2.29k|  }
 8989|  2.41k|  return consumed;
 8990|  2.47k|}
_ZN3ada14url_aggregator20append_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8395|      2|inline void url_aggregator::append_base_pathname(const std::string_view input) {
 8396|      2|  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
 8397|      2|          "\n", to_diagram());
 8398|      2|  ADA_ASSERT_TRUE(validate());
 8399|      2|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8400|       |#if ADA_DEVELOPMENT_CHECKS
 8401|       |  // computing the expected password.
 8402|       |  std::string path_expected(get_pathname());
 8403|       |  path_expected.append(input);
 8404|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8405|      2|  uint32_t ending_index = uint32_t(buffer.size());
 8406|      2|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8406:7): [True: 0, False: 2]
  ------------------
 8407|      0|    ending_index = components.search_start;
 8408|      2|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8408:14): [True: 0, False: 2]
  ------------------
 8409|      0|    ending_index = components.hash_start;
 8410|      0|  }
 8411|      2|  buffer.insert(ending_index, input);
 8412|       |
 8413|      2|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8413:7): [True: 0, False: 2]
  ------------------
 8414|      0|    components.search_start += uint32_t(input.size());
 8415|      0|  }
 8416|      2|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8416:7): [True: 0, False: 2]
  ------------------
 8417|      0|    components.hash_start += uint32_t(input.size());
 8418|      0|  }
 8419|       |#if ADA_DEVELOPMENT_CHECKS
 8420|       |  std::string path_after = std::string(get_pathname());
 8421|       |  ADA_ASSERT_EQUAL(
 8422|       |      path_expected, path_after,
 8423|       |      "append_base_pathname problem after inserting " + std::string(input));
 8424|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8425|      2|  ADA_ASSERT_TRUE(validate());
 8426|      2|}
_ZN3ada14url_aggregator20set_protocol_as_fileEv:
 8992|  4.82k|constexpr void url_aggregator::set_protocol_as_file() {
 8993|  4.82k|  ada_log("url_aggregator::set_protocol_as_file ");
 8994|  4.82k|  ADA_ASSERT_TRUE(validate());
 8995|  4.82k|  type = ada::scheme::type::FILE;
 8996|       |  // next line could overflow but unsigned arithmetic has well-defined
 8997|       |  // overflows.
 8998|  4.82k|  uint32_t new_difference = 5 - components.protocol_end;
 8999|       |
 9000|  4.82k|  if (buffer.empty()) {
  ------------------
  |  Branch (9000:7): [True: 546, False: 4.27k]
  ------------------
 9001|    546|    buffer.append("file:");
 9002|  4.27k|  } else {
 9003|  4.27k|    buffer.erase(0, components.protocol_end);
 9004|  4.27k|    buffer.insert(0, "file:");
 9005|  4.27k|  }
 9006|  4.82k|  components.protocol_end = 5;
 9007|       |
 9008|       |  // Update the rest of the components.
 9009|  4.82k|  components.username_end += new_difference;
 9010|  4.82k|  components.host_start += new_difference;
 9011|  4.82k|  components.host_end += new_difference;
 9012|  4.82k|  components.pathname_start += new_difference;
 9013|  4.82k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9013:7): [True: 0, False: 4.82k]
  ------------------
 9014|      0|    components.search_start += new_difference;
 9015|      0|  }
 9016|  4.82k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9016:7): [True: 0, False: 4.82k]
  ------------------
 9017|      0|    components.hash_start += new_difference;
 9018|      0|  }
 9019|  4.82k|  ADA_ASSERT_TRUE(validate());
 9020|  4.82k|}
_ZN3ada14url_aggregator14clear_pathnameEv:
 8732|     40|constexpr void url_aggregator::clear_pathname() {
 8733|     40|  ada_log("url_aggregator::clear_pathname");
 8734|     40|  ADA_ASSERT_TRUE(validate());
 8735|     40|  uint32_t ending_index = uint32_t(buffer.size());
 8736|     40|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8736:7): [True: 0, False: 40]
  ------------------
 8737|      0|    ending_index = components.search_start;
 8738|     40|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8738:14): [True: 0, False: 40]
  ------------------
 8739|      0|    ending_index = components.hash_start;
 8740|      0|  }
 8741|     40|  uint32_t pathname_length = ending_index - components.pathname_start;
 8742|     40|  buffer.erase(components.pathname_start, pathname_length);
 8743|     40|  uint32_t difference = pathname_length;
 8744|     40|  if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8744:7): [True: 0, False: 40]
  ------------------
 8745|      0|      buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (8745:7): [True: 0, False: 0]
  ------------------
 8746|      0|      buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (8746:7): [True: 0, False: 0]
  ------------------
 8747|      0|    components.pathname_start -= 2;
 8748|      0|    buffer.erase(components.host_end, 2);
 8749|      0|    difference += 2;
 8750|      0|  }
 8751|     40|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8751:7): [True: 0, False: 40]
  ------------------
 8752|      0|    components.search_start -= difference;
 8753|      0|  }
 8754|     40|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8754:7): [True: 0, False: 40]
  ------------------
 8755|      0|    components.hash_start -= difference;
 8756|      0|  }
 8757|     40|  ada_log("url_aggregator::clear_pathname completed, running checks...");
 8758|       |#if ADA_DEVELOPMENT_CHECKS
 8759|       |  ADA_ASSERT_EQUAL(get_pathname(), "",
 8760|       |                   "pathname should have been cleared on buffer=" + buffer +
 8761|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8762|       |#endif
 8763|     40|  ADA_ASSERT_TRUE(validate());
 8764|     40|  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
 8765|     40|}
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8077|    804|                                              const uint8_t character_set[]) {
 8078|    804|  const char* data = input.data();
 8079|    804|  const size_t size = input.size();
 8080|       |
 8081|       |  // Process 8 bytes at a time using unrolled loop
 8082|    804|  size_t i = 0;
 8083|  2.51k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (8083:10): [True: 1.87k, False: 642]
  ------------------
 8084|  1.87k|    unsigned char chunk[8];
 8085|  1.87k|    std::memcpy(&chunk, data + i,
 8086|  1.87k|                8);  // entices compiler to unconditionally process 8 characters
 8087|       |
 8088|       |    // Check 8 characters at once
 8089|  15.6k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (8089:24): [True: 13.9k, False: 1.71k]
  ------------------
 8090|  13.9k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (8090:11): [True: 162, False: 13.8k]
  ------------------
 8091|    162|        return i + j;
 8092|    162|      }
 8093|  13.9k|    }
 8094|  1.87k|  }
 8095|       |
 8096|       |  // Handle remaining bytes
 8097|  1.60k|  for (; i < size; i++) {
  ------------------
  |  Branch (8097:10): [True: 1.11k, False: 490]
  ------------------
 8098|  1.11k|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (8098:9): [True: 152, False: 966]
  ------------------
 8099|    152|      return i;
 8100|    152|    }
 8101|  1.11k|  }
 8102|       |
 8103|    490|  return size;
 8104|    642|}
_ZN3ada14url_aggregator10clear_portEv:
 8666|  1.77k|inline void url_aggregator::clear_port() {
 8667|  1.77k|  ada_log("url_aggregator::clear_port");
 8668|  1.77k|  ADA_ASSERT_TRUE(validate());
 8669|  1.77k|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (8669:7): [True: 1.77k, False: 0]
  ------------------
 8670|  1.77k|    return;
 8671|  1.77k|  }
 8672|      0|  uint32_t length = components.pathname_start - components.host_end;
 8673|      0|  buffer.erase(components.host_end, length);
 8674|      0|  components.pathname_start -= length;
 8675|      0|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8675:7): [True: 0, False: 0]
  ------------------
 8676|      0|    components.search_start -= length;
 8677|      0|  }
 8678|      0|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8678:7): [True: 0, False: 0]
  ------------------
 8679|      0|    components.hash_start -= length;
 8680|      0|  }
 8681|      0|  components.port = url_components::omitted;
 8682|      0|  ADA_ASSERT_TRUE(validate());
 8683|      0|}
_ZNK3ada14url_aggregator13has_authorityEv:
 8831|  47.0k|    const noexcept {
 8832|  47.0k|  ada_log("url_aggregator::has_authority");
 8833|       |  // Performance: instead of doing this potentially expensive check, we could
 8834|       |  // have a boolean in the struct.
 8835|  47.0k|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (8835:10): [True: 24.4k, False: 22.5k]
  ------------------
 8836|  24.4k|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (8836:10): [True: 24.4k, False: 0]
  ------------------
 8837|  24.4k|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (8837:10): [True: 24.4k, False: 0]
  ------------------
 8838|  47.0k|}
_ZNK3ada14url_aggregator12has_dash_dotEv:
 8911|  12.5k|[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
 8912|       |  // If url's host is null, url does not have an opaque path, url's path's size
 8913|       |  // is greater than 1, and url's path[0] is the empty string, then append
 8914|       |  // U+002F (/) followed by U+002E (.) to output.
 8915|  12.5k|  ada_log("url_aggregator::has_dash_dot");
 8916|       |#if ADA_DEVELOPMENT_CHECKS
 8917|       |  // If pathname_start and host_end are exactly two characters apart, then we
 8918|       |  // either have a one-digit port such as http://test.com:5?param=1 or else we
 8919|       |  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
 8920|       |  if (components.pathname_start == components.host_end + 2) {
 8921|       |    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
 8922|       |                     buffer[components.host_end + 1] == '.') ||
 8923|       |                    (buffer[components.host_end] == ':' &&
 8924|       |                     checkers::is_digit(buffer[components.host_end + 1])));
 8925|       |  }
 8926|       |  if (components.pathname_start == components.host_end + 2 &&
 8927|       |      buffer[components.host_end] == '/' &&
 8928|       |      buffer[components.host_end + 1] == '.') {
 8929|       |    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
 8930|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
 8931|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
 8932|       |  }
 8933|       |#endif
 8934|       |  // Performance: it should be uncommon for components.pathname_start ==
 8935|       |  // components.host_end + 2 to be true. So we put this check first in the
 8936|       |  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
 8937|       |  // more expensive, but should be uncommon.
 8938|  12.5k|  return components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8938:10): [True: 419, False: 12.1k]
  ------------------
 8939|    419|         !has_opaque_path && buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (8939:10): [True: 419, False: 0]
  |  Branch (8939:30): [True: 15, False: 404]
  ------------------
 8940|     15|         buffer[components.host_end + 1] == '.';
  ------------------
  |  Branch (8940:10): [True: 15, False: 0]
  ------------------
 8941|  12.5k|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 3972|  31.7k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 3941|  18.1k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 3942|  18.1k|    TL_ASSERT(has_value());
  ------------------
  |  | 1994|  18.1k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3942:5): [True: 18.1k, False: 0]
  ------------------
 3943|  18.1k|    return valptr();
 3944|  18.1k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3237|  18.1k|  T* valptr() { return std::addressof(this->m_val); }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3954|  3.24k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 3955|  3.24k|    TL_ASSERT(has_value());
  ------------------
  |  | 1994|  3.24k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (3955:5): [True: 3.24k, False: 0]
  ------------------
 3956|  3.24k|    return val();
 3957|  3.24k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3246|  3.24k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3247|  3.24k|    return this->m_val;
 3248|  3.24k|  }
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 3971|  63.9k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2569|  35.5k|  ~expected_storage_base() {
 2570|  35.5k|    if (m_has_val) {
  ------------------
  |  Branch (2570:9): [True: 16.1k, False: 19.3k]
  ------------------
 2571|  16.1k|      m_val.~T();
 2572|  16.1k|    }
 2573|  35.5k|  }
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1788|  34.6k|                                                       size_t pos2) {
 1789|       |#if ADA_DEVELOPMENT_CHECKS
 1790|       |  if (pos2 < pos1) {
 1791|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1792|       |              << std::endl;
 1793|       |    abort();
 1794|       |  }
 1795|       |#endif
 1796|  34.6k|  return input.substr(pos1, pos2 - pos1);
 1797|  34.6k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8260|  4.61k|    const noexcept {
 8261|  4.61k|  return buffer.size() == components.pathname_start;
 8262|  4.61k|}
_ZN3ada8checkers8is_digitEc:
 1220|  31.0k|constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
_ZNK3ada14url_aggregator12has_hostnameEv:
 8900|    340|constexpr bool url_aggregator::has_hostname() const noexcept {
 8901|    340|  return has_authority();
 8902|    340|}
_ZNK3ada14url_aggregator12has_passwordEv:
 8880|  5.24k|constexpr bool url_aggregator::has_password() const noexcept {
 8881|  5.24k|  ada_log("url_aggregator::has_password");
 8882|       |  // This function does not care about the length of the password
 8883|  5.24k|  return components.host_start > components.username_end &&
  ------------------
  |  Branch (8883:10): [True: 4.69k, False: 548]
  ------------------
 8884|  4.69k|         buffer[components.username_end] == ':';
  ------------------
  |  Branch (8884:10): [True: 4.69k, False: 0]
  ------------------
 8885|  5.24k|}
_ZNK3ada3url8get_hrefEv:
 7349|  12.9k|[[nodiscard]] ada_really_inline std::string url::get_href() const {
 7350|  12.9k|  if (is_special() && host.has_value() && username.empty() &&
  ------------------
  |  Branch (7350:7): [True: 10.1k, False: 2.77k]
  |  Branch (7350:23): [True: 10.1k, False: 0]
  |  Branch (7350:43): [True: 9.77k, False: 372]
  ------------------
 7351|  9.77k|      password.empty() && !port.has_value()) [[likely]] {
  ------------------
  |  Branch (7351:7): [True: 9.70k, False: 69]
  |  Branch (7351:27): [True: 8.89k, False: 813]
  ------------------
 7352|  8.89k|    const std::string_view scheme = ada::scheme::details::is_special_list[type];
 7353|  8.89k|    const size_t host_size = host->size();
 7354|  8.89k|    const size_t path_size = path.size();
 7355|  8.89k|    const size_t query_size = query.has_value() ? query->size() : 0;
  ------------------
  |  Branch (7355:31): [True: 386, False: 8.50k]
  ------------------
 7356|  8.89k|    const size_t hash_size = hash.has_value() ? hash->size() : 0;
  ------------------
  |  Branch (7356:30): [True: 255, False: 8.63k]
  ------------------
 7357|  8.89k|    const size_t total = scheme.size() + 3 + host_size + path_size +
 7358|  8.89k|                         (query.has_value() ? query_size + 1 : 0) +
  ------------------
  |  Branch (7358:27): [True: 386, False: 8.50k]
  ------------------
 7359|  8.89k|                         (hash.has_value() ? hash_size + 1 : 0);
  ------------------
  |  Branch (7359:27): [True: 255, False: 8.63k]
  ------------------
 7360|  8.89k|    std::string output(total, '\0');
 7361|  8.89k|    char* p = output.data();
 7362|  8.89k|    std::memcpy(p, scheme.data(), scheme.size());
 7363|  8.89k|    p += scheme.size();
 7364|  8.89k|    p[0] = ':';
 7365|  8.89k|    p[1] = '/';
 7366|  8.89k|    p[2] = '/';
 7367|  8.89k|    p += 3;
 7368|       |    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7369|  8.89k|    std::memcpy(p, host->data(), host_size);
 7370|  8.89k|    p += host_size;
 7371|  8.89k|    std::memcpy(p, path.data(), path_size);
 7372|  8.89k|    p += path_size;
 7373|  8.89k|    if (query.has_value()) {
  ------------------
  |  Branch (7373:9): [True: 386, False: 8.50k]
  ------------------
 7374|    386|      *p++ = '?';
 7375|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7376|    386|      std::memcpy(p, query->data(), query_size);
 7377|    386|      p += query_size;
 7378|    386|    }
 7379|  8.89k|    if (hash.has_value()) {
  ------------------
  |  Branch (7379:9): [True: 255, False: 8.63k]
  ------------------
 7380|    255|      *p++ = '#';
 7381|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7382|    255|      std::memcpy(p, hash->data(), hash_size);
 7383|    255|    }
 7384|  8.89k|    return output;
 7385|  8.89k|  }
 7386|       |
 7387|  4.02k|  std::string output;
 7388|  4.02k|  output.reserve(get_href_size());
 7389|       |
 7390|  4.02k|  if (is_special()) {
  ------------------
  |  Branch (7390:7): [True: 1.25k, False: 2.77k]
  ------------------
 7391|  1.25k|    output.append(ada::scheme::details::is_special_list[type]);
 7392|  1.25k|    output += ':';
 7393|  2.77k|  } else {
 7394|  2.77k|    output.append(non_special_scheme);
 7395|  2.77k|    output += ':';
 7396|  2.77k|  }
 7397|       |
 7398|  4.02k|  if (host.has_value()) {
  ------------------
  |  Branch (7398:7): [True: 1.73k, False: 2.29k]
  ------------------
 7399|  1.73k|    output += '/';
 7400|  1.73k|    output += '/';
 7401|  1.73k|    if (has_credentials()) {
  ------------------
  |  Branch (7401:9): [True: 494, False: 1.24k]
  ------------------
 7402|    494|      output.append(username);
 7403|    494|      if (!password.empty()) {
  ------------------
  |  Branch (7403:11): [True: 171, False: 323]
  ------------------
 7404|    171|        output += ':';
 7405|    171|        output.append(password);
 7406|    171|      }
 7407|    494|      output += '@';
 7408|    494|    }
 7409|  1.73k|    output.append(*host);
 7410|  1.73k|    if (port.has_value()) {
  ------------------
  |  Branch (7410:9): [True: 908, False: 828]
  ------------------
 7411|    908|      output += ':';
 7412|    908|      char port_buf[5];
 7413|    908|      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
 7414|    908|      (void)ec;
 7415|    908|      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
 7416|    908|    }
 7417|  2.29k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7417:14): [True: 1.35k, False: 943]
  |  Branch (7417:34): [True: 44, False: 1.30k]
  ------------------
 7418|       |    // If url's host is null, url does not have an opaque path, url's path's
 7419|       |    // size is greater than 1, and url's path[0] is the empty string, then
 7420|       |    // append U+002F (/) followed by U+002E (.) to output.
 7421|     44|    output += '/';
 7422|     44|    output += '.';
 7423|     44|  }
 7424|  4.02k|  output.append(path);
 7425|  4.02k|  if (query.has_value()) {
  ------------------
  |  Branch (7425:7): [True: 401, False: 3.62k]
  ------------------
 7426|    401|    output += '?';
 7427|    401|    output.append(*query);
 7428|    401|  }
 7429|  4.02k|  if (hash.has_value()) {
  ------------------
  |  Branch (7429:7): [True: 281, False: 3.74k]
  ------------------
 7430|    281|    output += '#';
 7431|    281|    output.append(*hash);
 7432|    281|  }
 7433|  4.02k|  return output;
 7434|  12.9k|}
_ZNK3ada14url_aggregator10has_searchEv:
 8809|  1.63k|[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
 8810|  1.63k|  ada_log("url_aggregator::has_search");
 8811|  1.63k|  return components.search_start != url_components::omitted;
 8812|  1.63k|}
_ZN3ada7helpers6concatIJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKcEEENS2_12basic_stringIcS5_NS2_9allocatorIcEEEEDpT_:
 1866|  19.0k|std::string concat(Args... args) {
 1867|  19.0k|  std::string answer;
 1868|  19.0k|  inner_concat(answer, args...);
 1869|  19.0k|  return answer;
 1870|  19.0k|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJPKcEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_DpT0_:
 1855|  19.0k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1856|  19.0k|  buffer.append(t);
 1857|  19.0k|  return inner_concat(buffer, args...);
 1858|  19.0k|}
_ZN3ada7helpers12inner_concatIPKcJEEEvRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEET_:
 1847|  19.0k|inline void inner_concat(std::string& buffer, T t) {
 1848|  19.0k|  buffer.append(t);
 1849|  19.0k|}
_ZN3ada3url10set_schemeEONSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 7331|  9.57k|inline void url::set_scheme(std::string&& new_scheme) noexcept {
 7332|  9.57k|  type = ada::scheme::get_scheme_type(new_scheme);
 7333|       |  // We only move the 'scheme' if it is non-special.
 7334|  9.57k|  if (!is_special()) {
  ------------------
  |  Branch (7334:7): [True: 4.67k, False: 4.89k]
  ------------------
 7335|  4.67k|    non_special_scheme = std::move(new_scheme);
 7336|  4.67k|  }
 7337|  9.57k|}
_ZN3ada7helpers6concatIJPKcNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEENS4_12basic_stringIcS7_NS4_9allocatorIcEEEEDpT_:
 1866|      2|std::string concat(Args... args) {
 1867|      2|  std::string answer;
 1868|      2|  inner_concat(answer, args...);
 1869|      2|  return answer;
 1870|      2|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEEvRNS4_12basic_stringIcS7_NS4_9allocatorIcEEEET_DpT0_:
 1855|      2|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1856|      2|  buffer.append(t);
 1857|      2|  return inner_concat(buffer, args...);
 1858|      2|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_:
 1847|      2|inline void inner_concat(std::string& buffer, T t) {
 1848|      2|  buffer.append(t);
 1849|      2|}

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

